Mercurial > audlegacy
changeset 688:cc1969408403 trunk
[svn] - add scrobbler support
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/Makefile.in Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,23 @@ +include ../../../mk/rules.mk +include ../../../mk/objective.mk + +SUBDIRS = tags + +OBJECTIVE_LIBS = libscrobbler.so + +LIBDIR = $(plugindir)/$(GENERAL_PLUGIN_DIR) + +LIBADD += $(GTK_LIBS) $(CURL_LIBS) $(MUSICBRAINZ_LIBS) tags/libmetatag.a + +SOURCES = \ + fmt.c \ + gtkstuff.c \ + md5.c \ + queue.c \ + scrobbler.c \ + xmms_scrobbler.c + + +CFLAGS += -fPIC -DPIC $(GTK_CFLAGS) $(CURL_CFLAGS) -I../../../intl -I../../.. + +OBJECTS = ${SOURCES:.c=.o}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/config.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,14 @@ +#include "../../../config.h" + +#ifndef __SCROBBLER_CONFIG_H__ +#define __SCROBBLER_CONFIG_H__ + +#define DEBUG 0 +#define META_DEBUG 0 +#define SUB_DEBUG 0 +#define CLIENT "Audacious" +#define USER_AGENT "AudioScrobbler/1.1" PACKAGE_NAME "/" PACKAGE_VERSION +#define MAKE_BMP +#define ALLOW_MULTIPLE + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/fmt.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,84 @@ +#include <wchar.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include "fmt.h" +#include <curl/curl.h> + +char *fmt_escape(char *str) +{ + return curl_escape(str, 0); +} + +char *fmt_unescape(char *str) +{ + return curl_unescape(str, 0); +} + +char *fmt_timestr(time_t t, int gmt) +{ + struct tm *tm; + static char buf[30]; + + tm = gmt ? gmtime(&t) : localtime(&t); + snprintf(buf, sizeof(buf), "%d-%.2d-%.2d %.2d:%.2d:%.2d", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + return buf; +} + +char *fmt_vastr(char *fmt, ...) +{ + va_list ap; + static char buf[4096]; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return buf; +} + +void fmt_debug(char *file, const char *fun, char *str) +{ + fprintf(stderr, "%s [%s] %s: %s\n", fmt_timestr(time(NULL), 0), + file, fun, str); +} + +char *fmt_string_pack(char *string, char *fmt, ...) +{ + int buflen = 0, stringlen = 0; + char buf[4096]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if(string != NULL) stringlen = strlen(string); + buflen = strlen(buf); + + string = realloc(string, stringlen + buflen + 1); + memcpy(string + stringlen, buf, buflen); + *(string + stringlen + buflen) = 0; + return string; +} + +int fmt_strcasecmp(const char *s1, const char *s2) +{ + while (toupper(*s1) == toupper(*s2++)) + if (!*s1++) + return 0; + return toupper(s1[0]) - toupper(s2[-1]); +} + +int fmt_strncasecmp(const char *s1, const char *s2, size_t n) +{ + while (toupper(*s1) == toupper(*s2++) && --n) + if(!*s1++) + return 0; + return n ? toupper(s1[0]) - toupper(s2[-1]) : 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/fmt.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,15 @@ +#ifndef FMT_H +#define FMT_H 1 +#define pdebug(s, b) if(b) { fmt_debug(__FILE__, __FUNCTION__, (s)); } + +#include <time.h> + +char *fmt_escape(char *); +char *fmt_unescape(char *); +char *fmt_timestr(time_t, int); +char *fmt_vastr(char *, ...); +void fmt_debug(char *, const char *, char *); +char *fmt_string_pack(char *, char *, ...); +int fmt_strcasecmp(const char *s1, const char *s2); +int fmt_strncasecmp(const char *s1, const char *s2, size_t n); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/gtkstuff.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,189 @@ +#include "libaudacious/util.h" +#include "libaudacious/configdb.h" + +#include <glib.h> +#include <glib/gi18n.h> + +#include <stdio.h> +#include <string.h> +#include "config.h" +#include "md5.h" + +static GtkWidget *eduname, + *edpwd; +static int errorbox_done; +void about_show(void) +{ + static GtkWidget *aboutbox; + gchar *tmp; + if (aboutbox) + return; + + tmp = g_strdup_printf("Audacious AudioScrobbler Plugin\n\n" + "Originally created by Audun Hove <audun@nlc.no> and Pipian <pipian@pipian.com>\n"); + aboutbox = xmms_show_message(_("About Scrobbler Plugin"), + _(tmp), + _("Ok"), FALSE, NULL, NULL); + + g_free(tmp); + gtk_signal_connect(GTK_OBJECT(aboutbox), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), &aboutbox); +} + +static void set_errorbox_done(GtkWidget *errorbox, GtkWidget **errorboxptr) +{ + errorbox_done = -1; + gtk_widget_destroyed(errorbox, errorboxptr); +} + +void init_errorbox_done(void) +{ + errorbox_done = 1; +} + +int get_errorbox_done(void) +{ + return errorbox_done; +} + +void errorbox_show(char *errortxt) +{ + static GtkWidget *errorbox; + gchar *tmp; + + if(errorbox_done != 1) + return; + errorbox_done = 0; + tmp = g_strdup_printf("There has been an error" + " that may require your attention.\n\n" + "Contents of server error:\n\n" + "%s\n", + errortxt); + + errorbox = xmms_show_message("Scrobbler Error", + tmp, + "OK", FALSE, NULL, NULL); + g_free(tmp); + gtk_signal_connect(GTK_OBJECT(errorbox), "destroy", + GTK_SIGNAL_FUNC(set_errorbox_done), &errorbox); +} + +static char *hexify(char *pass, int len) +{ + static char buf[33]; + char *bp = buf; + char hexchars[] = "0123456789abcdef"; + int i; + + memset(buf, 0, sizeof(buf)); + + for(i = 0; i < len; i++) { + *(bp++) = hexchars[(pass[i] >> 4) & 0x0f]; + *(bp++) = hexchars[pass[i] & 0x0f]; + } + *bp = 0; + return buf; +} + +static void saveconfig(GtkWidget *wid, gpointer data) +{ + ConfigDb *cfgfile; + + const char *pwd = gtk_entry_get_text(GTK_ENTRY(edpwd)); + const char *uid = gtk_entry_get_text(GTK_ENTRY(eduname)); + + if ((cfgfile = bmp_cfg_db_open())) { + + md5_state_t md5state; + unsigned char md5pword[16]; + + bmp_cfg_db_set_string(cfgfile, "audioscrobbler", "username", (char *)uid); + + if (pwd != NULL && pwd[0] != '\0') { + md5_init(&md5state); + md5_append(&md5state, (unsigned const char *)pwd, strlen(pwd)); + md5_finish(&md5state, md5pword); + bmp_cfg_db_set_string(cfgfile, "audioscrobbler", "password", + (char *)hexify(md5pword, sizeof(md5pword))); + } + bmp_cfg_db_close(cfgfile); + } + gtk_widget_destroy(GTK_WIDGET(data)); +} + +void configure_dialog(void) +{ + static GtkWidget *cnfdlg; + GtkWidget *btnok, + *btncancel, + *vbox, + *hbox, + *unhbox, + *pwhbox, + *lblun, + *lblpw, + *frame; + + ConfigDb *cfgfile; + + if (cnfdlg) + return; + + cnfdlg = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_type_hint(GTK_WINDOW(cnfdlg), + GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_title(GTK_WINDOW(cnfdlg), + "Scrobbler configuration"); + + gtk_signal_connect(GTK_OBJECT(cnfdlg), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), &cnfdlg); + + vbox = gtk_vbox_new(FALSE, 0); + + unhbox = gtk_hbox_new(FALSE, 0); + eduname = gtk_entry_new(); + lblun = gtk_label_new("Username"); + gtk_box_pack_start(GTK_BOX(unhbox), lblun, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(unhbox), eduname, FALSE, FALSE, 3); + + pwhbox = gtk_hbox_new(FALSE, 0); + edpwd = gtk_entry_new(); + lblpw = gtk_label_new("Password"); + gtk_entry_set_visibility(GTK_ENTRY(edpwd), FALSE); + gtk_box_pack_start(GTK_BOX(pwhbox), lblpw, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(pwhbox), edpwd, FALSE, FALSE, 3); + + gtk_box_pack_start(GTK_BOX(vbox), unhbox, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(vbox), pwhbox, FALSE, FALSE, 3); + + hbox = gtk_hbox_new(FALSE, 0); + + btnok = gtk_button_new_with_label("Ok"); + gtk_signal_connect(GTK_OBJECT(btnok), "clicked", + GTK_SIGNAL_FUNC(saveconfig), GTK_OBJECT(cnfdlg)); + + btncancel = gtk_button_new_with_label("Cancel"); + gtk_signal_connect_object(GTK_OBJECT(btncancel), "clicked", + GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(cnfdlg)); + gtk_box_pack_start(GTK_BOX(hbox), btnok, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(hbox), btncancel, FALSE, FALSE, 3); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); + + frame = gtk_frame_new(" The plugin will have to be restarted for changes to take effect! "); + gtk_container_add(GTK_CONTAINER(frame), vbox); + gtk_container_add(GTK_CONTAINER(cnfdlg), frame); + + if ((cfgfile = bmp_cfg_db_open())) { + gchar *username = NULL; + bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "username", + &username); + if (username) { + gtk_entry_set_text(GTK_ENTRY(eduname), username); + g_free(username); + } + bmp_cfg_db_close(cfgfile); + } + + gtk_widget_show_all(cnfdlg); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/gtkstuff.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,8 @@ +#ifndef GTKSTUFF_H +#define GTKSTUFF_H 1 +void about_show(void); +void errorbox_show(char *); +void init_errorbox_done(void); +int get_errorbox_done(void); +void configure_dialog(void); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/md5.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.1 2003/08/24 01:20:37 audhov Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/md5.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.1 2003/08/24 01:20:37 audhov Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/queue.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,153 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <curl/curl.h> +#include "tags/include/tags.h" +#include "queue.h" +#include "fmt.h" + +static item_t *q_queue = NULL; +static item_t *q_queue_last = NULL; +static int q_nitems; + +static void q_item_free(item_t *item) +{ + if (item == NULL) + return; + curl_free(item->artist); + curl_free(item->title); + curl_free(item->utctime); + curl_free(item->mb); + curl_free(item->album); + free(item); +} + +void q_put(metatag_t *meta, int len) +{ + item_t *item; + + item = malloc(sizeof(item_t)); + item->artist = fmt_escape(meta->artist); + item->title = fmt_escape(meta->title); + item->utctime = fmt_escape(fmt_timestr(time(NULL), 1)); + snprintf(item->len, sizeof(item->len), "%d", len); + if(meta->mb == NULL) + item->mb = fmt_escape(""); + else + item->mb = fmt_escape(meta->mb); + if(meta->album == NULL) + item->album = fmt_escape(""); + else + item->album = fmt_escape(meta->album); + + q_nitems++; + + item->next = NULL; + + if(q_queue_last == NULL) + q_queue = q_queue_last = item; + else + { + q_queue_last->next = item; + q_queue_last = item; + } +} + +item_t *q_put2(char *artist, char *title, char *len, char *time, + char *album, char *mb) +{ + char *temp = NULL; + item_t *item; + + item = calloc(1, sizeof(item_t)); + temp = fmt_unescape(artist); + item->artist = fmt_escape(temp); + curl_free(temp); + temp = fmt_unescape(title); + item->title = fmt_escape(temp); + curl_free(temp); + memcpy(item->len, len, sizeof(len)); + temp = fmt_unescape(time); + item->utctime = fmt_escape(temp); + curl_free(temp); + temp = fmt_unescape(album); + item->album = fmt_escape(temp); + curl_free(temp); + temp = fmt_unescape(mb); + item->mb = fmt_escape(temp); + curl_free(temp); + + q_nitems++; + + item->next = NULL; + if(q_queue_last == NULL) + q_queue = q_queue_last = item; + else + { + q_queue_last->next = item; + q_queue_last = item; + } + + return item; +} + +item_t *q_peek(void) +{ + if (q_nitems == 0) + return NULL; + return q_queue; +} + +item_t *q_peekall(int rewind) +{ + static item_t *citem = NULL; + item_t *temp_item; + + if (rewind) { + citem = q_queue; + return NULL; + } + + temp_item = citem; + + if(citem != NULL) + citem = citem->next; + + return temp_item; +} + +int q_get(void) +{ + item_t *item; + + if (q_nitems == 0) + return 0; + + item = q_queue; + + if(item == NULL) + return 0; + + q_nitems--; + q_queue = q_queue->next; + + q_item_free(item); + + if (q_nitems == 0) + { + q_queue_last = NULL; + return 0; + } + + return -1; +} + +void q_free(void) +{ + while (q_get()); +} + +int q_len(void) +{ + return q_nitems; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/queue.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,28 @@ +#ifndef QUEUE_H +#define QUEUE_H 1 + +#define I_ARTIST(i) i->artist +#define I_TITLE(i) i->title +#define I_TIME(i) i->utctime +#define I_LEN(i) i->len +#define I_MB(i) i->mb +#define I_ALBUM(i) i->album + +typedef struct { + char *artist, + *title, + *mb, + *album, + *utctime, + len[16]; + int numtries; + void *next; +} item_t; +void q_put(metatag_t *, int); +item_t *q_put2(char *, char *, char *, char *, char *, char *); +item_t *q_peek(void); +item_t *q_peekall(int); +int q_get(void); +void q_free(void); +int q_len(void); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/scrobbler.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,761 @@ +#include <pthread.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <curl/curl.h> +#include <stdio.h> +#include "fmt.h" +#include "md5.h" +#include "tags/include/tags.h" +#include "queue.h" +#include "scrobbler.h" +#include "config.h" +#include <glib.h> + +#define SCROBBLER_HS_URL "http://post.audioscrobbler.com" +#define SCROBBLER_CLI_ID "xms" +#define SCROBBLER_HS_WAIT 1800 +#define SCROBBLER_SB_WAIT 10 +#define SCROBBLER_VERSION "1.1" +#define CACHE_SIZE 1024 + +/* Scrobblerbackend for xmms plugin, first draft */ + +static int sc_hs_status, + sc_hs_timeout, + sc_hs_errors, + sc_sb_errors, + sc_bad_users, + sc_submit_interval, + sc_submit_timeout, + sc_srv_res_size, + sc_giveup, + sc_major_error_present; + +static char *sc_submit_url, + *sc_username, + *sc_password, + *sc_challenge_hash, + sc_response_hash[33], + *sc_srv_res, + sc_curl_errbuf[CURL_ERROR_SIZE], + *sc_major_error; + +static void dump_queue(); + +/* Error functions */ + +static void sc_throw_error(char *errortxt) +{ + sc_major_error_present = 1; + if(sc_major_error == NULL) + sc_major_error = strdup(errortxt); + + return; +} + +int sc_catch_error(void) +{ + return sc_major_error_present; +} + +char *sc_fetch_error(void) +{ + return sc_major_error; +} + +void sc_clear_error(void) +{ + sc_major_error_present = 0; + if(sc_major_error != NULL) + free(sc_major_error); + sc_major_error = NULL; + + return; +} + +static size_t sc_store_res(void *ptr, size_t size, + size_t nmemb, + void *stream) +{ + int len = size * nmemb; + + sc_srv_res = realloc(sc_srv_res, sc_srv_res_size + len + 1); + memcpy(sc_srv_res + sc_srv_res_size, + ptr, len); + sc_srv_res_size += len; + return len; +} + +static void sc_free_res(void) +{ + if(sc_srv_res != NULL) + free(sc_srv_res); + sc_srv_res = NULL; + sc_srv_res_size = 0; +} + +static int sc_parse_hs_res(void) +{ + char *interval; + + if (!sc_srv_res_size) { + pdebug("No reply from server", DEBUG); + return -1; + } + *(sc_srv_res + sc_srv_res_size) = 0; + + if (!strncmp(sc_srv_res, "FAILED ", 7)) { + interval = strstr(sc_srv_res, "INTERVAL"); + if(!interval) { + pdebug("missing INTERVAL", DEBUG); + } + else + { + *(interval - 1) = 0; + sc_submit_interval = strtol(interval + 8, NULL, 10); + } + + /* Throwing a major error, just in case */ + /* sc_throw_error(fmt_vastr("%s", sc_srv_res)); + sc_hs_errors++; */ + pdebug(fmt_vastr("error: %s", sc_srv_res), DEBUG); + + return -1; + } + + if (!strncmp(sc_srv_res, "UPDATE ", 7)) { + interval = strstr(sc_srv_res, "INTERVAL"); + if(!interval) + { + pdebug("missing INTERVAL", DEBUG); + } + else + { + *(interval - 1) = 0; + sc_submit_interval = strtol(interval + 8, NULL, 10); + } + + sc_submit_url = strchr(strchr(sc_srv_res, '\n') + 1, '\n') + 1; + *(sc_submit_url - 1) = 0; + sc_submit_url = strdup(sc_submit_url); + sc_challenge_hash = strchr(sc_srv_res, '\n') + 1; + *(sc_challenge_hash - 1) = 0; + sc_challenge_hash = strdup(sc_challenge_hash); + + /* Throwing major error. Need to alert client to update. */ + sc_throw_error(fmt_vastr("Please update Audacious.\n" + "Update available at: http://audacious-media-player.org")); + pdebug(fmt_vastr("update client: %s", sc_srv_res + 7), DEBUG); + + /* + * Russ isn't clear on whether we can submit with a not-updated + * client. Neither is RJ. I use what we did before. + */ + sc_giveup = -1; + return -1; + } + if (!strncmp(sc_srv_res, "UPTODATE\n", 9)) { + sc_bad_users = 0; + + interval = strstr(sc_srv_res, "INTERVAL"); + if (!interval) { + pdebug("missing INTERVAL", DEBUG); + /* + * This is probably a bad thing, but Russ seems to + * think its OK to assume that an UPTODATE response + * may not have an INTERVAL... We return -1 anyway. + */ + return -1; + } + else + { + *(interval - 1) = 0; + sc_submit_interval = strtol(interval + 8, NULL, 10); + } + + sc_submit_url = strchr(strchr(sc_srv_res, '\n') + 1, '\n') + 1; + *(sc_submit_url - 1) = 0; + sc_submit_url = strdup(sc_submit_url); + sc_challenge_hash = strchr(sc_srv_res, '\n') + 1; + *(sc_challenge_hash - 1) = 0; + sc_challenge_hash = strdup(sc_challenge_hash); + + return 0; + } + if(!strncmp(sc_srv_res, "BADUSER", 7)) { + /* Throwing major error. */ + sc_throw_error("Incorrect username/password.\n" + "Please fix in configuration."); + pdebug("incorrect username/password", DEBUG); + + interval = strstr(sc_srv_res, "INTERVAL"); + if(!interval) + { + pdebug("missing INTERVAL", DEBUG); + } + else + { + *(interval - 1) = 0; + sc_submit_interval = strtol(interval + 8, NULL, 10); + } + + return -1; + } + + pdebug(fmt_vastr("unknown server-reply '%s'", sc_srv_res), DEBUG); + return -1; +} + +static void hexify(char *pass, int len) +{ + char *bp = sc_response_hash; + char hexchars[] = "0123456789abcdef"; + int i; + + memset(sc_response_hash, 0, sizeof(sc_response_hash)); + + for(i = 0; i < len; i++) { + *(bp++) = hexchars[(pass[i] >> 4) & 0x0f]; + *(bp++) = hexchars[pass[i] & 0x0f]; + } + *bp = 0; + + return; +} + +static int sc_handshake(void) +{ + int status; + char buf[4096]; + CURL *curl; + + snprintf(buf, sizeof(buf), "%s/?hs=true&p=%s&c=%s&v=%s&u=%s", + SCROBBLER_HS_URL, SCROBBLER_VERSION, + SCROBBLER_CLI_ID, "0.3.8.1", sc_username); + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curl, CURLOPT_URL, buf); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + sc_store_res); + memset(sc_curl_errbuf, 0, sizeof(sc_curl_errbuf)); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, sc_curl_errbuf); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + status = curl_easy_perform(curl); + curl_easy_cleanup(curl); + + sc_hs_timeout = time(NULL) + SCROBBLER_HS_WAIT; + + if (status) { + pdebug(sc_curl_errbuf, DEBUG); + sc_hs_errors++; + sc_free_res(); + return -1; + } + + if (sc_parse_hs_res()) { + sc_hs_errors++; + sc_free_res(); + return -1; + } + + if (sc_challenge_hash != NULL) { + md5_state_t md5state; + unsigned char md5pword[16]; + + md5_init(&md5state); + /*pdebug(fmt_vastr("Pass Hash: %s", sc_password), DEBUG);*/ + md5_append(&md5state, (unsigned const char *)sc_password, + strlen(sc_password)); + /*pdebug(fmt_vastr("Challenge Hash: %s", sc_challenge_hash), DEBUG);*/ + md5_append(&md5state, (unsigned const char *)sc_challenge_hash, + strlen(sc_challenge_hash)); + md5_finish(&md5state, md5pword); + hexify(md5pword, sizeof(md5pword)); + /*pdebug(fmt_vastr("Response Hash: %s", sc_response_hash), DEBUG);*/ + } + + sc_hs_errors = 0; + sc_hs_status = 1; + + sc_free_res(); + + pdebug(fmt_vastr("submiturl: %s - interval: %d", + sc_submit_url, sc_submit_interval), DEBUG); + + return 0; +} + +static int sc_parse_sb_res(void) +{ + char *ch, *ch2; + + if (!sc_srv_res_size) { + pdebug("No response from server", DEBUG); + return -1; + } + *(sc_srv_res + sc_srv_res_size) = 0; + + if (!strncmp(sc_srv_res, "OK", 2)) { + if ((ch = strstr(sc_srv_res, "INTERVAL"))) { + sc_submit_interval = strtol(ch + 8, NULL, 10); + pdebug(fmt_vastr("got new interval: %d", + sc_submit_interval), DEBUG); + } + + pdebug(fmt_vastr("submission ok: %s", sc_srv_res), DEBUG); + + return 0; + } + + if (!strncmp(sc_srv_res, "BADAUTH", 7)) { + if ((ch = strstr(sc_srv_res, "INTERVAL"))) { + sc_submit_interval = strtol(ch + 8, NULL, 10); + pdebug(fmt_vastr("got new interval: %d", + sc_submit_interval), DEBUG); + } + + pdebug("incorrect username/password", DEBUG); + + sc_giveup = 0; + + /* + * We obviously aren't authenticated. The server might have + * lost our handshake status though, so let's try + * re-handshaking... This might not be proper. + * (we don't give up) + */ + sc_hs_status = 0; + + if(sc_challenge_hash != NULL) + free(sc_challenge_hash); + if(sc_submit_url != NULL) + free(sc_submit_url); + + sc_challenge_hash = sc_submit_url = NULL; + sc_bad_users++; + + if(sc_bad_users > 2) + { + pdebug("3 BADAUTH returns on submission. Halting " + "submissions until login fixed.", DEBUG) + sc_throw_error("Incorrect username/password.\n" + "Please fix in configuration."); + } + + return -1; + } + + if (!strncmp(sc_srv_res, "FAILED", 6)) { + if ((ch = strstr(sc_srv_res, "INTERVAL"))) { + sc_submit_interval = strtol(ch + 8, NULL, 10); + pdebug(fmt_vastr("got new interval: %d", + sc_submit_interval), DEBUG); + } + + /* This could be important. (Such as FAILED - Get new plugin) */ + /*sc_throw_error(fmt_vastr("%s", sc_srv_res));*/ + + pdebug(sc_srv_res, DEBUG); + + return -1; + } + + if (!strncmp(sc_srv_res, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">", 50)) { + ch = strstr(sc_srv_res, "<TITLE>") + 7; + ch2 = strstr(sc_srv_res, "</TITLE>"); + *ch2 = '\0'; + + pdebug(fmt_vastr("HTTP Error (%d): '%s'", + atoi(ch), ch + 4), DEBUG); + *ch2 = '<'; + + return -1; + } + + pdebug(fmt_vastr("unknown server-reply %s", sc_srv_res), DEBUG); + + return -1; +} + +static gchar *sc_itemtag(char c, int n, char *str) +{ + static char buf[256]; + snprintf(buf, 256, "&%c[%d]=%s", c, n, str); + return buf; +} + +#define cfa(f, l, n, v) \ +curl_formadd(f, l, CURLFORM_COPYNAME, n, \ + CURLFORM_PTRCONTENTS, v, CURLFORM_END) + +static int sc_generateentry(GString *submission) +{ + int i; + item_t *item; + + i = 0; +#ifdef ALLOW_MULTIPLE + q_peekall(1); + while ((item = q_peekall(0)) && i < 10) { +#else + item = q_peek(); +#endif + if (!item) + return i; + + g_string_append(submission,sc_itemtag('a',i,I_ARTIST(item))); + g_string_append(submission,sc_itemtag('t',i,I_TITLE(item))); + g_string_append(submission,sc_itemtag('l',i,I_LEN(item))); + g_string_append(submission,sc_itemtag('i',i,I_TIME(item))); + g_string_append(submission,sc_itemtag('m',i,I_MB(item))); + g_string_append(submission,sc_itemtag('b',i,I_ALBUM(item))); + + pdebug(fmt_vastr("a[%d]=%s t[%d]=%s l[%d]=%s i[%d]=%s m[%d]=%s b[%d]=%s", + i, I_ARTIST(item), + i, I_TITLE(item), + i, I_LEN(item), + i, I_TIME(item), + i, I_MB(item), + i, I_ALBUM(item)), DEBUG); +#ifdef ALLOW_MULTIPLE + i++; + } +#endif + + return i; +} + +static int sc_submitentry(gchar *entry) +{ + CURL *curl; + /* struct HttpPost *post = NULL , *last = NULL; */ + int status; + GString *submission; + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curl, CURLOPT_URL, sc_submit_url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + sc_store_res); + curl_easy_setopt(curl, CURLOPT_USERAGENT, USER_AGENT); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + /*cfa(&post, &last, "debug", "failed");*/ + + /*pdebug(fmt_vastr("Username: %s", sc_username), DEBUG);*/ + submission = g_string_new("u="); + g_string_append(submission,(gchar *)sc_username); + + /*pdebug(fmt_vastr("Response Hash: %s", sc_response_hash), DEBUG);*/ + g_string_append(submission,"&s="); + g_string_append(submission,(gchar *)sc_response_hash); + + g_string_append(submission, entry); + + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (char *)submission->str); + memset(sc_curl_errbuf, 0, sizeof(sc_curl_errbuf)); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, sc_curl_errbuf); + + /* + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, SCROBBLER_SB_WAIT); + */ + + status = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + + g_string_free(submission,TRUE); + + if (status) { + pdebug(sc_curl_errbuf, DEBUG); + sc_sb_errors++; + sc_free_res(); + return -1; + } + + if (sc_parse_sb_res()) { + sc_sb_errors++; + sc_free_res(); + pdebug(fmt_vastr("Retrying in %d secs, %d elements in queue", + sc_submit_interval, q_len()), DEBUG); + return -1; + } + sc_free_res(); + return 0; +} + +static void sc_handlequeue(GMutex *mutex) +{ + GString *submitentry; + int nsubmit; + int wait; + + if(sc_submit_timeout < time(NULL) && sc_bad_users < 3) + { + submitentry = g_string_new(""); + + g_mutex_lock(mutex); + + nsubmit = sc_generateentry(submitentry); + + g_mutex_unlock(mutex); + + if (nsubmit > 0) + { + pdebug(fmt_vastr("Number submitting: %d", nsubmit), DEBUG); + pdebug(fmt_vastr("Submission: %s", submitentry->str), DEBUG); + + if(!sc_submitentry(submitentry->str)) + { + g_mutex_lock(mutex); + +#ifdef ALLOW_MULTIPLE + q_free(); +#else + q_get(); +#endif + /* + * This should make sure that the queue doesn't + * get submitted multiple times on a nasty + * segfault... + */ + dump_queue(); + + g_mutex_unlock(mutex); + + sc_sb_errors = 0; + } + if(sc_sb_errors) + { + if(sc_sb_errors < 5) + /* Retry after 1 min */ + wait = 60; + else + wait = /* sc_submit_interval + */ + ( ((sc_sb_errors - 5) < 7) ? + (60 << (sc_sb_errors-5)) : + 7200 ); + + sc_submit_timeout = time(NULL) + wait; + + pdebug(fmt_vastr("Error while submitting. " + "Retrying after %d seconds.", wait), + DEBUG); + } + } + + g_string_free(submitentry, TRUE); + } +} + +static void read_cache(void) +{ + FILE *fd; + char *home, buf[PATH_MAX], *cache = NULL, *ptr1, *ptr2; + int cachesize, written, i = 0; + item_t *item; + + cachesize = written = 0; + + if(!(home = getenv("HOME"))) + return; + + snprintf(buf, sizeof(buf), "%s/.audacious/scrobblerqueue.txt", home); + + if (!(fd = fopen(buf, "r"))) + return; + pdebug(fmt_vastr("Opening %s", buf), DEBUG); + while(!feof(fd)) + { + cachesize += CACHE_SIZE; + cache = realloc(cache, cachesize + 1); + written += fread(cache + written, 1, CACHE_SIZE, fd); + cache[written] = '\0'; + } + fclose(fd); + ptr1 = cache; + while(ptr1 < cache + written - 1) + { + char *artist, *title, *len, *time, *album, *mb; + + pdebug("Pushed:", DEBUG); + ptr2 = strchr(ptr1, ' '); + artist = calloc(1, ptr2 - ptr1 + 1); + strncpy(artist, ptr1, ptr2 - ptr1); + ptr1 = ptr2 + 1; + ptr2 = strchr(ptr1, ' '); + title = calloc(1, ptr2 - ptr1 + 1); + strncpy(title, ptr1, ptr2 - ptr1); + ptr1 = ptr2 + 1; + ptr2 = strchr(ptr1, ' '); + len = calloc(1, ptr2 - ptr1 + 1); + strncpy(len, ptr1, ptr2 - ptr1); + ptr1 = ptr2 + 1; + ptr2 = strchr(ptr1, ' '); + time = calloc(1, ptr2 - ptr1 + 1); + strncpy(time, ptr1, ptr2 - ptr1); + ptr1 = ptr2 + 1; + ptr2 = strchr(ptr1, ' '); + album = calloc(1, ptr2 - ptr1 + 1); + strncpy(album, ptr1, ptr2 - ptr1); + ptr1 = ptr2 + 1; + ptr2 = strchr(ptr1, '\n'); + if(ptr2 != NULL) + *ptr2 = '\0'; + mb = calloc(1, strlen(ptr1) + 1); + strncpy(mb, ptr1, strlen(ptr1)); + if(ptr2 != NULL) + *ptr2 = '\n'; + /* Why is our save printing out CR/LF? */ + ptr1 = ptr2 + 1; + + item = q_put2(artist, title, len, time, album, mb); + pdebug(fmt_vastr("a[%d]=%s t[%d]=%s l[%d]=%s i[%d]=%s m[%d]=%s b[%d]=%s", + i, I_ARTIST(item), + i, I_TITLE(item), + i, I_LEN(item), + i, I_TIME(item), + i, I_MB(item), + i, I_ALBUM(item)), DEBUG); + free(artist); + free(title); + free(len); + free(time); + free(album); + free(mb); + + i++; + } + pdebug("Done loading cache.", DEBUG); +} + +static void dump_queue(void) +{ + FILE *fd; + item_t *item; + char *home, buf[PATH_MAX]; + + /*pdebug("Entering dump_queue();", DEBUG);*/ + + if (!(home = getenv("HOME"))) + { + pdebug("No HOME directory found. Cannot dump queue.", DEBUG); + return; + } + + snprintf(buf, sizeof(buf), "%s/.audacious/scrobblerqueue.txt", home); + + if (!(fd = fopen(buf, "w"))) + { + pdebug(fmt_vastr("Failure opening %s", buf), DEBUG); + return; + } + + pdebug(fmt_vastr("Opening %s", buf), DEBUG); + + q_peekall(1); + + while ((item = q_peekall(0))) { + fprintf(fd, "%s %s %s %s %s %s\n", + I_ARTIST(item), + I_TITLE(item), + I_LEN(item), + I_TIME(item), + I_ALBUM(item), + I_MB(item)); + } + + fclose(fd); +} + +/* This was made public */ + +void sc_cleaner(void) +{ + if(sc_submit_url != NULL) + free(sc_submit_url); + if(sc_username != NULL) + free(sc_username); + if(sc_password != NULL) + free(sc_password); + if(sc_challenge_hash != NULL) + free(sc_challenge_hash); + if(sc_srv_res != NULL) + free(sc_srv_res); + if(sc_major_error != NULL) + free(sc_major_error); + dump_queue(); + q_free(); + pdebug("scrobbler shutting down", DEBUG); +} + +static void sc_checkhandshake(void) +{ + int wait; + + if (sc_hs_status) + return; + if (sc_hs_timeout < time(NULL)) + { + sc_handshake(); + + if(sc_hs_errors) + { + if(sc_hs_errors < 5) + /* Retry after 60 seconds */ + wait = 60; + else + wait = /* sc_submit_interval + */ + ( ((sc_hs_errors - 5) < 7) ? + (60 << (sc_hs_errors-5)) : + 7200 ); + sc_hs_timeout = time(NULL) + wait; + pdebug(fmt_vastr("Error while handshaking. Retrying " + "after %d seconds.", wait), DEBUG); + } + } +} + +/**** Public *****/ + +/* Called at session startup*/ + +void sc_init(char *uname, char *pwd) +{ + sc_hs_status = sc_hs_timeout = sc_hs_errors = sc_submit_timeout = + sc_srv_res_size = sc_giveup = sc_major_error_present = + sc_bad_users = sc_sb_errors = 0; + sc_submit_interval = 100; + + sc_submit_url = sc_username = sc_password = sc_srv_res = + sc_challenge_hash = sc_major_error = NULL; + sc_username = strdup(uname); + sc_password = strdup(pwd); + read_cache(); + pdebug("scrobbler starting up", DEBUG); +} + +void sc_addentry(GMutex *mutex, metatag_t *meta, int len) +{ + g_mutex_lock(mutex); + q_put(meta, len); + /* + * This will help make sure the queue will be saved on a nasty + * segfault... + */ + dump_queue(); + g_mutex_unlock(mutex); +} + +/* Call periodically from the plugin */ +int sc_idle(GMutex *mutex) +{ + sc_checkhandshake(); + if (sc_hs_status) + sc_handlequeue(mutex); + + return sc_giveup; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/scrobbler.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,11 @@ +#ifndef NET_H +#define NET_H 1 + +int sc_idle(GMutex *); +void sc_init(char *, char *); +void sc_addentry(GMutex *, metatag_t *, int); +void sc_cleaner(void); +int sc_catch_error(void); +char *sc_fetch_error(void); +void sc_clear_error(void); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/Makefile.in Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,13 @@ +include ../../../../mk/rules.mk +include ../../../../mk/objective.mk + +OBJECTIVE_LIBS_NOINST = libmetatag.a + +LIBADD = $(MUSICBRAINZ_LIBS) + +SOURCES = ape.c cdaudio.c id3genres.c id3v1.c id3v2.c itunes.c tags.c \ + unicode.c vorbis.c wma.c + +CFLAGS += -fPIC -DPIC $(GTK_CFLAGS) $(MUSICBRAINZ_CFLAGS) -I../../../../intl -I../../../.. + +OBJECTS = ${SOURCES:.c=.o}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/ape.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,187 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "include/bmp_vfs.h" +#include "include/ape.h" +#include "include/endian.h" +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#define BUFFER_SIZE 4096 + +static ape_t *readItems(VFSFile *fp, int version) +{ + ape_t *ape = calloc(sizeof(ape_t), 1); + unsigned char *tag_buffer = NULL, *bp, cToInt[4]; + int size, start, i; + + ape->version = version; + + /* + * Now for the actual reading. + * APE2 and APE1 tags are identical for all the structure we care about. + */ + + vfs_fread(cToInt, 1, 4, fp); + size = le2int(cToInt); + vfs_fread(cToInt, 1, 4, fp); + ape->numitems = le2int(cToInt); + /* pdebug(fmt_vastr("tag size: %d", size)); + pdebug(fmt_vastr("items: %d", ape->numitems)); */ + vfs_fread(cToInt, 1, 4, fp); + /* Is it a footer? Header? */ + if((cToInt[3] & 0x20) == 0x0 || version == 1000) + start = 8 - size; + else + start = 8; + vfs_fseek(fp, start, SEEK_CUR); + + tag_buffer = realloc(tag_buffer, size); + vfs_fread(tag_buffer, 1, size, fp); + bp = tag_buffer; + + ape->items = realloc(ape->items, + (ape->numitems) * sizeof(apefielddata_t *)); + + for(i = 0; i < ape->numitems && strncmp(bp, "APETAGEX", 8) != 0; i++) + { + apefielddata_t *field = calloc(sizeof(apefielddata_t), 1); + + memcpy(cToInt, bp, 4); + bp += 8; + field->len = le2int(cToInt); + field->name = malloc(strlen(bp) + 1); + strcpy(field->name, bp); + bp += strlen(bp) + 1; + field->data = malloc(field->len + 1); + memcpy(field->data, bp, field->len); + *(field->data + field->len) = '\0'; + bp += field->len; + + ape->items[i] = field; + } + if(i < ape->numitems && strncmp(bp, "APETAGEX", 8) == 0) + { + ape->numitems = i; + ape->items = realloc(ape->items, + (ape->numitems) * sizeof(apefielddata_t *)); + } + + + free(tag_buffer); + + return ape; +} + +int findAPE(VFSFile *fp) +{ + unsigned char *tag_buffer, *bp, cToInt[4]; + int pb = 0, status = 0, pos = 0, i, ape_version; + + /* Find the tag header or footer and point the file pointer there. */ + tag_buffer = malloc(BUFFER_SIZE); + pb += vfs_fread(tag_buffer, 1, BUFFER_SIZE, fp); + bp = tag_buffer; + while(status == 0) + { + for(i = 0; i < BUFFER_SIZE - 8 && status == 0; i++) + { + bp++; + if(!strncmp(bp, "APETAGEX", 8)) + status = 1; + } + if(status == 1 || feof(fp)) + break; + memmove(tag_buffer, tag_buffer + BUFFER_SIZE - 7, 7); + pos += BUFFER_SIZE - 7; + pb += vfs_fread(tag_buffer + 7, 1, BUFFER_SIZE - 7, fp); + bp = tag_buffer; + } + if(status == 1) + { + vfs_fseek(fp, pos + (bp - tag_buffer) + 8, SEEK_SET); + + free(tag_buffer); + + /* Get the tag version. */ + vfs_fread(cToInt, 1, 4, fp); + ape_version = le2int(cToInt); + pdebug(ape_version == 1000 ? "Found APE1 tag..." : + ape_version == 2000 ? "Found APE2 tag..." : + "Found unknown APE tag...", META_DEBUG); + + return ape_version; + } + else + { + free(tag_buffer); + return 0; + } +} + +ape_t *readAPE(char *filename) +{ + VFSFile *fp; + ape_t *ape; + int status; + + fp = vfs_fopen(filename, "r"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + status = findAPE(fp); + if(status == 0) + { + vfs_fclose(fp); + return NULL; + } + ape = readItems(fp, status); + + vfs_fclose(fp); + + return ape; +} + +void freeAPE(ape_t *ape) +{ + int i; + + for(i = 0; i < ape->numitems; i++) + { + apefielddata_t *field; + + field = ape->items[i]; + free(field->name); + free(field->data); + free(field); + } + free(ape->items); + free(ape); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/cdaudio.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,153 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <musicbrainz/mb_c.h> +#include "include/cdaudio.h" +#include "include/endian.h" +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#define BUFFER_SIZE 4096 + +#ifdef MAKE_BMP +#include <libaudacious/vfs.h> +#define vfs_fopen vfs_fopen +#define vfs_fclose vfs_fclose +#define vfs_fread vfs_fread +#define vfs_fseek vfs_fseek +#define ftell vfs_ftell +#define VFSFile VFSFile +#endif + +/* + * Determining if a CD is being played is left up to the reader. + * + +int cdaudio_find(char *filename) +{ + We've got to find a way to ensure this works on all platforms. + + return !(fmt_strcasecmp(strrchr(filename, '.') + 1, "cda")); +} +*/ + +cdaudio_t *readCDAudio(char *filename, char track) +{ + int retVal; + musicbrainz_t mb; + char *tmp; + cdaudio_t *musicbrainz = calloc(sizeof(cdaudio_t), 1); + + memset(musicbrainz, 0, sizeof(cdaudio_t)); + + tmp = malloc(BUFFER_SIZE / 4 + 1); + mb = mb_New(); + mb_SetDevice(mb, filename); + pdebug("Submitting query to MusicBrainz...", META_DEBUG); + retVal = mb_Query(mb, MBQ_GetCDInfo); + if(retVal == 0) + { +#ifdef META_DEBUG + char error[129] = ""; + pdebug("ERROR: Query failed.", META_DEBUG); + mb_GetQueryError(mb, error, 128); + pdebug(fmt_vastr("REASON: %s", error), META_DEBUG); +#endif + mb_Delete(mb); + free(tmp); + free(musicbrainz); + return NULL; + } + pdebug("Selecting result...", META_DEBUG); + retVal = mb_Select1(mb, MBS_SelectAlbum, 1); + if(retVal == 0) + { + pdebug("ERROR: Album select failed.", META_DEBUG); + mb_Delete(mb); + free(tmp); + free(musicbrainz); + return NULL; + } + pdebug("Extracting MusicBrainz data from result...", META_DEBUG); + memset(tmp, '\0', BUFFER_SIZE / 4 + 1); + retVal = mb_GetResultData(mb, MBE_AlbumGetAlbumName, tmp, BUFFER_SIZE / 4); + if(retVal == 0) + { + pdebug("ERROR: Album title not found.", META_DEBUG); + musicbrainz->album = calloc(1, 1); + } + else + { + musicbrainz->album = malloc(strlen(tmp) + 1); + strcpy(musicbrainz->album, tmp); + } + memset(tmp, '\0', BUFFER_SIZE / 4 + 1); + retVal = mb_GetResultData1(mb, MBE_AlbumGetArtistName, tmp, BUFFER_SIZE / 4, track); + if(retVal == 0) + { + pdebug("ERROR: Artist name not found.", META_DEBUG); + musicbrainz->artist = calloc(1, 1); + } + else + { + musicbrainz->artist = malloc(strlen(tmp) + 1); + strcpy(musicbrainz->artist, tmp); + } + memset(tmp, '\0', BUFFER_SIZE / 4 + 1); + retVal = mb_GetResultData1(mb, MBE_AlbumGetTrackName, tmp, BUFFER_SIZE / 4, track); + if(retVal == 0) + { + pdebug("ERROR: Track title not found.", META_DEBUG); + musicbrainz->title = calloc(1, 1); + } + else + { + musicbrainz->title = malloc(strlen(tmp) + 1); + strcpy(musicbrainz->title, tmp); + } + memset(tmp, '\0', BUFFER_SIZE / 4 + 1); + retVal = mb_GetResultData1(mb, MBE_AlbumGetTrackId, tmp, BUFFER_SIZE / 4, track); + if(retVal == 0) + { + pdebug("ERROR: MBID not found.", META_DEBUG); + musicbrainz->mbid = calloc(1, 1); + } + else + { + musicbrainz->mbid = malloc(64); + mb_GetIDFromURL(mb, tmp, musicbrainz->mbid, 64); + } + mb_Delete(mb); + free(tmp); + + return musicbrainz; +} + +void freeCDAudio(cdaudio_t *musicbrainz) +{ + free(musicbrainz->mbid); + free(musicbrainz->title); + free(musicbrainz->artist); + free(musicbrainz->album); + free(musicbrainz); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/id3genres.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,79 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +const char *genre_list[148] = +{ + /* The following genres are defined in ID3v1 */ + "Blues", "Classic Rock", "Country", + "Dance", "Disco", "Funk", + "Grunge", "Hip-Hop", "Jazz", + "Metal", "New Age", "Oldies", + "Other", "Pop", "R&B", + "Rap", "Reggae", "Rock", + "Techno", "Industrial", "Alternative", + "Ska", "Death Metal", "Pranks", + "Soundtrack", "Euro-Techno", "Ambient", + "Trip-Hop", "Vocal", "Jazz+Funk", + "Fusion", "Trance", "Classical", + "Instrumental", "Acid", "House", + "Game", "Sound Clip", "Gospel", + "Noise", "AlternRock", "Bass", + "Soul", "Punk", "Space", + "Meditative", "Instrumental Pop", "Instrumental Rock", + "Ethnic", "Gothic", "Darkwave", + "Techno-Industrial", "Electronic", "Pop-Folk", + "Eurodance", "Dream", "Southern Rock", + "Comedy", "Cult", "Gangsta", + "Top 40", "Christian Rap", "Pop/Funk", + "Jungle", "Native American", "Cabaret", + "New Wave", "Psychedelic", "Rave", + "Showtunes", "Trailer", "Lo-Fi", + "Tribal", "Acid Punk", "Acid Jazz", + "Polka", "Retro", "Musical", + "Rock & Roll", "Hard Rock", + /* The following genres are defined in early versions of Winamp */ + "Folk", "Folk-Rock", "National Folk", + "Swing", "Fast Fusion", "Bebob", + "Latin", "Revival", "Celtic", + "Bluegrass", "Avantgarde", "Gothic Rock", + "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", + "Slow Rock", "Big Band", "Chorus", + "Easy Listening", "Acoustic", "Humour", + "Speech", "Chanson", "Opera", + "Chamber Music", "Sonata", "Symphony", + "Booty Bass", "Primus", "Porn Groove", + "Satire", + /* By Winamp 1.70, the following were added. */ + "Slow Jam", "Club", "Tango", + "Samba", "Folklore", + /* By Winamp 1.90, the following were added. */ + "Ballad", "Power Ballad", "Rhythmic Soul", + "Freestyle", "Duet", "Punk Rock", + "Drum Solo", "A capella", "Euro-House", + "Dance Hall", "Goa", "Drum & Bass", + "Club-House", "Hardcore", "Terror", + "Indie", "BritPop", "Negerpunk", + "Polsk Punk", "Beat", "Christian Gangsta Rap", + "Heavy Metal", "Black Metal", "Crossover", + "Contemporary Christian", "Christian Rock", + /* The following were added in Winamp 1.91 */ + "Merengue", "Salsa", "Trash Metal", + "Anime", "JPop", "SynthPop" +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/id3v1.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,149 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "include/bmp_vfs.h" +#include "include/id3v1.h" +#include "include/endian.h" +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#define BUFFER_SIZE 4096 + +static void cleanID3v1(unsigned char *data, size_t size) +{ + int i; + + for(i = size - 1; i > -1; i--) + { + if(data[i] == ' ') data[i] = '\0'; + else break; + } +} + +static void cleanComment(unsigned char *data) +{ + int i; + + for(i = 27; i > -1; i--) + { + if(data[i] == ' ' || data[i] == '\0') data[i] = '\0'; + else break; + } +} + +int findID3v1(VFSFile *fp) +{ + char tag_id[4] = ""; + + vfs_fread(tag_id, 1, 3, fp); + + if(!strncmp(tag_id, "TAG", 3)) + return 1; + else + return 0; +} + +id3v1_t *readID3v1(char *filename) +{ + VFSFile *fp; + id3v1_t *id3v1 = NULL; + int status; + + fp = vfs_fopen(filename, "rb"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + pdebug("Searching for tag...", META_DEBUG); + vfs_fseek(fp, -128, SEEK_END); + status = findID3v1(fp); + if(status) + { + unsigned char *data; + + id3v1 = calloc(sizeof(id3v1_t), 1); + data = malloc(31); + *(data + 30) = '\0'; + vfs_fread(data, 1, 30, fp); + cleanID3v1(data, 30); + // id3v1->title = realloc(id3v1->title, 31); + if(strcmp(data, "")) + iso88591_to_utf8(data, strlen(data), &id3v1->title); + else + id3v1->title = NULL; + vfs_fread(data, 1, 30, fp); + cleanID3v1(data, 30); + if(strcmp(data, "")) + iso88591_to_utf8(data, strlen(data), &id3v1->artist); + else + id3v1->artist = NULL; + vfs_fread(data, 1, 30, fp); + cleanID3v1(data, 30); + if(strcmp(data, "")) + iso88591_to_utf8(data, strlen(data), &id3v1->album); + else + id3v1->album = NULL; + data = realloc(data, 5); + *(data + 4) = '\0'; + vfs_fread(data, 1, 4, fp); + cleanID3v1(data, 4); + if(strcmp(data, "")) + iso88591_to_utf8(data, strlen(data), &id3v1->year); + else + id3v1->year = NULL; + data = realloc(data, 31); + *(data + 30) = '\0'; + vfs_fread(data, 1, 30, fp); + cleanComment(data); + id3v1->comment = realloc(id3v1->comment, 31); + memset(id3v1->comment, 0, 31); + memcpy(id3v1->comment, data, 30); + if(data[28] == '\0' && data[29] != '\0') + id3v1->track = data[29]; + else + id3v1->track = 255; + free(data); + vfs_fread(&id3v1->genre, 1, 1, fp); + } + + vfs_fclose(fp); + + return id3v1; +} + +void freeID3v1(id3v1_t *id3v1) +{ + if(id3v1->title != NULL) + free(id3v1->title); + if(id3v1->artist != NULL) + free(id3v1->artist); + if(id3v1->album != NULL) + free(id3v1->album); + if(id3v1->year != NULL) + free(id3v1->year); + free(id3v1->comment); + free(id3v1); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/id3v2.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,708 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "include/bmp_vfs.h" +#include "include/id3v2.h" +#include "include/endian.h" +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#define BUFFER_SIZE 4096 + +id3_lookup_t id3v22_lookup[] = +{ + {"BUF", ID3V22_BUF}, {"CNT", ID3V22_CNT}, {"COM", ID3V22_COM}, + {"CRA", ID3V22_CRA}, {"CRM", ID3V22_CRM}, {"ETC", ID3V22_ETC}, + {"EQU", ID3V22_EQU}, {"GEO", ID3V22_GEO}, {"IPL", ID3V22_IPL}, + {"LNK", ID3V22_LNK}, {"MCI", ID3V22_MCI}, {"MLL", ID3V22_MLL}, + {"PIC", ID3V22_PIC}, {"POP", ID3V22_POP}, {"REV", ID3V22_REV}, + {"RVA", ID3V22_RVA}, {"SLT", ID3V22_SLT}, {"STC", ID3V22_STC}, + {"TAL", ID3V22_TAL}, {"TBP", ID3V22_TBP}, {"TCM", ID3V22_TCM}, + {"TCO", ID3V22_TCO}, {"TCR", ID3V22_TCR}, {"TDA", ID3V22_TDA}, + {"TDY", ID3V22_TDY}, {"TEN", ID3V22_TEN}, {"TFT", ID3V22_TFT}, + {"TIM", ID3V22_TIM}, {"TKE", ID3V22_TKE}, {"TLA", ID3V22_TLA}, + {"TLE", ID3V22_TLE}, {"TMT", ID3V22_TMT}, {"TOA", ID3V22_TOA}, + {"TOF", ID3V22_TOF}, {"TOL", ID3V22_TOL}, {"TOR", ID3V22_TOR}, + {"TOT", ID3V22_TOT}, {"TP1", ID3V22_TP1}, {"TP2", ID3V22_TP2}, + {"TP3", ID3V22_TP3}, {"TP4", ID3V22_TP4}, {"TPA", ID3V22_TPA}, + {"TPB", ID3V22_TPB}, {"TRC", ID3V22_TRC}, {"TRD", ID3V22_TRD}, + {"TRK", ID3V22_TRK}, {"TSI", ID3V22_TSI}, {"TSS", ID3V22_TSS}, + {"TT1", ID3V22_TT1}, {"TT2", ID3V22_TT2}, {"TT3", ID3V22_TT3}, + {"TXT", ID3V22_TXT}, {"TXX", ID3V22_TXX}, {"TYE", ID3V22_TYE}, + {"UFI", ID3V22_UFI}, {"ULT", ID3V22_ULT}, {"WAF", ID3V22_WAF}, + {"WAR", ID3V22_WAR}, {"WAS", ID3V22_WAS}, {"WCM", ID3V22_WCM}, + {"WCP", ID3V22_WCP}, {"WPB", ID3V22_WPB}, {"WXX", ID3V22_WXX}, + {NULL, -1} +}; + +id3_lookup_t id3v24_lookup[] = +{ + {"AENC", ID3V24_AENC}, {"APIC", ID3V24_APIC}, {"ASPI", ID3V24_ASPI}, + {"COMM", ID3V24_COMM}, {"COMR", ID3V24_COMR}, {"ENCR", ID3V24_ENCR}, + {"EQU2", ID3V24_EQU2}, {"ETCO", ID3V24_ETCO}, {"GEOB", ID3V24_GEOB}, + {"GRID", ID3V24_GRID}, {"LINK", ID3V24_LINK}, {"MCDI", ID3V24_MCDI}, + {"MLLT", ID3V24_MLLT}, {"OWNE", ID3V24_OWNE}, {"PRIV", ID3V24_PRIV}, + {"PCNT", ID3V24_PCNT}, {"POPM", ID3V24_POPM}, {"POSS", ID3V24_POSS}, + {"RBUF", ID3V24_RBUF}, {"RVA2", ID3V24_RVA2}, {"RVRB", ID3V24_RVRB}, + {"SEEK", ID3V24_SEEK}, {"SIGN", ID3V24_SIGN}, {"SYLT", ID3V24_SYLT}, + {"SYTC", ID3V24_SYTC}, {"TALB", ID3V24_TALB}, {"TBPM", ID3V24_TBPM}, + {"TCOM", ID3V24_TCOM}, {"TCON", ID3V24_TCON}, {"TCOP", ID3V24_TCOP}, + {"TDEN", ID3V24_TDEN}, {"TDLY", ID3V24_TDLY}, {"TDOR", ID3V24_TDOR}, + {"TDRC", ID3V24_TDRC}, {"TDRL", ID3V24_TDRL}, {"TDTG", ID3V24_TDTG}, + {"TENC", ID3V24_TENC}, {"TEXT", ID3V24_TEXT}, {"TFLT", ID3V24_TFLT}, + {"TIPL", ID3V24_TIPL}, {"TIT1", ID3V24_TIT1}, {"TIT2", ID3V24_TIT2}, + {"TIT3", ID3V24_TIT3}, {"TKEY", ID3V24_TKEY}, {"TLAN", ID3V24_TLAN}, + {"TLEN", ID3V24_TLEN}, {"TMCL", ID3V24_TMCL}, {"TMED", ID3V24_TMED}, + {"TMOO", ID3V24_TMOO}, {"TOAL", ID3V24_TOAL}, {"TOFN", ID3V24_TOFN}, + {"TOLY", ID3V24_TOLY}, {"TOPE", ID3V24_TOPE}, {"TOWN", ID3V24_TOWN}, + {"TPE1", ID3V24_TPE1}, {"TPE2", ID3V24_TPE2}, {"TPE3", ID3V24_TPE3}, + {"TPE4", ID3V24_TPE4}, {"TPOS", ID3V24_TPOS}, {"TPRO", ID3V24_TPRO}, + {"TPUB", ID3V24_TPUB}, {"TRCK", ID3V24_TRCK}, {"TRSN", ID3V24_TRSN}, + {"TRSO", ID3V24_TRSO}, {"TSOA", ID3V24_TSOA}, {"TSOP", ID3V24_TSOP}, + {"TSOT", ID3V24_TSOT}, {"TSRC", ID3V24_TSRC}, {"TSSE", ID3V24_TSSE}, + {"TSST", ID3V24_TSST}, {"TXXX", ID3V24_TXXX}, {"UFID", ID3V24_UFID}, + {"USER", ID3V24_USER}, {"USLT", ID3V24_USLT}, {"WCOM", ID3V24_WCOM}, + {"WCOP", ID3V24_WCOP}, {"WOAF", ID3V24_WOAF}, {"WOAR", ID3V24_WOAR}, + {"WOAS", ID3V24_WOAS}, {"WORS", ID3V24_WORS}, {"WPAY", ID3V24_WPAY}, + {"WPUB", ID3V24_WPUB}, {"WXXX", ID3V24_WXXX}, {NULL, -1} +}; + +id3_lookup_t id3v23_lookup[] = +{ + {"AENC", ID3V23_AENC}, {"APIC", ID3V23_APIC}, {"COMM", ID3V23_COMM}, + {"COMR", ID3V23_COMR}, {"ENCR", ID3V23_ENCR}, {"EQUA", ID3V23_EQUA}, + {"ETCO", ID3V23_ETCO}, {"GEOB", ID3V23_GEOB}, {"GRID", ID3V23_GRID}, + {"IPLS", ID3V23_IPLS}, {"LINK", ID3V23_LINK}, {"MCDI", ID3V23_MCDI}, + {"MLLT", ID3V23_MLLT}, {"OWNE", ID3V23_OWNE}, {"PRIV", ID3V23_PRIV}, + {"PCNT", ID3V23_PCNT}, {"POPM", ID3V23_POPM}, {"POSS", ID3V23_POSS}, + {"RBUF", ID3V23_RBUF}, {"RVAD", ID3V23_RVAD}, {"RVRB", ID3V23_RVRB}, + {"SYLT", ID3V23_SYLT}, {"SYTC", ID3V23_SYTC}, {"TALB", ID3V23_TALB}, + {"TBPM", ID3V23_TBPM}, {"TCOM", ID3V23_TCOM}, {"TCON", ID3V23_TCON}, + {"TCOP", ID3V23_TCOP}, {"TDAT", ID3V23_TDAT}, {"TDLY", ID3V23_TDLY}, + {"TENC", ID3V23_TENC}, {"TEXT", ID3V23_TEXT}, {"TFLT", ID3V23_TFLT}, + {"TIME", ID3V23_TIME}, {"TIT1", ID3V23_TIT1}, {"TIT2", ID3V23_TIT2}, + {"TIT3", ID3V23_TIT3}, {"TKEY", ID3V23_TKEY}, {"TLAN", ID3V23_TLAN}, + {"TLEN", ID3V23_TLEN}, {"TMED", ID3V23_TMED}, {"TOAL", ID3V23_TOAL}, + {"TOFN", ID3V23_TOFN}, {"TOLY", ID3V23_TOLY}, {"TOPE", ID3V23_TOPE}, + {"TORY", ID3V23_TORY}, {"TOWN", ID3V23_TOWN}, {"TPE1", ID3V23_TPE1}, + {"TPE2", ID3V23_TPE2}, {"TPE3", ID3V23_TPE3}, {"TPE4", ID3V23_TPE4}, + {"TPOS", ID3V23_TPOS}, {"TPUB", ID3V23_TPUB}, {"TRCK", ID3V23_TRCK}, + {"TRDA", ID3V23_TRDA}, {"TRSN", ID3V23_TRSN}, {"TRSO", ID3V23_TRSO}, + {"TSIZ", ID3V23_TSIZ}, {"TSRC", ID3V23_TSRC}, {"TSSE", ID3V23_TSSE}, + {"TYER", ID3V23_TYER}, {"TXXX", ID3V23_TXXX}, {"UFID", ID3V23_UFID}, + {"USER", ID3V23_USER}, {"USLT", ID3V23_USLT}, {"WCOM", ID3V23_WCOM}, + {"WCOP", ID3V23_WCOP}, {"WOAF", ID3V23_WOAF}, {"WOAR", ID3V23_WOAR}, + {"WOAS", ID3V23_WOAS}, {"WORS", ID3V23_WORS}, {"WPAY", ID3V23_WPAY}, + {"WPUB", ID3V23_WPUB}, {"WXXX", ID3V23_WXXX}, {NULL, -1} +}; + + +typedef struct +{ + unsigned char *check; + int count; +} resync_t; + +typedef struct +{ + int unsync, extended, size; + char version[2]; +} id3header_t; + +static resync_t *checkunsync(char *syncCheck, int size) +{ + int i, j; + resync_t *sync; + + sync = malloc(sizeof(resync_t)); + + sync->check = syncCheck; + sync->count = 0; + + if(size == 0) + size = strlen(sync->check); + + for(i = 0; i < size; i++) + { + if(sync->check[i] == 0xFF && sync->check[i + 1] == 0x00) + { + for(j = i + 1; j < size - 1; j++) + syncCheck[j] = syncCheck[j + 1]; + sync->check[j] = '\0'; + sync->count++; + } + } + + return sync; +} + +static void unsync(char *data, char *bp) +{ + resync_t *unsynced; + char *syncFix = NULL; + int i; + + unsynced = checkunsync(data, 0); + while(unsynced->count > 0) + { + if(syncFix != NULL) + syncFix = realloc(syncFix, unsynced->count); + else + syncFix = malloc(unsynced->count); + memcpy(syncFix, bp, unsynced->count); + bp += unsynced->count; + for(i = 0; i < unsynced->count; i++) + data[4 - unsynced->count + i] = syncFix[i]; + free(unsynced); + unsynced = checkunsync(data, 0); + } + free(unsynced); + free(syncFix); +} + +/* + * Header: + * + * identifier: 3 bytes "ID3" ("3DI" if footer) + * version: 2 bytes + * flags: 1 byte + * tag size: 4 bytes + */ +static id3header_t *read_header(VFSFile *fp) +{ + id3header_t *id3_data = calloc(sizeof(id3header_t), 1); + char id3_flags, cToInt[4]; + int bottom = 0; + + vfs_fread(cToInt, 1, 3, fp); + if(strncmp(cToInt, "3DI", 3) == 0) + bottom = 1; + vfs_fread(id3_data->version, 1, 2, fp); + vfs_fread(&id3_flags, 1, 1, fp); + if((id3_flags & 0x80) == 0x80) + id3_data->unsync = 1; + if((id3_flags & 0x40) == 0x40 && id3_data->version[0] > 0x02) + id3_data->extended = 1; + vfs_fread(cToInt, 1, 4, fp); + id3_data->size = synchsafe2int(cToInt); + if(bottom == 1) + vfs_fseek(fp, -10 - id3_data->size, SEEK_CUR); + +#ifdef META_DEBUG + if(id3_data->version[0] == 0x04) + { + pdebug("Version: ID3v2.4", META_DEBUG); + } + else if(id3_data->version[0] == 0x03) + { + pdebug("Version: ID3v2.3", META_DEBUG); + } + else if(id3_data->version[0] == 0x02) + { + pdebug("Version: ID3v2.2", META_DEBUG); + } +#endif + if(id3_data->version[0] < 0x02 || id3_data->version[0] > 0x04) + { + free(id3_data); + return NULL; + } + + return id3_data; +} + +static int id3_lookupframe(char *tag, int tagver) +{ + int i; + + switch (tagver) { + case ID3v22: + for (i = 0; id3v22_lookup[i].frameid; i++) + if (!strcmp(tag, id3v22_lookup[i].frameid)) + return id3v22_lookup[i].code; + return -1; + break; + case ID3v23: + for (i = 0; id3v23_lookup[i].frameid; i++) + if (!strcmp(tag, id3v23_lookup[i].frameid)) + return id3v23_lookup[i].code; + return -1; + break; + case ID3v24: + for (i = 0; id3v23_lookup[i].frameid; i++) + if (!strcmp(tag, id3v24_lookup[i].frameid)) + return id3v24_lookup[i].code; + return -1; + break; + } + return -1; +} + +static framedata_t *parseFrame(char **bp, char *end, id3header_t *id3_data) +{ + static unsigned char frameid[5]; + unsigned char frameflags[2] = "", cToInt[5]; + int framesize, frameidcode; + framedata_t *framedata; + + /* TODO: Unsync, decompress, decrypt, grouping, data length */ + switch (id3_data->version[0]) { + case 2: + if (end - *bp < 6) + return NULL; + frameid[3] = 0; + memcpy(frameid, *bp, 3); + *bp += 3; + frameidcode = id3_lookupframe(frameid, ID3v22); + memcpy(cToInt, *bp, 3); + cToInt[3] = 0; + if(id3_data->unsync) + unsync(cToInt, *bp); + framesize = be24int(cToInt); + *bp += 3; + break; + case 3: + if (end - *bp < 10) + return NULL; + frameid[4] = 0; + memcpy(frameid, *bp, 4); + *bp += 4; + frameidcode = id3_lookupframe(frameid, ID3v23); + memcpy(cToInt, *bp, 4); + if(id3_data->unsync) + unsync(cToInt, *bp); + framesize = be2int(cToInt); + *bp += 4; + + memcpy(frameflags, *bp, 2); + *bp += 2; + break; + case 4: + if (end - *bp < 10) + return NULL; + frameid[4] = 0; + memcpy(frameid, *bp, 4); + *bp += 4; + frameidcode = id3_lookupframe(frameid, ID3v24); + memcpy(cToInt, *bp, 4); + framesize = synchsafe2int(cToInt); + *bp += 4; + + memcpy(frameflags, *bp, 2); + *bp += 2; + break; + default: + /* Should not be reached */ + return NULL; + } + + /* printf("found id:%s, size:%-8d\n", frameid, framesize); */ + if (framesize > end - *bp) + return NULL; + framedata = calloc(sizeof(framedata_t), 1); + framedata->frameid = frameidcode; + if(id3_data->unsync) + frameflags[1] |= 0x02; + framedata->flags = malloc(2); + memcpy(framedata->flags, frameflags, 2); + if(id3_data->version[0] == 0x04) + { + /* + * 4 bytes extra for compression or original size + * 1 byte extra for encyption + * 1 byte extra for grouping + */ + if((frameflags[1] & 0x08) == 0x08 || + (frameflags[1] & 0x01) == 0x01) + { + *bp += 4; + framesize -= 4; + } + if((frameflags[1] & 0x04) == 0x04) + { + *bp++; + framesize--; + } + if((frameflags[1] & 0x40) == 0x40) + { + *bp++; + framesize--; + } + } + else if(id3_data->version[0] == 0x03) + { + /* + * 4 bytes extra for compression or original size + * 1 byte extra for encyption + * 1 byte extra for grouping + */ + if((frameflags[1] & 0x80) == 0x80) + { + char tmp[4]; + + memcpy(tmp, *bp, 4); + *bp += 4; + if((frameflags[1] & 0x02) == 0x02) + unsync(tmp, *bp); + framesize -= 4; + } + if((frameflags[1] & 0x40) == 0x40) + { + *bp++; + framesize--; + } + if((frameflags[1] & 0x20) == 0x20) + { + *bp++; + framesize--; + } + } + framedata->len = framesize; + framedata->data = malloc(framesize); + memcpy(framedata->data, *bp, framesize); + *bp += framesize; + + /* Parse text appropriately to UTF-8. */ + if(frameid[0] == 'T' && strcmp(frameid, "TXXX") && + strcmp(frameid, "TXX")) + { + unsigned char *ptr, *data = NULL, *utf = NULL; + int encoding; + + ptr = framedata->data; + + if(framedata->len == 0) + { + framedata->data = realloc(framedata->data, 1); + strcpy(framedata->data, "\0"); + return framedata; + } + + encoding = *(ptr++); + data = realloc(data, framedata->len); + *(data + framedata->len - 1) = '\0'; + memcpy(data, ptr, framedata->len - 1); + if((framedata->flags[1] & 0x02) == 0x02) + { + resync_t *unsync = checkunsync(data, framedata->len); + framedata->len -= unsync->count; + free(unsync); + } + if(encoding == 0x00) + { + if(utf != NULL) + free(utf); + iso88591_to_utf8(data, framedata->len - 1, &utf); + } + else if(encoding == 0x01) + { + if(utf != NULL) + free(utf); + utf16bom_to_utf8(data, framedata->len - 1, &utf); + } + else if(encoding == 0x02) + { + if(utf != NULL) + free(utf); + utf16be_to_utf8(data, framedata->len - 1, &utf); + } + else if(encoding == 0x03) + { + utf = realloc(utf, framedata->len); + strcpy(utf, data); + } + framedata->len = strlen(utf) + 1; + framedata->data = realloc(framedata->data, framedata->len); + strcpy(framedata->data, utf); + framedata->data[framedata->len - 1] = '\0'; + free(utf); + free(data); + } + /* Or unsync. */ + else + { + unsigned char *data = NULL, *ptr; + + ptr = framedata->data; + + data = realloc(data, framedata->len); + // *(data + framedata->len - 1) = '\0'; + memcpy(data, ptr, framedata->len); + if((framedata->flags[1] & 0x02) == 0x02) + { + resync_t *unsync = checkunsync(data, framedata->len); + framedata->len -= unsync->count; + free(unsync); + } + framedata->data = realloc(framedata->data, framedata->len); + memcpy(framedata->data, data, framedata->len); + free(data); + } + + return framedata; +} + +static id3v2_t *readFrames(char *bp, char *end, id3header_t *id3_data) +{ + id3v2_t *id3v2 = calloc(sizeof(id3v2_t), 1); + + while(bp < end) + { + framedata_t *framedata = NULL; + + if(*bp == '\0') + break; + + framedata = parseFrame(&bp, end, id3_data); + + id3v2->items = realloc(id3v2->items, (id3v2->numitems + 1) * + sizeof(framedata_t *)); + id3v2->items[id3v2->numitems++] = framedata; + } + id3v2->version = id3_data->version[0]; + + return id3v2; +} + +/*unsigned char *ID3v2_parseText(framedata_t *frame) +{ + unsigned char *data = NULL, *utf = NULL, *ptr; + char encoding; + + ptr = frame->data; + + encoding = *(ptr++); + data = realloc(data, frame->len); + memset(data, '\0', frame->len); + memcpy(data, ptr, frame->len - 1); + if((frame->flags[1] & 0x02) == 0x02) + { + resync_t *unsync = checkunsync(data, 0); + free(unsync); + } + if(encoding == 0x00) + { + if(utf != NULL) + free(utf); + iso88591_to_utf8(data, &utf); + } + else if(encoding == 0x01) + { + if(utf != NULL) + free(utf); + utf16bom_to_utf8(data, frame->len - 1, &utf); + } + else if(encoding == 0x02) + { + if(utf != NULL) + free(utf); + utf16be_to_utf8(data, frame->len - 1, &utf); + } + else if(encoding == 0x03) + { + utf = realloc(utf, strlen(data) + 1); + strcpy(utf, data); + } + free(data); + + return utf; +} + +unsigned char *ID3v2_getData(framedata_t *frame) +{ + unsigned char *data = NULL, *ptr; + + ptr = frame->data; + + data = realloc(data, frame->len + 1); + memset(data, '\0', frame->len + 1); + memcpy(data, ptr, frame->len); + if((frame->flags[1] & 0x02) == 0x02) + { + resync_t *unsync = checkunsync(data, frame->len); + free(unsync); + } + + return data; +}*/ + +int findID3v2(VFSFile *fp) +{ + unsigned char tag_buffer[BUFFER_SIZE], *bp = tag_buffer; + int pos, search = -1, i, status = 0, charsRead; + + charsRead = vfs_fread(tag_buffer, 1, 10, fp); + pos = 0; + bp = tag_buffer; + while(status == 0 && !feof(fp)) + { + if(search == -1) + { + if((strncmp(bp, "ID3", 3) == 0 || + strncmp(bp, "3DI", 3) == 0)) + status = 1; + else + { + vfs_fseek(fp, 3, SEEK_END); + charsRead = vfs_fread(tag_buffer, 1, 3, fp); + search = -2; + } + } + else + { + if(search == -2) + { + bp = tag_buffer; + pos = vfs_ftell(fp); + if((strncmp(bp, "ID3", 3) == 0 || + strncmp(bp, "3DI", 3) == 0)) status = 1; + search = 1; + } + if(status != 1) + { + pos = vfs_ftell(fp) - BUFFER_SIZE; + vfs_fseek(fp, pos, SEEK_SET); + charsRead = vfs_fread(tag_buffer, 1, BUFFER_SIZE, + fp); + + bp = tag_buffer; + for(i = 0; i < charsRead - 3 && status == 0; + i++) + { + bp++; + if((strncmp(bp, "ID3", 3) == 0 || + strncmp(bp, "3DI", 3) == 0)) + status = 1; + } + if(status == 1) + pos += bp - tag_buffer; + pos -= BUFFER_SIZE - 9; + if((pos < -BUFFER_SIZE + 9 || ferror(fp)) && + status != 1) + status = -1; + } + } + /* + * An ID3v2 tag can be detected with the following pattern: + * + * $49 44 33 yy yy xx zz zz zz zz + * + * Where yy is less than $FF, xx is the 'flags' byte and zz is + * less than $80. + */ + if(status == 1 && *(bp + 3) < 0xFF && *(bp + 4) < 0xFF && + *(bp + 6) < 0x80 && *(bp + 7) < 0x80 && + *(bp + 8) < 0x80 && *(bp + 9) < 0x80) + status = 1; + else if(status != -1) + status = 0; + if(search == 0) + search = -1; + } + if(status < 0 || feof(fp)) + return -1; + else + return pos; +} + +id3v2_t *readID3v2(char *filename) +{ + VFSFile *fp; + id3v2_t *id3v2 = NULL; + int pos; + + fp = vfs_fopen(filename, "rb"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + pos = findID3v2(fp); + if(pos > -1) + { + id3header_t *id3_data; + char *tag_buffer, *bp; + + /* Found the tag. */ + vfs_fseek(fp, pos, SEEK_SET); + pdebug("Found ID3v2 tag...", META_DEBUG); + /* Read the header */ + id3_data = read_header(fp); + if(id3_data == NULL) + { + pdebug("Or not.", META_DEBUG); + vfs_fclose(fp); + return NULL; + } + /* Read tag into buffer */ + tag_buffer = malloc(id3_data->size); + vfs_fread(tag_buffer, 1, id3_data->size, fp); + bp = tag_buffer; + /* Skip extended header */ + if(id3_data->extended) + { + char cToInt[4]; + + memcpy(cToInt, bp, 4); + bp += 4; + if(id3_data->version[0] == 0x03 && + id3_data->unsync == 1) + unsync(cToInt, bp); + if(id3_data->version[0] > 0x03) + bp += synchsafe2int(cToInt); + else + bp += be2int(cToInt); + } + /* Read frames into id3v2_t */ + id3v2 = readFrames(bp, tag_buffer + id3_data->size, id3_data); + + free(tag_buffer); + free(id3_data); + } + + vfs_fclose(fp); + + return id3v2; +} + +void freeID3v2(id3v2_t *id3v2) +{ + int i; + + for(i = 0; i < id3v2->numitems; i++) + { + framedata_t *frame; + + frame = id3v2->items[i]; + free(frame->flags); + free(frame->data); + free(frame); + } + free(id3v2->items); + free(id3v2); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/ape.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,42 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef APE_H +#define APE_H 1 +typedef struct +{ + unsigned int len; + unsigned char *data, *name; +} apefielddata_t; + +typedef struct +{ + unsigned int numitems, version; + apefielddata_t **items; +} ape_t; + +#ifndef MAKE_BMP +int findAPE(VFSFile *); +#else +int findAPE(VFSFile *); +#endif +ape_t *readAPE(char *); +void freeAPE(ape_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/bmp_vfs.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,18 @@ +#ifndef TAG_BMP_VFS_H +#define TAG_BMP_VFS_H 1 +#include <libaudacious/vfs.h> +#define fopen(fp, set) vfs_fopen(fp, set); feof_ctr = 1 +#define fclose(fp) vfs_fclose(fp); feof_ctr = 0 +#define fread feof_ctr = vfs_fread +#define fwrite vfs_fwrite +#define fseek vfs_fseek +#define ftell vfs_ftell +#define feof(fp) feof_ctr == 0 +#define ferror(fp) feof_ctr == 0 +#define FILE VFSFile + +/* BMP VFS does not have vfs_feof, so we have a makeshift method. */ + +static size_t feof_ctr; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/cdaudio.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,31 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CDAUDIO_H +#define CDAUDIO_H 1 + +typedef struct +{ + unsigned char *title, *artist, *album, *mbid; +} cdaudio_t; + +cdaudio_t *readCDAudio(char *, char); +void freeCDAudio(cdaudio_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/endian.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,51 @@ +/* + * libmetatag: - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ENDIAN_H +#define ENDIAN_H 1 +#define le2long(le) (le2long_internal(((unsigned char *)(le)))) +#define le2long_internal(le) \ +((le[0] << 0) | (le[1] << 8) | (le[2] << 16) | (le[3] << 24) \ +(le[4] << 32) | (le[5] << 40) | (le[6] << 48) | (le[7] << 56)) +#define le2int(le) (le2int_internal(((unsigned char *)(le)))) +#define le2int_internal(le) \ +((le[0] << 0) | (le[1] << 8) | (le[2] << 16) | (le[3] << 24)) +#define le2short(le) (le2short_internal(((unsigned char *)(le)))) +#define le2short_internal(le) \ +((le[0] << 0) | (le[1] << 8)) +#define be2int(be) (be2int_internal(((unsigned char *)(be)))) +#define be2int_internal(be) \ +((be[3] << 0) | (be[2] << 8) | (be[1] << 16) | (be[0] << 24)) +#define be24int(be) (be24int_internal(((unsigned char *)(be)))) +#define be24int_internal(be) \ +((be[2] << 0) | (be[1] << 8) | (be[0] << 16)) +#define be2short(be) (be2short_internal(((unsigned char *)(be)))) +#define be2short_internal(be) \ +((be[1] << 0) | (be[0] << 8)) +#define flac2int(flac) (flac2int_internal(((unsigned char *)(flac)))) +#define flac2int_internal(flac) \ +((flac[1] << 16) | (flac[2] << 8) | (flac[3] << 0)) +#define synchsafe2int(synch) (synchsafe2int_internal(((unsigned char *)(synch)))) +#define synchsafe2int_internal(synch) \ +((synch[0] << 21) | (synch[1] << 14) | \ +(synch[2] << 7) | (synch[3] << 0)) +#define tagid2int(bp) be2int(bp) +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/id3v1.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,36 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ID3V1_H +#define ID3V1_H 1 + +typedef struct +{ + unsigned char *title, *artist, *album, *year, *comment, track, genre; +} id3v1_t; + +#ifndef MAKE_BMP +int findID3v1(VFSFile *); +#else +int findID3v1(VFSFile *); +#endif +id3v1_t *readID3v1(char *); +void freeID3v1(id3v1_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/id3v2.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,114 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ID3V2_H +#define ID3V2_H 1 + +typedef struct +{ + int frameid, len; + unsigned char *data, *flags; +} framedata_t; + +typedef struct +{ + int numitems, version; + framedata_t **items; +} id3v2_t; + +typedef struct +{ + char *frameid; + int code; +} id3_lookup_t; + +enum +{ + ID3v22, + ID3v23, + ID3v24 +}; + +enum +{ + ID3V24_AENC, ID3V24_APIC, ID3V24_ASPI, ID3V24_COMM, ID3V24_COMR, + ID3V24_ENCR, ID3V24_EQU2, ID3V24_ETCO, ID3V24_GEOB, ID3V24_GRID, + ID3V24_LINK, ID3V24_MCDI, ID3V24_MLLT, ID3V24_OWNE, ID3V24_PRIV, + ID3V24_PCNT, ID3V24_POPM, ID3V24_POSS, ID3V24_RBUF, ID3V24_RVA2, + ID3V24_RVRB, ID3V24_SEEK, ID3V24_SIGN, ID3V24_SYLT, ID3V24_SYTC, + ID3V24_TALB, ID3V24_TBPM, ID3V24_TCOM, ID3V24_TCON, ID3V24_TCOP, + ID3V24_TDEN, ID3V24_TDLY, ID3V24_TDOR, ID3V24_TDRC, ID3V24_TDRL, + ID3V24_TDTG, ID3V24_TENC, ID3V24_TEXT, ID3V24_TFLT, ID3V24_TIPL, + ID3V24_TIT1, ID3V24_TIT2, ID3V24_TIT3, ID3V24_TKEY, ID3V24_TLAN, + ID3V24_TLEN, ID3V24_TMCL, ID3V24_TMED, ID3V24_TMOO, ID3V24_TOAL, + ID3V24_TOFN, ID3V24_TOLY, ID3V24_TOPE, ID3V24_TOWN, ID3V24_TPE1, + ID3V24_TPE2, ID3V24_TPE3, ID3V24_TPE4, ID3V24_TPOS, ID3V24_TPRO, + ID3V24_TPUB, ID3V24_TRCK, ID3V24_TRSN, ID3V24_TRSO, ID3V24_TSOA, + ID3V24_TSOP, ID3V24_TSOT, ID3V24_TSRC, ID3V24_TSSE, ID3V24_TSST, + ID3V24_TXXX, ID3V24_UFID, ID3V24_USER, ID3V24_USLT, ID3V24_WCOM, + ID3V24_WCOP, ID3V24_WOAF, ID3V24_WOAR, ID3V24_WOAS, ID3V24_WORS, + ID3V24_WPAY, ID3V24_WPUB, ID3V24_WXXX +}; + +enum +{ + ID3V22_BUF, ID3V22_CNT, ID3V22_COM, ID3V22_CRA, ID3V22_CRM, ID3V22_ETC, + ID3V22_EQU, ID3V22_GEO, ID3V22_IPL, ID3V22_LNK, ID3V22_MCI, ID3V22_MLL, + ID3V22_PIC, ID3V22_POP, ID3V22_REV, ID3V22_RVA, ID3V22_SLT, ID3V22_STC, + ID3V22_TAL, ID3V22_TBP, ID3V22_TCM, ID3V22_TCO, ID3V22_TCR, ID3V22_TDA, + ID3V22_TDY, ID3V22_TEN, ID3V22_TFT, ID3V22_TIM, ID3V22_TKE, ID3V22_TLA, + ID3V22_TLE, ID3V22_TMT, ID3V22_TOA, ID3V22_TOF, ID3V22_TOL, ID3V22_TOR, + ID3V22_TOT, ID3V22_TP1, ID3V22_TP2, ID3V22_TP3, ID3V22_TP4, ID3V22_TPA, + ID3V22_TPB, ID3V22_TRC, ID3V22_TRD, ID3V22_TRK, ID3V22_TSI, ID3V22_TSS, + ID3V22_TT1, ID3V22_TT2, ID3V22_TT3, ID3V22_TXT, ID3V22_TXX, ID3V22_TYE, + ID3V22_UFI, ID3V22_ULT, ID3V22_WAF, ID3V22_WAR, ID3V22_WAS, ID3V22_WCM, + ID3V22_WCP, ID3V22_WPB, ID3V22_WXX +}; + +enum +{ + ID3V23_AENC, ID3V23_APIC, ID3V23_COMM, ID3V23_COMR, ID3V23_ENCR, + ID3V23_EQUA, ID3V23_ETCO, ID3V23_GEOB, ID3V23_GRID, ID3V23_IPLS, + ID3V23_LINK, ID3V23_MCDI, ID3V23_MLLT, ID3V23_OWNE, ID3V23_PRIV, + ID3V23_PCNT, ID3V23_POPM, ID3V23_POSS, ID3V23_RBUF, ID3V23_RVAD, + ID3V23_RVRB, ID3V23_SYLT, ID3V23_SYTC, ID3V23_TALB, ID3V23_TBPM, + ID3V23_TCOM, ID3V23_TCON, ID3V23_TCOP, ID3V23_TDAT, ID3V23_TDLY, + ID3V23_TENC, ID3V23_TEXT, ID3V23_TFLT, ID3V23_TIME, ID3V23_TIT1, + ID3V23_TIT2, ID3V23_TIT3, ID3V23_TKEY, ID3V23_TLAN, ID3V23_TLEN, + ID3V23_TMED, ID3V23_TOAL, ID3V23_TOFN, ID3V23_TOLY, ID3V23_TOPE, + ID3V23_TORY, ID3V23_TOWN, ID3V23_TPE1, ID3V23_TPE2, ID3V23_TPE3, + ID3V23_TPE4, ID3V23_TPOS, ID3V23_TPUB, ID3V23_TRCK, ID3V23_TRDA, + ID3V23_TRSN, ID3V23_TRSO, ID3V23_TSIZ, ID3V23_TSRC, ID3V23_TSSE, + ID3V23_TYER, ID3V23_TXXX, ID3V23_UFID, ID3V23_USER, ID3V23_USLT, + ID3V23_WCOM, ID3V23_WCOP, ID3V23_WOAF, ID3V23_WOAR, ID3V23_WOAS, + ID3V23_WORS, ID3V23_WPAY, ID3V23_WPUB, ID3V23_WXXX +}; + + +unsigned char *ID3v2_parseText(framedata_t *); +unsigned char *ID3v2_getData(framedata_t *); +#ifndef MAKE_BMP +int findID3v2(VFSFile *); +#else +int findID3v2(VFSFile *); +#endif +id3v2_t *readID3v2(char *); +void freeID3v2(id3v2_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/itunes.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,36 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ITUNES_H +#define ITUNES_H 1 +typedef struct +{ + unsigned char *title, *artist, *album, *genre, *year, *copyright, + track, maxtrack, disc, maxdisc; +} itunes_t; + +#ifndef MAKE_BMP +int findiTunes(VFSFile *); +#else +int findiTunes(VFSFile *); +#endif +itunes_t *readiTunes(char *); +void freeiTunes(itunes_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/tags.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,77 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef METATAGS_H +#define METATAGS_H 1 + +#include <libaudacious/vfs.h> + +#include "wma.h" +#include "id3v2.h" +#include "id3v1.h" +#include "vorbis.h" +#include "itunes.h" +#include "ape.h" +#include "cdaudio.h" + +extern const char *genre_list[148]; + +/* + * Note: This struct has some signs to determine what tags a file has + * and it is left to the program to interpret them. The main unsigned chars + * are merely used as references directly to something in one of the substructs. + * + */ + +typedef struct { + unsigned char *artist, + *title, + *mb, + *album, + *year, + *track, + *genre; + int has_wma, + has_id3v1, + has_id3v2, + has_ape, + has_vorbis, + has_flac, + has_oggflac, + has_speex, + has_itunes, + has_cdaudio, + prefer_ape; + wma_t *wma; + id3v1_t *id3v1; + id3v2_t *id3v2; + ape_t *ape; + vorbis_t *vorbis, + *flac, + *oggflac, + *speex; + itunes_t *itunes; + cdaudio_t *cdaudio; +} metatag_t; + +void get_tag_data(metatag_t *, char *, int); +metatag_t *metatag_new(void); +void metatag_delete(metatag_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/unicode.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,29 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef UNICODE_H +#define UNICODE_H 1 +wchar_t *utf8_to_wchar(unsigned char *, size_t); +unsigned char *wchar_to_utf8(wchar_t *, size_t); +void iso88591_to_utf8(unsigned char *, size_t, unsigned char **); +void utf16bom_to_utf8(unsigned char *, size_t, unsigned char **); +void utf16be_to_utf8(unsigned char *, size_t, unsigned char **); +void utf16le_to_utf8(unsigned char *, size_t, unsigned char **); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/vorbis.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,58 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef VORBIS_H +#define VORBIS_H 1 + +#define READ_VORBIS 1 +#define READ_FLAC 2 +#define READ_OGGFLAC 3 +#define READ_SPEEX 4 + +typedef struct +{ + unsigned int len; + unsigned char *data, *name; +} vorbisfielddata_t; + +typedef struct +{ + unsigned int numitems, vendorlen; + unsigned char *vendor; + vorbisfielddata_t **items; +} vorbis_t; + +#ifndef MAKE_BMP +int findVorbis(VFSFile *); +int findFlac(VFSFile *); +int findOggFlac(VFSFile *); +int findSpeex(VFSFile *); +#else +int findVorbis(VFSFile *); +int findFlac(VFSFile *); +int findOggFlac(VFSFile *); +int findSpeex(VFSFile *); +#endif +vorbis_t *readVorbis(char *); +vorbis_t *readFlac(char *); +vorbis_t *readOggFlac(char *); +vorbis_t *readSpeex(char *); +void freeVorbis(vorbis_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/include/wma.h Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,42 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef WMA_H +#define WMA_H 1 +typedef struct +{ + unsigned char *data, *name; + int type; +} attribute_t; + +typedef struct +{ + unsigned int numitems; + attribute_t **items; +} wma_t; + +#ifndef MAKE_BMP +int findWMA(VFSFile *); +#else +int findWMA(VFSFile *); +#endif +wma_t *readWMA(char *); +void freeWMA(wma_t *); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/itunes.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,303 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "include/bmp_vfs.h" +#include "include/itunes.h" +#include "include/endian.h" +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#define BUFFER_SIZE 4096 +#define NAME_ATOM ((0xa9 << 24) | ('n' << 16) | ('a' << 8) | ('m' << 0)) +#define ARTIST_ATOM ((0xa9 << 24) | ('A' << 16) | ('R' << 8) | ('T' << 0)) +#define ALBUM_ATOM ((0xa9 << 24) | ('a' << 16) | ('l' << 8) | ('b' << 0)) +#define YEAR_ATOM ((0xa9 << 24) | ('d' << 16) | ('a' << 8) | ('y' << 0)) +#define GENRE_ATOM (('g' << 24) | ('n' << 16) | ('r' << 8) | ('e' << 0)) +#define TRACK_ATOM (('t' << 24) | ('r' << 16) | ('k' << 8) | ('n' << 0)) +#define DISC_ATOM (('d' << 24) | ('i' << 16) | ('s' << 8) | ('k' << 0)) +#define COPYRIGHT_ATOM (('c' << 24) | ('p' << 16) | ('r' << 8) | ('t' << 0)) + +static itunes_t *readAtoms(VFSFile *fp) +{ + itunes_t *itunes = calloc(sizeof(itunes_t), 1); + unsigned char *tag_buffer, *bp, cToInt[4]; + int size, meta_size; + + vfs_fread(cToInt, 1, 4, fp); + size = be2int(cToInt) - 4; + tag_buffer = malloc(size); + vfs_fread(tag_buffer, 1, size, fp); + bp = tag_buffer; + bp += 8; + while(bp - tag_buffer < size) + { + unsigned char **tag_data = NULL; + switch(tagid2int(bp)) + { + case NAME_ATOM: + tag_data = &itunes->title; + break; + case ARTIST_ATOM: + tag_data = &itunes->artist; + break; + case ALBUM_ATOM: + tag_data = &itunes->album; + break; + /* + * Genre is weird. I don't know how user input genres + * work. Use at your own risk. Is terminated with an + * extra 0. + */ + case GENRE_ATOM: + tag_data = &itunes->genre; + break; + case YEAR_ATOM: + tag_data = &itunes->year; + break; + case COPYRIGHT_ATOM: + tag_data = &itunes->copyright; + break; + } + if( tagid2int(bp) == NAME_ATOM || + tagid2int(bp) == ARTIST_ATOM || + tagid2int(bp) == ALBUM_ATOM || + tagid2int(bp) == GENRE_ATOM || + tagid2int(bp) == YEAR_ATOM || + tagid2int(bp) == COPYRIGHT_ATOM) + { + bp += 4; + memcpy(cToInt, bp, 4); + meta_size = be2int(cToInt) - 16; + bp += 16; + *tag_data = realloc(*tag_data, meta_size + 1); + *(*tag_data + meta_size) = '\0'; + strncpy(*tag_data, bp, meta_size); + bp += meta_size; + } + /* + * Track number is easier, but unverified. + * Disc number is similar. Doesn't skip at end though. + * + * Assuming max of what a char can hold. + */ + else if(tagid2int(bp) == TRACK_ATOM || + tagid2int(bp) == DISC_ATOM) + { + unsigned char *tag_num_data, *tag_num_max_data, skip; + if(tagid2int(bp) == TRACK_ATOM) + { + tag_num_data = &itunes->track; + tag_num_max_data = &itunes->maxtrack; + skip = 2; + } + else if(tagid2int(bp) == DISC_ATOM) + { + tag_num_data = &itunes->disc; + tag_num_max_data = &itunes->maxdisc; + skip = 0; + } + bp += 4; + memcpy(cToInt, bp, 4); + meta_size = be2int(cToInt) - 16; + bp += 19; + *tag_num_data = *bp; + bp += 2; + *tag_num_max_data = *bp; + bp += 1 + skip; + } + /* + * TODO: + * + * I'd like to handle rtng (Rating) but I don't know how. + * What are the xxIDs? aART? + * Where is Grouping, BPM, Composer? + * + * In any case, do not handle anything else. + */ + else + { + bp -= 4; + memcpy(cToInt, bp, 4); + meta_size = be2int(cToInt); + bp += meta_size; + } + bp += 4; + } + + free(tag_buffer); + + return itunes; +} + +int findiTunes(VFSFile *fp) +{ + unsigned char *tag_buffer, *bp, cToInt[4], *start_pos; + int parent_size, atom_size, pos = 0; + + /* + * Find the ILST atom and point the file pointer there and return + * the atom size. + * + * Please note that this could easily not work (especially when not + * encoded with iTunes) as this is mainly based off of a reference + * file encoded by iTunes and the QTFileFormat documentation released + * by Apple. It's not based off any official documentation of M4A. + * + * As a result of not being based off official documentation, this is + * EXTREMELY likely to return 0 (i.e. no data found). + * + * First we assume that ftyp is the first atom, and M4A is the type. + */ + vfs_fread(cToInt, 1, 4, fp); + atom_size = be2int(cToInt) - 4; + tag_buffer = malloc(8); + vfs_fread(tag_buffer, 1, 8, fp); + if(strncmp(tag_buffer, "ftypM4A ", 8)) + { + free(tag_buffer); + return -1; + } + vfs_fseek(fp, -8, SEEK_CUR); + tag_buffer = realloc(tag_buffer, atom_size); + vfs_fread(tag_buffer, 1, atom_size, fp); + /* Now keep skipping until we hit a moov container atom */ + while(!feof(fp)) + { + vfs_fread(cToInt, 1, 4, fp); + atom_size = be2int(cToInt) - 4; + tag_buffer = realloc(tag_buffer, atom_size); + pos = ftell(fp); + vfs_fread(tag_buffer, 1, atom_size, fp); + if(!strncmp(tag_buffer, "moov", 4)) + break; + } + if(feof(fp)) + { + free(tag_buffer); + return -1; + } + parent_size = atom_size; + /* Now looking for child udta atom (NOT in trak) */ + bp = tag_buffer + 4; + while(bp - tag_buffer < parent_size) + { + memcpy(cToInt, bp, 4); + atom_size = be2int(cToInt) - 4; + bp += 4; + if(!strncmp(bp, "udta", 4)) + break; + bp += atom_size; + } + if(strncmp(bp, "udta", 4)) + { + free(tag_buffer); + return -1; + } + parent_size = atom_size; + start_pos = bp; + bp += 4; + /* Now looking for child meta atom */ + while(bp - start_pos < parent_size) + { + memcpy(cToInt, bp, 4); + atom_size = be2int(cToInt) - 4; + bp += 4; + if(!strncmp(bp, "meta", 4)) + break; + bp += atom_size; + } + if(strncmp(bp, "meta", 4)) + { + free(tag_buffer); + return -1; + } + parent_size = atom_size; + start_pos = bp; + bp += 8; + /* Now looking for child ilst atom */ + while(bp - start_pos < parent_size) + { + memcpy(cToInt, bp, 4); + atom_size = be2int(cToInt) - 4; + bp += 4; + if(!strncmp(bp, "ilst", 4)) + break; + bp += atom_size; + } + if(strncmp(bp, "ilst", 4)) + { + free(tag_buffer); + return -1; + } + bp -= 4; + vfs_fseek(fp, bp - tag_buffer + pos, SEEK_SET); + + free(tag_buffer); + + return atom_size; +} + +itunes_t *readiTunes(char *filename) +{ + VFSFile *fp; + itunes_t *itunes; + int status; + + fp = vfs_fopen(filename, "r"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + status = findiTunes(fp); + if(status == -1) + { + vfs_fclose(fp); + return NULL; + } + itunes = readAtoms(fp); + + vfs_fclose(fp); + + return itunes; +} + +void freeiTunes(itunes_t *itunes) +{ + if(itunes->title != NULL) + free(itunes->title); + if(itunes->artist != NULL) + free(itunes->artist); + if(itunes->album != NULL) + free(itunes->album); + if(itunes->year != NULL) + free(itunes->year); + if(itunes->genre != NULL) + free(itunes->genre); + free(itunes); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/tags.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,820 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libaudacious/vfs.h" + +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#include "include/tags.h" +#include "include/endian.h" + +void tag_exists(metatag_t *meta, char *filename) +{ + VFSFile *fp; + int status = 0; + + /* Check for CD Audio + if(findCDAudio(filename)) + { + pdebug("Found CD Audio...", META_DEBUG); + tags[0] = CD_AUDIO; + + return tags; + }*/ + + fp = vfs_fopen(filename, "r"); + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + + return; + } + + /* Check for ID3v1 */ + vfs_fseek(fp, -128, SEEK_END); + if(findID3v1(fp)) + { + pdebug("Found ID3v1 tag...", META_DEBUG); + meta->has_id3v1 = 1; + } + + /* Check for ID3v2 */ + vfs_fseek(fp, 0, SEEK_SET); + status = findID3v2(fp); + if(status > -1) + { + pdebug("Found ID3v2 tag...", META_DEBUG); + meta->has_id3v2 = 1; + } + status = 0; + + /* Check for Ogg Vorbis */ + vfs_fseek(fp, 0, SEEK_SET); + status = findVorbis(fp); + if(status > -1) + { + pdebug("Found Vorbis comment block...", META_DEBUG); + meta->has_vorbis = 1; + } + status = 0; + + /* Check for FLAC */ + vfs_fseek(fp, 0, SEEK_SET); + if(findFlac(fp)) + { + pdebug("Found FLAC tag...", META_DEBUG); + meta->has_flac = 1; + } + + /* Check for OggFLAC */ + vfs_fseek(fp, 0, SEEK_SET); + status = findOggFlac(fp); + if(status > -1) + { + pdebug("Found OggFLAC...", META_DEBUG); + meta->has_oggflac = 1; + } + status = 0; + + /* Check for Speex */ + vfs_fseek(fp, 0, SEEK_SET); + status = findSpeex(fp); + if(status > -1) + { + pdebug("Found Speex...", META_DEBUG); + meta->has_speex = 1; + } + status = 0; + + /* Check for APE */ + vfs_fseek(fp, 0, SEEK_SET); + status = findAPE(fp); + if(status > 0) + { + pdebug("Found APE tag...", META_DEBUG); + meta->has_ape = 1; + } + status = 0; + + /* Check for iTunes M4A */ + vfs_fseek(fp, 0, SEEK_SET); + status = findiTunes(fp); + if(status > -1) + { + pdebug("Found iTunes tag...", META_DEBUG); + meta->has_itunes = 1; + } + + /* Check for the devil tag: WMA */ + vfs_fseek(fp, 0, SEEK_SET); + status = findWMA(fp); + if(status > -1) + { + pdebug("Found WMA tag...", META_DEBUG); + meta->has_wma = 1; + } + + vfs_fclose(fp); + + return; +} + +void metaCD(metatag_t *meta, char *filename, int track) +{ + int tmp; + + pdebug("Getting CD Audio metadata...", META_DEBUG); + meta->cdaudio = readCDAudio(filename, track); + if(meta->cdaudio == NULL) + { + pdebug("Error getting metadata", META_DEBUG); + + return; + } + + meta->has_cdaudio = 1; + + pdebug("Reading metadata into structs...", META_DEBUG); + meta->artist = meta->cdaudio->artist; + meta->title = meta->cdaudio->title; + meta->mb = realloc(meta->mb, strlen(meta->cdaudio->mbid) + 1); + strcpy(meta->mb, meta->cdaudio->mbid); + meta->album = meta->cdaudio->album; + meta->year = NULL; + meta->genre = NULL; + /* Special track handling... Yay! */ + meta->track = realloc(meta->track, 4); + tmp = snprintf(meta->track, 3, "%d", track); + *(meta->track + tmp) = '\0'; + + return; +} /* End CD Audio support */ + +static ape_t *fetchAPE(char *filename) +{ + ape_t *ape; + + pdebug("Getting APE tag metadata...", META_DEBUG); + ape = readAPE(filename); + + return ape; +} /* End APE read */ + +void metaAPE(metatag_t *meta) +{ + int i, t = 0; + ape_t *ape = meta->ape; + + for(i = 0; i < ape->numitems; i++) + { + apefielddata_t *item = ape->items[i]; + + if(!strcmp(item->name, "Title")) + { + pdebug("Found Title!", META_DEBUG); + meta->title = item->data; + } + else if(!strcmp(item->name, "Artist")) + { + pdebug("Found Artist!", META_DEBUG); + meta->artist = item->data; + } + else if(strcmp(item->name, "Album") == 0) + { + pdebug("Found Album!", META_DEBUG); + meta->album = item->data; + } + else if(strcmp(item->name, "Year") == 0) + { + pdebug("Found Year!", META_DEBUG); + meta->year = item->data; + } + else if(strcmp(item->name, "Genre") == 0) + { + pdebug("Found Genre!", META_DEBUG); + meta->genre = realloc(meta->genre, strlen(item->data) + + 1); + strcpy(meta->genre, item->data); + } + else if(strcmp(item->name, "Track") == 0) + { + pdebug("Found Track!", META_DEBUG); + meta->track = realloc(meta->track, strlen(item->data) + + 1); + strcpy(meta->track, item->data); + } + else if(strcmp(item->name, "Comment") == 0) + { + unsigned char *comment_value = NULL, *eq_pos, *sep_pos, + *subvalue; + + subvalue = item->data; + sep_pos = strchr(subvalue, '|'); + while(sep_pos != NULL || t == 0) + { + t = 1; + if(sep_pos != NULL) + *sep_pos = '\0'; + comment_value = realloc(comment_value, + strlen(subvalue) + 1); + strcpy(comment_value, subvalue); + if(sep_pos != NULL) + sep_pos++; + eq_pos = strchr(comment_value, '='); + if(eq_pos != NULL) + { + *eq_pos = '\0'; + eq_pos++; + if(!strcmp(comment_value, + "musicbrainz_trackid")) + { + /* ??? */ + pdebug("Found MusicBrainz Track ID!", META_DEBUG); + meta->mb = realloc(meta->mb, + strlen(eq_pos) + 1); + strcpy(meta->mb, eq_pos); + break; + } + } + if(sep_pos != NULL) + { + t = 0; + subvalue = sep_pos; + sep_pos = strchr(subvalue, '|'); + } + } + t = 0; + if(comment_value != NULL) + free(comment_value); + } + } +} /* End APE parsing */ + +static vorbis_t *fetchVorbis(char *filename, int tag_type) +{ + vorbis_t *comments = NULL; + + /* Several slightly different methods of getting the data... */ + if(tag_type == READ_VORBIS) + { + pdebug("Getting Vorbis comment metadata...", META_DEBUG); + comments = readVorbis(filename); + } + else if(tag_type == READ_FLAC) + { + pdebug("Getting FLAC tag metadata...", META_DEBUG); + comments = readFlac(filename); + } + else if(tag_type == READ_OGGFLAC) + { + pdebug("Getting OggFLAC tag metadata...", META_DEBUG); + comments = readOggFlac(filename); + } + else if(tag_type == READ_SPEEX) + { + pdebug("Getting Speex tag metadata...", META_DEBUG); + comments = readSpeex(filename); + } + if(comments == NULL) + pdebug("Error in Vorbis Comment read", META_DEBUG); + + return comments; +} /* End Vorbis/FLAC/OggFLAC/Speex read */ + +void metaVorbis(metatag_t *meta) +{ + int i; + vorbis_t *comments = NULL; + + /* I'm not expecting more than one vorbis tag */ + if(meta->has_vorbis) + comments = meta->vorbis; + else if(meta->has_flac) + comments = meta->flac; + else if(meta->has_oggflac) + comments = meta->oggflac; + else if(meta->has_speex) + comments = meta->speex; + if(comments == NULL) + return; + + for(i = 0; i < comments->numitems; i++) + { + vorbisfielddata_t *field = comments->items[i]; + if(!fmt_strcasecmp(field->name, "TITLE")) + { + pdebug("Found Title!", META_DEBUG); + + meta->title = field->data; + } + /* Prefer PERFORMER to ARTIST */ + else if(!fmt_strcasecmp(field->name, "PERFORMER")) + { + pdebug("Found Artist!", META_DEBUG); + + meta->artist = field->data; + } + else if(!fmt_strcasecmp(field->name, "ARTIST") && meta->artist == NULL) + { + pdebug("Found Artist!", META_DEBUG); + + meta->artist = field->data; + } + else if(!fmt_strcasecmp(field->name, "ALBUM")) + { + pdebug("Found Album!", META_DEBUG); + + meta->album = field->data; + } + else if(!fmt_strcasecmp(field->name, "MUSICBRAINZ_TRACKID")) + { + pdebug("Found MusicBrainz Track ID!", META_DEBUG); + + meta->mb = realloc(meta->mb, strlen(field->data) + 1); + memset(meta->mb, '\0', strlen(field->data) + 1); + memcpy(meta->mb, field->data, strlen(field->data)); + } + else if(!fmt_strcasecmp(field->name, "GENRE")) + { + pdebug("Found Genre!", META_DEBUG); + + meta->genre = realloc(meta->genre, strlen(field->data) + + 1); + memset(meta->genre, '\0', strlen(field->data) + 1); + memcpy(meta->genre, field->data, strlen(field->data)); + } + else if(!fmt_strcasecmp(field->name, "TRACKNUMBER")) + { + pdebug("Found Track!", META_DEBUG); + + meta->track = realloc(meta->track, strlen(field->data) + + 1); + memset(meta->track, '\0', strlen(field->data) + 1); + memcpy(meta->track, field->data, strlen(field->data)); + } + } + + return; +} /* End Vorbis/FLAC/OggFLAC/Speex parsing */ + +static id3v2_t *fetchID3v2(char *filename) +{ + id3v2_t *id3v2; + + pdebug("Getting ID3v2 tag metadata...", META_DEBUG); + id3v2 = readID3v2(filename); + if(id3v2 == NULL) + pdebug("Error in readID3v2()", META_DEBUG); + + return id3v2; +} /* End ID3v2 read */ + +void metaID3v2(metatag_t *meta) +{ + int i; + id3v2_t *id3v2 = meta->id3v2; + + for(i = 0; i < id3v2->numitems; i++) + { + unsigned char *data = NULL, *utf = NULL; + framedata_t *frame = id3v2->items[i]; + if( (id3v2->version == 2 && frame->frameid == ID3V22_TT2) || + (id3v2->version == 3 && frame->frameid == ID3V23_TIT2) || + (id3v2->version == 4 && frame->frameid == ID3V24_TIT2)) + { + pdebug("Found Title!", META_DEBUG); + + meta->title = frame->data; + } + else if((id3v2->version == 2 && frame->frameid == ID3V22_TP1) || + (id3v2->version == 3 && frame->frameid == ID3V23_TPE1) || + (id3v2->version == 4 && frame->frameid == ID3V24_TPE1)) + { + pdebug("Found Artist!", META_DEBUG); + + meta->artist = frame->data; + } + else if((id3v2->version == 2 && frame->frameid == ID3V22_TAL) || + (id3v2->version == 3 && frame->frameid == ID3V23_TALB) || + (id3v2->version == 4 && frame->frameid == ID3V24_TALB)) + { + pdebug("Found Album!", META_DEBUG); + + meta->album = frame->data; + } + /* No strict year for ID3v2.4 */ + else if((id3v2->version == 2 && frame->frameid == ID3V22_TYE) || + (id3v2->version == 3 && frame->frameid == ID3V23_TYER)) + { + pdebug("Found Year!", META_DEBUG); + + meta->year = frame->data; + } + /* Won't translate ID3v1 genres yet */ + else if((id3v2->version == 2 && frame->frameid == ID3V22_TCO) || + (id3v2->version == 3 && frame->frameid == ID3V23_TCON) || + (id3v2->version == 4 && frame->frameid == ID3V24_TCON)) + { + pdebug("Found Genre!", META_DEBUG); + + meta->genre = realloc(meta->genre, frame->len); + memset(meta->genre, '\0', frame->len); + memcpy(meta->genre, frame->data, frame->len); + } + else if((id3v2->version == 2 && frame->frameid == ID3V22_TRK) || + (id3v2->version == 3 && frame->frameid == ID3V23_TRCK) || + (id3v2->version == 4 && frame->frameid == ID3V24_TRCK)) + { + pdebug("Found Track!", META_DEBUG); + + meta->track = realloc(meta->track, frame->len); + memset(meta->track, '\0', frame->len); + memcpy(meta->track, frame->data, frame->len); + } + else if((id3v2->version == 2 && frame->frameid == ID3V22_UFI) || + (id3v2->version == 3 && frame->frameid == ID3V23_UFID) || + (id3v2->version == 4 && frame->frameid == ID3V24_UFID)) + { + data = frame->data; + + if(!strcmp(data, "http://musicbrainz.org")) + { + pdebug("Found MusicBrainz Track ID!", META_DEBUG); + + utf = data + 23; + + meta->mb = realloc(meta->mb, frame->len - 22); + memcpy(meta->mb, utf, frame->len - 23); + *(meta->mb + frame->len - 23) = 0; + } + } + } +} /* End ID3v2 parsing */ + +static itunes_t *fetchiTunes(char *filename) +{ + itunes_t *itunes; + + pdebug("Getting iTunes tag metadata...", META_DEBUG); + itunes = readiTunes(filename); + + return itunes; +} /* End M4A read */ + +void metaiTunes(metatag_t *meta) +{ + itunes_t *itunes = meta->itunes; + int tmp; + + if(itunes->title != NULL) + { + pdebug("Found Title!", META_DEBUG); + + meta->title = itunes->title; + } + if(itunes->artist != NULL) + { + pdebug("Found Artist!", META_DEBUG); + + meta->artist = itunes->artist; + } + if(itunes->album != NULL) + { + pdebug("Found Album!", META_DEBUG); + + meta->album = itunes->album; + } + /* I don't read genre (yet) and I won't read max track number. */ + if(itunes->track > 0 && itunes->track != 255) + { + pdebug("Found Track!", META_DEBUG); + + meta->track = realloc(meta->track, 4); + tmp = snprintf(meta->track, 3, "%d", itunes->track); + *(meta->track + tmp) = '\0'; + } + if(itunes->year != NULL) + { + pdebug("Found Year!", META_DEBUG); + + meta->year = itunes->year; + } +} /* End M4A parsing */ + +static wma_t *fetchWMA(char *filename) +{ + wma_t *wma; + + pdebug("Getting WMA metadata...", META_DEBUG); + wma = readWMA(filename); + + return wma; +} /* End WMA read */ + +void metaWMA(metatag_t *meta) +{ + wma_t *wma = meta->wma; + int i, tmp; + + for(i = 0; i < wma->numitems; i++) + { + attribute_t *attribute = wma->items[i]; + + if(!strcmp(attribute->name, "Title")) + { + pdebug("Found Title!", META_DEBUG); + + meta->title = attribute->data; + } + else if(!strcmp(attribute->name, "Author")) + { + pdebug("Found Artist!", META_DEBUG); + + meta->artist = attribute->data; + } + else if(!strcmp(attribute->name, "WM/AlbumTitle")) + { + pdebug("Found Album!", META_DEBUG); + + meta->album = attribute->data; + } + else if(!strcmp(attribute->name, "WM/Year")) + { + pdebug("Found Year!", META_DEBUG); + + meta->year = attribute->data; + } + else if(!strcmp(attribute->name, "WM/Genre")) + { + pdebug("Found Genre!", META_DEBUG); + + meta->genre = realloc(meta->genre, + strlen(attribute->data) + 1); + strcpy(meta->genre, attribute->data); + } + else if(!strcmp(attribute->name, "WM/TrackNumber")) + { + pdebug("Found Track!", META_DEBUG); + + meta->track = realloc(meta->track, 4); + tmp = snprintf(meta->track, 3, "%d", + le2int(attribute->data)); + *(meta->track + tmp) = '\0'; + } + } +} + +static id3v1_t *fetchID3v1(char *filename) +{ + id3v1_t *id3v1; + + pdebug("Getting ID3v1 tag metadata...", META_DEBUG); + id3v1 = readID3v1(filename); + + return id3v1; +} /* End ID3v1 read */ + +void metaID3v1(metatag_t *meta) +{ + int tmp; + id3v1_t *id3v1 = meta->id3v1; + + if(id3v1->title != NULL) + { + pdebug("Found Title!", META_DEBUG); + + meta->title = id3v1->title; + } + if(id3v1->artist != NULL) + { + pdebug("Found Artist!", META_DEBUG); + + meta->artist = id3v1->artist; + } + if(id3v1->album != NULL) + { + pdebug("Found Album!", META_DEBUG); + + meta->album = id3v1->album; + } + if(id3v1->year != NULL) + { + pdebug("Found Year!", META_DEBUG); + + meta->year = id3v1->year; + } + if(id3v1->track != 255) + { + pdebug("Found Track!", META_DEBUG); + + meta->track = realloc(meta->track, 4); + tmp = snprintf(meta->track, 3, "%d", id3v1->track); + *(meta->track + tmp) = '\0'; + } + /* I assume unassigned genre's are 255 */ + if(id3v1->genre != 255 && id3v1->genre < sizeof(genre_list) + / sizeof(char *)) + { + pdebug("Found Genre!", META_DEBUG); + + meta->genre = realloc(meta->genre, + strlen(genre_list[id3v1->genre]) + 1); + strcpy(meta->genre, genre_list[id3v1->genre]); + } + /* + * This next one is unofficial, but maybe someone will use it. + * I don't think anyone's going to trigger this by accident. + * + * Specification: + * In comment field (ID3v1 or ID3v1.1): + * 1 byte: \0 + * 10 bytes: "MBTRACKID=" identifier + * 16 bytes: binary representation of Track ID + * 1 byte: \0 + * + * e.g. for "Missing" on Evanescence's "Bring Me to Life" single + * + * Track ID = 9c2567cb-4a8b-4096-b105-dada6b95a08b + * + * Therefore, comment field would equal: + * "MBID: \156%g\203J\139@\150\177\005\218\218k\149\160\139" + * in ASCII (with three digit decimal escape characters) + */ + if(!strncmp((id3v1->comment) + 1, "MBTRACKID=", 10)) + { + pdebug("Found MusicBrainz Track ID!", META_DEBUG); + + meta->mb = realloc(meta->mb, 37); + tmp = sprintf(meta->mb, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + id3v1->comment[11], + id3v1->comment[12], + id3v1->comment[13], + id3v1->comment[14], + id3v1->comment[15], + id3v1->comment[16], + id3v1->comment[17], + id3v1->comment[18], + id3v1->comment[19], + id3v1->comment[20], + id3v1->comment[21], + id3v1->comment[22], + id3v1->comment[23], + id3v1->comment[24], + id3v1->comment[25], + id3v1->comment[26]); + *(meta->mb + tmp) = '\0'; + } +} /* End ID3v1 parsing */ + +void get_tag_data(metatag_t *meta, char *filename, int track) +{ + if(track > 0) + { + metaCD(meta, filename, track); + + return; + } + else + { + tag_exists(meta, filename); + if(meta->has_id3v1) + meta->id3v1 = fetchID3v1(filename); + if(meta->has_id3v2) + meta->id3v2 = fetchID3v2(filename); + if(meta->has_ape) + meta->ape = fetchAPE(filename); + if(meta->has_vorbis) + meta->vorbis = fetchVorbis(filename, READ_VORBIS); + if(meta->has_flac) + meta->flac = fetchVorbis(filename, READ_FLAC); + if(meta->has_oggflac) + meta->oggflac = fetchVorbis(filename, READ_OGGFLAC); + if(meta->has_speex) + meta->speex = fetchVorbis(filename, READ_SPEEX); + if(meta->has_itunes) + meta->itunes = fetchiTunes(filename); + if(meta->has_wma) + meta->wma = fetchWMA(filename); + } + + /* + * This order is rather arbitrary, but puts tags you'd EXPECT to + * be the exclusive tag in the file first (i.e. vorbis and itunes). + * Thus we return afterwards. + */ + + if(meta->has_vorbis || meta->has_flac || meta->has_oggflac || + meta->has_speex) + { + metaVorbis(meta); + + return; + } + else if(meta->has_itunes) + { + metaiTunes(meta); + + return; + } + else if(meta->has_wma) + { + metaWMA(meta); + + return; + } + /* + * OK, here's the trick: APE preferred to ID3v2? + * or ID3v2 preferred to APE? ID3v1 loses regardless. + * Thus it's put first to be overwritten. + */ + if(meta->has_id3v1) + metaID3v1(meta); + /* A little dirty for now, but it's not too difficult */ + if(!meta->prefer_ape) + { + if(meta->has_ape) + metaAPE(meta); + if(meta->has_id3v2) + metaID3v2(meta); + } + else if(meta->prefer_ape) + { + if(meta->has_id3v2) + metaID3v2(meta); + if(meta->has_ape) + metaAPE(meta); + } + + return; +} + +void metatag_delete(metatag_t *meta) +{ + /* + * Genre, track, and MBID are the exceptions and must be freed + * (Thanks ID3v1) + */ + if(meta->track != NULL) + free(meta->track); + if(meta->genre != NULL) + free(meta->genre); + if(meta->mb != NULL) + free(meta->mb); + if(meta->wma != NULL) + freeWMA(meta->wma); + if(meta->id3v1 != NULL) + freeID3v1(meta->id3v1); + if(meta->id3v2 != NULL) + freeID3v2(meta->id3v2); + if(meta->ape != NULL) + freeAPE(meta->ape); + if(meta->vorbis != NULL) + freeVorbis(meta->vorbis); + if(meta->flac != NULL) + freeVorbis(meta->flac); + if(meta->oggflac != NULL) + freeVorbis(meta->oggflac); + if(meta->speex != NULL) + freeVorbis(meta->speex); + if(meta->itunes != NULL) + freeiTunes(meta->itunes); + if(meta->cdaudio != NULL) + freeCDAudio(meta->cdaudio); + free(meta); +} + +metatag_t *metatag_new(void) +{ + metatag_t *ret; + + if(!(ret = calloc(sizeof(*ret), 1))) + return NULL; +#ifdef PREFER_APE + ret->prefer_ape = 1; +#endif + + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/unicode.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,214 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdlib.h> +#include <wchar.h> +#include <string.h> +#include "include/endian.h" +#include "include/unicode.h" + +wchar_t *utf8_to_wchar(unsigned char *utf, size_t memsize) +{ + int i, j = 0; + wchar_t *mem; + + mem = calloc(sizeof(wchar_t) * (memsize + 1), 1); + + for(i = 0; i < memsize;) + { + if(utf[i] < 0x80) + mem[j++] = utf[i++]; + else if(utf[i] < 0xE0) + { + mem[j++] = ((utf[i] & 0x1F) << 6) | + (utf[i + 1] & 0x3F); + i += 2; + } + else if(utf[i] < 0xF0) + { + mem[j++] = ((utf[i] & 0x0F) << 12) | + ((utf[i + 1] & 0x3F) << 6) | + (utf[i + 2] & 0x3F); + i += 3; + } + else if(utf[i] < 0xF8) + { + mem[j++] = ((utf[i] & 0x07) << 18) | + ((utf[i + 1] & 0x3F) << 12) | + ((utf[i + 2] & 0x3F) << 6) | + (utf[i + 2] & 0x3F); + i += 4; + } + else if(utf[i] < 0xFC) + { + mem[j++] = ((utf[i] & 0x03) << 24) | + ((utf[i + 1] & 0x3F) << 18) | + ((utf[i + 2] & 0x3F) << 12) | + ((utf[i + 3] & 0x3F) << 6) | + (utf[i + 4] & 0x3F); + i += 5; + } + else if(utf[i] >= 0xFC) + { + mem[j++] = ((utf[i] & 0x01) << 30) | + ((utf[i + 1] & 0x3F) << 24) | + ((utf[i + 2] & 0x3F) << 18) | + ((utf[i + 3] & 0x3F) << 12) | + ((utf[i + 4] & 0x3F) << 6) | + (utf[i + 5] & 0x3F); + i += 6; + } + } + + mem = realloc(mem, sizeof(wchar_t) * (j + 1)); + + return mem; +} + +unsigned char *wchar_to_utf8(wchar_t *wchar, size_t memsize) +{ + int i; + unsigned char *mem, *ptr; + + mem = calloc(memsize * 6 + 1, 1); + ptr = mem; + + for(i = 0; i < memsize; i++) + { + if(wchar[i] < 0x80) + { + *ptr++ = wchar[i] & 0x7F; + } + else if(wchar[i] < 0x800) + { + *ptr++ = 0xC0 | ((wchar[i] >> 6) & 0x1F); + *ptr++ = 0x80 | (wchar[i] & 0x3F); + } + else if(wchar[i] < 0x10000) + { + *ptr++ = 0xE0 | ((wchar[i] >> 12) & 0x0F); + *ptr++ = 0x80 | ((wchar[i] >> 6) & 0x3F); + *ptr++ = 0x80 | (wchar[i] & 0x3F); + } + else if(wchar[i] < 0x200000) + { + *ptr++ = 0xF0 | ((wchar[i] >> 18) & 0x07); + *ptr++ = 0x80 | ((wchar[i] >> 12) & 0x3F); + *ptr++ = 0x80 | ((wchar[i] >> 6) & 0x3F); + *ptr++ = 0x80 | (wchar[i] & 0x3F); + } + else if(wchar[i] < 0x4000000) + { + *ptr++ = 0xF8 | ((wchar[i] >> 24) & 0x03); + *ptr++ = 0x80 | ((wchar[i] >> 18) & 0x3F); + *ptr++ = 0x80 | ((wchar[i] >> 12) & 0x3F); + *ptr++ = 0x80 | ((wchar[i] >> 6) & 0x3F); + *ptr++ = 0x80 | (wchar[i] & 0x3F); + } + else if(wchar[i] < 0x80000000) + { + *ptr++ = 0xFC | ((wchar[i] >> 30) & 0x01); + *ptr++ = 0x80 | ((wchar[i] >> 24) & 0x3F); + *ptr++ = 0x80 | ((wchar[i] >> 18) & 0x3F); + *ptr++ = 0x80 | ((wchar[i] >> 12) & 0x3F); + *ptr++ = 0x80 | ((wchar[i] >> 6) & 0x3F); + *ptr++ = 0x80 | (wchar[i] & 0x3F); + } + } + + mem = realloc(mem, ptr - mem + 1); + + return mem; +} + +void iso88591_to_utf8(unsigned char *iso, size_t memsize, + unsigned char **utf) +{ + int i; + wchar_t *wchar; + + wchar = calloc(sizeof(wchar_t) * (memsize + 1), 1); + for(i = 0; i < memsize; i++) wchar[i] = iso[i]; + *utf = wchar_to_utf8(wchar, memsize); + free(wchar); +} + +void utf16bom_to_utf8(unsigned char *utf16, size_t memsize, + unsigned char **utf) +{ + wchar_t *wchar; + unsigned char utf16char[2]; + int endian = 0, i; + + wchar = calloc(sizeof(wchar_t) * memsize / 2 - 1, 1); + for(i = 0; i < memsize; i += 2) + { + if(i == 0) + { + if(utf16[i] == 0xFF) endian = 0; + else if(utf16[i] == 0xFE) endian = 1; + } + else + { + utf16char[0] = utf16[i]; + utf16char[1] = utf16[i + 1]; + if(endian == 1) wchar[i / 2 - 1] = be2short(utf16char); + else if(endian == 0) wchar[i / 2 - 1] = le2short(utf16char); + } + } + *utf = wchar_to_utf8(wchar, memsize / 2 - 1); + free(wchar); +} + +void utf16be_to_utf8(unsigned char *utf16, size_t memsize, + unsigned char **utf) +{ + wchar_t *wchar; + unsigned char utf16char[2]; + int i; + + wchar = calloc(sizeof(wchar_t) * (memsize / 2), 1); + for(i = 0; i < memsize; i += 2) + { + utf16char[0] = utf16[i]; + utf16char[1] = utf16[i + 1]; + wchar[i / 2] = be2short(utf16char); + } + *utf = wchar_to_utf8(wchar, memsize / 2); + free(wchar); +} + +void utf16le_to_utf8(unsigned char *utf16, size_t memsize, + unsigned char **utf) +{ + wchar_t *wchar; + unsigned char utf16char[2]; + int i; + + wchar = calloc(sizeof(wchar_t) * (memsize / 2), 1); + for(i = 0; i < memsize; i += 2) + { + utf16char[0] = utf16[i]; + utf16char[1] = utf16[i + 1]; + wchar[i / 2] = le2short(utf16char); + } + *utf = wchar_to_utf8(wchar, memsize / 2); + free(wchar); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/vorbis.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,413 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2003, 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "include/bmp_vfs.h" +#include "include/vorbis.h" +#include "include/endian.h" +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#define BUFFER_SIZE 4096 + +static vorbis_t *readComments(VFSFile *fp) +{ + vorbis_t *comments = calloc(sizeof(vorbis_t), 1); + unsigned char cToInt[4]; + int i, lines, j = 0; + + vfs_fread(cToInt, 1, 4, fp); + comments->vendorlen = le2int(cToInt); + comments->vendor = malloc(comments->vendorlen); + vfs_fread(comments->vendor, 1, comments->vendorlen, fp); + vfs_fread(cToInt, 1, 4, fp); + lines = comments->numitems = le2int(cToInt); + comments->items = realloc(comments->items, + (comments->numitems) * sizeof(vorbisfielddata_t *)); + for(i = 0; i < lines; i++) + { + unsigned char *data, *dp; + vorbisfielddata_t *fielddata = + calloc(sizeof(vorbisfielddata_t), 1); + + vfs_fread(cToInt, 1, 4, fp); + fielddata->len = le2int(cToInt); + data = malloc(fielddata->len); + vfs_fread(data, 1, fielddata->len, fp); + dp = strchr(data, '='); + if(dp == NULL) + { + pdebug("No '=' in comment!", META_DEBUG); + comments->numitems--; + free(data); + continue; + } + *dp = '\0'; + dp++; + fielddata->name = malloc(strlen(data) + 1); + fielddata->data = malloc(fielddata->len - strlen(data)); + *(fielddata->data + fielddata->len - strlen(data) - 1) = '\0'; + strcpy(fielddata->name, data); + strncpy(fielddata->data, dp, fielddata->len - strlen(data) - 1); + + comments->items[j++] = fielddata; + + free(data); + } + + return comments; +} + +int findVorbis(VFSFile *fp) +{ + char tag_id[5] = ""; + unsigned char *tag_buffer, *bp, vorbis_type; + int status = 0, pos = -1; + + vfs_fread(tag_id, 1, 4, fp); + if(strcmp(tag_id, "OggS")) + return -1; + tag_buffer = malloc(23); + vfs_fread(tag_buffer, 1, 23, fp); + bp = tag_buffer; + while(status == 0) + { + unsigned char segments, *lacing; + unsigned int pagelen = 0, i; + bp += 22; + segments = *bp; + lacing = malloc(segments); + vfs_fread(lacing, 1, segments, fp); + for(i = 0; i < segments; i++) + pagelen += lacing[i]; + tag_buffer = realloc(tag_buffer, pagelen); + vfs_fread(tag_buffer, 1, pagelen, fp); + bp = tag_buffer; + for(i = 0; i < segments && status == 0;) + { + if(strncmp(bp + 1, "vorbis", 6) == 0) + { + vorbis_type = *bp; + if(vorbis_type == 0x03) + { + pos = ftell(fp) - pagelen + + (bp - tag_buffer); + status = 1; + } + } + bp += lacing[i++]; + } + if(status == 1 || feof(fp)) + { + free(lacing); + break; + } + tag_buffer = realloc(tag_buffer, 27); + vfs_fread(tag_buffer, 1, 27, fp); + bp = tag_buffer + 4; + free(lacing); + } + + free(tag_buffer); + + if(feof(fp)) + return -1; + else + return pos; +} + +int findFlac(VFSFile *fp) +{ + unsigned char tag_id[5] = ""; + int pos; + + vfs_fread(tag_id, 1, 4, fp); + if(strcmp(tag_id, "fLaC")) + return 0; + while(1) + { + vfs_fread(tag_id, 1, 4, fp); + if((tag_id[0] & 0x7F) == 4) + return 1; + else if((tag_id[0] & 0x80) == 0x80) + return 0; + else if(feof(fp)) + return 0; + else + { + pos = flac2int(tag_id); + vfs_fseek(fp, pos, SEEK_CUR); + } + } +} + +int findOggFlac(VFSFile *fp) +{ + char tag_id[5] = ""; + unsigned char *tag_buffer, *bp; + int status = 0, pos = -1; + + vfs_fread(tag_id, 1, 4, fp); + if(strcmp(tag_id, "OggS")) + return -1; + /* I assume first page always has only "fLaC" */ + tag_buffer = malloc(28); + vfs_fread(tag_buffer, 1, 28, fp); + bp = tag_buffer + 24; + if(strncmp(bp, "fLaC", 4)) + { + free(tag_buffer); + return -1; + } + tag_buffer = realloc(tag_buffer, 27); + vfs_fread(tag_buffer, 1, 27, fp); + bp = tag_buffer + 4; + while(status == 0) + { + unsigned char segments, *lacing = NULL; + unsigned int pagelen = 0, i; + bp += 22; + segments = *bp; + lacing = realloc(lacing, segments); + vfs_fread(lacing, 1, segments, fp); + for(i = 0; i < segments; i++) + pagelen += lacing[i]; + tag_buffer = realloc(tag_buffer, pagelen); + vfs_fread(tag_buffer, 1, pagelen, fp); + bp = tag_buffer; + for(i = 0; i < segments && status == 0;) + { + if((bp[0] & 0x7F) == 4) + { + pos = ftell(fp) - pagelen + + (bp - tag_buffer); + status = 1; + } + else if((tag_id[0] & 0x80) == 0x80) + { + free(tag_buffer); + free(lacing); + return -1; + } + else + bp += lacing[i++]; + } + if(status == 1 || feof(fp)) + break; + tag_buffer = realloc(tag_buffer, 27); + vfs_fread(tag_buffer, 1, 27, fp); + bp = tag_buffer + 4; + free(lacing); + } + + free(tag_buffer); + + if(feof(fp)) + return -1; + else + return pos; +} + +int findSpeex(VFSFile *fp) +{ + char tag_id[5] = ""; + unsigned char *tag_buffer, *bp, segments, *lacing = NULL; + unsigned int pagelen = 0, i; + int pos = -1; + + vfs_fread(tag_id, 1, 4, fp); + if(strcmp(tag_id, "OggS")) + return -1; + tag_buffer = malloc(23); + vfs_fread(tag_buffer, 1, 23, fp); + bp = tag_buffer + 22; + segments = *bp; + lacing = malloc(segments); + vfs_fread(lacing, 1, segments, fp); + for(i = 0; i < segments; i++) + pagelen += lacing[i]; + tag_buffer = realloc(tag_buffer, pagelen); + vfs_fread(tag_buffer, 1, pagelen, fp); + bp = tag_buffer; + if(strncmp(bp, "Speex ", 8)) + { + free(lacing); + free(tag_buffer); + return -1; + } + tag_buffer = realloc(tag_buffer, 27); + vfs_fread(tag_buffer, 1, 27, fp); + bp = tag_buffer + 26; + segments = *bp; + lacing = realloc(lacing, segments); + vfs_fread(lacing, 1, segments, fp); + pos = ftell(fp); + + free(tag_buffer); + free(lacing); + + if(feof(fp)) + return -1; + else + return pos; +} + +vorbis_t *readVorbis(char *filename) +{ + VFSFile *fp; + vorbis_t *comments; + int pos; + + fp = vfs_fopen(filename, "r"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + pos = findVorbis(fp); + if(pos < 0) + { + vfs_fclose(fp); + return NULL; + } + vfs_fseek(fp, pos + 7, SEEK_SET); + comments = readComments(fp); + + vfs_fclose(fp); + + return comments; +} + +vorbis_t *readFlac(char *filename) +{ + VFSFile *fp; + vorbis_t *comments; + int status; + + fp = vfs_fopen(filename, "r"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + status = findFlac(fp); + if(!status) + { + vfs_fclose(fp); + return NULL; + } + comments = readComments(fp); + + vfs_fclose(fp); + + return comments; +} + +vorbis_t *readOggFlac(char *filename) +{ + VFSFile *fp; + vorbis_t *comments; + int pos; + + fp = vfs_fopen(filename, "r"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + pos = findOggFlac(fp); + if(pos < 0) + { + vfs_fclose(fp); + return NULL; + } + vfs_fseek(fp, pos + 4, SEEK_SET); + comments = readComments(fp); + + vfs_fclose(fp); + + return comments; +} + +vorbis_t *readSpeex(char *filename) +{ + VFSFile *fp; + vorbis_t *comments; + int pos; + + fp = vfs_fopen(filename, "r"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + pos = findSpeex(fp); + if(pos < 0) + { + vfs_fclose(fp); + return NULL; + } + vfs_fseek(fp, pos, SEEK_SET); + comments = readComments(fp); + + vfs_fclose(fp); + + return comments; +} + +void freeVorbis(vorbis_t *comments) +{ + int i; + + for(i = 0; i < comments->numitems; i++) + { + vorbisfielddata_t *field; + + field = comments->items[i]; + free(field->data); + free(field->name); + free(field); + } + free(comments->items); + free(comments->vendor); + free(comments); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/tags/wma.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,305 @@ +/* + * libmetatag - A media file tag-reader library + * Copyright (C) 2004 Pipian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "include/bmp_vfs.h" +#include "include/wma.h" +#include "include/endian.h" +#include "../fmt.h" +#include "../config.h" +#include "include/unicode.h" +#define WMA_GUID (unsigned char [16]) \ + { 0x30, 0x26, 0xB2, 0x75, \ + 0x8E, 0x66, \ + 0xCF, 0x11, \ + 0xA6, 0xD9, \ + 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C } +#define CONTENT_GUID (unsigned char [16]) \ + { 0x33, 0x26, 0xB2, 0x75, \ + 0x8E, 0x66, \ + 0xCF, 0x11, \ + 0xA6, 0xD9, \ + 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C } +#define EXTENDED_GUID (unsigned char [16]) \ + { 0x40, 0xA4, 0xD0, 0xD2, \ + 0x07, 0xE3, \ + 0xD2, 0x11, \ + 0x97, 0xF0, \ + 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 } +#define BUFFER_SIZE 4096 + +static wma_t *readAttributes(VFSFile *fp, int pos) +{ + wma_t *wma = calloc(sizeof(wma_t), 1); + attribute_t *attribute; + unsigned char *tag_buffer = NULL, *bp, cToInt[8], *data = NULL; + int title_size, author_size, copyright_size, desc_size, rating_size, + size, primary_items, i; + + vfs_fseek(fp, pos, SEEK_SET); + vfs_fread(cToInt, 1, 8, fp); + /* Yes, it's 64 bits in size, but I'm lazy and don't want to adjust. */ + size = le2int(cToInt); + tag_buffer = malloc(size - 24); + vfs_fread(tag_buffer, 1, size - 24, fp); + bp = tag_buffer; + title_size = le2short(bp); + bp += 2; + author_size = le2short(bp); + bp += 2; + copyright_size = le2short(bp); + bp += 2; + desc_size = le2short(bp); + bp += 2; + rating_size = le2short(bp); + bp += 2; + if(title_size > 0) + { + attribute = calloc(sizeof(attribute_t), 1); + wma->items = realloc(wma->items, + (wma->numitems + 1) * sizeof(attribute_t *)); + attribute->name = malloc(6); + strcpy(attribute->name, "Title"); + data = malloc(title_size); + memcpy(data, bp, title_size); + bp += title_size; + utf16le_to_utf8(data, title_size, &attribute->data); + attribute->type = 0; + wma->items[wma->numitems++] = attribute; + free(data); + } + if(author_size > 0) + { + attribute = calloc(sizeof(attribute_t), 1); + wma->items = realloc(wma->items, + (wma->numitems + 1) * sizeof(attribute_t *)); + attribute->name = malloc(7); + strcpy(attribute->name, "Author"); + data = malloc(author_size); + memcpy(data, bp, author_size); + bp += author_size; + utf16le_to_utf8(data, author_size, &attribute->data); + attribute->type = 0; + wma->items[wma->numitems++] = attribute; + free(data); + } + if(copyright_size > 0) + { + attribute = calloc(sizeof(attribute_t), 1); + wma->items = realloc(wma->items, + (wma->numitems + 1) * sizeof(attribute_t *)); + attribute->name = malloc(10); + strcpy(attribute->name, "Copyright"); + data = malloc(copyright_size); + memcpy(data, bp, copyright_size); + bp += copyright_size; + utf16le_to_utf8(data, copyright_size, &attribute->data); + attribute->type = 0; + wma->items[wma->numitems++] = attribute; + free(data); + } + if(desc_size > 0) + { + attribute = calloc(sizeof(attribute_t), 1); + wma->items = realloc(wma->items, + (wma->numitems + 1) * sizeof(attribute_t *)); + attribute->name = malloc(12); + strcpy(attribute->name, "Description"); + data = malloc(desc_size); + memcpy(data, bp, desc_size); + bp += desc_size; + utf16le_to_utf8(data, desc_size, &attribute->data); + attribute->type = 0; + wma->items[wma->numitems++] = attribute; + free(data); + } + if(rating_size > 0) + { + attribute = calloc(sizeof(attribute_t), 1); + wma->items = realloc(wma->items, + (wma->numitems + 1) * sizeof(attribute_t *)); + attribute->name = malloc(7); + strcpy(attribute->name, "Rating"); + data = malloc(rating_size); + memcpy(data, bp, rating_size); + bp += rating_size; + utf16le_to_utf8(data, desc_size, &attribute->data); + attribute->type = 0; + wma->items[wma->numitems++] = attribute; + free(data); + } + primary_items = wma->numitems; + + vfs_fread(tag_buffer, 16, 1, fp); + if(memcmp(tag_buffer, EXTENDED_GUID, 16)) + { + free(tag_buffer); + return wma; + } + vfs_fread(cToInt, 8, 1, fp); + /* + * Another 64-bit breakage. If you've got that large an amount of + * metadata, you've got a problem. + */ + size = le2int(cToInt); + tag_buffer = realloc(tag_buffer, size); + vfs_fread(tag_buffer, size, 1, fp); + bp = tag_buffer; + memcpy(cToInt, bp, 2); + wma->numitems += le2short(cToInt); + wma->items = realloc(wma->items, + wma->numitems * sizeof(attribute_t *)); + bp += 2; + for(i = primary_items; i < wma->numitems; i++) + { + int type; + + attribute = calloc(sizeof(attribute_t), 1); + + memcpy(cToInt, bp, 2); + size = le2short(cToInt); + bp += 2; + data = malloc(size); + memcpy(data, bp, size); + utf16le_to_utf8(data, size, &attribute->name); + bp += size; + memcpy(cToInt, bp, 2); + type = le2short(cToInt); + attribute->type = type; + bp += 2; + memcpy(cToInt, bp, 2); + size = le2short(cToInt); + bp += 2; + data = realloc(data, size); + memcpy(data, bp, size); + switch(type) + { + /* Type 0 is Little-endian UTF16 */ + case 0: + utf16le_to_utf8(data, size, &attribute->data); + break; + /* + * Type 1 is binary + * Type 2 is boolean + * Type 3 is 32-bit integer (signed?) + * Type 4 is double + */ + case 1: + case 2: + case 3: + case 4: + default: + attribute->data = malloc(size); + memcpy(attribute->data, data, size); + } + bp += size; + + wma->items[i] = attribute; + } + + free(tag_buffer); + + return wma; +} + +int findWMA(VFSFile *fp) +{ + unsigned char *tag_buffer, *bp; + + tag_buffer = malloc(BUFFER_SIZE); + vfs_fread(tag_buffer, 1, BUFFER_SIZE, fp); + bp = tag_buffer; + if(memcmp(bp, WMA_GUID, 16)) + { + free(tag_buffer); + return -1; + } + bp += 30; + if(memcmp(bp, CONTENT_GUID, 16)) + { + free(tag_buffer); + return -1; + } + /* + * It's stupid to reject if no Extended Content GUID + * is found... This code is in here in case though... + * + bp += 16; + memcpy(cToInt, bp, 8); + size = le2long(cToInt); + bp += size - 16; + if(!memcmp(bp, EXTENDED_GUID, 16)) + { + free(tag_buffer); + return 0; + } + */ + free(tag_buffer); + return bp - tag_buffer + 16; +} + +wma_t *readWMA(char *filename) +{ + VFSFile *fp; + wma_t *wma; + int status; + + fp = vfs_fopen(filename, "r"); + + if(!fp) + { + pdebug("Couldn't open file!", META_DEBUG); + return NULL; + } + + vfs_fseek(fp, 0, SEEK_SET); + + pdebug("Searching for tag...", META_DEBUG); + status = findWMA(fp); + if(status == 0) + { + vfs_fclose(fp); + return NULL; + } + wma = readAttributes(fp, status); + + vfs_fclose(fp); + + return wma; +} + +void freeWMA(wma_t *wma) +{ + int i; + + for(i = 0; i < wma->numitems; i++) + { + attribute_t *attribute; + + attribute = wma->items[i]; + free(attribute->name); + free(attribute->data); + free(attribute); + } + free(wma->items); + free(wma); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/General/scrobbler/xmms_scrobbler.c Sat Feb 25 12:11:20 2006 -0800 @@ -0,0 +1,493 @@ +#include <audacious/plugin.h> +#include <libaudacious/configdb.h> +#include <libaudacious/beepctrl.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <wchar.h> +#include <sys/time.h> + +#include "tags/include/tags.h" +#include "scrobbler.h" +#include "gtkstuff.h" +#include "config.h" +#include "fmt.h" +#include "tags/include/unicode.h" + +#define XS_CS xmms_scrobbler.xmms_session + +typedef struct submit_t +{ + int dosubmit, pos_c, len; +} submit_t; + +static void init(void); +static void cleanup(void); +static void *xs_thread(void *); +static void *hs_thread(void *); +static int going; + +static GThread *pt_scrobbler; +static GMutex *m_scrobbler; +static GThread *pt_handshake; + +static GeneralPlugin xmms_scrobbler = +{ + NULL, + NULL, + -1, + NULL, + init, + about_show, + configure_dialog, + cleanup +}; + +static void init(void) +{ + char *username = NULL, *password = NULL; + ConfigDb *cfgfile; + going = 1; + GError **moo = NULL; + + if ((cfgfile = bmp_cfg_db_open()) != NULL) { + bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "username", + &username); + bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "password", + &password); + bmp_cfg_db_close(cfgfile); + } + if ((!username || !password) || (!*username || !*password)) { + pdebug("username/password not found - not starting", + DEBUG); + going = 0; + return; + } + sc_init(username, password); + g_free(username); + g_free(password); + m_scrobbler = g_mutex_new(); + if ((pt_scrobbler = g_thread_create(xs_thread, m_scrobbler, TRUE, moo)) == NULL) { + pdebug(fmt_vastr("Error creating scrobbler thread: %s", moo), DEBUG); + going = 0; + return; + } + init_errorbox_done(); + if ((pt_handshake = g_thread_create(hs_thread, m_scrobbler, TRUE, NULL)) == NULL) { + pdebug("Error creating handshake thread", DEBUG); + going = 0; + return; + } + pdebug("plugin started", DEBUG); +} + +static void cleanup(void) +{ + if (!going) + return; + pdebug("about to lock mutex", DEBUG); + g_mutex_lock(m_scrobbler); + pdebug("locked mutex", DEBUG); + going = 0; + g_mutex_unlock(m_scrobbler); + pdebug("joining threads", DEBUG); + g_thread_join(pt_scrobbler); + + g_thread_join(pt_handshake); + + sc_cleaner(); +} + +static char ishttp(const char *a) +{ + char *tmp, *bp; + int status = 0; + + if (!a || !*a) + return 0; + + tmp = strdup(a); + for (bp = tmp; *bp; bp++) + *bp = toupper(*bp); + if (strstr(tmp, "HTTP://")) + status = -1; + free(tmp); + return status; +} + +/* Following code thanks to nosuke + * + * It should probably be cleaned up + */ +static submit_t get_song_status(void) +{ + static int pos_c, playlistlen_c, playtime_c, time_c, + pos_p = 0, playlistlen_p = 0, playtime_p = 0, + playtime_i = 0, time_i = 0, + playtime_ofs = 0; + static char *file_c = NULL, *file_p = NULL; + + static enum playstatus { + ps_stop, ps_play, ps_pause + } ps_c, ps_p = ps_stop; + + static int submitted = 0, changed, seeked, repeat, + filechanged, rewind, len = 0; + + static enum state { + start, stop, pause, restart, playing, pausing, stopping + } playstate; + + submit_t dosubmit; + + struct timeval timetmp; + + /* clear dosubmit */ + dosubmit.dosubmit = dosubmit.pos_c = dosubmit.len = 0; + + /* current music number */ + pos_c = xmms_remote_get_playlist_pos(XS_CS); + /* current file name */ + file_c = xmms_remote_get_playlist_file(XS_CS, pos_c); + /* total number */ + playlistlen_c = xmms_remote_get_playlist_length(XS_CS); + /* current playtime */ + playtime_c = xmms_remote_get_output_time(XS_CS); + /* total length */ + len = xmms_remote_get_playlist_time(XS_CS, pos_c); + + /* current time (ms) */ + gettimeofday(&timetmp, NULL); + time_c = timetmp.tv_sec * 1000 + timetmp.tv_usec / 1000; + + /* current status */ + if( xmms_remote_is_paused(XS_CS) ) { + ps_c = ps_pause; + }else if( xmms_remote_is_playing(XS_CS) ) { + ps_c = ps_play; + }else{ + ps_c = ps_stop; + } + + /* repeat setting */ + repeat = xmms_remote_is_repeat(XS_CS); + + /* +#ifdef MAKE_XMMS + // advance setting (required xmms-1.2.11 or over) + advance = xmms_remote_is_advance(XS_CS); +#else + advance = 1; +#endif + */ + + if( ps_p == ps_stop && ps_c == ps_stop ) playstate = stopping; + else if( ps_p == ps_stop && ps_c == ps_play ) playstate = start; + /* else if( ps_p == ps_stop && ps_c == ps_pause ) ; */ + else if( ps_p == ps_play && ps_c == ps_play ) playstate = playing; + else if( ps_p == ps_play && ps_c == ps_stop ) playstate = stop; + else if( ps_p == ps_play && ps_c == ps_pause ) playstate = pause; + else if( ps_p == ps_pause && ps_c == ps_pause ) playstate = pausing; + else if( ps_p == ps_pause && ps_c == ps_play ) playstate = restart; + else if( ps_p == ps_pause && ps_c == ps_stop ) playstate = stop; + else playstate = stopping; + + /* filename has changed */ + if( !(file_p == NULL && file_c == NULL) && + ((file_p == NULL && file_c != NULL) || + (file_p != NULL && file_c == NULL) || + (file_p != NULL && file_c != NULL && strcmp(file_c, file_p))) ){ + filechanged = 1; + pdebug("*** filechange ***", SUB_DEBUG); + }else{ + filechanged = 0; + } + if( file_c == NULL ){ len = 0; } + + /* whole rewind has occurred (maybe) */ + if( len != 0 && len - (playtime_p - playtime_c) < 3000 ){ + rewind = 1; + pdebug("*** rewind ***", SUB_DEBUG); + }else{ + rewind = 0; + } + + + changed = 0; + seeked = 0; + + switch( playstate ){ + case start: + pdebug("*** START ***", SUB_DEBUG); + break; + case stop: + pdebug("*** STOP ***", SUB_DEBUG); + len = 0; + break; + case pause: + pdebug("*** PAUSE ***", SUB_DEBUG); + playtime_ofs += playtime_c - playtime_i; /* save playtime */ + break; + case restart: + pdebug("*** RESTART ***", SUB_DEBUG); + playtime_i = playtime_c; /* restore playtime */ + break; + case playing: + if( (playtime_c < playtime_p) || /* back */ + ( (playtime_c - playtime_i) - (time_c - time_i) > 3000 ) + /* forward */ + ) { + seeked = 1; + } + + if( filechanged || /* filename has changed */ + ( !filechanged && /* filename has not changed... */ + /* (( rewind && (repeat && (!advance || + (pos_c == 0 && playlistlen_c == 1 )))) || */ + /* looping with only one file */ + (( pos_c == 0 && playlistlen_c == 1 && repeat + && rewind ) || + /* looping? */ + ( pos_p == pos_c && rewind ) || + + ( pos_p != pos_c && seeked ) || + /* skip from current music to next music, + which has the same filename as previous one */ + ( pos_p < pos_c && playtime_c < playtime_p ) || + /* current song has removed from playlist + but the next (following) song has the same + filename */ + ( playlistlen_p > playlistlen_c + && playtime_c < playtime_p )))){ + pdebug("*** CHANGE ***",SUB_DEBUG); + pdebug(fmt_vastr(" filechanged = %d",filechanged),SUB_DEBUG); + pdebug(fmt_vastr(" pos_c = %d",pos_c),SUB_DEBUG); + pdebug(fmt_vastr(" pos_p = %d",pos_p),SUB_DEBUG); + pdebug(fmt_vastr(" rewind = %d", rewind),SUB_DEBUG); + pdebug(fmt_vastr(" seeked = %d", seeked),SUB_DEBUG); + pdebug(fmt_vastr(" playtime_c = %d", playtime_c),SUB_DEBUG); + pdebug(fmt_vastr(" playtime_p = %d", playtime_p),SUB_DEBUG); + pdebug(fmt_vastr(" playlistlen_c = %d", playlistlen_p), + SUB_DEBUG); + pdebug(fmt_vastr(" playlistlen_p = %d", playlistlen_p), + SUB_DEBUG); + changed = 1; + seeked = 0; + }else if( seeked ) { + seeked = 1; + pdebug("*** SEEK ***", SUB_DEBUG); + } + + break; + case pausing: + if(playtime_c != playtime_p){ + pdebug("*** SEEK ***", SUB_DEBUG); + seeked = 1; + } + break; + case stopping: + len = 0; + break; + default: + pdebug("*** unknown state tranfer!!! ***", SUB_DEBUG); + break; + } + + + if( playstate == start || changed || (seeked && !submitted) ){ + /* reset counter */ + pdebug(" <<< reset counter >>>", SUB_DEBUG); + + submitted = 0; + playtime_ofs = 0; + playtime_i = playtime_c; + time_i = time_c; + + }else{ + /* check playtime for submitting */ + if( !submitted ){ + if( len > 30 * 1000 && + /* len < 30 *60 * 1000 && // crazy rule!!! */ + ( + (playtime_ofs + playtime_c - playtime_i > len / 2) || + (playtime_ofs + playtime_c - playtime_i > 240 * 1000) + /* (playtime_c - playtime_i > 10 * 1000)// for debug */ + )){ + pdebug("*** submitting requirements are satisfied.", + SUB_DEBUG); + pdebug(fmt_vastr(" len = %d, playtime = %d", + len / 1000, (playtime_c - playtime_i)/1000 ), + SUB_DEBUG); + submitted = 1; + dosubmit.dosubmit = 1; + dosubmit.pos_c = pos_c; + dosubmit.len = len; + } + } + } + + /* keep current value for next iteration */ + ps_p = ps_c; + file_p = file_c; + playtime_p = playtime_c; + pos_p = pos_c; + playlistlen_p = playlistlen_c; + + return dosubmit; +} + +static void *xs_thread(void *data) +{ + int run = 1, i; + char *charpos, *dirname; + gboolean direxists; + submit_t dosubmit; + + while (run) { + /* Error catching */ + if(sc_catch_error()) + { + errorbox_show(sc_fetch_error()); + if(get_errorbox_done()) + { + init_errorbox_done(); + sc_clear_error(); + } + } + + /* Check for ability to submit */ + dosubmit = get_song_status(); + + if(dosubmit.dosubmit) { + char *fname, /**title, *artist,*/ *tmp = NULL; /**sep*/ + int track = 0; + metatag_t *meta; + + pdebug("Submitting song.", DEBUG); + + meta = metatag_new(); + + fname = xmms_remote_get_playlist_file(0,dosubmit.pos_c); + if (ishttp(fname)) { + g_free(fname); + continue; + } + charpos = strrchr(fname, '.'); + if(charpos != NULL && + !fmt_strncasecmp(charpos + 1, "cda", 3)) + { + ConfigDb *cfgfile; + + if ((cfgfile = bmp_cfg_db_open()) + != NULL) + { + char *direntry = calloc(32, 1); + + dirname = fname; + tmp = strrchr(fname, '.'); + *tmp = '\0'; + track = (char)atoi(tmp - 2); + pdebug(fmt_vastr("Track: %d", track), + DEBUG); + tmp = strrchr(dirname, '/'); + *(tmp + 1) = '\0'; + direxists = bmp_cfg_db_get_string( + cfgfile, "CDDA", + "directory", &fname); + for(i = 0; direxists == TRUE + && strcmp(dirname, fname) == 0;) + { + i++; + snprintf(direntry, 31, + "directory%d", i); + g_free(fname); + direxists = + bmp_cfg_db_get_string( + cfgfile, "CDDA", + direntry, &fname); + } + if(i > 0) + { + snprintf(direntry, 31, + "device%d", i); + } + else + { + snprintf(direntry, 31, + "device"); + } + g_free(fname); + bmp_cfg_db_get_string(cfgfile, "CDDA", + direntry, &fname); + bmp_cfg_db_close(cfgfile); + free(direntry); + pdebug(fmt_vastr("CD Device: %s", + fname), DEBUG); + } + } + + pdebug(fmt_vastr("get_tag_data, %s", fname), DEBUG); + get_tag_data(meta, fname, track); + + if(meta->artist != NULL && meta->title != NULL) + { + pdebug(fmt_vastr( + "submitting artist: %s, title: %s", + meta->artist, meta->title), DEBUG); + sc_addentry(m_scrobbler, meta, + dosubmit.len/1000); + } + else + pdebug("couldn't determine artist - " + "title, not submitting", + DEBUG); + /* g_free(tmp); */ + g_free(fname); + metatag_delete(meta); + } + g_mutex_lock(m_scrobbler); + run = going; + g_mutex_unlock(m_scrobbler); + usleep(100000); + } + pdebug("scrobbler thread: exiting", DEBUG); + g_thread_exit(NULL); + + return NULL; +} + +static void *hs_thread(void *data) +{ + int run = 1; + + while(run) + { + if(sc_idle(m_scrobbler)) + { + pdebug("Giving up due to fatal error", DEBUG); + g_mutex_lock(m_scrobbler); + going = 0; + g_mutex_lock(m_scrobbler); + } + g_mutex_lock(m_scrobbler); + run = going; + g_mutex_unlock(m_scrobbler); + sleep(1); + } + pdebug("handshake thread: exiting", DEBUG); + g_thread_exit(NULL); + + return NULL; +} + +GeneralPlugin *get_gplugin_info(void) +{ + xmms_scrobbler.description = g_strdup_printf(_("Scrobbler Plugin")); + return &xmms_scrobbler; +}
--- a/configure.ac Sat Feb 25 09:36:36 2006 -0800 +++ b/configure.ac Sat Feb 25 12:11:20 2006 -0800 @@ -728,6 +728,100 @@ ;; esac +dnl *** Scrobbler *** + +scrobbler="yes" + +dnl Checks for libmusicbrainz +AC_CHECK_LIB(musicbrainz, mb_GetVersion,, + echo "*" + echo "* The MusicBrainz client library needs to be installed " + echo "* to build this plugin. Please download the library from " + echo "* http://www.musicbrainz.org/download " + echo "*" + echo "* Scrobbler support will not be built." + echo "*" + scrobbler="no" + ,-lstdc++) + +OLD_LIBS="$LIBS" +LIBS="-lmusicbrainz -lstdc++" +AC_TRY_RUN([ +#include <musicbrainz/mb_c.h> + +int main () +{ + int major, minor, rev; + musicbrainz_t o; + + o = mb_New(); + mb_GetVersion(o, &major, &minor, &rev); + mb_Delete(o); + if (major >= 2) + { + return 0; + } + + return -1; +} +],have_mbver=true,have_mbver=false,have_mbver=false) + +AC_MSG_CHECKING([for version >= 2.0.0 in -lmusicbrainz]) +if test "$have_mbver" = "false"; then + AC_MSG_RESULT([no]) + echo "*" + echo "* Version 2.0.0 or higher of the MusicBrainz " + echo "* client library needs to be installed to build this application. " + echo "* Please download the library from: " + echo "* http://musicbrainz.org/products/client/download.html " + echo "*" + echo "* Scrobbler support will not be built." + echo "*" + scrobbler="no" +else + AC_MSG_RESULT([yes]) + MUSICBRAINZ_LIBS="-lmusicbrainz -lstdc++" +fi +LIBS="$OLD_LIBS" + +dnl libcurl check + +my_cv_curl_vers=NONE +dnl check is the plain-text version of the required version +check="7.9.7" +dnl check_hex must be UPPERCASE if any hex letters are present +check_hex="070907" + +AC_MSG_CHECKING([for curl >= $check]) + +if eval curl-config --version 2>/dev/null >/dev/null; then + ver=`curl-config --version | sed -e "s/libcurl //g"` + hex_ver=`curl-config --vernum | tr 'a-f' 'A-F'` + ok=`echo "ibase=16; if($hex_ver>=$check_hex) $hex_ver else 0" | bc` + + if test x$ok != x0; then + my_cv_curl_vers="$ver" + AC_MSG_RESULT([$my_cv_curl_vers]) + CURL_LIBS=`curl-config --libs` + CURL_CFLAGS=`curl-config --cflags` + else + AC_MSG_RESULT([no]) + AC_MSG_WARN([$ver is too old. Need version $check or higher. Scrobbler support will not be built.]) + fi +else + AC_MSG_RESULT([no]) + AC_MSG_WARN([curl-config was not found. Scrobbler support will not be built.]) +fi + +if test "x$scrobbler" = "xyes"; then + GENERAL_PLUGINS="$GENERAL_PLUGINS scrobbler" +fi + +AC_SUBST(CURL_CFLAGS) +AC_SUBST(CURL_LIBS) +AC_SUBST(MUSICBRAINZ_LIBS) + +dnl *** End of Scrobbler checks *** AC_SUBST(ARCH_DEFINES) AM_CONDITIONAL(ARCH_X86, test "x$arch_type" = "xix86") @@ -773,9 +867,6 @@ AC_SUBST(BEEP_DEFINES) -PLUGIN_LDFLAGS='-module -avoid-version -export-symbols-regex "get_.plugin_info"' -AC_SUBST(PLUGIN_LDFLAGS) - AC_SUBST(beepdir) AC_SUBST(plugindir) @@ -835,6 +926,8 @@ Plugins/General/song_change/Makefile Plugins/General/lirc/Makefile Plugins/General/inetctl/Makefile + Plugins/General/scrobbler/Makefile + Plugins/General/scrobbler/tags/Makefile Plugins/Effect/Makefile Plugins/Effect/ladspa/Makefile po/Makefile.in @@ -903,6 +996,7 @@ echo " Song Change: yes" echo " Internet Control: yes" echo " LIRC: $have_lirc" +echo " AudioScrobbler Client: $scrobbler" echo echo " Effect" echo " ------"
--- a/mk/rules.mk.in Sat Feb 25 09:36:36 2006 -0800 +++ b/mk/rules.mk.in Sat Feb 25 12:11:20 2006 -0800 @@ -23,13 +23,6 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in $(srcdir)/audacious.1.in \ - $(srcdir)/audacious.pc.in $(srcdir)/audacious.spec.in \ - $(srcdir)/config.h.in $(top_srcdir)/configure \ - $(top_srcdir)/intl/Makefile.in ABOUT-NLS AUTHORS COPYING \ - ChangeLog INSTALL NEWS compile config.guess config.rpath \ - config.sub depcomp install-sh ltmain.sh missing mkinstalldirs subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 CONFIG_HEADER = config.h @@ -37,12 +30,6 @@ intl/Makefile SOURCES = DIST_SOURCES = -RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ - html-recursive info-recursive install-data-recursive \ - install-exec-recursive install-info-recursive \ - install-recursive installcheck-recursive installdirs-recursive \ - pdf-recursive ps-recursive uninstall-info-recursive \ - uninstall-recursive man1dir = $(mandir)/man1 am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(pkgconfigdir)" NROFF = nroff @@ -312,14 +299,6 @@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ -ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = intl libaudacious audacious Plugins po icons skin -man_MANS = audacious.1 -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = audacious.pc -EXTRA_DIST = \ - FAQ \ - audacious.spec \ - README.bmp \ - $(pkgconfig_DATA) \ - $(wildcard m4/*.m4) +CURL_CFLAGS = @CURL_CFLAGS@ +CURL_LIBS = @CURL_LIBS@ +MUSICBRAINZ_LIBS = @MUSICBRAINZ_LIBS@