From f8c98952aa5a8a30a50160db864217d6bf942a8f Mon Sep 17 00:00:00 2001 From: Karl 'vollmerk' Vollmer Date: Tue, 31 Jul 2007 07:22:18 +0000 Subject: fixed now playing, hopefully once and for all, added title text to the now playing username that displays the agent they are using to play said song, added distinct sessions and session lengths for each stream instance --- config/ampache.cfg.php.dist | 5 ++ docs/CHANGELOG | 4 ++ lib/class/stream.class.php | 85 ++++++++++++++++++++--- lib/class/update.class.php | 41 ++++++++++++ lib/general.lib.php | 4 -- lib/stream.lib.php | 119 +++++++++++++++------------------ lib/ui.lib.php | 37 ---------- play/index.php | 11 ++- templates/show_now_playing.inc.php | 1 + templates/show_now_playing_row.inc.php | 2 +- 10 files changed, 186 insertions(+), 123 deletions(-) diff --git a/config/ampache.cfg.php.dist b/config/ampache.cfg.php.dist index beba1357..40ddd3b5 100644 --- a/config/ampache.cfg.php.dist +++ b/config/ampache.cfg.php.dist @@ -49,6 +49,11 @@ database_password = password ; DEFAULT: 900 session_length = 900 +; Length that the session for a single streaming instance will last +; the default is 15min. With some clients, and long songs this can +; cause playback to stop, increase this value if you experience that +stream_length = 900 + ; This length defines how long a 'remember me' session and cookie will ; last, the default is 900, same as length. It is up to the administrator ; of the box to increase this, for reference 86400 = 1 day diff --git a/docs/CHANGELOG b/docs/CHANGELOG index a8a8c1d7..494a0a97 100755 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -4,6 +4,10 @@ -------------------------------------------------------------------------- v.3.4-Alpha2 + - Fixed now playing, hopefully once and for all + - Added distinct sessions for each stream action, also allows + for independent session lengths between interface + and streaming - Added paging on all song displays including search - Fixed searching via quicksearch bar - Added 'Your' playlists to leftbar with play and view links diff --git a/lib/class/stream.class.php b/lib/class/stream.class.php index 6fca492c..fe40de79 100644 --- a/lib/class/stream.class.php +++ b/lib/class/stream.class.php @@ -45,13 +45,14 @@ class Stream { $this->type = $type; $this->songs = $song_ids; $this->web_path = Config::get('web_path'); + $this->user_id = $GLOBALS['user']->id; if (Config::get('force_http_play')) { $this->web_path = preg_replace("/https/", "http",$this->web_path); } - $this->sess = session_id(); - $this->user_id = $GLOBALS['user']->id; + // Generate the session ID + $this->session = md5(uniqid(rand(), true));; } // Constructor @@ -67,6 +68,12 @@ class Stream { return false; } + // We're starting insert the session into session_stream + if (!$this->insert_session()) { + debug_event('stream','Session Insertion failure, aboring','3'); + return false; + } + $methods = get_class_methods('Stream'); $create_function = "create_" . $this->type; @@ -93,6 +100,68 @@ class Stream { } // manual_url_add + /** + * insert_session + * This inserts a row into the session_stream table + */ + private function insert_session() { + + $expire = time() + Config::get('stream_length'); + + $sql = "INSERT INTO `session_stream` (`id`,`expire`,`user`) " . + "VALUES('$this->session','$expire','$this->user_id')"; + $db_results = Dba::query($sql); + + if (!$db_results) { return false; } + + return true; + + } // insert_session + + /** + * session_exists + * This checks to see if the passed stream session exists and is valid + */ + public static function session_exists($sid) { + + $sid = Dba::escape($sid); + $time = time(); + + $sql = "SELECT * FROM `session_stream` WHERE `id`='$sid' AND `expire` > '$time'"; + $db_results = Dba::query($sql); + + if ($row = Dba::fetch_assoc($db_results)) { + return true; + } + + return false; + + } // session_exists + + /** + * extend_session + * This takes the passed sid and does a replace into also setting the user + * agent and IP also do a little GC in this function + */ + public static function extend_session($sid,$uid) { + + $expire = time() + Config::get('stream_length'); + $sid = Dba::escape($sid); + $agent = Dba::escape($_SERVER['HTTP_USER_AGENT']); + $ip = ip2int($_SERVER['REMOTE_ADDR']); + $uid = Dba::escape($uid); + + $sql = "UPDATE `session_stream` SET `expire`='$expire', `agent`='$agent', `ip`='$ip' " . + "WHERE `id`='$sid'"; + $db_results = Dba::query($sql); + + $sql = "DELETE FROM `session_stream` WHERE `ip`='$ip' AND `agent`='$agent' AND `user`='$uid' AND `id` != '$sid'"; + $db_results = Dba::query($sql); + + return true; + + } // extend_session + /** * create_simplem3u * this creates a simple m3u without any of the extended information @@ -118,7 +187,7 @@ class Stream { if ($GLOBALS['user']->prefs['play_type'] == 'downsample') { $ds = $GLOBALS['user']->prefs['sample_rate']; } - echo "$this->web_path/play/index.php?song=$song_id&uid=$this->user_id&sid=$this->sess&ds=$ds&stupidwinamp=." . $song->type . "\n"; + echo $song->get_url($this->session); } // end foreach /* Foreach the additional URLs */ @@ -155,7 +224,7 @@ class Stream { $song->format(); echo "#EXTINF:$song->time," . $song->f_artist_full . " - " . $song->title . "\n"; - echo $song->get_url() . "\n"; + echo $song->get_url($this->session) . "\n"; } // end foreach /* Foreach URLS */ @@ -186,7 +255,7 @@ class Stream { $song = new Song($song_id); $song->format(); $song_name = $song->f_artist_full . " - " . $song->title . "." . $song->type; - $song_url = $song->get_url(); + $song_url = $song->get_url($this->session); echo "File" . $i . "=$song_url\n"; echo "Title" . $i . "=$song_name\n"; echo "Length" . $i . "=$song->time\n"; @@ -220,7 +289,7 @@ class Stream { foreach ($this->songs as $song_id) { $song = new Song($song_id); $song->format(); - $url = $song->get_url(); + $url = $song->get_url($this->session); $song_name = $song->f_artist_full . " - " . $song->title . "." . $song->type; echo "\n"; @@ -264,7 +333,7 @@ class Stream { $song->format(); $xml = array(); - $xml['track']['location'] = $song->get_url() . $flash_hack; + $xml['track']['location'] = $song->get_url($this->session) . $flash_hack; $xml['track']['identifier'] = $xml['track']['location']; $xml['track']['title'] = $song->title; $xml['track']['creator'] = $song->f_artist_full; @@ -392,7 +461,7 @@ class Stream { header("Content-Type: audio/x-pn-realaudio ram;"); foreach ($this->songs as $song_id) { $song = new Song($song_id); - echo "$this->web_path/play/index.php?song=$song_id&uid=$this->user_id&sid=$this->sess&stupidwinamp=." . $song->type . "\n"; + echo $song->get_url($this->session); } // foreach songs } // create_ram diff --git a/lib/class/update.class.php b/lib/class/update.class.php index bd1be7af..2af04264 100644 --- a/lib/class/update.class.php +++ b/lib/class/update.class.php @@ -215,6 +215,11 @@ class Update { $version[] = array('version' => '340006','description' => $update_string); + $update_string = '- Added new session_stream table for sessions tied directly to stream instances.
' . + '- Altered the session table, making value a LONGTEXT.
'; + + $version[] = array('version' => '340007','description' => $update_string); + return $version; } // populate_version @@ -807,5 +812,41 @@ class Update { } // update_340006 + /** + * update_340007 + * This update converts the session.value to a longtext + * and adds a session_stream table + */ + public static function update_340007() { + + // Tweak the session table to handle larger session vars for my page-a-nation hotness + $sql = "ALTER TABLE `session` CHANGE `value` `value` LONGTEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL"; + $db_results = Dba::query($sql); + + // Create the new stream table where we will store stream SIDs + $sql = "CREATE TABLE `session_stream` ( " . + "`id` VARCHAR( 64 ) NOT NULL , " . + "`user` INT( 11 ) UNSIGNED NOT NULL , " . + "`agent` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL , " . + "`expire` INT( 11 ) UNSIGNED NOT NULL , " . + "`ip` INT( 11 ) UNSIGNED NULL , " . + "PRIMARY KEY ( `id` ) " . + ") ENGINE = MYISAM"; + $db_results = Dba::query($sql); + + // Change the now playing to use stream session ids for its ID + $sql = "ALTER TABLE `now_playing` CHANGE `id` `id` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL"; + $db_results = Dba::query($sql); + + // Now longer needed because of the new hotness + $sql = "ALTER TABLE `now_playing` DROP `session`"; + $db_results = Dba::query($sql); + + self::set_version('db_version','340007'); + + return true; + + } // update_340007 + } // end update class ?> diff --git a/lib/general.lib.php b/lib/general.lib.php index 5c724f18..3d60014f 100644 --- a/lib/general.lib.php +++ b/lib/general.lib.php @@ -114,10 +114,6 @@ function error_results($param,$clobber=0) /** * session_exists * checks to make sure they've specified a valid session, can handle xmlrpc - * @package General - * @cataogry Verify - * @todo Have XMLRPC check extend remote session - * @todo actually check */ function session_exists($sid,$xml_rpc=0) { diff --git a/lib/stream.lib.php b/lib/stream.lib.php index 00416a7d..df07e821 100644 --- a/lib/stream.lib.php +++ b/lib/stream.lib.php @@ -20,30 +20,6 @@ */ -/** - * delete_now_playing - * This function checks to see if we should delete the last now playing entry now that it's - * finished streaming the song. Basicly this is an exception for WMP10 - * @package General - * @catagory Now Playing - */ -function delete_now_playing($insert_id) { - - $user_agent = $_SERVER['HTTP_USER_AGENT']; - - // Account for WMP and the Flash Player -// if (stristr($user_agent,"NSPlayer") || $_REQUEST['flash_hack'] == 1) { - // Commented out until I can figure out the - // trick to making this work -// return true; -// } - - // Remove the song from the now_playing table - $sql = "DELETE FROM `now_playing` WHERE `id` = '$insert_id'"; - $db_result = Dba::query($sql); - -} // delete_now_playing - /** * gc_now_playing * this is a garbage collection function for now playing this is called every time something @@ -53,18 +29,16 @@ function delete_now_playing($insert_id) { */ function gc_now_playing() { - /* Account for WMP11's Initial Streaming */ - //if (strstr($_SERVER['HTTP_USER_AGENT'],"WMFSDK/11")) { return false; } + // Delete expired songs + $sql = "DELETE FROM `now_playing` WHERE `expire` < '$time'"; + $db_result = Dba::query($sql); - $time = time(); - $session_id = Dba::escape($_REQUEST['sid']); - if (strlen($session_id)) { - $session_sql = " OR session = '$session_id'"; - } + // Remove any now playing entries for session_streams that have been GC'd + $sql = "DELETE FROM `now_playing` USING `now_playing` " . + "LEFT JOIN `session_stream` ON `session_stream`.`id`=`now_playing`.`id` " . + "WHERE `session_stream`.`id` IS NULL"; + $db_results = Dba::query($sql); - $sql = "DELETE FROM `now_playing` WHERE `expire` < '$time'" . $session_sql; - $db_result = Dba::query($sql); - } // gc_now_playing /** @@ -73,41 +47,16 @@ function gc_now_playing() { * we use this function because we need to do thing differently * depending upon which play is actually streaming */ -function insert_now_playing($song_id,$uid,$song_length) { +function insert_now_playing($song_id,$uid,$song_length,$sid) { - $user_agent = $_SERVER['HTTP_USER_AGENT']; $time = time()+$song_length; - $session_id = Dba::escape($_REQUEST['sid']); - - /* Check for against a list of clients that have abusive traffic patterns causing - * faulty now playing data and return without inserting - */ - $banned_clients = array('Audacious/1.3'); - - foreach ($banned_clients as $banned_agent) { - if (stristr($user_agent,$banned_agent)) { - debug_event('Banned Agent',$banned_agent . ' clients now playing data not entered because Ampache is unable to handle its request pattern','5'); - return false; - } - } - - /* Windows Media Player is evil and it makes multiple requests per song */ - if (stristr($user_agent,"Windows-Media-Player")) { $session_id = ' '; } - - /* Check for Windows Media Player 11 */ - if (strstr($user_agent,'NSPlayer/11') AND !strstr($user_agent,'WMFSDK/11')) { $session_id = ' '; } - - // If they are using Windows media player - if (strstr($user_agent,"NSPlayer") || $_REQUEST['flash_hack'] == 1) { $session_id = ' '; } + $session_id = Dba::escape($sid); - $sql = "INSERT INTO now_playing (`song_id`, `user`, `expire`,`session`)" . - " VALUES ('$song_id', '$uid', '$time','$session_id')"; - - $db_result = Dba::query($sql); - - $insert_id = Dba::insert_id(); - - return $insert_id; + // Do a replace into ensuring that this client always only has a single row + $sql = "REPLACE INTO `now_playing` (`id`,`song_id`, `user`, `expire`)" . + " VALUES ('$session_id','$song_id', '$uid', '$time')"; + + $db_result = Dba::query($sql); } // insert_now_playing @@ -125,6 +74,44 @@ function clear_now_playing() { } // clear_now_playing +/** + * show_now_playing + * shows the now playing template + */ +function show_now_playing() { + + $web_path = Config::get('web_path'); + $results = get_now_playing(); + require Config::get('prefix') . '/templates/show_now_playing.inc.php'; + +} // show_now_playing + +/** + * get_now_playing + * gets the now playing information + */ +function get_now_playing($filter='') { + + $sql = "SELECT `session_stream`.`agent`,`now_playing`.`song_id`,`now_playing`.`user` FROM `now_playing` " . + "LEFT JOIN `session_stream` ON `session_stream`.`id`=`now_playing`.`id` " . + "ORDER BY `now_playing`.`expire` DESC"; + $db_results = Dba::query($sql); + + $results = array(); + + /* While we've got stuff playing */ + while ($r = Dba::fetch_assoc($db_results)) { + $song = new Song($r['song_id']); + $song->format(); + $np_user = new User($r['user']); + $results[] = array('song'=>$song,'user'=>$np_user,'agent'=>$r['agent']); + } // end while + + return $results; + +} // get_now_playing + + /** * check_lock_songs * This checks to see if the song is already playing, if it is then it prevents the user diff --git a/lib/ui.lib.php b/lib/ui.lib.php index 5c0ec15a..7e164e45 100644 --- a/lib/ui.lib.php +++ b/lib/ui.lib.php @@ -221,18 +221,6 @@ function show_footer() { } // show_footer -/** - * show_now_playing - * shows the now playing template - */ -function show_now_playing() { - - $web_path = Config::get('web_path'); - $results = get_now_playing(); - require Config::get('prefix') . '/templates/show_now_playing.inc.php'; - -} // show_now_playing - /** * show_user_registration * this function is called for a new user @@ -257,31 +245,6 @@ function show_play_selected() { } // show_play_selected -/** - * get_now_playing - * gets the now playing information - * @package Web Interface - * @catagory Get - */ -function get_now_playing($filter='') { - - $sql = "SELECT `song_id`,`user` FROM `now_playing` ORDER BY `id` DESC"; - $db_results = Dba::query($sql); - - $results = array(); - - /* While we've got stuff playing */ - while ($r = Dba::fetch_assoc($db_results)) { - $song = new Song($r['song_id']); - $song->format(); - $np_user = new User($r['user']); - $results[] = array('song'=>$song,'user'=>$np_user); - } // end while - - return $results; - -} // get_now_playing - /* * Artist Ratings - Implemented by SoundOfEmotion * diff --git a/play/index.php b/play/index.php index 438db254..cf4d4963 100644 --- a/play/index.php +++ b/play/index.php @@ -73,15 +73,15 @@ if (Config::get('xml_rpc')) { } // If require session is set then we need to make sure we're legit -if (Config::get('require_session') OR $xml_rpc) { - if(!session_exists($sid,$xml_rpc)) { +if (Config::get('require_session')) { + if(!Stream::session_exists($sid)) { debug_event('session_expired',"Streaming Access Denied: " . $GLOBALS['user']->username . "'s session has expired",'3'); die(_("Session Expired: please log in again at") . " " . Config::get('web_path') . "/login.php"); } // Now that we've confirmed the session is valid // extend it - extend_session($sid); + Stream::extend_session($sid,$uid); } @@ -272,7 +272,7 @@ $chunk_size = '4096'; $song->size = $song->size + ($chunk_size*2); // Put this song in the now_playing table -$lastid = insert_now_playing($song->id,$uid,$song->time); +insert_now_playing($song->id,$uid,$song->time,$sid); if ($start) { debug_event('seek','Content-Range header recieved, skipping ahead ' . $start . ' bytes out of ' . $song->size,'5'); @@ -305,9 +305,6 @@ while (!feof($fp) && (connection_status() == 0)) { $bytesStreamed += $chunk_size; } -/* Delete the Now Playing Entry */ -delete_now_playing($lastid); - if ($bytesStreamed > $minBytesStreamed) { $user->update_stats($song->id); /* If this is a voting tmp playlist remove the entry */ diff --git a/templates/show_now_playing.inc.php b/templates/show_now_playing.inc.php index 0bdba057..3e5b259f 100644 --- a/templates/show_now_playing.inc.php +++ b/templates/show_now_playing.inc.php @@ -35,6 +35,7 @@ if (count($results)) { foreach ($results as $item) { $song = $item['song']; $np_user = $item['user']; + $agent = $item['agent']; /* If we've gotten a non-song object just skip this row */ if (!is_object($song)) { continue; } diff --git a/templates/show_now_playing_row.inc.php b/templates/show_now_playing_row.inc.php index 0ff867b6..cb4331d4 100644 --- a/templates/show_now_playing_row.inc.php +++ b/templates/show_now_playing_row.inc.php @@ -25,7 +25,7 @@ $album = scrub_out(truncate_with_ellipsis($song->f_album_full,'25')); $artist = scrub_out(truncate_with_ellipsis($song->f_artist_full,'25')); ?> :
- fullname); ?>

+ fullname); ?>


-- cgit