Mercurial > audlegacy-plugins
diff src/scrobbler/plugin.c @ 346:76047737ea49 trunk
[svn] - rename plugin glue to plugin.c
author | nenolod |
---|---|
date | Fri, 08 Dec 2006 19:34:32 -0800 |
parents | src/scrobbler/xmms_scrobbler.c@03c1ae10bc8d |
children | b4f1ad07fcd9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/scrobbler/plugin.c Fri Dec 08 19:34:32 2006 -0800 @@ -0,0 +1,492 @@ +#include "settings.h" +#include "config.h" + +#include <glib.h> +#include <glib/gi18n.h> + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include <audacious/plugin.h> +#include <audacious/prefswin.h> +#include <audacious/playlist.h> +#include <audacious/configdb.h> +#include <audacious/beepctrl.h> + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <wchar.h> +#include <sys/time.h> + +#include "scrobbler.h" +#include "gerpok.h" +#include "gtkstuff.h" +#include "config.h" +#include "fmt.h" +#include "configure.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 sc_going, ge_going; +static GtkWidget *cfgdlg; + +static GThread *pt_scrobbler; +static GMutex *m_scrobbler; +static GThread *pt_handshake; + +static GeneralPlugin xmms_scrobbler = +{ + NULL, + NULL, + -1, + NULL, + init, + about_show, + NULL, + cleanup +}; + +static void init(void) +{ + char *username = NULL, *password = NULL; + char *ge_username = NULL, *ge_password = NULL; + ConfigDb *cfgfile; + sc_going = 1; + ge_going = 1; + GError **moo = NULL; + cfgdlg = create_cfgdlg(); + + prefswin_page_new(cfgdlg, "Scrobbler", DATA_DIR "/images/audioscrobbler.png"); + + 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_get_string(cfgfile, "audioscrobbler", "ge_username", + &ge_username); + bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "ge_password", + &ge_password); + bmp_cfg_db_close(cfgfile); + } + + if ((!username || !password) || (!*username || !*password)) + { + pdebug("username/password not found - not starting last.fm support", + DEBUG); + sc_going = 0; + } + else + sc_init(username, password); + + g_free(username); + g_free(password); + + if ((!ge_username || !ge_password) || (!*ge_username || !*ge_password)) + { + pdebug("username/password not found - not starting Gerpok support", + DEBUG); + ge_going = 0; + } + else + gerpok_sc_init(ge_username, ge_password); + + g_free(ge_username); + g_free(ge_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); + sc_going = 0; + ge_going = 0; + return; + } + + if ((pt_handshake = g_thread_create(hs_thread, m_scrobbler, TRUE, NULL)) == NULL) + { + pdebug(fmt_vastr("Error creating handshake thread: %s", moo), DEBUG); + sc_going = 0; + ge_going = 0; + return; + } + + pdebug("plugin started", DEBUG); +} + +static void cleanup(void) +{ + g_free (xmms_scrobbler.description); + xmms_scrobbler.description = NULL; + + prefswin_page_destroy(cfgdlg); + + if (!sc_going && !ge_going) + return; + pdebug("about to lock mutex", DEBUG); + g_mutex_lock(m_scrobbler); + pdebug("locked mutex", DEBUG); + sc_going = 0; + ge_going = 0; + g_mutex_unlock(m_scrobbler); + pdebug("joining threads", DEBUG); + g_thread_join(pt_scrobbler); + + g_thread_join(pt_handshake); + + sc_cleaner(); + gerpok_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((int) *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; + + if (file_p != NULL) + { + g_free(file_p); + file_p = NULL; + } + }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; + } + } + } + + g_free(file_p); + + /* 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 __attribute__((unused))) +{ + int run = 1; + submit_t dosubmit; + + while (run) { + /* Error catching */ + if(sc_catch_error()) + { + errorbox_show(sc_fetch_error()); + sc_clear_error(); + } + + if(gerpok_sc_catch_error()) + { + errorbox_show(gerpok_sc_fetch_error()); + gerpok_sc_clear_error(); + } + + /* Check for ability to submit */ + dosubmit = get_song_status(); + + if(dosubmit.dosubmit) { + TitleInput *tuple; + + pdebug("Submitting song.", DEBUG); + + tuple = playlist_get_tuple(dosubmit.pos_c); + + if (ishttp(tuple->file_name)) + continue; + + if(tuple->performer != NULL && tuple->track_name != NULL) + { + pdebug(fmt_vastr( + "submitting artist: %s, title: %s", + tuple->performer, tuple->track_name), DEBUG); + sc_addentry(m_scrobbler, tuple, + dosubmit.len/1000); + gerpok_sc_addentry(m_scrobbler, tuple, + dosubmit.len/1000); + } + else + pdebug("tuple does not contain an artist or a title, not submitting.", DEBUG); + } + g_mutex_lock(m_scrobbler); + run = (sc_going != 0 || ge_going != 0); + g_mutex_unlock(m_scrobbler); + g_usleep(100000); + } + pdebug("scrobbler thread: exiting", DEBUG); + g_thread_exit(NULL); + + return NULL; +} + +static void *hs_thread(void *data __attribute__((unused))) +{ + int run = 1; + + while(run) + { + if(sc_idle(m_scrobbler)) + { + pdebug("Giving up due to fatal error", DEBUG); + g_mutex_lock(m_scrobbler); + sc_going = 0; + g_mutex_lock(m_scrobbler); + } + + if(gerpok_sc_idle(m_scrobbler)) + { + pdebug("Giving up due to fatal error", DEBUG); + g_mutex_lock(m_scrobbler); + ge_going = 0; + g_mutex_lock(m_scrobbler); + } + + g_mutex_lock(m_scrobbler); + run = (sc_going != 0 || ge_going != 0); + g_mutex_unlock(m_scrobbler); + g_usleep(1000000); + } + 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; +}