Mercurial > audlegacy-plugins
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 345:213ccf5b5ea5 | 346:76047737ea49 |
|---|---|
| 1 #include "settings.h" | |
| 2 #include "config.h" | |
| 3 | |
| 4 #include <glib.h> | |
| 5 #include <glib/gi18n.h> | |
| 6 | |
| 7 #include <gdk/gdkkeysyms.h> | |
| 8 #include <gtk/gtk.h> | |
| 9 | |
| 10 #include <audacious/plugin.h> | |
| 11 #include <audacious/prefswin.h> | |
| 12 #include <audacious/playlist.h> | |
| 13 #include <audacious/configdb.h> | |
| 14 #include <audacious/beepctrl.h> | |
| 15 | |
| 16 #include <unistd.h> | |
| 17 #include <stdio.h> | |
| 18 #include <stdlib.h> | |
| 19 #include <string.h> | |
| 20 #include <ctype.h> | |
| 21 #include <wchar.h> | |
| 22 #include <sys/time.h> | |
| 23 | |
| 24 #include "scrobbler.h" | |
| 25 #include "gerpok.h" | |
| 26 #include "gtkstuff.h" | |
| 27 #include "config.h" | |
| 28 #include "fmt.h" | |
| 29 #include "configure.h" | |
| 30 | |
| 31 #define XS_CS xmms_scrobbler.xmms_session | |
| 32 | |
| 33 typedef struct submit_t | |
| 34 { | |
| 35 int dosubmit, pos_c, len; | |
| 36 } submit_t; | |
| 37 | |
| 38 static void init(void); | |
| 39 static void cleanup(void); | |
| 40 static void *xs_thread(void *); | |
| 41 static void *hs_thread(void *); | |
| 42 static int sc_going, ge_going; | |
| 43 static GtkWidget *cfgdlg; | |
| 44 | |
| 45 static GThread *pt_scrobbler; | |
| 46 static GMutex *m_scrobbler; | |
| 47 static GThread *pt_handshake; | |
| 48 | |
| 49 static GeneralPlugin xmms_scrobbler = | |
| 50 { | |
| 51 NULL, | |
| 52 NULL, | |
| 53 -1, | |
| 54 NULL, | |
| 55 init, | |
| 56 about_show, | |
| 57 NULL, | |
| 58 cleanup | |
| 59 }; | |
| 60 | |
| 61 static void init(void) | |
| 62 { | |
| 63 char *username = NULL, *password = NULL; | |
| 64 char *ge_username = NULL, *ge_password = NULL; | |
| 65 ConfigDb *cfgfile; | |
| 66 sc_going = 1; | |
| 67 ge_going = 1; | |
| 68 GError **moo = NULL; | |
| 69 cfgdlg = create_cfgdlg(); | |
| 70 | |
| 71 prefswin_page_new(cfgdlg, "Scrobbler", DATA_DIR "/images/audioscrobbler.png"); | |
| 72 | |
| 73 if ((cfgfile = bmp_cfg_db_open()) != NULL) { | |
| 74 bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "username", | |
| 75 &username); | |
| 76 bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "password", | |
| 77 &password); | |
| 78 bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "ge_username", | |
| 79 &ge_username); | |
| 80 bmp_cfg_db_get_string(cfgfile, "audioscrobbler", "ge_password", | |
| 81 &ge_password); | |
| 82 bmp_cfg_db_close(cfgfile); | |
| 83 } | |
| 84 | |
| 85 if ((!username || !password) || (!*username || !*password)) | |
| 86 { | |
| 87 pdebug("username/password not found - not starting last.fm support", | |
| 88 DEBUG); | |
| 89 sc_going = 0; | |
| 90 } | |
| 91 else | |
| 92 sc_init(username, password); | |
| 93 | |
| 94 g_free(username); | |
| 95 g_free(password); | |
| 96 | |
| 97 if ((!ge_username || !ge_password) || (!*ge_username || !*ge_password)) | |
| 98 { | |
| 99 pdebug("username/password not found - not starting Gerpok support", | |
| 100 DEBUG); | |
| 101 ge_going = 0; | |
| 102 } | |
| 103 else | |
| 104 gerpok_sc_init(ge_username, ge_password); | |
| 105 | |
| 106 g_free(ge_username); | |
| 107 g_free(ge_password); | |
| 108 | |
| 109 m_scrobbler = g_mutex_new(); | |
| 110 if ((pt_scrobbler = g_thread_create(xs_thread, m_scrobbler, TRUE, moo)) == NULL) | |
| 111 { | |
| 112 pdebug(fmt_vastr("Error creating scrobbler thread: %s", moo), DEBUG); | |
| 113 sc_going = 0; | |
| 114 ge_going = 0; | |
| 115 return; | |
| 116 } | |
| 117 | |
| 118 if ((pt_handshake = g_thread_create(hs_thread, m_scrobbler, TRUE, NULL)) == NULL) | |
| 119 { | |
| 120 pdebug(fmt_vastr("Error creating handshake thread: %s", moo), DEBUG); | |
| 121 sc_going = 0; | |
| 122 ge_going = 0; | |
| 123 return; | |
| 124 } | |
| 125 | |
| 126 pdebug("plugin started", DEBUG); | |
| 127 } | |
| 128 | |
| 129 static void cleanup(void) | |
| 130 { | |
| 131 g_free (xmms_scrobbler.description); | |
| 132 xmms_scrobbler.description = NULL; | |
| 133 | |
| 134 prefswin_page_destroy(cfgdlg); | |
| 135 | |
| 136 if (!sc_going && !ge_going) | |
| 137 return; | |
| 138 pdebug("about to lock mutex", DEBUG); | |
| 139 g_mutex_lock(m_scrobbler); | |
| 140 pdebug("locked mutex", DEBUG); | |
| 141 sc_going = 0; | |
| 142 ge_going = 0; | |
| 143 g_mutex_unlock(m_scrobbler); | |
| 144 pdebug("joining threads", DEBUG); | |
| 145 g_thread_join(pt_scrobbler); | |
| 146 | |
| 147 g_thread_join(pt_handshake); | |
| 148 | |
| 149 sc_cleaner(); | |
| 150 gerpok_sc_cleaner(); | |
| 151 } | |
| 152 | |
| 153 static char ishttp(const char *a) | |
| 154 { | |
| 155 char *tmp, *bp; | |
| 156 int status = 0; | |
| 157 | |
| 158 if (!a || !*a) | |
| 159 return 0; | |
| 160 | |
| 161 tmp = strdup(a); | |
| 162 for (bp = tmp; *bp; bp++) | |
| 163 *bp = toupper((int) *bp); | |
| 164 if (strstr(tmp, "HTTP://")) | |
| 165 status = -1; | |
| 166 free(tmp); | |
| 167 return status; | |
| 168 } | |
| 169 | |
| 170 /* Following code thanks to nosuke | |
| 171 * | |
| 172 * It should probably be cleaned up | |
| 173 */ | |
| 174 static submit_t get_song_status(void) | |
| 175 { | |
| 176 static int pos_c, playlistlen_c, playtime_c, time_c, | |
| 177 pos_p = 0, playlistlen_p = 0, playtime_p = 0, | |
| 178 playtime_i = 0, time_i = 0, | |
| 179 playtime_ofs = 0; | |
| 180 static char *file_c = NULL, *file_p = NULL; | |
| 181 | |
| 182 static enum playstatus { | |
| 183 ps_stop, ps_play, ps_pause | |
| 184 } ps_c, ps_p = ps_stop; | |
| 185 | |
| 186 static int submitted = 0, changed, seeked, repeat, | |
| 187 filechanged, rewind, len = 0; | |
| 188 | |
| 189 static enum state { | |
| 190 start, stop, pause, restart, playing, pausing, stopping | |
| 191 } playstate; | |
| 192 | |
| 193 submit_t dosubmit; | |
| 194 | |
| 195 struct timeval timetmp; | |
| 196 | |
| 197 /* clear dosubmit */ | |
| 198 dosubmit.dosubmit = dosubmit.pos_c = dosubmit.len = 0; | |
| 199 | |
| 200 /* current music number */ | |
| 201 pos_c = xmms_remote_get_playlist_pos(XS_CS); | |
| 202 /* current file name */ | |
| 203 file_c = xmms_remote_get_playlist_file(XS_CS, pos_c); | |
| 204 /* total number */ | |
| 205 playlistlen_c = xmms_remote_get_playlist_length(XS_CS); | |
| 206 /* current playtime */ | |
| 207 playtime_c = xmms_remote_get_output_time(XS_CS); | |
| 208 /* total length */ | |
| 209 len = xmms_remote_get_playlist_time(XS_CS, pos_c); | |
| 210 | |
| 211 /* current time (ms) */ | |
| 212 gettimeofday(&timetmp, NULL); | |
| 213 time_c = timetmp.tv_sec * 1000 + timetmp.tv_usec / 1000; | |
| 214 | |
| 215 /* current status */ | |
| 216 if( xmms_remote_is_paused(XS_CS) ) { | |
| 217 ps_c = ps_pause; | |
| 218 }else if( xmms_remote_is_playing(XS_CS) ) { | |
| 219 ps_c = ps_play; | |
| 220 }else{ | |
| 221 ps_c = ps_stop; | |
| 222 } | |
| 223 | |
| 224 /* repeat setting */ | |
| 225 repeat = xmms_remote_is_repeat(XS_CS); | |
| 226 | |
| 227 /* | |
| 228 #ifdef MAKE_XMMS | |
| 229 // advance setting (required xmms-1.2.11 or over) | |
| 230 advance = xmms_remote_is_advance(XS_CS); | |
| 231 #else | |
| 232 advance = 1; | |
| 233 #endif | |
| 234 */ | |
| 235 | |
| 236 if( ps_p == ps_stop && ps_c == ps_stop ) playstate = stopping; | |
| 237 else if( ps_p == ps_stop && ps_c == ps_play ) playstate = start; | |
| 238 /* else if( ps_p == ps_stop && ps_c == ps_pause ) ; */ | |
| 239 else if( ps_p == ps_play && ps_c == ps_play ) playstate = playing; | |
| 240 else if( ps_p == ps_play && ps_c == ps_stop ) playstate = stop; | |
| 241 else if( ps_p == ps_play && ps_c == ps_pause ) playstate = pause; | |
| 242 else if( ps_p == ps_pause && ps_c == ps_pause ) playstate = pausing; | |
| 243 else if( ps_p == ps_pause && ps_c == ps_play ) playstate = restart; | |
| 244 else if( ps_p == ps_pause && ps_c == ps_stop ) playstate = stop; | |
| 245 else playstate = stopping; | |
| 246 | |
| 247 /* filename has changed */ | |
| 248 if( !(file_p == NULL && file_c == NULL) && | |
| 249 ((file_p == NULL && file_c != NULL) || | |
| 250 (file_p != NULL && file_c == NULL) || | |
| 251 (file_p != NULL && file_c != NULL && strcmp(file_c, file_p))) ){ | |
| 252 filechanged = 1; | |
| 253 pdebug("*** filechange ***", SUB_DEBUG); | |
| 254 }else{ | |
| 255 filechanged = 0; | |
| 256 } | |
| 257 if( file_c == NULL ){ len = 0; } | |
| 258 | |
| 259 /* whole rewind has occurred (maybe) */ | |
| 260 if( len != 0 && len - (playtime_p - playtime_c) < 3000 ){ | |
| 261 rewind = 1; | |
| 262 pdebug("*** rewind ***", SUB_DEBUG); | |
| 263 }else{ | |
| 264 rewind = 0; | |
| 265 } | |
| 266 | |
| 267 | |
| 268 changed = 0; | |
| 269 seeked = 0; | |
| 270 | |
| 271 switch( playstate ){ | |
| 272 case start: | |
| 273 pdebug("*** START ***", SUB_DEBUG); | |
| 274 break; | |
| 275 case stop: | |
| 276 pdebug("*** STOP ***", SUB_DEBUG); | |
| 277 len = 0; | |
| 278 break; | |
| 279 case pause: | |
| 280 pdebug("*** PAUSE ***", SUB_DEBUG); | |
| 281 playtime_ofs += playtime_c - playtime_i; /* save playtime */ | |
| 282 break; | |
| 283 case restart: | |
| 284 pdebug("*** RESTART ***", SUB_DEBUG); | |
| 285 playtime_i = playtime_c; /* restore playtime */ | |
| 286 break; | |
| 287 case playing: | |
| 288 if( (playtime_c < playtime_p) || /* back */ | |
| 289 ( (playtime_c - playtime_i) - (time_c - time_i) > 3000 ) | |
| 290 /* forward */ | |
| 291 ) { | |
| 292 seeked = 1; | |
| 293 } | |
| 294 | |
| 295 if( filechanged || /* filename has changed */ | |
| 296 ( !filechanged && /* filename has not changed... */ | |
| 297 /* (( rewind && (repeat && (!advance || | |
| 298 (pos_c == 0 && playlistlen_c == 1 )))) || */ | |
| 299 /* looping with only one file */ | |
| 300 (( pos_c == 0 && playlistlen_c == 1 && repeat | |
| 301 && rewind ) || | |
| 302 /* looping? */ | |
| 303 ( pos_p == pos_c && rewind ) || | |
| 304 | |
| 305 ( pos_p != pos_c && seeked ) || | |
| 306 /* skip from current music to next music, | |
| 307 which has the same filename as previous one */ | |
| 308 ( pos_p < pos_c && playtime_c < playtime_p ) || | |
| 309 /* current song has removed from playlist | |
| 310 but the next (following) song has the same | |
| 311 filename */ | |
| 312 ( playlistlen_p > playlistlen_c | |
| 313 && playtime_c < playtime_p )))){ | |
| 314 pdebug("*** CHANGE ***",SUB_DEBUG); | |
| 315 pdebug(fmt_vastr(" filechanged = %d",filechanged),SUB_DEBUG); | |
| 316 pdebug(fmt_vastr(" pos_c = %d",pos_c),SUB_DEBUG); | |
| 317 pdebug(fmt_vastr(" pos_p = %d",pos_p),SUB_DEBUG); | |
| 318 pdebug(fmt_vastr(" rewind = %d", rewind),SUB_DEBUG); | |
| 319 pdebug(fmt_vastr(" seeked = %d", seeked),SUB_DEBUG); | |
| 320 pdebug(fmt_vastr(" playtime_c = %d", playtime_c),SUB_DEBUG); | |
| 321 pdebug(fmt_vastr(" playtime_p = %d", playtime_p),SUB_DEBUG); | |
| 322 pdebug(fmt_vastr(" playlistlen_c = %d", playlistlen_p), | |
| 323 SUB_DEBUG); | |
| 324 pdebug(fmt_vastr(" playlistlen_p = %d", playlistlen_p), | |
| 325 SUB_DEBUG); | |
| 326 changed = 1; | |
| 327 seeked = 0; | |
| 328 | |
| 329 if (file_p != NULL) | |
| 330 { | |
| 331 g_free(file_p); | |
| 332 file_p = NULL; | |
| 333 } | |
| 334 }else if( seeked ) { | |
| 335 seeked = 1; | |
| 336 pdebug("*** SEEK ***", SUB_DEBUG); | |
| 337 } | |
| 338 | |
| 339 break; | |
| 340 case pausing: | |
| 341 if(playtime_c != playtime_p){ | |
| 342 pdebug("*** SEEK ***", SUB_DEBUG); | |
| 343 seeked = 1; | |
| 344 } | |
| 345 break; | |
| 346 case stopping: | |
| 347 len = 0; | |
| 348 break; | |
| 349 default: | |
| 350 pdebug("*** unknown state tranfer!!! ***", SUB_DEBUG); | |
| 351 break; | |
| 352 } | |
| 353 | |
| 354 | |
| 355 if( playstate == start || changed || (seeked && !submitted) ){ | |
| 356 /* reset counter */ | |
| 357 pdebug(" <<< reset counter >>>", SUB_DEBUG); | |
| 358 | |
| 359 submitted = 0; | |
| 360 playtime_ofs = 0; | |
| 361 playtime_i = playtime_c; | |
| 362 time_i = time_c; | |
| 363 | |
| 364 }else{ | |
| 365 /* check playtime for submitting */ | |
| 366 if( !submitted ){ | |
| 367 if( len > 30 * 1000 && | |
| 368 /* len < 30 *60 * 1000 && // crazy rule!!! */ | |
| 369 ( | |
| 370 (playtime_ofs + playtime_c - playtime_i > len / 2) || | |
| 371 (playtime_ofs + playtime_c - playtime_i > 240 * 1000) | |
| 372 /* (playtime_c - playtime_i > 10 * 1000)// for debug */ | |
| 373 )){ | |
| 374 pdebug("*** submitting requirements are satisfied.", | |
| 375 SUB_DEBUG); | |
| 376 pdebug(fmt_vastr(" len = %d, playtime = %d", | |
| 377 len / 1000, (playtime_c - playtime_i)/1000 ), | |
| 378 SUB_DEBUG); | |
| 379 submitted = 1; | |
| 380 dosubmit.dosubmit = 1; | |
| 381 dosubmit.pos_c = pos_c; | |
| 382 dosubmit.len = len; | |
| 383 } | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 g_free(file_p); | |
| 388 | |
| 389 /* keep current value for next iteration */ | |
| 390 ps_p = ps_c; | |
| 391 file_p = file_c; | |
| 392 playtime_p = playtime_c; | |
| 393 pos_p = pos_c; | |
| 394 playlistlen_p = playlistlen_c; | |
| 395 | |
| 396 return dosubmit; | |
| 397 } | |
| 398 | |
| 399 static void *xs_thread(void *data __attribute__((unused))) | |
| 400 { | |
| 401 int run = 1; | |
| 402 submit_t dosubmit; | |
| 403 | |
| 404 while (run) { | |
| 405 /* Error catching */ | |
| 406 if(sc_catch_error()) | |
| 407 { | |
| 408 errorbox_show(sc_fetch_error()); | |
| 409 sc_clear_error(); | |
| 410 } | |
| 411 | |
| 412 if(gerpok_sc_catch_error()) | |
| 413 { | |
| 414 errorbox_show(gerpok_sc_fetch_error()); | |
| 415 gerpok_sc_clear_error(); | |
| 416 } | |
| 417 | |
| 418 /* Check for ability to submit */ | |
| 419 dosubmit = get_song_status(); | |
| 420 | |
| 421 if(dosubmit.dosubmit) { | |
| 422 TitleInput *tuple; | |
| 423 | |
| 424 pdebug("Submitting song.", DEBUG); | |
| 425 | |
| 426 tuple = playlist_get_tuple(dosubmit.pos_c); | |
| 427 | |
| 428 if (ishttp(tuple->file_name)) | |
| 429 continue; | |
| 430 | |
| 431 if(tuple->performer != NULL && tuple->track_name != NULL) | |
| 432 { | |
| 433 pdebug(fmt_vastr( | |
| 434 "submitting artist: %s, title: %s", | |
| 435 tuple->performer, tuple->track_name), DEBUG); | |
| 436 sc_addentry(m_scrobbler, tuple, | |
| 437 dosubmit.len/1000); | |
| 438 gerpok_sc_addentry(m_scrobbler, tuple, | |
| 439 dosubmit.len/1000); | |
| 440 } | |
| 441 else | |
| 442 pdebug("tuple does not contain an artist or a title, not submitting.", DEBUG); | |
| 443 } | |
| 444 g_mutex_lock(m_scrobbler); | |
| 445 run = (sc_going != 0 || ge_going != 0); | |
| 446 g_mutex_unlock(m_scrobbler); | |
| 447 g_usleep(100000); | |
| 448 } | |
| 449 pdebug("scrobbler thread: exiting", DEBUG); | |
| 450 g_thread_exit(NULL); | |
| 451 | |
| 452 return NULL; | |
| 453 } | |
| 454 | |
| 455 static void *hs_thread(void *data __attribute__((unused))) | |
| 456 { | |
| 457 int run = 1; | |
| 458 | |
| 459 while(run) | |
| 460 { | |
| 461 if(sc_idle(m_scrobbler)) | |
| 462 { | |
| 463 pdebug("Giving up due to fatal error", DEBUG); | |
| 464 g_mutex_lock(m_scrobbler); | |
| 465 sc_going = 0; | |
| 466 g_mutex_lock(m_scrobbler); | |
| 467 } | |
| 468 | |
| 469 if(gerpok_sc_idle(m_scrobbler)) | |
| 470 { | |
| 471 pdebug("Giving up due to fatal error", DEBUG); | |
| 472 g_mutex_lock(m_scrobbler); | |
| 473 ge_going = 0; | |
| 474 g_mutex_lock(m_scrobbler); | |
| 475 } | |
| 476 | |
| 477 g_mutex_lock(m_scrobbler); | |
| 478 run = (sc_going != 0 || ge_going != 0); | |
| 479 g_mutex_unlock(m_scrobbler); | |
| 480 g_usleep(1000000); | |
| 481 } | |
| 482 pdebug("handshake thread: exiting", DEBUG); | |
| 483 g_thread_exit(NULL); | |
| 484 | |
| 485 return NULL; | |
| 486 } | |
| 487 | |
| 488 GeneralPlugin *get_gplugin_info(void) | |
| 489 { | |
| 490 xmms_scrobbler.description = g_strdup_printf(_("Scrobbler Plugin")); | |
| 491 return &xmms_scrobbler; | |
| 492 } |
