diff options
-rwxr-xr-x | docs/CHANGELOG | 3 | ||||
-rw-r--r-- | lib/class/scrobbler.class.php | 47 | ||||
-rw-r--r-- | lib/class/stats.class.php | 22 | ||||
-rw-r--r-- | lib/class/user.class.php | 43 | ||||
-rw-r--r-- | lib/install.php | 8 | ||||
-rw-r--r-- | lib/preferences.php | 2 | ||||
-rw-r--r-- | login.php | 19 | ||||
-rw-r--r-- | modules/plugins/Lastfm.plugin.php | 122 |
8 files changed, 202 insertions, 64 deletions
diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 6e2bd506..4b099eca 100755 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -4,6 +4,9 @@ -------------------------------------------------------------------------- v.3.4-Alpha2 + - Fixed double posting of songs on a single stream with some + clients + - Updated LastFM protocol to v1.2 - Fixed copyright notices (Thx porthose) - Fixed single downloads - Fixed weird CSS issue with a crafty little hack diff --git a/lib/class/scrobbler.class.php b/lib/class/scrobbler.class.php index 26aad0f1..200d2895 100644 --- a/lib/class/scrobbler.class.php +++ b/lib/class/scrobbler.class.php @@ -34,12 +34,15 @@ class scrobbler { * Constructor * This is the constructer it takes a username and password */ - public function __construct($username, $password) { + public function __construct($username, $password,$host='',$port='',$url='',$challenge='') { $this->error_msg = ''; $this->username = trim($username); $this->password = trim($password); - $this->challenge = ''; + $this->challenge = $challenge; + $this->submit_host = $host; + $this->submit_port = $port; + $this->submit_url = $url; $this->queued_tracks = array(); } // scrobbler @@ -69,15 +72,17 @@ class scrobbler { */ public function handshake() { - $as_socket = @fsockopen('post.audioscrobbler.com', 80, $errno, $errstr, 15); + $as_socket = @fsockopen('post.audioscrobbler.com', 80, $errno, $errstr, 5); if(!$as_socket) { $this->error_msg = $errstr; return false; } - $username = rawurlencode($this->username); + $username = rawurlencode($this->username); + $timestamp = time(); + $auth_token = rawurlencode(md5($this->password . $timestamp)); - $get_string = "GET /?hs=true&p=1.1&c=apa&v=0.1&u=$username HTTP/1.1\r\n"; + $get_string = "GET /?hs=true&p=1.2&c=apa&v=0.1&u=$username&t=$timestamp&a=$auth_token HTTP/1.1\r\n"; fwrite($as_socket, $get_string); fwrite($as_socket, "Host: post.audioscrobbler.com\r\n"); @@ -107,7 +112,7 @@ class scrobbler { return false; } - if(preg_match('/http:\/\/(.*):(\d+)(.*)/', $response[2], $matches)) { + if(preg_match('/http:\/\/(.*):(\d+)(.*)/', $response[3], $matches)) { $data['submit_host'] = $matches[1]; $data['submit_port'] = $matches[2]; $data['submit_url'] = $matches[3]; @@ -127,21 +132,20 @@ class scrobbler { * submit the track or talk to LastFM in anyway, kind of useless for our uses but its * here, and that's how it is. */ - public function queue_track($artist, $album, $track, $timestamp, $length) { - $date = gmdate('Y-m-d H:i:s', $timestamp); - $mydate = date('Y-m-d H:i:s T', $timestamp); + public function queue_track($artist, $album, $title, $timestamp, $length,$track) { - if($length < 30) { - debug_event('lastfm',"Not queuing track, too short",'5'); + if ($length < 30) { + debug_event('LastFM',"Not queuing track, too short",'5'); return false; } $newtrack = array(); $newtrack['artist'] = $artist; $newtrack['album'] = $album; + $newtrack['title'] = $title; $newtrack['track'] = $track; $newtrack['length'] = $length; - $newtrack['time'] = $date; + $newtrack['time'] = $timestamp; $this->queued_tracks[$timestamp] = $newtrack; return true; @@ -165,17 +169,18 @@ class scrobbler { ksort($this->queued_tracks); // build the query string - $query_str = 'u='.rawurlencode($this->username).'&s='.rawurlencode(md5($this->password.$this->challenge)).'&'; + $query_str = 's='.rawurlencode($this->challenge).'&'; $i = 0; foreach($this->queued_tracks as $track) { - $query_str .= "a[$i]=".rawurlencode($track['artist'])."&t[$i]=".rawurlencode($track['track'])."&b[$i]=".rawurlencode($track['album'])."&"; + $query_str .= "a[$i]=".rawurlencode($track['artist'])."&t[$i]=".rawurlencode($track['title'])."&b[$i]=".rawurlencode($track['album'])."&"; $query_str .= "m[$i]=&l[$i]=".rawurlencode($track['length'])."&i[$i]=".rawurlencode($track['time'])."&"; + $query_str .= "n[$i]=" . rawurlencode($track['track']) . "&o[$i]=P&r[$i]=&"; $i++; } - $as_socket = @fsockopen($this->submit_host, $this->submit_port, $errno, $errstr, 15); + $as_socket = @fsockopen($this->submit_host, $this->submit_port, $errno, $errstr, 5); if(!$as_socket) { $this->error_msg = $errstr; @@ -186,11 +191,12 @@ class scrobbler { fwrite($as_socket, $action); fwrite($as_socket, "Host: ".$this->submit_host."\r\n"); fwrite($as_socket, "Accept: */*\r\n"); + fwrite($as_socket, "User-Agent: Ampache/3.4\r\n"); fwrite($as_socket, "Content-type: application/x-www-form-urlencoded\r\n"); fwrite($as_socket, "Content-length: ".strlen($query_str)."\r\n\r\n"); fwrite($as_socket, $query_str."\r\n\r\n"); - +debug_event('LastFM',$query_str,'1'); $buffer = ''; while(!feof($as_socket)) { $buffer .= fread($as_socket, 8192); @@ -213,15 +219,20 @@ class scrobbler { return false; } if(substr($response[0], 0, 7) == 'BADAUTH') { - $this->error_msg = 'Invalid username/password'; + $this->error_msg = 'Invalid username/password (' . $response[0] . ')'; return false; } + if (substr($response[0],0,10) == 'BADSESSION') { + $this->error_msg = 'Invalid Session passed (' . trim($response[0]) . ')'; + return false; + } if(substr($response[0], 0, 2) != 'OK') { - $this->error_msg = 'Unknown error submitting tracks'. + $this->error_msg = 'Response Not ok, unknown error'. "\nDebug output:\n".$buffer; return false; } + debug_event('fooo',$buffer,'1'); return true; } // submit_tracks diff --git a/lib/class/stats.class.php b/lib/class/stats.class.php index ab6896fb..a687cd69 100644 --- a/lib/class/stats.class.php +++ b/lib/class/stats.class.php @@ -66,6 +66,28 @@ class Stats { } } // insert + + /** + * get_last_song + * This returns the full data for the last song that was played, including when it + * was played, this is used by, among other things, the LastFM plugin to figure out + * if we should re-submit or if this is a duplicate / if it's too soon. This takes an + * optional user_id because when streaming we don't have $GLOBALS() + */ + public static function get_last_song($user_id='') { + + $user_id = $user_id ? $user_id : $GLOBALS['user']->id; + + $user_id = Dba::escape($user_id); + + $sql = "SELECT * FROM `object_count` WHERE `user`='$user_id' AND `object_type`='song' ORDER BY `date` DESC LIMIT 1"; + $db_results = Dba::query($sql); + + $results = Dba::fetch_assoc($db_results); + + return $results; + + } // get_last_song /** * get_top diff --git a/lib/class/user.class.php b/lib/class/user.class.php index 32b5d59d..ca74badf 100644 --- a/lib/class/user.class.php +++ b/lib/class/user.class.php @@ -558,41 +558,34 @@ class User { public function update_stats($song_id) { $song_info = new Song($song_id); + $song_info->format(); $user = $this->id; if (!strlen($song_info->file)) { return false; } + // Make sure we didn't just play this song + $data = Stats::get_last_song($this->id); + $last_song = new Song($data['object_id']); + if ($data['date']+($song_info->time/2) >= time()) { + debug_event('Stats','Not collecting stats less then 50% of song has elapsed','3'); + return false; + } + + // Check if lastfm is loaded, if so run the update + if (Plugin::is_installed('Last.FM')) { + $lastfm = new Plugin('Lastfm'); + if ($lastfm->_plugin->load($this->prefs,$this->id)) { + $lastfm->_plugin->submit($song_info,$this->id); + } + } // end if is_installed + + // Do this last so the 'last played checks are correct' Stats::insert('song',$song_id,$user); Stats::insert('album',$song_info->album,$user); Stats::insert('artist',$song_info->artist,$user); Stats::insert('genre',$song_info->genre,$user); - /** - * Record this play to LastFM - * because it lags like the dickens try twice on everything - */ - if (!empty($this->prefs['lastfm_user']) AND !empty($this->prefs['lastfm_pass'])) { - $song_info->format(); - - $lastfm = new scrobbler($this->prefs['lastfm_user'],$this->prefs['lastfm_pass']); - $lastfm->submit_host = $this->prefs['lastfm_host']; - $lastfm->submit_port = $this->prefs['lastfm_port']; - $lastfm->submit_url = $this->prefs['lastfm_url']; - $lastfm->challenge = $this->prefs['lastfm_challenge']; - - if (!$lastfm->queue_track($song_info->f_artist_full,$song_info->f_album_full,$song_info->title,time(),$song_info->time)) { - debug_event('LastFM','Error: Queue Failed: ' . $lastfm->error_msg,'3'); - } - - $submit = $lastfm->submit_tracks(); - - /* Try again if it fails */ - if (!$submit) { sleep(1); $submit = $lastfm->submit_tracks(); } - if (!$submit) { - debug_event('LastFM','Error Submit Failed: ' . $lastfm->error_msg,'3'); - } - } // record to LastFM } // update_stats /** diff --git a/lib/install.php b/lib/install.php index eae63abd..779f9564 100644 --- a/lib/install.php +++ b/lib/install.php @@ -98,6 +98,14 @@ function install_check_status($configfile) { */ function install_insert_db($username,$password,$hostname,$database) { + // Make sure that the database name is valid + $is_valid = preg_match("/([^\d\w\_])/",$database,$matches); + + if (count($matches)) { + Error::add('general','Error: Database name invalid must not be a reserved word, and must be Alphanumeric'); + return false; + } + $data['database_username'] = $username; $data['database_password'] = $password; $data['database_hostname'] = $hostname; diff --git a/lib/preferences.php b/lib/preferences.php index e3cd19c3..fe404331 100644 --- a/lib/preferences.php +++ b/lib/preferences.php @@ -113,8 +113,8 @@ function update_preferences($pref_id=0) { $value = validate_bitrate($value); break; /* MD5 the LastFM & MyStrands so it's not plainTXT */ - case 'mystrands_pass': case 'lastfm_pass': + case 'mystrands_pass': /* If it's our default blanking thing then don't use it */ if ($value == '******') { unset($_REQUEST[$name]); break; } $value = md5($value); @@ -126,25 +126,6 @@ if ($auth['success']) { // Reload the Preferences from the database init_preferences(); - // Do the handshake with LastFM if they are configured as such to let it know we might submit some stuff soon - if ($user->prefs['lastfm_user'] AND $user->prefs['lastfm_pass']) { - $lastfm = new scrobbler($user->prefs['lastfm_user'],$user->prefs['lastfm_pass']); - - /* Attempt handshake */ - $handshake = $lastfm->handshake(); - if (!$handshake) { - debug_event('LastFM','Handshake Failed: ' . $lastfm->error_msg,'3'); - } - - // Update the preferences - Preference::update('lastfm_port',$user->id,$handshake['submit_port']); - Preference::update('lastfm_host',$user->id,$handshake['submit_host']); - Preference::update('lastfm_url',$user->id,$handshake['submit_url']); - Preference::update('lastfm_challenge',$user->id,$handshake['challenge']); - - } // if LastFM - - /* Make sure they are actually trying to get to this site and don't try to redirect them back into * an admin section **/ diff --git a/modules/plugins/Lastfm.plugin.php b/modules/plugins/Lastfm.plugin.php index 7407d7ba..20de8ea5 100644 --- a/modules/plugins/Lastfm.plugin.php +++ b/modules/plugins/Lastfm.plugin.php @@ -28,6 +28,15 @@ class AmpacheLastfm { public $min_ampache ='340007'; public $max_ampache ='340008'; + // These are internal settings used by this class, run this->load to + // fill em out + private $username; + private $password; + private $hostname; + private $port; + private $path; + private $challenge; + /** * Constructor * This function does nothing... @@ -59,7 +68,7 @@ class AmpacheLastfm { * This is a required plugin function it removes the required preferences from * the database returning it to its origional form */ - function uninstall() { + public function uninstall() { /* We need to remove the preivously added preferences */ $sql = "DELETE FROM `preference` WHERE `name`='lastfm_pass' OR `name`='lastfm_user' " . @@ -68,5 +77,116 @@ class AmpacheLastfm { } // uninstall + /** + * submit + * This takes care of queueing and then submiting the tracks eventually this will make sure + * that you've haven't + */ + public function submit($song,$user_id) { + + // Before we start let's pull the last song submited by this user + $previous = Stats::get_last_song($user_id); + + $diff = time() - $previous['date']; + + // Make sure it wasn't within the last min + if ($diff < 60) { + debug_event('LastFM','Last song played within ' . $diff . ' seconds, not recording stats','3'); + return false; + } + + if ($song->time < 30) { + debug_event('LastFM','Song less then 30 seconds not queueing','3'); + return false; + } + + // Create our scrobbler with everything this time and then queue it + $scrobbler = new scrobbler($this->username,$this->password,$this->hostname,$this->port,$this->path,$this->challenge); + if (!$scrobbler->queue_track($song->f_artist_full,$song->f_album_full,$song->title,time(),$song->time,$song->track)) { + return false; + } + + // Go ahead and submit it now + if (!$scrobbler->submit_tracks()) { + debug_event('LastFM','Error Submit Failed: ' . $scrobbler->error_msg,'3'); + return false; + } + + return true; + + } // submit + + /** + * set_handshake + * This runs a handshake and properly updates the preferences as needed, it returns the data + * as an array so we don't have to requery the db. This requires a userid so it knows who's + * crap to update + */ + public function set_handshake($user_id) { + + $scrobbler = new scrobbler($this->username,$this->password); + $data = $scrobbler->handshake(); + + if (!$data) { + debug_event('LastFM','Handshake Failed: ' . $scrobbler->error_msg,'3'); + return false; + } + + $this->hostname = $data['submit_host']; + $this->port = $data['submit_port']; + $this->path = $data['submit_url']; + $this->challenge = $data['challenge']; + + // Update the preferences + Preference::update('lastfm_port',$user_id,$data['submit_port']); + Preference::update('lastfm_host',$user_id,$data['submit_host']); + Preference::update('lastfm_url',$user_id,$data['submit_url']); + Preference::update('lastfm_challenge',$user_id,$data['challenge']); + + return true; + + } // set_handshake + + /** + * load + * This loads up the data we need into this object, this stuff comes from the preferences + * it's passed as a key'd array + */ + public function load($data,$user_id) { + + if (isset($data['lastfm_user'])) { + $this->username = $data['lastfm_user']; + } + else { + return false; + } + if (isset($data['lastfm_pass'])) { + $this->password = $data['lastfm_pass']; + } + else { + return false; + } + + // If we don't have the other stuff try to get it before giving up + if (!$data['lastfm_host'] || !$data['lastfm_port'] || !$data['lastfm_url'] || !$data['lastfm_challenge']) { + debug_event('LastFM','Running Handshake, missing information','3'); + if (!$this->set_handshake($user_id)) { + debug_event('LastFM','Handshake failed, you lose','3'); + return false; + } + } + else { + $this->hostname = $data['lastfm_host']; + $this->port = $data['lastfm_port']; + $this->path = $data['lastfm_url']; + $this->challenge = $data['lastfm_challenge']; + } + + + return true; + + } // load + + } // end AmpacheLastfm ?> |