From fe16c8a92f90fc1633bd7d3724e873f623525f1c Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
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 <alloca.h>
 #endif
-#include <dispatch/dispatch.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -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

