1+ package org .telegram .messenger ;
2+
3+ import android .content .Context ;
4+ import android .content .SharedPreferences ;
5+
6+ import java .security .MessageDigest ;
7+ import java .util .HashMap ;
8+ import java .util .Map ;
9+ import java .util .TreeMap ;
10+
11+ public class LastFmHelper {
12+
13+ private static LastFmHelper instance ;
14+ private SharedPreferences prefs ;
15+
16+ public static LastFmHelper getInstance () {
17+ if (instance == null ) {
18+ instance = new LastFmHelper ();
19+ }
20+ return instance ;
21+ }
22+
23+ private LastFmHelper () {
24+ if (ApplicationLoader .applicationContext != null ) {
25+ prefs = ApplicationLoader .applicationContext .getSharedPreferences ("lastfm" , Context .MODE_PRIVATE );
26+ }
27+ }
28+
29+ public boolean isLoggedIn () {
30+ return prefs != null && prefs .getBoolean ("logged_in" , false );
31+ }
32+
33+ public String getUsername () {
34+ return prefs != null ? prefs .getString ("username" , "" ) : "" ;
35+ }
36+
37+ public void logout () {
38+ if (prefs != null ) {
39+ prefs .edit ().clear ().apply ();
40+ }
41+ }
42+
43+ public void login (String username , String password , LoginCallback callback ) {
44+ Utilities .globalQueue .postRunnable (() -> {
45+ try {
46+ // Шаг 1: Получаем токен
47+ String token = getToken ();
48+ if (token == null ) {
49+ AndroidUtilities .runOnUIThread (() -> callback .onError ("Failed to get token" ));
50+ return ;
51+ }
52+
53+ // Шаг 2: Получаем сессию
54+ String sessionKey = getSession (username , password , token );
55+ if (sessionKey != null ) {
56+ prefs .edit ()
57+ .putBoolean ("logged_in" , true )
58+ .putString ("username" , username )
59+ .putString ("session_key" , sessionKey )
60+ .apply ();
61+ AndroidUtilities .runOnUIThread (() -> callback .onSuccess ());
62+ } else {
63+ AndroidUtilities .runOnUIThread (() -> callback .onError ("Invalid credentials" ));
64+ }
65+ } catch (Exception e ) {
66+ FileLog .e (e );
67+ AndroidUtilities .runOnUIThread (() -> callback .onError (e .getMessage ()));
68+ }
69+ });
70+ }
71+
72+ public interface LoginCallback {
73+ void onSuccess ();
74+ void onError (String error );
75+ }
76+
77+ public void scrobbleTrack (String artist , String track , String album , long timestamp ) {
78+ if (!isLoggedIn () || artist == null || track == null ) {
79+ return ;
80+ }
81+
82+ Map <String , String > params = new HashMap <>();
83+ params .put ("method" , "track.scrobble" );
84+ params .put ("api_key" , BuildVars .LASTFM_API_KEY );
85+ params .put ("artist" , artist );
86+ params .put ("track" , track );
87+ if (album != null ) {
88+ params .put ("album" , album );
89+ }
90+ params .put ("timestamp" , String .valueOf (timestamp ));
91+ params .put ("sk" , getSessionKey ());
92+ params .put ("api_sig" , generateApiSignature (params ));
93+
94+ // Отправляем запрос в фоновом потоке
95+ Utilities .globalQueue .postRunnable (() -> {
96+ try {
97+ sendPostRequestVoid (params );
98+ if (BuildVars .LOGS_ENABLED ) {
99+ FileLog .d ("Last.fm scrobbled: " + artist + " - " + track );
100+ }
101+ } catch (Exception e ) {
102+ FileLog .e (e );
103+ }
104+ });
105+ }
106+
107+ public void updateNowPlaying (String artist , String track , String album ) {
108+ if (!isLoggedIn () || artist == null || track == null ) {
109+ return ;
110+ }
111+
112+ Map <String , String > params = new HashMap <>();
113+ params .put ("method" , "track.updateNowPlaying" );
114+ params .put ("api_key" , BuildVars .LASTFM_API_KEY );
115+ params .put ("artist" , artist );
116+ params .put ("track" , track );
117+ if (album != null ) {
118+ params .put ("album" , album );
119+ }
120+ params .put ("sk" , getSessionKey ());
121+ params .put ("api_sig" , generateApiSignature (params ));
122+
123+ // Отправляем запрос в фоновом потоке
124+ Utilities .globalQueue .postRunnable (() -> {
125+ try {
126+ sendPostRequestVoid (params );
127+ if (BuildVars .LOGS_ENABLED ) {
128+ FileLog .d ("Last.fm now playing: " + artist + " - " + track );
129+ }
130+ } catch (Exception e ) {
131+ FileLog .e (e );
132+ }
133+ });
134+ }
135+
136+ private String getSessionKey () {
137+ return prefs != null ? prefs .getString ("session_key" , "" ) : "" ;
138+ }
139+
140+ public String generateApiSignature (Map <String , String > params ) {
141+ TreeMap <String , String > sortedParams = new TreeMap <>(params );
142+ StringBuilder signature = new StringBuilder ();
143+
144+ for (Map .Entry <String , String > entry : sortedParams .entrySet ()) {
145+ signature .append (entry .getKey ()).append (entry .getValue ());
146+ }
147+ signature .append (BuildVars .LASTFM_API_SECRET );
148+
149+ return md5 (signature .toString ());
150+ }
151+
152+ private String md5 (String input ) {
153+ try {
154+ MessageDigest md = MessageDigest .getInstance ("MD5" );
155+ byte [] messageDigest = md .digest (input .getBytes ());
156+ StringBuilder hexString = new StringBuilder ();
157+ for (byte b : messageDigest ) {
158+ String hex = Integer .toHexString (0xff & b );
159+ if (hex .length () == 1 ) {
160+ hexString .append ('0' );
161+ }
162+ hexString .append (hex );
163+ }
164+ return hexString .toString ();
165+ } catch (Exception e ) {
166+ return "" ;
167+ }
168+ }
169+
170+ private String getToken () {
171+ try {
172+ Map <String , String > params = new HashMap <>();
173+ params .put ("method" , "auth.getToken" );
174+ params .put ("api_key" , BuildVars .LASTFM_API_KEY );
175+ params .put ("api_sig" , generateApiSignature (params ));
176+
177+ String response = sendGetRequest (params );
178+ // Парсим XML ответ для получения токена
179+ // Упрощенная реализация - в реальном проекте нужен XML парсер
180+ if (response != null && response .contains ("<token>" )) {
181+ int start = response .indexOf ("<token>" ) + 7 ;
182+ int end = response .indexOf ("</token>" );
183+ if (start > 6 && end > start ) {
184+ return response .substring (start , end );
185+ }
186+ }
187+ } catch (Exception e ) {
188+ FileLog .e (e );
189+ }
190+ return null ;
191+ }
192+
193+ private String getSession (String username , String password , String token ) {
194+ try {
195+ Map <String , String > params = new HashMap <>();
196+ params .put ("method" , "auth.getMobileSession" );
197+ params .put ("api_key" , BuildVars .LASTFM_API_KEY );
198+ params .put ("username" , username );
199+ params .put ("password" , password );
200+ params .put ("api_sig" , generateApiSignature (params ));
201+
202+ String response = sendPostRequest (params );
203+ // Парсим XML ответ для получения ключа сессии
204+ if (response != null && response .contains ("<key>" )) {
205+ int start = response .indexOf ("<key>" ) + 5 ;
206+ int end = response .indexOf ("</key>" );
207+ if (start > 4 && end > start ) {
208+ return response .substring (start , end );
209+ }
210+ }
211+ } catch (Exception e ) {
212+ FileLog .e (e );
213+ }
214+ return null ;
215+ }
216+
217+ private String sendGetRequest (Map <String , String > params ) {
218+ try {
219+ StringBuilder urlBuilder = new StringBuilder (BuildVars .LASTFM_API_URL + "?" );
220+ for (Map .Entry <String , String > entry : params .entrySet ()) {
221+ if (urlBuilder .length () > BuildVars .LASTFM_API_URL .length () + 1 ) {
222+ urlBuilder .append ('&' );
223+ }
224+ urlBuilder .append (java .net .URLEncoder .encode (entry .getKey (), "UTF-8" ));
225+ urlBuilder .append ('=' );
226+ urlBuilder .append (java .net .URLEncoder .encode (entry .getValue (), "UTF-8" ));
227+ }
228+
229+ java .net .URL url = new java .net .URL (urlBuilder .toString ());
230+ java .net .HttpURLConnection connection = (java .net .HttpURLConnection ) url .openConnection ();
231+ connection .setRequestMethod ("GET" );
232+
233+ int responseCode = connection .getResponseCode ();
234+ if (responseCode == 200 ) {
235+ java .io .BufferedReader reader = new java .io .BufferedReader (
236+ new java .io .InputStreamReader (connection .getInputStream ()));
237+ StringBuilder response = new StringBuilder ();
238+ String line ;
239+ while ((line = reader .readLine ()) != null ) {
240+ response .append (line );
241+ }
242+ reader .close ();
243+ connection .disconnect ();
244+ return response .toString ();
245+ }
246+ connection .disconnect ();
247+ } catch (Exception e ) {
248+ FileLog .e (e );
249+ }
250+ return null ;
251+ }
252+
253+ private String sendPostRequest (Map <String , String > params ) {
254+ try {
255+ java .net .URL url = new java .net .URL (BuildVars .LASTFM_API_URL );
256+ java .net .HttpURLConnection connection = (java .net .HttpURLConnection ) url .openConnection ();
257+ connection .setRequestMethod ("POST" );
258+ connection .setDoOutput (true );
259+ connection .setRequestProperty ("Content-Type" , "application/x-www-form-urlencoded" );
260+
261+ StringBuilder postData = new StringBuilder ();
262+ for (Map .Entry <String , String > entry : params .entrySet ()) {
263+ if (postData .length () != 0 ) {
264+ postData .append ('&' );
265+ }
266+ postData .append (java .net .URLEncoder .encode (entry .getKey (), "UTF-8" ));
267+ postData .append ('=' );
268+ postData .append (java .net .URLEncoder .encode (entry .getValue (), "UTF-8" ));
269+ }
270+
271+ byte [] postDataBytes = postData .toString ().getBytes ("UTF-8" );
272+ connection .setRequestProperty ("Content-Length" , String .valueOf (postDataBytes .length ));
273+ connection .getOutputStream ().write (postDataBytes );
274+
275+ int responseCode = connection .getResponseCode ();
276+ if (responseCode == 200 ) {
277+ java .io .BufferedReader reader = new java .io .BufferedReader (
278+ new java .io .InputStreamReader (connection .getInputStream ()));
279+ StringBuilder response = new StringBuilder ();
280+ String line ;
281+ while ((line = reader .readLine ()) != null ) {
282+ response .append (line );
283+ }
284+ reader .close ();
285+ connection .disconnect ();
286+ return response .toString ();
287+ }
288+
289+ if (BuildVars .LOGS_ENABLED ) {
290+ FileLog .d ("Last.fm API response code: " + responseCode );
291+ }
292+
293+ connection .disconnect ();
294+ } catch (Exception e ) {
295+ FileLog .e (e );
296+ }
297+ return null ;
298+ }
299+
300+ private void sendPostRequestVoid (Map <String , String > params ) {
301+ try {
302+ java .net .URL url = new java .net .URL (BuildVars .LASTFM_API_URL );
303+ java .net .HttpURLConnection connection = (java .net .HttpURLConnection ) url .openConnection ();
304+ connection .setRequestMethod ("POST" );
305+ connection .setDoOutput (true );
306+ connection .setRequestProperty ("Content-Type" , "application/x-www-form-urlencoded" );
307+
308+ StringBuilder postData = new StringBuilder ();
309+ for (Map .Entry <String , String > entry : params .entrySet ()) {
310+ if (postData .length () != 0 ) {
311+ postData .append ('&' );
312+ }
313+ postData .append (java .net .URLEncoder .encode (entry .getKey (), "UTF-8" ));
314+ postData .append ('=' );
315+ postData .append (java .net .URLEncoder .encode (entry .getValue (), "UTF-8" ));
316+ }
317+
318+ byte [] postDataBytes = postData .toString ().getBytes ("UTF-8" );
319+ connection .setRequestProperty ("Content-Length" , String .valueOf (postDataBytes .length ));
320+ connection .getOutputStream ().write (postDataBytes );
321+
322+ int responseCode = connection .getResponseCode ();
323+ if (BuildVars .LOGS_ENABLED ) {
324+ FileLog .d ("Last.fm API response code: " + responseCode );
325+ }
326+
327+ connection .disconnect ();
328+ } catch (Exception e ) {
329+ FileLog .e (e );
330+ }
331+ }
332+ }
0 commit comments