summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/class/browse.class.php14
-rw-r--r--lib/class/query.class.php1170
-rw-r--r--lib/class/stream.class.php9
-rw-r--r--lib/class/xmldata.class.php13
-rw-r--r--modules/flash/xspf_player.php42
-rw-r--r--server/xml.server.php9
-rw-r--r--stream.php10
-rw-r--r--templates/show_search_options.inc.php14
-rw-r--r--templates/show_xspf_player.inc.php43
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();
diff --git a/stream.php b/stream.php
index 1c3da0de..115be99c 100644
--- a/stream.php
+++ b/stream.php
@@ -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" />