summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xdocs/CHANGELOG3
-rw-r--r--lib/class/scrobbler.class.php47
-rw-r--r--lib/class/stats.class.php22
-rw-r--r--lib/class/user.class.php43
-rw-r--r--lib/install.php8
-rw-r--r--lib/preferences.php2
-rw-r--r--login.php19
-rw-r--r--modules/plugins/Lastfm.plugin.php122
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);
diff --git a/login.php b/login.php
index dc86d477..7c15ab65 100644
--- a/login.php
+++ b/login.php
@@ -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
?>