📄 audioscrobbler.c
字号:
/***************************************************************************** * audioscrobbler.c : audioscrobbler submission plugin ***************************************************************************** * Copyright © 2006-2008 the VideoLAN team * $Id: 711fec7c961649406c9e47b5ad780f1f3659ac24 $ * * Author: Rafaël Carré <funman at videolanorg> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************//* audioscrobbler protocol version: 1.2 * http://www.audioscrobbler.net/development/protocol/ * * TODO: "Now Playing" feature (not mandatory) *//***************************************************************************** * Preamble *****************************************************************************/#if defined( WIN32 ) #include <time.h> #endif #ifdef HAVE_CONFIG_H# include "config.h"#endif#include <vlc_common.h>#include <vlc_plugin.h>#include <vlc_interface.h>#include <vlc_meta.h>#include <vlc_md5.h>#include <vlc_block.h>#include <vlc_stream.h>#include <vlc_url.h>#include <vlc_network.h>#include <vlc_interface.h>#include <vlc_playlist.h>/***************************************************************************** * Local prototypes *****************************************************************************/#define QUEUE_MAX 50/* Keeps track of metadata to be submitted */typedef struct audioscrobbler_song_t{ char *psz_a; /**< track artist */ char *psz_t; /**< track title */ char *psz_b; /**< track album */ char *psz_n; /**< track number */ int i_l; /**< track length */ char *psz_m; /**< musicbrainz id */ time_t date; /**< date since epoch */ mtime_t i_start; /**< playing start */} audioscrobbler_song_t;struct intf_sys_t{ audioscrobbler_song_t p_queue[QUEUE_MAX]; /**< songs not submitted yet*/ int i_songs; /**< number of songs */ vlc_mutex_t lock; /**< p_sys mutex */ /* data about audioscrobbler session */ mtime_t next_exchange; /**< when can we send data */ unsigned int i_interval; /**< waiting interval (secs)*/ /* submission of played songs */ char *psz_submit_host; /**< where to submit data */ int i_submit_port; /**< port to which submit */ char *psz_submit_file; /**< file to which submit */ /* submission of playing song */#if 0 //NOT USED char *psz_nowp_host; /**< where to submit data */ int i_nowp_port; /**< port to which submit */ char *psz_nowp_file; /**< file to which submit */#endif bool b_handshaked; /**< are we authenticated ? */ char psz_auth_token[33]; /**< Authentication token */ /* data about song currently playing */ audioscrobbler_song_t p_current_song; /**< song being played */ mtime_t time_pause; /**< time when vlc paused */ mtime_t time_total_pauses; /**< total time in pause */ bool b_submit; /**< do we have to submit ? */ bool b_state_cb; /**< if we registered the * "state" callback */ bool b_meta_read; /**< if we read the song's * metadata already */};static int Open ( vlc_object_t * );static void Close ( vlc_object_t * );static void Run ( intf_thread_t * );static int ItemChange ( vlc_object_t *, const char *, vlc_value_t, vlc_value_t, void * );static int PlayingChange ( vlc_object_t *, const char *, vlc_value_t, vlc_value_t, void * );static void AddToQueue ( intf_thread_t * );static int Handshake ( intf_thread_t * );static int ReadMetaData ( intf_thread_t * );static void DeleteSong ( audioscrobbler_song_t* );static int ParseURL ( char *, char **, char **, int * );static void HandleInterval ( mtime_t *, unsigned int * );/***************************************************************************** * Module descriptor ****************************************************************************/#define USERNAME_TEXT N_("Username")#define USERNAME_LONGTEXT N_("The username of your last.fm account")#define PASSWORD_TEXT N_("Password")#define PASSWORD_LONGTEXT N_("The password of your last.fm account")/* This error value is used when last.fm plugin has to be unloaded. */#define VLC_AUDIOSCROBBLER_EFATAL -69/* last.fm client identifier */#define CLIENT_NAME PACKAGE#define CLIENT_VERSION VERSION/* HTTP POST request : to submit data */#define POST_REQUEST "POST /%s HTTP/1.1\n" \ "Accept-Encoding: identity\n" \ "Content-length: %u\n" \ "Connection: close\n" \ "Content-type: application/x-www-form-urlencoded\n" \ "Host: %s\n" \ "User-agent: VLC Media Player/%s\r\n" \ "\r\n" \ "%s\r\n" \ "\r\n"vlc_module_begin(); set_category( CAT_INTERFACE ); set_subcategory( SUBCAT_INTERFACE_CONTROL ); set_shortname( N_( "Audioscrobbler" ) ); set_description( N_("Submission of played songs to last.fm") ); add_string( "lastfm-username", "", NULL, USERNAME_TEXT, USERNAME_LONGTEXT, false ); add_password( "lastfm-password", "", NULL, PASSWORD_TEXT, PASSWORD_LONGTEXT, false ); set_capability( "interface", 0 ); set_callbacks( Open, Close );vlc_module_end();/***************************************************************************** * Open: initialize and create stuff *****************************************************************************/static int Open( vlc_object_t *p_this ){ playlist_t *p_playlist; intf_thread_t *p_intf = ( intf_thread_t* ) p_this; intf_sys_t *p_sys = calloc( 1, sizeof( intf_sys_t ) ); if( !p_sys ) return VLC_ENOMEM; p_intf->p_sys = p_sys; vlc_mutex_init( &p_sys->lock ); p_playlist = pl_Yield( p_intf ); PL_LOCK; var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf ); PL_UNLOCK; pl_Release( p_intf ); p_intf->pf_run = Run; return VLC_SUCCESS;}/***************************************************************************** * Close: destroy interface stuff *****************************************************************************/static void Close( vlc_object_t *p_this ){ playlist_t *p_playlist; input_thread_t *p_input; intf_thread_t *p_intf = ( intf_thread_t* ) p_this; intf_sys_t *p_sys = p_intf->p_sys; p_playlist = pl_Yield( p_intf ); if( p_playlist ) { PL_LOCK; var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf ); p_input = p_playlist->p_input; if ( p_input ) { vlc_object_yield( p_input ); if( p_sys->b_state_cb ) var_DelCallback( p_input, "state", PlayingChange, p_intf ); vlc_object_release( p_input ); } PL_UNLOCK; pl_Release( p_intf ); } p_intf->b_dead = true; /* we lock the mutex in case p_sys is being accessed from a callback */ vlc_mutex_lock ( &p_sys->lock ); int i; for( i = 0; i < p_sys->i_songs; i++ ) DeleteSong( &p_sys->p_queue[i] ); free( p_sys->psz_submit_host ); free( p_sys->psz_submit_file );#if 0 //NOT USED free( p_sys->psz_nowp_host ); free( p_sys->psz_nowp_file );#endif vlc_mutex_unlock ( &p_sys->lock ); vlc_mutex_destroy( &p_sys->lock ); free( p_sys );}/***************************************************************************** * Run : call Handshake() then submit songs *****************************************************************************/static void Run( intf_thread_t *p_intf ){ char *psz_submit, *psz_submit_song, *psz_submit_tmp; int i_net_ret; int i_song; uint8_t p_buffer[1024]; char *p_buffer_pos; int i_post_socket; intf_sys_t *p_sys = p_intf->p_sys; /* main loop */ for( ;; ) { bool b_wait = false; vlc_object_lock( p_intf ); if( !vlc_object_alive( p_intf ) ) { vlc_object_unlock( p_intf ); msg_Dbg( p_intf, "audioscrobbler is dying"); return; } if( mdate() < p_sys->next_exchange ) /* wait until we can resubmit, i.e. */ b_wait = vlc_object_timedwait( p_intf, p_sys->next_exchange ) == 0; else /* wait for data to submit */ /* we are signaled each time there is a song to submit */ vlc_object_wait( p_intf ); vlc_object_unlock( p_intf ); if( b_wait ) continue; /* holding on until next_exchange */ /* handshake if needed */ if( p_sys->b_handshaked == false ) { msg_Dbg( p_intf, "Handshaking with last.fm ..." ); switch( Handshake( p_intf ) ) { case VLC_ENOMEM: return; case VLC_ENOVAR: /* username not set */ intf_UserFatal( p_intf, false, _("Last.fm username not set"), _("Please set a username or disable the " "audioscrobbler plugin, and restart VLC.\n" "Visit http://www.last.fm/join/ to get an account.") ); return; case VLC_SUCCESS: msg_Dbg( p_intf, "Handshake successfull :)" ); p_sys->b_handshaked = true; p_sys->i_interval = 0; p_sys->next_exchange = mdate(); break; case VLC_AUDIOSCROBBLER_EFATAL: msg_Warn( p_intf, "Exiting..." ); return; case VLC_EGENERIC: default: /* protocol error : we'll try later */ HandleInterval( &p_sys->next_exchange, &p_sys->i_interval ); break; } /* if handshake failed let's restart the loop */ if( p_sys->b_handshaked == false ) continue; } msg_Dbg( p_intf, "Going to submit some data..." ); /* The session may be invalid if there is a trailing \n */ char *psz_ln = strrchr( p_sys->psz_auth_token, '\n' ); if( psz_ln ) *psz_ln = '\0'; if( !asprintf( &psz_submit, "s=%s", p_sys->psz_auth_token ) ) { /* Out of memory */ return; } /* forge the HTTP POST request */ vlc_mutex_lock( &p_sys->lock ); audioscrobbler_song_t *p_song; for( i_song = 0 ; i_song < p_sys->i_songs ; i_song++ ) { p_song = &p_sys->p_queue[i_song]; if( !asprintf( &psz_submit_song, "&a%%5B%d%%5D=%s"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -