From fe16c8a92f90fc1633bd7d3724e873f623525f1c Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Sun, 8 Jun 2025 15:55:55 +0800 Subject: [PATCH 07/12] Revert breaking 76b2914, fixes lastfm Reverts https://github.com/DeaDBeeF-Player/deadbeef/commit/76b29141017645a97c4fbf140b2e00e84e0046e9 --- plugins/lastfm/Makefile.am | 4 +- plugins/lastfm/lastfm.c | 319 ++++++++++++++++++------------------- 2 files changed, 156 insertions(+), 167 deletions(-) diff --git a/plugins/lastfm/Makefile.am b/plugins/lastfm/Makefile.am index 78f294caa..49a970ea7 100644 --- a/plugins/lastfm/Makefile.am +++ b/plugins/lastfm/Makefile.am @@ -3,6 +3,6 @@ pkglib_LTLIBRARIES = lastfm.la lastfm_la_SOURCES = lastfm.c lastfm_la_LDFLAGS = -module -avoid-version -lastfm_la_LIBADD = $(LDADD) $(CURL_LIBS) $(DISPATCH_LIBS) -lastfm_la_CFLAGS = -std=c99 $(CURL_CFLAGS) $(DISPATCH_CFLAGS) -I@top_srcdir@/include +lastfm_la_LIBADD = $(LDADD) $(CURL_LIBS) +lastfm_la_CFLAGS = -std=c99 $(CURL_CFLAGS) -I@top_srcdir@/include endif diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c index 186110c1c..0b7efb4d9 100644 --- a/plugins/lastfm/lastfm.c +++ b/plugins/lastfm/lastfm.c @@ -1,6 +1,6 @@ /* Last.fm scrobbler plugin for DeaDBeeF Player - Copyright (C) 2009-2014 Oleksiy Yakovenko + Copyright (C) 2009-2014 Alexey Yakovenko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -23,7 +23,6 @@ #ifdef HAVE_ALLOCA_H #include #endif -#include #include #include #include @@ -41,15 +40,15 @@ static DB_misc_t plugin; static DB_functions_t *deadbeef; #define LFM_CLIENTID "ddb" -#define SCROBBLER_URL_LFM "https://post.audioscrobbler.com" -#define SCROBBLER_URL_LIBRE "https://turtle.libre.fm" +#define SCROBBLER_URL_LFM "http://post.audioscrobbler.com" +#define SCROBBLER_URL_LIBRE "http://turtle.libre.fm" #if defined(__MINGW32__) -#define LOOKUP_URL_FORMAT "cmd /c start https://www.last.fm/music/%s/_/%s" +#define LOOKUP_URL_FORMAT "cmd /c start http://www.last.fm/music/%s/_/%s" #elif defined(__APPLE__) -#define LOOKUP_URL_FORMAT "open 'https://www.last.fm/music/%s/_/%s' &" +#define LOOKUP_URL_FORMAT "open 'http://www.last.fm/music/%s/_/%s' &" #else -#define LOOKUP_URL_FORMAT "xdg-open 'https://www.last.fm/music/%s/_/%s' &" +#define LOOKUP_URL_FORMAT "xdg-open 'http://www.last.fm/music/%s/_/%s' &" #endif static char lfm_user[100]; @@ -61,10 +60,10 @@ static char lfm_sess[SESS_ID_MAX]; static char lfm_nowplaying_url[256]; static char lfm_submission_url[256]; -static int terminate; -static dispatch_queue_t sync_queue; -static dispatch_queue_t request_queue; - +static uintptr_t lfm_mutex; +static uintptr_t lfm_cond; +static int lfm_stopthread; +static intptr_t lfm_tid; #define META_FIELD_SIZE 200 @@ -79,17 +78,22 @@ static uint8_t lfm_reply[MAX_REPLY]; static size_t lfm_reply_sz; static char lfm_err[CURL_ERROR_SIZE]; -static void -lfm_submit (ddb_playItem_t *it, time_t started_timestamp, float playtime); +static char lfm_nowplaying[2048]; // packet for nowplaying, or "" +#define LFM_SUBMISSION_QUEUE_SIZE 50 -static void -lfm_send_nowplaying (const char *lfm_nowplaying); +typedef struct { + DB_playItem_t *it; + time_t started_timestamp; + float playtime; +} subm_item_t; + +static subm_item_t lfm_subm_queue[LFM_SUBMISSION_QUEUE_SIZE]; static void lfm_update_auth (void) { deadbeef->conf_lock (); - const char *user = deadbeef->conf_get_str_fast ("lastfm.secret.login", ""); - const char *pass = deadbeef->conf_get_str_fast ("lastfm.secret.password", ""); + const char *user = deadbeef->conf_get_str_fast ("lastfm.login", ""); + const char *pass = deadbeef->conf_get_str_fast ("lastfm.password", ""); if (strcmp (user, lfm_user) || strcmp (pass, lfm_pass)) { strcpy (lfm_user, user); strcpy (lfm_pass, pass); @@ -101,11 +105,7 @@ lfm_update_auth (void) { static size_t lastfm_curl_res (void *ptr, size_t size, size_t nmemb, void *stream) { - __block int need_cancel = 0; - dispatch_sync(sync_queue, ^{ - need_cancel = terminate; - }); - if (need_cancel) { + if (lfm_stopthread) { trace ("lfm: lastfm_curl_res: aborting current request\n"); return 0; } @@ -121,11 +121,7 @@ lastfm_curl_res (void *ptr, size_t size, size_t nmemb, void *stream) static int lfm_curl_control (void *stream, double dltotal, double dlnow, double ultotal, double ulnow) { - __block int need_cancel = 0; - dispatch_sync(sync_queue, ^{ - need_cancel = terminate; - }); - if (need_cancel) { + if (lfm_stopthread) { trace ("lfm: aborting current request\n"); return -1; } @@ -147,9 +143,6 @@ curl_req_send (const char *req, const char *post) { curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, lfm_curl_control); -#ifdef __MINGW32__ - curl_easy_setopt (curl, CURLOPT_CAINFO, getenv ("CURL_CA_BUNDLE")); -#endif char ua[100]; deadbeef->conf_get_str ("network.http_user_agent", "deadbeef", ua, sizeof (ua)); curl_easy_setopt (curl, CURLOPT_USERAGENT, ua); @@ -543,28 +536,16 @@ lastfm_songstarted (ddb_event_track_t *ev, uintptr_t data) { if (!deadbeef->conf_get_int ("lastfm.enable", 0)) { return 0; } + deadbeef->mutex_lock (lfm_mutex); + if (lfm_format_uri (-1, ev->track, lfm_nowplaying, sizeof (lfm_nowplaying), ev->started_timestamp, 120) < 0) { + lfm_nowplaying[0] = 0; + } +// trace ("%s\n", lfm_nowplaying); + deadbeef->mutex_unlock (lfm_mutex); + if (lfm_nowplaying[0]) { + deadbeef->cond_signal (lfm_cond); + } - ddb_playItem_t *it = ev->track; - time_t started_timestamp = ev->started_timestamp; - deadbeef->pl_item_ref (it); - dispatch_async(request_queue, ^{ - __block int need_cancel = 0; - dispatch_sync(sync_queue, ^{ - need_cancel = terminate; - }); - if (need_cancel) { - deadbeef->pl_item_unref (it); - return; - } - char lfm_nowplaying[2048]; - if (lfm_format_uri (-1, it, lfm_nowplaying, sizeof (lfm_nowplaying), started_timestamp, 120) > 0) { - // try to send nowplaying - if (lfm_nowplaying[0] && !deadbeef->conf_get_int ("lastfm.disable_np", 0)) { - lfm_send_nowplaying (lfm_nowplaying); - } - } - deadbeef->pl_item_unref (it); - }); return 0; } @@ -604,32 +585,36 @@ lastfm_songchanged (ddb_event_trackchange_t *ev, uintptr_t data) { trace ("lfm: not enough metadata for submission, artist=%s, title=%s, album=%s\n", deadbeef->pl_find_meta_with_override (ev->from, "artist"), deadbeef->pl_find_meta_with_override (ev->from, "title"), deadbeef->pl_find_meta_with_override (ev->from, "album")); return 0; } - - ddb_playItem_t *it = ev->from; - deadbeef->pl_item_ref (it); // unreffed by lfm_submit - time_t started_timestamp = ev->started_timestamp; - float playtime = ev->playtime; - - dispatch_async(request_queue, ^{ - lfm_submit (it, started_timestamp, playtime); - }); + deadbeef->mutex_lock (lfm_mutex); + // find free place in queue + for (int i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) { + if (!lfm_subm_queue[i].it) { + trace ("lfm: song is now in queue for submission\n"); + lfm_subm_queue[i].it = ev->from; + lfm_subm_queue[i].started_timestamp = ev->started_timestamp; + lfm_subm_queue[i].playtime = ev->playtime; + deadbeef->pl_item_ref (ev->from); + break; + } + } + deadbeef->mutex_unlock (lfm_mutex); + deadbeef->cond_signal (lfm_cond); return 0; } static void -lfm_send_nowplaying (const char *nowplaying_uri) { +lfm_send_nowplaying (void) { if (auth () < 0) { trace ("auth failed! nowplaying cancelled.\n"); + lfm_nowplaying[0] = 0; return; } trace ("auth successful! setting nowplaying\n"); char s[SESS_ID_MAX + 4]; snprintf (s, sizeof (s), "s=%s&", lfm_sess); - - char lfm_nowplaying[2048] = ""; - - snprintf (lfm_nowplaying, sizeof (lfm_nowplaying), "%s%s", nowplaying_uri, s); + size_t l = strlen (lfm_nowplaying); + strcpy (lfm_nowplaying+l, s); trace ("content:\n%s\n", lfm_nowplaying); #if !LFM_NOSEND for (int attempts = 2; attempts > 0; attempts--) { @@ -647,7 +632,7 @@ lfm_send_nowplaying (const char *nowplaying_uri) { } trace ("success! retrying send nowplaying...\n"); snprintf (s, sizeof (s), "s=%s&", lfm_sess); - snprintf (lfm_nowplaying, sizeof (lfm_nowplaying), "%s%s", nowplaying_uri, s); + strcpy (lfm_nowplaying+l, s); continue; // retry with new session } } @@ -659,35 +644,40 @@ lfm_send_nowplaying (const char *nowplaying_uri) { break; } #endif + lfm_nowplaying[0] = 0; } static void -lfm_send_submission (ddb_playItem_t *it, time_t started_timestamp, float playtime) { - trace ("lfm_send_submission\n"); +lfm_send_submissions (void) { + trace ("lfm_send_submissions\n"); + int i; char req[1024*50]; int idx = 0; char *r = req; int len = sizeof (req); - int res = lfm_format_uri (idx, it, r, len, started_timestamp, playtime); - if (res < 0) { - trace ("lfm: failed to format uri\n"); - deadbeef->pl_item_unref (it); - return; + int res; + deadbeef->mutex_lock (lfm_mutex); + for (i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) { + if (lfm_subm_queue[i].it) { + res = lfm_format_uri (idx, lfm_subm_queue[i].it, r, len, lfm_subm_queue[i].started_timestamp, lfm_subm_queue[i].playtime); + if (res < 0) { + trace ("lfm: failed to format uri\n"); + return; + } + len -= res; + r += res; + idx++; + } } - len -= res; - r += res; - idx++; + deadbeef->mutex_unlock (lfm_mutex); if (!idx) { - deadbeef->pl_item_unref (it); return; } if (auth () < 0) { - deadbeef->pl_item_unref (it); return; } res = snprintf (r, len, "s=%s&", lfm_sess); if (res > len) { - deadbeef->pl_item_unref (it); return; } trace ("submission req string:\n%s\n", req); @@ -710,34 +700,66 @@ lfm_send_submission (ddb_playItem_t *it, time_t started_timestamp, float playtim continue; // retry with new session } } + else { + trace ("submission successful, response:\n%s\n", lfm_reply); + deadbeef->mutex_lock (lfm_mutex); + for (i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) { + if (lfm_subm_queue[i].it) { + deadbeef->pl_item_unref (lfm_subm_queue[i].it); + lfm_subm_queue[i].it = NULL; + lfm_subm_queue[i].started_timestamp = 0; + } + } + deadbeef->mutex_unlock (lfm_mutex); + } } curl_req_cleanup (); break; } - deadbeef->pl_item_unref (it); #else trace ("submission successful (NOSEND=1):\n"); - deadbeef->pl_item_unref (it); + deadbeef->mutex_lock (lfm_mutex); + for (i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) { + if (lfm_subm_queue[i].it) { + deadbeef->pl_item_unref (lfm_subm_queue[i].it); + lfm_subm_queue[i].it = NULL; + lfm_subm_queue[i].started_timestamp = 0; + + } + } + deadbeef->mutex_unlock (lfm_mutex); #endif } static void -lfm_submit (ddb_playItem_t *it, time_t started_timestamp, float playtime) { - __block int need_cancel = 0; - dispatch_sync(sync_queue, ^{ - need_cancel = terminate; - }); - if (need_cancel) { - deadbeef->pl_item_unref (it); - return; - } +lfm_thread (void *ctx) { + //trace ("lfm_thread started\n"); + for (;;) { + if (lfm_stopthread) { + deadbeef->mutex_unlock (lfm_mutex); + trace ("lfm_thread end\n"); + return; + } + trace ("lfm wating for cond...\n"); + deadbeef->cond_wait (lfm_cond, lfm_mutex); + if (lfm_stopthread) { + deadbeef->mutex_unlock (lfm_mutex); + trace ("lfm_thread end[2]\n"); + return; + } + trace ("cond signalled!\n"); + deadbeef->mutex_unlock (lfm_mutex); - if (!deadbeef->conf_get_int ("lastfm.enable", 0)) { - deadbeef->pl_item_unref (it); - return; + if (!deadbeef->conf_get_int ("lastfm.enable", 0)) { + continue; + } + trace ("lfm sending nowplaying...\n"); + lfm_send_submissions (); + // try to send nowplaying + if (lfm_nowplaying[0] && !deadbeef->conf_get_int ("lastfm.disable_np", 0)) { + lfm_send_nowplaying (); + } } - trace ("lfm sending submissions...\n"); - lfm_send_submission (it, started_timestamp, playtime); } static int @@ -749,49 +771,19 @@ lfm_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { case DB_EV_SONGCHANGED: lastfm_songchanged ((ddb_event_trackchange_t *)ctx, 0); break; - case DB_EV_CONFIGCHANGED: - if (deadbeef->conf_get_int ("lastfm.trace", 0)) { - plugin.plugin.flags |= DDB_PLUGIN_FLAG_LOGGING; - } - else { - plugin.plugin.flags &= ~DDB_PLUGIN_FLAG_LOGGING; - } - break; } return 0; } -static void -_migrate_secrets(void) { - const char *user = deadbeef->conf_get_str_fast ("lastfm.secret.login", NULL); - const char *pass = deadbeef->conf_get_str_fast ("lastfm.secret.password", NULL); - - if (user == NULL) { - user = deadbeef->conf_get_str_fast ("lastfm.login", NULL); - if (user != NULL) { - deadbeef->conf_set_str("lastfm.secret.login", user); - deadbeef->conf_remove_items("lastfm.login"); - } - } - - if (pass == NULL) { - pass = deadbeef->conf_get_str_fast ("lastfm.password", NULL); - if (pass != NULL) { - deadbeef->conf_set_str("lastfm.secret.password", pass); - deadbeef->conf_remove_items("lastfm.password"); - } - } - - deadbeef->conf_save(); -} - static int lastfm_start (void) { - terminate = 0; - request_queue = dispatch_queue_create("LastfmRequestQueue", NULL); - sync_queue = dispatch_queue_create("LastfmSyncQueue", NULL); - - _migrate_secrets(); + if (lfm_mutex) { + return -1; + } + lfm_stopthread = 0; + lfm_mutex = deadbeef->mutex_create_nonrecursive (); + lfm_cond = deadbeef->cond_create (); + lfm_tid = deadbeef->thread_start (lfm_thread, NULL); return 0; } @@ -799,14 +791,17 @@ lastfm_start (void) { static int lastfm_stop (void) { trace ("lastfm_stop\n"); - dispatch_sync(sync_queue, ^{ - terminate = 1; // prevent any new items from being scheduled - }); - - dispatch_sync(request_queue, ^{ - }); - dispatch_release(request_queue); - dispatch_release(sync_queue); + if (lfm_mutex) { + lfm_stopthread = 1; + + trace ("lfm_stop signalling cond\n"); + deadbeef->cond_signal (lfm_cond); + trace ("waiting for thread to finish\n"); + deadbeef->thread_join (lfm_tid); + lfm_tid = 0; + deadbeef->cond_free (lfm_cond); + deadbeef->mutex_free (lfm_mutex); + } return 0; } @@ -817,31 +812,25 @@ lfm_action_lookup (DB_plugin_action_t *action, ddb_action_context_t ctx) { char artist[META_FIELD_SIZE]; char title[META_FIELD_SIZE]; - ddb_playlist_t *plt = NULL; if (ctx == DDB_ACTION_CTX_SELECTION) { // find first selected - plt = deadbeef->plt_get_curr (); - } - else if (ctx == DDB_ACTION_CTX_PLAYLIST) { - plt = deadbeef->action_get_playlist(); - } - else if (ctx == DDB_ACTION_CTX_NOWPLAYING) { - it = deadbeef->streamer_get_playing_track_safe (); - } - - if (plt) { - it = deadbeef->plt_get_first (plt, PL_MAIN); - while (it) { - if (deadbeef->pl_is_selected (it)) { - break; + ddb_playlist_t *plt = deadbeef->plt_get_curr (); + if (plt) { + it = deadbeef->plt_get_first (plt, PL_MAIN); + while (it) { + if (deadbeef->pl_is_selected (it)) { + break; + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; } - DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); - deadbeef->pl_item_unref (it); - it = next; + deadbeef->plt_unref (plt); } - deadbeef->plt_unref (plt); } - + else if (ctx == DDB_ACTION_CTX_NOWPLAYING) { + it = deadbeef->streamer_get_playing_track (); + } if (!it) { goto out; } @@ -882,7 +871,7 @@ out: } static DB_plugin_action_t lookup_action = { - .title = "Lookup on Last.fm", + .title = "Lookup On Last.fm", .name = "lfm_lookup", .flags = DB_ACTION_SINGLE_TRACK | DB_ACTION_ADD_MENU, .callback2 = lfm_action_lookup, @@ -910,13 +899,12 @@ lfm_get_actions (DB_playItem_t *it) static const char settings_dlg[] = "property \"Enable scrobbler\" checkbox lastfm.enable 0;" "property \"Disable nowplaying\" checkbox lastfm.disable_np 0;" - "property Username entry lastfm.secret.login \"\";\n" - "property Password password lastfm.secret.password \"\";" + "property Username entry lastfm.login \"\";\n" + "property Password password lastfm.password \"\";" "property \"Scrobble URL\" entry lastfm.scrobbler_url \""SCROBBLER_URL_LFM"\";" "property \"Prefer Album Artist over Artist field\" checkbox lastfm.prefer_album_artist 0;" "property \"Send MusicBrainz ID\" checkbox lastfm.mbid 0;" "property \"Submit tracks shorter than 30 seconds (not recommended)\" checkbox lastfm.submit_tiny_tracks 0;" - "property \"Enable logging\" checkbox lastfm.trace 0;\n"; ; // define plugin interface @@ -925,11 +913,12 @@ static DB_misc_t plugin = { .plugin.version_major = 1, .plugin.version_minor = 0, .plugin.type = DB_PLUGIN_MISC, +// .plugin.flags = DDB_PLUGIN_FLAG_LOGGING, .plugin.name = "last.fm scrobbler", .plugin.descr = "Sends played songs information to your last.fm account, or other service that use AudioScrobbler protocol", .plugin.copyright = "Last.fm scrobbler plugin for DeaDBeeF Player\n" - "Copyright (C) 2009-2014 Oleksiy Yakovenko\n" + "Copyright (C) 2009-2014 Alexey Yakovenko\n" "\n" "This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public License\n" -- 2.49.0