diff options
-rw-r--r-- | config/ampache.cfg.php.dist | 5 | ||||
-rwxr-xr-x | docs/CHANGELOG | 4 | ||||
-rw-r--r-- | lib/class/stream.class.php | 85 | ||||
-rw-r--r-- | lib/class/update.class.php | 41 | ||||
-rw-r--r-- | lib/general.lib.php | 4 | ||||
-rw-r--r-- | lib/stream.lib.php | 119 | ||||
-rw-r--r-- | lib/ui.lib.php | 37 | ||||
-rw-r--r-- | play/index.php | 11 | ||||
-rw-r--r-- | templates/show_now_playing.inc.php | 1 | ||||
-rw-r--r-- | 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; @@ -94,6 +101,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 "<ENTRY>\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.<br />' . + '- Altered the session table, making value a LONGTEXT.<br />'; + + $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 @@ -126,6 +75,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 * from streaming it 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 @@ -222,18 +222,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 * registration @@ -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')); ?> <td class="np_cell_left"><b><?php echo _('Username'); ?></b>:<br /> - <a href="<?php echo $web_path; ?>/stats.php?action=show_user&user_id=<?php echo $np_user->id; ?>"><?php echo scrub_out($np_user->fullname); ?></a><br /><br /> + <a title="<?php echo scrub_out($agent); ?>" href="<?php echo $web_path; ?>/stats.php?action=show_user&user_id=<?php echo $np_user->id; ?>"><?php echo scrub_out($np_user->fullname); ?></a><br /><br /> <!-- ## modified ##--> <strong><?php echo _('Song'); ?></strong><br /> <a title="<?php echo scrub_out($song->title); ?>" href="<?php echo $web_path; ?>/stream.php?action=single_song&song_id=<?php echo $song->id; ?>"> |