diff options
-rw-r--r-- | lib/class/browse.class.php | 14 | ||||
-rw-r--r-- | lib/class/query.class.php | 1170 | ||||
-rw-r--r-- | lib/class/stream.class.php | 9 | ||||
-rw-r--r-- | lib/class/xmldata.class.php | 13 | ||||
-rw-r--r-- | modules/flash/xspf_player.php | 42 | ||||
-rw-r--r-- | server/xml.server.php | 9 | ||||
-rw-r--r-- | stream.php | 10 | ||||
-rw-r--r-- | templates/show_search_options.inc.php | 14 | ||||
-rw-r--r-- | templates/show_xspf_player.inc.php | 43 |
9 files changed, 1236 insertions, 88 deletions
diff --git a/lib/class/browse.class.php b/lib/class/browse.class.php index e455db57..414482fb 100644 --- a/lib/class/browse.class.php +++ b/lib/class/browse.class.php @@ -100,6 +100,7 @@ class Browse { case 'update_gt': $_SESSION['browse']['filter'][self::$type][$key] = intval($value); break; + case 'exact_match': case 'alpha_match': case 'starts_with': if (self::$static_content) { return false; } @@ -258,11 +259,11 @@ class Browse { switch (self::$type) { case 'album': - $valid_array = array('show_art','starts_with','alpha_match','add','update'); + $valid_array = array('show_art','starts_with','exact_match','alpha_match','add','update'); break; case 'artist': case 'song': - $valid_array = array('add_lt','add_gt','update_lt','update_gt','alpha_match','starts_with'); + $valid_array = array('add_lt','add_gt','update_lt','update_gt','exact_match','alpha_match','starts_with'); break; case 'live_stream': $valid_array = array('alpha_match','starts_with'); @@ -825,6 +826,9 @@ class Browse { if (self::$type == 'song') { switch($filter) { + case 'exact_match': + $filter_sql = " `song`.`title` = '" . Dba::escape($value) . "' AND "; + break; case 'alpha_match': $filter_sql = " `song`.`title` LIKE '%" . Dba::escape($value) . "%' AND "; break; @@ -865,6 +869,9 @@ class Browse { } // if it is a song elseif (self::$type == 'album') { switch($filter) { + case 'exact_match': + $filter_sql = " `album`.`name` = '" . Dba::escape($value) . "' AND "; + break; case 'alpha_match': $filter_sql = " `album`.`name` LIKE '%" . Dba::escape($value) . "%' AND "; break; @@ -885,6 +892,9 @@ class Browse { } // end album elseif (self::$type == 'artist') { switch($filter) { + case 'exact_match': + $filter_sql = " `artist`.`name` = '" . Dba::escape($value) . "' AND "; + break; case 'alpha_match': $filter_sql = " `artist`.`name` LIKE '%" . Dba::escape($value) . "%' AND "; break; diff --git a/lib/class/query.class.php b/lib/class/query.class.php new file mode 100644 index 00000000..2fa20656 --- /dev/null +++ b/lib/class/query.class.php @@ -0,0 +1,1170 @@ +<?php +/* + + Copyright (c) Ampache.org + All rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License v2 + as published by the Free Software Foundation. + + 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +/** + * Query Class + * This handles all of the sql/filtering for the ampache database + * this was seperated out from browse, to accomodate Dynamic Playlists + */ +class Query { + + // Public static vars that are cached + public static $sql; + public static $start; + public static $offset; + public static $total_objects; + public static $type; + + // Boolean if this is a simple browse method (use different paging code) + public static $simple_browse; + + // Static Content, this is defaulted to false, if set to true then when we can't + // apply any filters that would change the result set. + public static $static_content = false; + private static $_cache = array(); + private static $_state = array(); + + /** + * constructor + * This should never be called + */ + private function __construct() { + + // Rien a faire + + } // __construct + + + /** + * set_filter + * This saves the filter data we pass it into the session + * This is done here so that it's easy to modify where the data + * is saved should I change my mind in the future. It also makes + * a single point for whitelist tweaks etc + */ + public static function set_filter($key,$value) { + + switch ($key) { + case 'show_art': + if (self::get_filter($key)) { + unset(self::$_state['filter'][self::$type][$key]); + } + else { + self::$_state['filter'][self::$type][$key] = 1; + } + break; + case 'tag': + if (is_array($value)) { + self::$_state['filter'][self::$type][$key] = $value; + } + elseif (is_numeric($value)) { + self::$_state['filter'][self::$type][$key] = array($value); + } + else { + self::$_state['filter'][self::$type][$key] = array(); + } + break; + case 'artist': + case 'album': + self::$_state['filter'][self::$type][$key] = $value; + break; + case 'min_count': + + case 'unplayed': + case 'rated': + + break; + case 'add_lt': + case 'add_gt': + case 'update_lt': + case 'update_gt': + self::$_state['filter'][self::$type][$key] = intval($value); + break; + case 'exact_match': + case 'alpha_match': + case 'starts_with': + if (self::$static_content) { return false; } + self::$_state['filter'][self::$type][$key] = $value; + break; + case 'playlist_type': + // They must be content managers to turn this off + if (self::$_state['filter'][self::$type][$key] AND Access::check('interface','50')) { unset(self::$_state['filter'][self::$type][$key]); } + else { self::$_state['filter'][self::$type][$key] = '1'; } + break; + default: + // Rien a faire + return false; + break; + } // end switch + + // If we've set a filter we need to reset the totals + self::reset_total(); + self::set_start(0); + + return true; + + } // set_filter + + /** + * reset + * Reset everything + */ + public static function reset() { + + self::reset_base(); + self::reset_filters(); + self::reset_total(); + self::reset_join(); + self::reset_select(); + self::reset_having(); + self::reset_supplemental_objects(); + self::set_simple_browse(0); + self::set_start(0); + + } // reset + + /** + * reset_base + * this resets the base string + */ + public static function reset_base() { + + self::$_state['base'][self::$type] = false; + + } // reset_base + + /** + * reset_select + * This resets the select fields that we've added so far + */ + public static function reset_select() { + + self::$_state['select'][self::$type] = array(); + + } // reset_select + + /** + * reset_having + * Null out the having clause + */ + public static function reset_having() { + + unset(self::$_state['having'][self::$type]); + + } // reset_having + + /** + * reset_join + * clears the joins if there are any + */ + public static function reset_join() { + + unset(self::$_state['join'][self::$type]); + + } // reset_join + + /** + * reset_filter + * This is a wrapper function that resets the filters + */ + public static function reset_filters() { + + self::$_state['filter'] = array(); + + } // reset_filters + + /** + * reset_supplemental_objects + * This clears any sup objects we've added, normally called on every set_type + */ + public static function reset_supplemental_objects() { + + self::$_state[self::$type]['supplemental'] = array(); + + } // reset_supplemental_objects + + /** + * reset_total + * This resets the total for the browse type + */ + public static function reset_total() { + + unset(self::$_state['total'][self::$type]); + + } // reset_total + + /** + * get_filter + * returns the specified filter value + */ + public static function get_filter($key) { + + // Simple enough, but if we ever move this crap + return self::$_state['filter'][self::$type][$key]; + + } // get_filter + + /** + * get_total + * This returns the toal number of obejcts for this current sort type. If it's already cached used it! + * if they pass us an array then use that! + */ + public static function get_total($objects=false) { + + // If they pass something then just return that + if (is_array($objects) and !self::is_simple_browse()) { + return count($objects); + } + + // See if we can find it in the cache + if (isset(self::$_state['total'][self::$type])) { + return self::$_state['total'][self::$type]; + } + + $db_results = Dba::query(self::get_base_sql() . self::get_filter_sql() . self::get_sort_sql()); + $num_rows = Dba::num_rows($db_results); + + self::$_state['total'][self::$type] = $num_rows; + + return $num_rows; + + } // get_total + + /** + * get_allowed_filters + * This returns an array of the allowed filters based on the type of object we are working + * with, this is used to display the 'filter' sidebar stuff, must be called post browse stuff + */ + public static function get_allowed_filters() { + + switch (self::$type) { + case 'album': + $valid_array = array('show_art','starts_with','exact_match','alpha_match','add','update'); + break; + case 'artist': + case 'song': + $valid_array = array('add_lt','add_gt','update_lt','update_gt','exact_match','alpha_match','starts_with'); + break; + case 'live_stream': + $valid_array = array('alpha_match','starts_with'); + break; + case 'playlist': + $valid_array = array('alpha_match','starts_with'); + if (Access::check('interface','50')) { + array_push($valid_array,'playlist_type'); + } + break; + case 'tag': + $valid_array = array('object_type'); + break; + default: + $valid_array = array(); + break; + } // switch on the browsetype + + return $valid_array; + + } // get_allowed_filters + + /** + * set_type + * This sets the type of object that we want to browse by + * we do this here so we only have to maintain a single whitelist + * and if I want to change the location I only have to do it here + */ + public static function set_type($type) { + + switch($type) { + case 'user': + case 'video': + case 'playlist': + case 'playlist_song': + case 'song': + case 'flagged': + case 'catalog': + case 'album': + case 'artist': + case 'tag': + case 'playlist_localplay': + case 'shoutbox': + case 'live_stream': + case 'democratic': + // Set it + self::$type = $type; + self::load_start(); + break; + default: + // Rien a faire + break; + } // end type whitelist + } // set_type + + /** + * get_type + * This returns the type of the browse we currently are using + */ + public static function get_type() { + + return self::$type; + + } // get_type + + /** + * set_sort + * This sets the current sort(s) + */ + public static function set_sort($sort,$order='') { + + switch (self::get_type()) { + case 'playlist_song': + case 'song': + $valid_array = array('title','year','track','time','album','artist'); + break; + case 'artist': + $valid_array = array('name','album'); + break; + case 'tag': + $valid_array = array('tag'); + break; + case 'album': + $valid_array = array('name','year','artist'); + break; + case 'playlist': + $valid_array = array('name','user'); + break; + case 'shoutbox': + $valid_array = array('date','user','sticky'); + break; + case 'live_stream': + $valid_array = array('name','call_sign','frequency'); + break; + case 'video': + $valid_array = array('title','video_codec','audio_codec'); + break; + case 'user': + $valid_array = array('fullname','username','last_seen','create_date'); + break; + } // end switch + + // If it's not in our list, smeg off! + if (!in_array($sort,$valid_array)) { + return false; + } + + if ($order) { + $order = ($order == 'DESC') ? 'DESC' : 'ASC'; + self::$_state['sort'][self::$type] = array(); + self::$_state['sort'][self::$type][$sort] = $order; + } + elseif (self::$_state['sort'][self::$type][$sort] == 'DESC') { + // Reset it till I can figure out how to interface the hotness + self::$_state['sort'][self::$type] = array(); + self::$_state['sort'][self::$type][$sort] = 'ASC'; + } + else { + // Reset it till I can figure out how to interface the hotness + self::$_state['sort'][self::$type] = array(); + self::$_state['sort'][self::$type][$sort] = 'DESC'; + } + + self::resort_objects(); + + } // set_sort + + /** + * set_select + * This appends more information to the select part of the SQL statement, we're going to move to the + * %%SELECT%% style queries, as I think it's the only way to do this.... + */ + public static function set_select($field) { + + self::$_state['select'][self::$type][] = $field; + + } // set_select + + /** + * set_join + * This sets the joins for the current browse object + */ + public static function set_join($type,$table,$source,$dest,$priority=100) { + + self::$_state['join'][self::$type][$priority][$table] = strtoupper($type) . ' JOIN ' . $table . ' ON ' . $source . '=' . $dest; + + } // set_join + + /** + * set_having + * This sets the "HAVING" part of the query, we can only have one.. god this is ugly + */ + public static function set_having($condition) { + + self::$_state['having'][self::$type] = $condition; + + } // set_having + + /** + * set_start + * This sets the start point for our show functions + * We need to store this in the session so that it can be pulled + * back, if they hit the back button + */ + public static function set_start($start) { + + if (!self::$static_content) { + self::$_state[self::$type]['start'] = intval($start); + } + self::$start = intval($start); + + } // set_start + + /** + * set_simple_browse + * This sets the current browse object to a 'simple' browse method + * which means use the base query provided and expand from there + */ + public static function set_simple_browse($value) { + + $value = make_bool($value); + self::$_state['simple'][self::$type] = $value; + + } // set_simple_browse + + /** + * set_static_content + * This sets true/false if the content of this browse + * should be static, if they are then content filtering/altering + * methods will be skipped + */ + public static function set_static_content($value) { + + $value = make_bool($value); + self::$static_content = $value; + + // We want to start at 0 it's static + if ($value) { + self::set_start('0'); + } + + self::$_state[self::$type]['static'] = $value; + + } // set_static_content + + /** + * is_simple_browse + * this returns true or false if the current browse type is set to static + */ + public static function is_simple_browse() { + + return self::$_state['simple'][self::$type]; + + } // is_simple_browse + + /** + * load_start + * This returns a stored start point for the browse mojo + */ + public static function load_start() { + + self::$start = intval(self::$_state[self::$type]['start']); + + } // end load_start + + /** + * get_saved + * This looks in the session for the saved + * stuff and returns what it finds + */ + public static function get_saved() { + + // See if we have it in the local cache first + if (is_array(self::$_cache['browse'][self::$type])) { + return self::$_cache['browse'][self::$type]; + } + + if (!self::is_simple_browse()) { + // If not then we're going to need to read from the database :( + $sid = session_id() . '::' . self::$type; + + $sql = "SELECT `data` FROM `tmp_browse` WHERE `sid`='$sid'"; + $db_results = Dba::read($sql); + + $row = Dba::fetch_assoc($db_results); + + $objects = unserialize($row['data']); + } + else { + $objects = self::get_objects(); + } + + return $objects; + + } // get_saved + + /** + * get_objects + * This gets an array of the ids of the objects that we are + * currently browsing by it applies the sql and logic based + * filters + */ + public static function get_objects() { + + // First we need to get the SQL statement we are going to run + // This has to run against any possible filters (dependent on type) + $sql = self::get_sql(); + $db_results = Dba::query($sql); + + $results = array(); + while ($data = Dba::fetch_assoc($db_results)) { + $results[] = $data; + } + + $results = self::post_process($results); + $filtered = array(); + foreach ($results as $data) { + // Make sure that this object passes the logic filter + if (self::logic_filter($data['id'])) { + $filtered[] = $data['id']; + } + } // end while + + // Save what we've found and then return it + self::save_objects($filtered); + + return $filtered; + + } // get_objects + + /** + * get_supplemental_objects + * This returns an array of 'class','id' for additional objects that need to be + * created before we start this whole browsing thing + */ + public static function get_supplemental_objects() { + + $objects = self::$_state['supplemental'][self::$type]; + + if (!is_array($objects)) { $objects = array(); } + + return $objects; + + } // get_supplemental_objects + + /** + * add_supplemental_object + * This will add a suplemental object that has to be created + */ + public static function add_supplemental_object($class,$uid) { + + self::$_state['supplemental'][self::$type][$class] = intval($uid); + + return true; + + } // add_supplemental_object + + /** + * set_base_sql + * This saves the base sql statement we are going to use. + */ + private static function set_base_sql() { + + // Only allow it to be set once + if (strlen(self::$_state['base'][self::$type])) { return true; } + + switch (self::$type) { + case 'album': + self::set_select("DISTINCT(`album`.`id`)"); + $sql = "SELECT %%SELECT%% FROM `album` "; + break; + case 'artist': + self::set_select("DISTINCT(`artist`.`id`)"); + $sql = "SELECT %%SELECT%% FROM `artist` "; + break; + case 'user': + self::set_select("`user`.`id`"); + $sql = "SELECT %%SELECT%% FROM `user` "; + break; + case 'live_stream': + self::set_select("`live_stream`.`id`"); + $sql = "SELECT %%SELECT%% FROM `live_stream` "; + break; + case 'playlist': + self::set_select("`playlist`.`id`"); + $sql = "SELECT %%SELECT%% FROM `playlist` "; + break; + case 'flagged': + self::set_select("`flagged`.`id`"); + $sql = "SELECT %%SELECT%% FROM `flagged` "; + break; + case 'shoutbox': + self::set_select("`user_shout`.`id`"); + $sql = "SELECT %%SELECT%% FROM `user_shout` "; + break; + case 'video': + self::set_select("`video`.`id`"); + $sql = "SELECT %%SELECT%% FROM `video` "; + break; + case 'tag': + self::set_select("`tag`.`id`"); + self::set_join('left','tag_map','`tag_map`.`tag_id`','`tag`.`id`',1); + $sql = "SELECT %%SELECT%% FROM `tag` "; + break; + case 'playlist_song': + case 'song': + default: + self::set_select("DISTINCT(`song`.`id`)"); + $sql = "SELECT %%SELECT%% FROM `song` "; + break; + } // end base sql + + self::$_state['base'][self::$type] = $sql; + + } // set_base_sql + + /** + * get_select + * This returns the selects in a format that is friendly for a sql statement + */ + private static function get_select() { + + $select_string = implode(self::$_state['select'][self::$type],", "); + return $select_string; + + } // get_select + + /** + * get_base_sql + * This returns the base sql statement all parsed up, this should be called after all set operations + */ + private static function get_base_sql() { + + // Legacy code, should be removed once other code is updated + //FIXME: REMOVE + if (!self::$_state['base'][self::$type]) { self::set_base_sql(); } + + $sql = str_replace("%%SELECT%%",self::get_select(),self::$_state['base'][self::$type]); + + return $sql; + + } // get_base_sql + + /** + * get_filter_sql + * This returns the filter part of the sql statement + */ + private static function get_filter_sql() { + + if (!is_array(self::$_state['filter'][self::$type])) { + return ''; + } + + $sql = "WHERE 1=1 AND "; + + foreach (self::$_state['filter'][self::$type] as $key=>$value) { + $sql .= self::sql_filter($key,$value); + } + + $sql = rtrim($sql,'AND ') . ' '; + + return $sql; + + } // get_filter_sql + + /** + * get_sort_sql + * Returns the sort sql part + */ + private static function get_sort_sql() { + + if (!is_array(self::$_state['sort'][self::$type])) { return ''; } + + $sql = 'ORDER BY '; + + foreach (self::$_state['sort'][self::$type] as $key=>$value) { + $sql .= self::sql_sort($key,$value); + } + + $sql = rtrim($sql,'ORDER BY '); + $sql = rtrim($sql,','); + + return $sql; + + } // get_sort_sql + + /** + * get_limit_sql + * This returns the limit part of the sql statement + */ + private static function get_limit_sql() { + + if (!self::is_simple_browse()) { return ''; } + + $sql = ' LIMIT ' . intval(self::$start) . ',' . intval(self::$offset); + + return $sql; + + } // get_limit_sql + + /** + * get_join_sql + * This returns the joins that this browse may need to work correctly + */ + private static function get_join_sql() { + + if (!is_array(self::$_state['join'][self::$type])) { + return ''; + } + + $sql = ''; + + // We need to itterate through these from 0 - 100 so that we add the joins in the right order + foreach (self::$_state['join'][self::$type] as $joins) { + foreach ($joins as $join) { + $sql .= $join . ' '; + } // end foreach joins at this level + } // end foreach of this level of joins + + return $sql; + + } // get_join_sql + + /** + * get_having_sql + * this returns the having sql stuff, if we've got anything + */ + public static function get_having_sql() { + + $sql = self::$_state['having'][self::$type]; + + return $sql; + + } // get_having_sql + + /** + * get_sql + * This returns the sql statement we are going to use this has to be run + * every time we get the objects because it depends on the filters and the + * type of object we are currently browsing + */ + public static function get_sql() { + + $sql = self::get_base_sql(); + + // No matter what we have to check the catalog based filters... maybe I'm not sure about this + //$where_sql .= self::sql_filter('catalog',''); + + $filter_sql = self::get_filter_sql(); + $join_sql = self::get_join_sql(); + $having_sql = self::get_having_sql(); + $order_sql = self::get_sort_sql(); + $limit_sql = self::get_limit_sql(); + + $final_sql = $sql . $join_sql . $filter_sql . $having_sql . $order_sql . $limit_sql; + + return $final_sql; + + } // get_sql + + /** + * post_process + * This does some additional work on the results that we've received before returning them + */ + private static function post_process($results) { + + $tags = self::$_state['filter']['tag']; + + if (!is_array($tags) || sizeof($tags) < 2) { + return $results; + } + $cnt = sizeof($tags); + $ar = array(); + + foreach($results as $row) { + $ar[$row['id']]++; + } + + $res = array(); + + foreach($ar as $k=>$v) { + if ($v >= $cnt) { + $res[] = array('id' => $k); + } + } // end foreach + + return $res; + + } // post_process + + /** + * sql_filter + * This takes a filter name and value and if it is possible + * to filter by this name on this type returns the approiate sql + * if not returns nothing + */ + private static function sql_filter($filter,$value) { + + $filter_sql = ''; + + if (self::$type == 'song') { + switch($filter) { + case 'exact_match': + $filter_sql = " `song`.`title` = '" . Dba::escape($value) . "' AND "; + break; + case 'alpha_match': + $filter_sql = " `song`.`title` LIKE '%" . Dba::escape($value) . "%' AND "; + break; + case 'starts_with': + $filter_sql = " `song`.`title` LIKE '" . Dba::escape($value) . "%' AND "; + break; + case 'unplayed': + $filter_sql = " `song`.`played`='0' AND "; + break; + case 'album': + $filter_sql = " `song`.`album` = '". Dba::escape($value) . "' AND "; + break; + case 'artist': + $filter_sql = " `song`.`artist` = '". Dba::escape($value) . "' AND "; + break; + case 'add_gt': + $filter_sql = " `song`.`addition_time` >= '" . Dba::escape($value) . "' AND "; + break; + case 'add_lt': + $filter_sql = " `song`.`addition_time` <= '" . Dba::escape($value) . "` AND "; + break; + case 'update_gt': + $filter_sql = " `song`.`update_time` >= '" . Dba::escape($value) . "' AND "; + break; + case 'update_lt': + $filter_sql = " `song`.`update_time` <= '" . Dba::escape($value) . "' AND "; + break; + case 'catalog': + $catalogs = $GLOBALS['user']->get_catalogs(); + if (!count($catalogs)) { break; } + $filter_sql .= " `song`.`catalog` IN (" . implode(',',$GLOBALS['user']->get_catalogs()) . ") AND "; + break; + default: + // Rien a faire + break; + } // end list of sqlable filters + + } // if it is a song + elseif (self::$type == 'album') { + switch($filter) { + case 'exact_match': + $filter_sql = " `album`.`name` = '" . Dba::escape($value) . "' AND "; + break; + case 'alpha_match': + $filter_sql = " `album`.`name` LIKE '%" . Dba::escape($value) . "%' AND "; + break; + case 'starts_with': + $filter_sql = " `album`.`name` LIKE '" . Dba::escape($value) . "%' AND "; + break; + case 'artist': + $filter_sql = " `artist`.`id` = '". Dba::escape($value) . "' AND "; + break; + case 'add': + self::set_join('left','`song`','`song`.`album`','`album`.`id`'); + case 'update': + self::set_join('left','`song`','`song`.`album`','`album`.`id`'); + default: + // Rien a faire + break; + } + } // end album + elseif (self::$type == 'artist') { + switch($filter) { + case 'exact_match': + $filter_sql = " `artist`.`name` = '" . Dba::escape($value) . "' AND "; + break; + case 'alpha_match': + $filter_sql = " `artist`.`name` LIKE '%" . Dba::escape($value) . "%' AND "; + break; + case 'starts_with': + $filter_sql = " `artist`.`name` LIKE '" . Dba::escape($value) . "%' AND "; + break; + default: + // Rien a faire + break; + } // end filter + } // end artist + elseif (self::$type == 'live_stream') { + switch ($filter) { + case 'alpha_match': + $filter_sql = " `live_stream`.`name` LIKE '%" . Dba::escape($value) . "%' AND "; + break; + case 'starts_with': + $filter_sql = " `live_stream`.`name` LIKE '" . Dba::escape($value) . "%' AND "; + break; + default: + // Rien a faire + break; + } // end filter + } // end live_stream + elseif (self::$type == 'playlist') { + switch ($filter) { + case 'alpha_match': + $filter_sql = " `playlist`.`name` LIKE '%" . Dba::escape($value) . "%' AND "; + break; + case 'starts_with': + $filter_sql = " `playlist`.`name` LIKE '" . Dba::escape($value) . "%' AND "; + break; + case 'playlist_type': + $user_id = intval($GLOBALS['user']->id); + $filter_sql = " (`playlist`.`type` = 'public' OR `playlist`.`user`='$user_id') AND "; + break; + default; + // Rien a faire + break; + } // end filter + } // end playlist + + return $filter_sql; + + } // sql_filter + + /** + * logic_filter + * This runs the filters that we can't easily apply + * to the sql so they have to be done after the fact + * these should be limited as they are often intensive and + * require additional queries per object... :( + */ + private static function logic_filter($object_id) { + + return true; + + } // logic_filter + + /** + * sql_sort + * This builds any order bys we need to do + * to sort the results as best we can, there is also + * a logic based sort that will come later as that's + * a lot more complicated + */ + private static function sql_sort($field,$order) { + + if ($order != 'DESC') { $order == 'ASC'; } + + // Depending on the type of browsing we are doing we can apply different filters that apply to different fields + switch (self::$type) { + case 'song': + switch($field) { + case 'title'; + $sql = "`song`.`title`"; + break; + case 'year': + $sql = "`song`.`year`"; + break; + case 'time': + $sql = "`song`.`time`"; + break; + case 'track': + $sql = "`song`.`track`"; + break; + case 'album': + $sql = '`album`.`name`'; + self::set_join('left','`album`','`album`.`id`','`song`.`album`'); + break; + case 'artist': + $sql = '`artist`.`name`'; + self::set_join('left','`artist`','`artist`.`id`','`song`.`artist`'); + break; + default: + // Rien a faire + break; + } // end switch + break; + case 'album': + switch($field) { + case 'name': + $sql = "`album`.`name` $order, `album`.`disk`"; + break; + case 'artist': + $sql = "`artist`.`name`"; + self::set_join('left','`song`','`song`.`album`','`album`.`id`'); + self::set_join('left','`artist`','`song`.`artist`','`artist`.`id`'); + break; + case 'year': + $sql = "`album`.`year`"; + break; + } // end switch + break; + case 'artist': + switch ($field) { + case 'name': + $sql = "`artist`.`name`"; + break; + } // end switch + break; + case 'playlist': + switch ($field) { + case 'type': + $sql = "`playlist`.`type`"; + break; + case 'name': + $sql = "`playlist`.`name`"; + break; + case 'user': + $sql = "`playlist`.`user`"; + break; + } // end switch + break; + case 'live_stream': + switch ($field) { + case 'name': + $sql = "`live_stream`.`name`"; + break; + case 'call_sign': + $sql = "`live_stream`.`call_sign`"; + break; + case 'frequency': + $sql = "`live_stream`.`frequency`"; + break; + } // end switch + break; + case 'genre': + switch ($field) { + case 'name': + $sql = "`genre`.`name`"; + break; + } // end switch + break; + case 'user': + switch ($field) { + case 'username': + $sql = "`user`.`username`"; + break; + case 'fullname': + $sql = "`user`.`fullname`"; + break; + case 'last_seen': + $sql = "`user`.`last_seen`"; + break; + case 'create_date': + $sql = "`user`.`create_date`"; + break; + } // end switch + break; + case 'video': + switch ($field) { + case 'title': + $sql = "`video`.`title`"; + break; + } + break; + default: + // Rien a faire + break; + } // end switch + + if ($sql) { $sql_sort = "$sql $order,"; } + + return $sql_sort; + + } // sql_sort + + /** + * resort_objects + * This takes the existing objects, looks at the current + * sort method and then re-sorts them This is internally + * called by the set_sort() function + */ + private static function resort_objects() { + + // There are two ways to do this.. the easy way... + // and the vollmer way, hopefully we don't have to + // do it the vollmer way + if (self::is_simple_browse()) { + debug_event('resort_objects','is_simple_browse','4'); + $sql = self::get_sql(); + } + else { + debug_event('resort_objects','not is_simple_browse','4'); + // First pull the objects + $objects = self::get_saved(); + + // If there's nothing there don't do anything + if (!count($objects)) { + debug_event('resort_objects','no objects found','4'); + return false; + } + $type = self::$type; + $where_sql = "WHERE `$type`.`id` IN ("; + + foreach ($objects as $object_id) { + $object_id = Dba::escape($object_id); + $where_sql .= "'$object_id',"; + } + $where_sql = rtrim($where_sql,','); + + $where_sql .= ")"; + + $sql = self::get_base_sql(); + + $order_sql = " ORDER BY "; + + foreach (self::$_state['sort'][self::$type] as $key=>$value) { + $order_sql .= self::sql_sort($key,$value); + } + // Clean her up + $order_sql = rtrim($order_sql,"ORDER BY "); + $order_sql = rtrim($order_sql,","); + + $sql = $sql . self::get_join_sql() . $where_sql . $order_sql; + } // if not simple + + debug_event('resort_objects','final sql: ' . $sql,'4'); + $db_results = Dba::query($sql); + + while ($row = Dba::fetch_assoc($db_results)) { + $results[] = $row['id']; + } + + self::save_objects($results); + + return true; + + } // resort_objects + + /** + * _auto_init + * this function reloads information back from the session + * it is called on creation of the class + */ + public static function _auto_init() { + + self::$offset = Config::get('offset_limit') ? Config::get('offset_limit') : '25'; + self::$_state = $_SESSION['browse']; + + } // _auto_init + + /** + * Desctruction + * This is run on object destrunction, done here to allow for changes + * later + */ + public function __destruct() { + + $_SESSION['browse'] = self::$_state; + + } // destructor + +} // query diff --git a/lib/class/stream.class.php b/lib/class/stream.class.php index 9bfb4d78..17ef8c55 100644 --- a/lib/class/stream.class.php +++ b/lib/class/stream.class.php @@ -46,16 +46,13 @@ class Stream { * Constructor for the stream class takes a type and an array * of song ids */ - public function __construct($type='m3u', $media_ids=0) { + public function __construct($type='m3u', $media_ids) { $this->type = $type; $this->media = $media_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); - } + if (!is_array($this->media)) { settype($this->media,'array'); } } // Constructor @@ -438,7 +435,7 @@ class Stream { $xml['track']['identifier'] = $xml['track']['location']; $xml['track']['duration'] = $length * 1000; - $result .= xmlData::keyed_array($xml,1); + $result .= xmlData::keyed_array($xml,1) } // end foreach diff --git a/lib/class/xmldata.class.php b/lib/class/xmldata.class.php index 2fb18f19..01eb98cb 100644 --- a/lib/class/xmldata.class.php +++ b/lib/class/xmldata.class.php @@ -135,7 +135,7 @@ class xmlData { // If it's an array, run again if (is_array($value)) { $value = self::keyed_array($value,1); - $string .= "\t<$key>$value</$key>\n"; + $string .= "<$key>\n$value\n</$key>\n"; } else { $string .= "\t<$key><![CDATA[$value]]></$key>\n"; @@ -345,13 +345,12 @@ class xmlData { switch (self::$type) { case 'xspf': $header = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" . - "<!-- XML Generated by Ampache v." . Config::get('version') . " -->\n"; - "<playlist version = \"1\" xmlns=\"http://xspf.org/ns/0/\">\n ". + "<playlist version = \"1\" xmlns=\"http://xspf.org/ns/0/\">\n " . "<title>Ampache XSPF Playlist</title>\n" . - "<creator>" . Config::get('site_title') . "</creator>\n" . - "<annotation>" . Config::get('site_title') . "</annotation>\n" . + "<creator>" . scrub_out(Config::get('site_title')) . "</creator>\n" . + "<annotation>" . scrub_out(Config::get('site_title')) . "</annotation>\n" . "<info>". Config::get('web_path') ."</info>\n" . - "<trackList>\n\n\n\n"; + "<trackList>\n"; break; case 'itunes': $header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" . @@ -393,7 +392,7 @@ class xmlData { $footer = "\t\t</dict>\t\n</dict>\n</plist>\n"; break; case 'xspf': - $footer = "\t</trackList>\n</playlist>\n"; + $footer = "</trackList>\n</playlist>\n"; break; case 'rss': $footer = "\n</channel>\n</rss>\n"; diff --git a/modules/flash/xspf_player.php b/modules/flash/xspf_player.php index 53cc8b29..39b8d93b 100644 --- a/modules/flash/xspf_player.php +++ b/modules/flash/xspf_player.php @@ -21,50 +21,18 @@ require_once '../../lib/init.php'; -$web_path = Config::get('web_path'); - -/* Attempt to build the temp playlist */ -$action = scrub_in($_REQUEST['action']); - -switch ($action) { +// Switch on actions +switch ($_REQUEST['action']) { default: case 'tmp_playlist': // Set for hackage! $_REQUEST['flash_hack'] = 1; - $tmp_playlist = new tmpPlaylist($_REQUEST['tmp_id']); - $objects = $tmp_playlist->get_items(); - - //Recurse through the objects - foreach ($objects as $object_data) { - // Switch on the type of object we've got in here - switch ($object_data['1']) { - case 'radio': - $radio = new Radio($object_data['0']); - $urls[] = $radio->url; - $song_ids[] = '-1'; - break; - case 'song': - $song_ids[] = $object_data['0']; - break; - default: - $random_url = Random::play_url($object_data['1']); - // If there's something to actually add - if ($random_url) { - $urls[] = $random_url; - } - break; - } // end switch on type - } // end foreach - $stream = new Stream('xspf',$song_ids); - if (is_array($urls)) { - foreach ($urls as $url) { - $stream->manual_url_add($url); - } - } + $objects = $GLOBALS['user']->playlist->get_items(); + $stream = new Stream('xspf',$objects); $stream->start(); break; case 'show': - $play_url = Config::get('web_path') . '/modules/flash/xspf_player.php?tmp_id=' . scrub_out($_REQUEST['tmpplaylist_id']); + $play_url = Config::get('web_path') . '/modules/flash/xspf_player.php'; require_once Config::get('prefix') . '/templates/show_xspf_player.inc.php'; break; } // end switch diff --git a/server/xml.server.php b/server/xml.server.php index fab6867e..08ca6a8c 100644 --- a/server/xml.server.php +++ b/server/xml.server.php @@ -112,6 +112,7 @@ switch ($_REQUEST['action']) { Browse::set_sort('name','ASC'); Api::set_filter('alpha_match',$_REQUEST['filter']); + Api::set_filter('exact_match',$_REQUEST['exact']); // Set the offset xmlData::set_offset($_REQUEST['offset']); @@ -153,6 +154,7 @@ switch ($_REQUEST['action']) { Browse::set_sort('name','ASC'); Api::set_filter('alpha_match',$_REQUEST['filter']); + Api::set_filter('exact_match',$_REQUEST['exact']); $albums = Browse::get_objects(); // Set the offset @@ -182,6 +184,7 @@ switch ($_REQUEST['action']) { Browse::set_sort('name','ASC'); Api::set_filter('alpha_match',$_REQUEST['filter']); + Api::set_filter('exact_match',$_REQUEST['exact']); $genres = Browse::get_objects(); // Set the offset @@ -232,6 +235,7 @@ switch ($_REQUEST['action']) { Browse::set_sort('title','ASC'); Api::set_filter('alpha_match',$_REQUEST['filter']); + Api::set_filter('exact_match',$_REQUEST['exact']); Api::set_filter('add',$_REQUEST['add']); $songs = Browse::get_objects(); @@ -261,9 +265,8 @@ switch ($_REQUEST['action']) { Browse::set_type('playlist'); Browse::set_sort('name','ASC'); - if ($_REQUEST['filter']) { - Browse::set_filter('alpha_match',$_REQUEST['filter']); - } + Api::set_filter('exact_match',$_REQUEST['exact']); + Api::set_filter('alpha_match',$_REQUEST['filter']); $playlist_ids = Browse::get_objects(); @@ -67,7 +67,7 @@ switch ($_REQUEST['action']) { } // end switch on type break; case 'single_song': - $media_ids[] = scrub_in($_REQUEST['song_id']); + $media_ids[] = array('song',scrub_in($_REQUEST['song_id'])); break; case 'your_popular_songs': $media_ids = get_popular_songs($_REQUEST['limit'], 'your', $GLOBALS['user']->id); @@ -92,10 +92,6 @@ switch ($_REQUEST['action']) { $album = new Album($_REQUEST['album_id']); $media_ids = $album->get_songs(); break; - case 'random_genre': - $genre = new Genre($_REQUEST['genre']); - $media_ids = $genre->get_random_songs(); - break; case 'playlist': $playlist = new Playlist($_REQUEST['playlist_id']); $media_ids = $playlist->get_songs($_REQUEST['song']); @@ -168,7 +164,7 @@ switch ($_REQUEST['method']) { if (is_array($urls)) { $stream->manual_url_add($urls); } - $stream->start(); - break; + $stream->start(); + } // end method switch ?> diff --git a/templates/show_search_options.inc.php b/templates/show_search_options.inc.php index 57c1d9ae..d2e4919c 100644 --- a/templates/show_search_options.inc.php +++ b/templates/show_search_options.inc.php @@ -20,12 +20,18 @@ */ ?> -<?php show_box_top(_('Options')); ?> -<div id="search_options"> +<?php show_box_top(_('Options'),'info-box'); ?> +<div id="information_actions"> <ul> - <li><?php echo Ajax::text('?action=basket&type=browse_set&object_type=song',_('Add Search Results'),'add_search_results'); ?></li> +<li> + <?php echo Ajax::button('?action=basket&type=browse_set&object_type=song','add',_('Add Search Results'),'add_search_results'); ?> + <?php echo _('Add Search Results'); ?> +</li> <?php if (Access::check_function('batch_download')) { ?> - <li><a href="<?php echo Config::get('web_path'); ?>/batch.php?action=browse"><?php echo _('Batch Download'); ?></a></li> +<li> + <a href="<?php echo Config::get('web_path'); ?>/batch.php?action=browse"><?php echo get_user_icon('batch_download'); ?></a> + <?php echo _('Batch Download'); ?> +</li> <?php } ?> </ul> </div> diff --git a/templates/show_xspf_player.inc.php b/templates/show_xspf_player.inc.php index bc387f7f..1ca54abd 100644 --- a/templates/show_xspf_player.inc.php +++ b/templates/show_xspf_player.inc.php @@ -26,35 +26,34 @@ <!-- //Disable right mouse click Script to hide the source url for the flash player it prevents ripping music a bit. //When used together with locked songs this will help just a bit more. -function clickIE4(){ -if (event.button==2){ -return false; -} -} +//function clickIE4(){ +//if (event.button==2){ +//return false; +//} +//} -function clickNS4(e){ -if (document.layers||document.getElementById&&!document.all){ -if (e.which==2||e.which==3){ -return false; -} -} -} +//function clickNS4(e){ +//if (document.layers||document.getElementById&&!document.all){ +//if (e.which==2||e.which==3){ +//return false; +//} +//} +//} -if (document.layers){ -document.captureEvents(Event.MOUSEDOWN); -document.onmousedown=clickNS4; -} -else if (document.all&&!document.getElementById){ -document.onmousedown=clickIE4; -} - -document.oncontextmenu=new Function("return false") +//if (document.layers){ +//document.captureEvents(Event.MOUSEDOWN); +//document.onmousedown=clickNS4; +//} +//else if (document.all&&!document.getElementById){ +//document.onmousedown=clickIE4; +//} +//document.oncontextmenu=new Function("return false") // --> </script> <div id="mp3player"> <?php -$player_url = sprintf("%s/modules/flash/xspf_jukebox.swf?autoplay=true&repeat_playlist=false&crossFade=false&shuffle=false&skin_url=%s/modules/flash/Original/&playlist_url=%s",Config::get('web_path'),Config::get('web_path'),$play_url); +$player_url = sprintf("%s/modules/flash/xspf_jukebox.swf?autoplay=true&repeat_playlist=true&crossFade=false&shuffle=false&skin_url=%s/modules/flash/Original/&playlist_url=%s",Config::get('web_path'),Config::get('web_path'),$play_url); ?> <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="400" height="170" id="xspf_player" align="middle"> <param name="pluginspage" value="http://www.macromedia.com/go/getflashplayer" /> |