diff options
Diffstat (limited to '')
-rw-r--r-- | src/input/lastfm_input_plugin.c | 228 |
1 files changed, 228 insertions, 0 deletions
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, +}; |