[vlc-devel] [PATCH 2/3] Audioscrobbler: Update the scrobbling protocol to the version 2.0.
Samuel Pitoiset
samuel.pitoiset at gmail.com
Wed Mar 7 23:05:58 CET 2012
---
modules/misc/audioscrobbler.c | 268 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 268 insertions(+)
diff --git a/modules/misc/audioscrobbler.c b/modules/misc/audioscrobbler.c
index ddcc284..1672012 100644
--- a/modules/misc/audioscrobbler.c
+++ b/modules/misc/audioscrobbler.c
@@ -1181,6 +1181,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[1204], 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, strlen(p_song->psz_a) + 1, "%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, 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