diff options
author | Max Kellermann <max@duempel.org> | 2009-03-02 23:11:31 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2009-03-02 23:11:31 +0100 |
commit | 4220e6b0adccf2f09a82fb85768b7aecc5744b6c (patch) | |
tree | 55509fd1eb5dcbad42a7f0c3af7a19107a2e9647 | |
parent | 9cd5b0af74b61967164598382e5ef66774e766a8 (diff) | |
download | mpd-4220e6b0adccf2f09a82fb85768b7aecc5744b6c.tar.gz mpd-4220e6b0adccf2f09a82fb85768b7aecc5744b6c.tar.xz mpd-4220e6b0adccf2f09a82fb85768b7aecc5744b6c.zip |
input_lastfm: new input plugin for last.fm radio
The lastfm input plugin enables MPD to play lastfm:// URLs. This
plugin is not complete yet: it plays only the first song in the
last.fm playlist, and the playlist parser isn't even implemented
properly.
Diffstat (limited to '')
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | src/input/lastfm_input_plugin.c | 228 | ||||
-rw-r--r-- | src/input/lastfm_input_plugin.h | 24 | ||||
-rw-r--r-- | src/input_stream.c | 5 | ||||
-rw-r--r-- | src/ls.c | 3 |
7 files changed, 280 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index c0941967a..da0b87103 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,6 +71,7 @@ mpd_headers = \ src/input_stream.h \ src/input/file_input_plugin.h \ src/input/curl_input_plugin.h \ + src/input/lastfm_input_plugin.h \ src/input/mms_input_plugin.h \ src/icy_metadata.h \ src/client.h \ @@ -414,6 +415,10 @@ if HAVE_CURL INPUT_SRC += src/input/curl_input_plugin.c src/icy_metadata.c endif +if ENABLE_LASTFM +INPUT_SRC += src/input/lastfm_input_plugin.c +endif + if ENABLE_MMS INPUT_SRC += src/input/mms_input_plugin.c endif @@ -3,6 +3,7 @@ ver 0.15 - (200?/??/??) - parse Icy-Metadata - added support for the MMS protocol - hide HTTP password in playlist + - lastfm: new input plugin for last.fm radio * tags: - support the "album artist" tag - support MusicBrainz tags diff --git a/configure.ac b/configure.ac index 5f5f70a10..1e1669085 100644 --- a/configure.ac +++ b/configure.ac @@ -253,6 +253,20 @@ if test x$enable_curl = xyes; then fi AM_CONDITIONAL(HAVE_CURL, test x$enable_curl = xyes) +AC_ARG_ENABLE(lastfm, + AS_HELP_STRING([--enable-lastfm], + [enable support for last.fm radio (default: disable)]),, + [enable_lastfm=no]) + +if test x$enable_lastfm = xyes; then + if test x$enable_curl != xyes; then + AC_MSG_ERROR([Cannot enable last.fm radio without curl]) + fi + + AC_DEFINE(ENABLE_LASTFM, 1, [Define when last.fm radio is enabled]), +fi +AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes) + AC_ARG_ENABLE(mms, AS_HELP_STRING([--enable-mms], [enable the MMS protocol with libmms (default: disable)]),, diff --git a/src/input/lastfm_input_plugin.c b/src/input/lastfm_input_plugin.c new file mode 100644 index 000000000..2e592a9c8 --- /dev/null +++ b/src/input/lastfm_input_plugin.c @@ -0,0 +1,228 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann <max@duempel.org> + * This project's homepage is: http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "input/lastfm_input_plugin.h" +#include "input/curl_input_plugin.h" +#include "input_plugin.h" +#include "conf.h" + +#include <string.h> + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "input_lastfm" + +static const char *lastfm_user, *lastfm_password; + +static bool +lastfm_input_init(const struct config_param *param) +{ + lastfm_user = config_get_block_string(param, "user", NULL); + lastfm_password = config_get_block_string(param, "password", NULL); + + return lastfm_user != NULL && lastfm_password != NULL; +} + +static char * +lastfm_get(const char *url) +{ + struct input_stream input_stream; + bool success; + int ret; + char buffer[4096]; + size_t length = 0, nbytes; + + success = input_stream_open(&input_stream, url); + if (!success) + return NULL; + + while (!input_stream.ready) { + ret = input_stream_buffer(&input_stream); + if (ret < 0) { + input_stream_close(&input_stream); + return NULL; + } + } + + do { + nbytes = input_stream_read(&input_stream, buffer + length, + sizeof(buffer) - length); + if (nbytes == 0) { + if (input_stream_eof(&input_stream)) + break; + + /* I/O error */ + input_stream_close(&input_stream); + return NULL; + } + + length += nbytes; + } while (length < sizeof(buffer)); + + input_stream_close(&input_stream); + return g_strndup(buffer, length); +} + +static char * +lastfm_find(const char *response, const char *name) +{ + size_t name_length = strlen(name); + + while (true) { + const char *eol = strchr(response, '\n'); + if (eol == NULL) + return NULL; + + if (strncmp(response, name, name_length) == 0 && + response[name_length] == '=') { + response += name_length + 1; + return g_strndup(response, eol - response); + } + + response = eol + 1; + } +} + +static bool +lastfm_input_open(struct input_stream *is, const char *url) +{ + char *md5, *p, *q, *response, *session, *stream_url; + bool success; + + if (strncmp(url, "lastfm://", 9) != 0) + return false; + + /* handshake */ + +#if GLIB_CHECK_VERSION(2,16,0) + q = g_uri_escape_string(lastfm_user, NULL, false); +#else + q = g_strdup(lastfm_username); +#endif + +#if GLIB_CHECK_VERSION(2,16,0) + if (strlen(lastfm_password) != 32) + md5 = g_compute_checksum_for_string(G_CHECKSUM_MD5, + lastfm_password, + strlen(lastfm_password)); + else +#endif + md5 = g_strdup(lastfm_password); + + p = g_strconcat("http://ws.audioscrobbler.com/radio/handshake.php?" + "version=1.1.1&platform=linux&" + "username=", q, "&" + "passwordmd5=", md5, "&debug=0&partner=", NULL); + g_free(q); + g_free(md5); + + response = lastfm_get(p); + g_free(p); + if (response == NULL) + return false; + + /* extract session id from response */ + + session = lastfm_find(response, "session"); + stream_url = lastfm_find(response, "stream_url"); + g_free(response); + if (session == NULL || stream_url == NULL) { + g_free(session); + g_free(stream_url); + return false; + } + +#if GLIB_CHECK_VERSION(2,16,0) + q = g_uri_escape_string(session, NULL, false); + g_free(session); + session = q; +#endif + + /* "adjust" last.fm radio */ + + if (strlen(url) > 9) { + char *escaped_url; + +#if GLIB_CHECK_VERSION(2,16,0) + escaped_url = g_uri_escape_string(url, NULL, false); +#else + escaped_url = g_strdup(url); +#endif + + p = g_strconcat("http://ws.audioscrobbler.com/radio/adjust.php?" + "session=", session, "&url=", escaped_url, "&debug=0", + NULL); + g_free(escaped_url); + + response = lastfm_get(p); + g_free(response); + g_free(p); + + if (response == NULL) { + g_free(session); + g_free(stream_url); + return false; + } + } + + /* load the last.fm playlist */ + + p = g_strconcat("http://ws.audioscrobbler.com/radio/xspf.php?" + "sk=", session, "&discovery=0&desktop=1.5.1.31879", + NULL); + g_free(session); + + response = lastfm_get(p); + g_free(p); + + if (response == NULL) { + g_free(stream_url); + return false; + } + + p = strstr(response, "<location>"); + if (p == NULL) { + g_free(response); + g_free(stream_url); + return false; + } + + p += 10; + q = strchr(p, '<'); + + if (q == NULL) { + g_free(response); + g_free(stream_url); + return false; + } + + g_free(stream_url); + stream_url = g_strndup(p, q - p); + g_free(response); + + /* now really open the last.fm radio stream */ + + success = input_stream_open(is, stream_url); + g_free(stream_url); + return success; +} + +const struct input_plugin lastfm_input_plugin = { + .name = "lastfm", + .init = lastfm_input_init, + .open = lastfm_input_open, +}; diff --git a/src/input/lastfm_input_plugin.h b/src/input/lastfm_input_plugin.h new file mode 100644 index 000000000..8a2854214 --- /dev/null +++ b/src/input/lastfm_input_plugin.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LASTFM_INPUT_PLUGIN_H +#define LASTFM_INPUT_PLUGIN_H + +extern const struct input_plugin lastfm_input_plugin; + +#endif diff --git a/src/input_stream.c b/src/input_stream.c index e07a3ab52..95644f5d8 100644 --- a/src/input_stream.c +++ b/src/input_stream.c @@ -30,6 +30,8 @@ #include "input/curl_input_plugin.h" #endif +#include "input/lastfm_input_plugin.h" + #ifdef ENABLE_MMS #include "input/mms_input_plugin.h" #endif @@ -46,6 +48,9 @@ static const struct input_plugin *const input_plugins[] = { #ifdef HAVE_CURL &input_plugin_curl, #endif +#ifdef ENABLE_LASTFM + &lastfm_input_plugin, +#endif #ifdef ENABLE_MMS &input_plugin_mms, #endif @@ -28,6 +28,9 @@ static const char *remoteUrlPrefixes[] = { #ifdef HAVE_CURL "http://", #endif +#ifdef ENABLE_LASTFM + "lastfm://", +#endif #ifdef ENABLE_MMS "mms://", "mmsh://", |