[vlc-devel] [PATCH 2/3] Audioscrobbler: Update the scrobbling protocol to the version 2.0.

samuel.pitoiset at gmail.com samuel.pitoiset at gmail.com
Sun Mar 11 12:05:02 CET 2012


From: Samuel Pitoiset <samuel.pitoiset at gmail.com>

---
 modules/misc/audioscrobbler.c |  268 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 268 insertions(+)

diff --git a/modules/misc/audioscrobbler.c b/modules/misc/audioscrobbler.c
index 0da533e..28d5a80 100644
--- a/modules/misc/audioscrobbler.c
+++ b/modules/misc/audioscrobbler.c
@@ -1154,6 +1154,274 @@ static void HandleInterval(mtime_t *next, unsigned int *i_interval)
 }
 
 /*****************************************************************************
+ * Scrobble : scrobble songs
+ *****************************************************************************/
+static int Scrobble(intf_thread_t *p_this)
+{
+    intf_sys_t *p_sys = p_this->p_sys;
+    xml_reader_t *p_xml_reader = NULL;
+    vlc_array_t p_params;
+    stream_t *p_stream;
+    uint8_t p_buf[1024];
+    char *psz_request;
+    int i_song;
+
+    vlc_array_init(&p_params);
+
+    vlc_mutex_lock(&p_sys->lock);
+    for (i_song = 0 ; i_song < p_sys->i_songs ; i_song++)
+    {
+        audioscrobbler_song_t *p_song = &p_sys->p_queue[i_song];
+        char psz_key[1024], psz_val[1024];
+
+        /* album */
+        sprintf(psz_key, "album[%d]", i_song);
+        snprintf(psz_val, 1024, "%s", p_song->psz_b);
+        AddParam(&p_params, strdup(psz_key), strdup(psz_val));
+
+        /* artist */
+        sprintf(psz_key, "artist[%d]", i_song);
+        snprintf(psz_val, 1024, "%s", p_song->psz_a);
+        AddParam(&p_params, strdup(psz_key), strdup(psz_val));
+
+        /* track */
+        sprintf(psz_key, "track[%d]", i_song);
+        snprintf(psz_val, 1024, "%s", p_song->psz_t);
+        AddParam(&p_params, strdup(psz_key), strdup(psz_val));
+
+        /* timestamp */
+        sprintf(psz_key, "timestamp[%d]", i_song);
+        sprintf(psz_val, "%u", (unsigned)p_song->date);
+        AddParam(&p_params, strdup(psz_key), strdup(psz_val));
+
+        /* duration */
+        sprintf(psz_key, "duration[%d]", i_song);
+        sprintf(psz_val, "%d", p_song->i_l);
+        AddParam(&p_params, strdup(psz_key), strdup(psz_val));
+
+        /* track number */
+        sprintf(psz_key, "trackNumber[%d]", i_song);
+        snprintf(psz_val, 1024, "%s", p_song->psz_n);
+        AddParam(&p_params, strdup(psz_key), strdup(psz_val));
+
+        /* MusicBrainz track ID */
+        sprintf(psz_key, "mbid[%d]", i_song);
+        snprintf(psz_val, 1024, "%s", p_song->psz_m);
+        AddParam(&p_params, strdup(psz_key), strdup(psz_val));
+    }
+    vlc_mutex_unlock(&p_sys->lock);
+
+    /* add the method name to the parameters list */
+    AddParam(&p_params, "method", "track.scrobble");
+
+    /* build the HTTP request */
+    psz_request = BuildSignedRequest(p_this, &p_params, POST);
+    vlc_array_clear(&p_params);
+
+    if (!psz_request)
+        return VLC_ENOMEM;
+
+    /* initialize the connection */
+    int i_sock = net_ConnectTCP(p_this, p_sys->p_submit_url.psz_host, 80);
+    if (i_sock == -1)
+    {
+        free(psz_request);
+        return VLC_EGENERIC;
+    }
+
+    /* transmit the request */
+    int i_net_ret = net_Printf(p_this, i_sock, NULL, "%s", psz_request);
+    free(psz_request);
+
+    if (i_net_ret == -1)
+        goto proto;
+
+    /* read the answer */
+    int i_read = net_Read(p_this, i_sock, NULL, p_buf, sizeof(p_buf) - 1, false);
+    if (i_read <= 0)
+        goto proto;
+
+    /* close the connection */
+    net_Close(i_sock);
+    p_buf[i_read] = '\0';
+
+    /* remove answer headers */
+    char *psz_xml = strstr((const char*) p_buf, "<?xml");
+    if (!psz_xml)
+        goto proto;
+
+    /* create a stream reading answer */
+    p_stream = stream_MemoryNew(p_this, (uint8_t*) psz_xml, strlen(psz_xml), true);
+    if (!p_stream)
+        goto proto;
+
+    /* create an XML reader */
+    p_xml_reader = xml_ReaderCreate(p_this, p_stream);
+    if (!p_xml_reader)
+        goto proto;
+
+    /* check root node */
+    const char *node;
+    if (xml_ReaderNextNode(p_xml_reader, &node) != XML_READER_STARTELEM)
+    {
+        msg_Err(p_this, "invalid file (no root node)");
+        goto proto;
+    }
+
+    if (strcasecmp(node, "lfm"))
+    {
+        msg_Err(p_this, "invalid root node <%s>", node);
+        goto proto;
+    }
+
+    /* check response status */
+    const char *attr, *value;
+    if ((attr = xml_ReaderNextAttr(p_xml_reader, &value)))
+    {
+        if (strcasecmp(attr, "status"))
+        {
+            msg_Err(p_this, "invalid attribute \"%s\"", attr);
+            goto proto;
+        }
+
+        if (strcasecmp(value, "ok"))
+        {
+            /* an error occured */
+            if (!strcasecmp(value, "4"))
+            {
+                /* authentication failed, bad username/password combination */
+                msg_Err(p_this, "last.fm username or password is incorrect.");
+            }
+
+            if (!strcasecmp(value, "10"))
+            {
+                /* invalid API key */
+                msg_Err(p_this, "You must be granted a valid key by last.fm");
+            }
+
+            if (!strcasecmp(value, "26"))
+            {
+                /* suspend API key */
+                msg_Err(p_this, "Access for your account has been suspended.");
+            }
+
+            goto proto;
+        }
+    }
+
+    stream_Delete(p_stream);
+    xml_ReaderDelete(p_xml_reader);
+
+    return VLC_SUCCESS;
+
+proto:
+    net_Close(i_sock);
+    if (p_stream)
+        stream_Delete(p_stream);
+    if (p_xml_reader)
+        xml_ReaderDelete(p_xml_reader);
+
+    return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Run : call Handshake() then submit songs
+ *****************************************************************************/
+static void NewRun(intf_thread_t *p_this)
+{
+    int                     canc = vlc_savecancel();
+    bool                    b_handshaked = false;
+
+    /* data about audioscrobbler session */
+    mtime_t                 next_exchange;      /**< when can we send data  */
+    unsigned int            i_interval;         /**< waiting interval (secs)*/
+
+    intf_sys_t *p_sys = p_this->p_sys;
+
+    /* main loop */
+    for (;;)
+    {
+        vlc_restorecancel(canc);
+        vlc_mutex_lock(&p_sys->lock);
+        mutex_cleanup_push(&p_sys->lock);
+
+        do
+            vlc_cond_wait(&p_sys->wait, &p_sys->lock);
+        while (mdate() < next_exchange);
+
+        vlc_cleanup_run();
+        canc = vlc_savecancel();
+
+        /* handshake if needed */
+        if (!b_handshaked)
+        {
+            msg_Dbg(p_this, "Handshaking with last.fm ...");
+
+            switch(Handshake(p_this))
+            {
+                case VLC_ENOMEM:
+                    return;
+
+                case VLC_ENOVAR:
+                    /* username not set */
+                    dialog_Fatal(p_this,
+                        _("Last.fm username not set"),
+                        "%s", _("Please set a username or disable the "
+                        "audioscrobbler plugin, and restart VLC.\n"
+                        "Visit http://www.last.fm/join/ to get an account.")
+                   );
+                    return;
+
+                case VLC_SUCCESS:
+                    msg_Dbg(p_this, "Handshake successfull :)");
+                    b_handshaked = true;
+                    i_interval = 0;
+                    next_exchange = mdate();
+                    break;
+
+                case VLC_AUDIOSCROBBLER_EFATAL:
+                    msg_Warn(p_this, "Exiting...");
+                    return;
+
+                case VLC_EGENERIC:
+                default:
+                    /* protocol error : we'll try later */
+                    HandleInterval(&next_exchange, &i_interval);
+                    break;
+            }
+            /* if handshake failed let's restart the loop */
+            if (!b_handshaked)
+                continue;
+        }
+
+        msg_Dbg(p_this, "Going to submit some data...");
+
+        switch (Scrobble(p_this))
+        {
+            case VLC_SUCCESS:
+                /* submission successful */
+                for (int i = 0; i < p_sys->i_songs; i++)
+                    DeleteSong(&p_sys->p_queue[i]);
+                p_sys->i_songs = 0;
+                i_interval = 0;
+                next_exchange = mdate();
+                msg_Dbg(p_this, "Submission successful!");
+                break;
+            case VLC_EGENERIC:
+                /* protocol error : we'll try later */
+                HandleInterval(&next_exchange, &i_interval);
+                b_handshaked = false;
+                break;
+            case VLC_ENOMEM:
+            default:
+                return;
+                break;
+        }
+    }
+    vlc_restorecancel(canc);
+}
+
+/*****************************************************************************
  * Run : call Handshake() then submit songs
  *****************************************************************************/
 static void Run(intf_thread_t *p_intf)
-- 
1.7.9.3




More information about the vlc-devel mailing list