summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorpb1dft <pb1dft@ampache>2007-02-26 19:24:51 +0000
committerpb1dft <pb1dft@ampache>2007-02-26 19:24:51 +0000
commit1681e9eef3af7099c50e33e5e632ca9b047d0d84 (patch)
treed25e45d3523b263d5074d11ea1459203ba237185 /contrib
parent2e0f0a31e59030747921587380a4ca6e49fcae97 (diff)
downloadampache-1681e9eef3af7099c50e33e5e632ca9b047d0d84.tar.gz
ampache-1681e9eef3af7099c50e33e5e632ca9b047d0d84.tar.bz2
ampache-1681e9eef3af7099c50e33e5e632ca9b047d0d84.zip
Comitted initial Rio Player Plugin. This is going to need a lot of work ;)
Diffstat (limited to 'contrib')
-rw-r--r--contrib/plugins/RioPlayer/README35
-rw-r--r--contrib/plugins/RioPlayer/RioPlayer.plugin.php87
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/layout/en_UK/all_infobin0 -> 312 bytes
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/layout/en_UK/big_titlebin0 -> 196 bytes
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/layout/en_UK/index5
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/layout/en_UK/inverse_scopebin0 -> 196 bytes
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/layout/en_UK/remainingbin0 -> 312 bytes
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/layout/en_UK/scopebin0 -> 164 bytes
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/rio.conf161
-rw-r--r--contrib/plugins/RioPlayer/modules/rio/rio.php1759
-rwxr-xr-xcontrib/plugins/RioPlayer/ssdp.pl105
11 files changed, 2152 insertions, 0 deletions
diff --git a/contrib/plugins/RioPlayer/README b/contrib/plugins/RioPlayer/README
new file mode 100644
index 00000000..15a1eee0
--- /dev/null
+++ b/contrib/plugins/RioPlayer/README
@@ -0,0 +1,35 @@
+This is a crappy readme for the Rio Player plugin
+
+Work in progress... Plugin isn't functional yet..
+
+INSTALLATION
+
+A full helpfile will be available at http://ampache.bountysource.com/wiki as soon as possible
+
+
+NFS Setup
+
+Edit you're /etc/exports and add the line below.
+
+/your/nfs/path (rw,no_root_squash)
+
+Create a directory in your NFS root with the ip address of your box as the folder name like;
+/your/nfs/path/192.168.0.6
+untar the mercury.arf in this folder with the command tar xf mercury.arf this file can be found in the normal Audio Receiver
+setup ("c:\Program Files\Audio Receiver") when unchanged.
+
+APACHE setup
+
+Add this to your apache configuration;
+
+Alias /layout/ "<ampache_root>/modules/rio/layout/"
+Alias /query "<ampache_root>/modules/rio/rio.php"
+Alias /results "<ampache_root>/modules/rio/rio.php"
+AliasMatch ^/content/ "<ampache_root>/modules/rio/rio.php"
+AliasMatch ^/favourites/ "<ampache_root>/modules/rio/rio.php"
+AliasMatch ^/list/ "<ampache_root>/modules/rio/rio.php"
+AliasMatch ^/tags "<ampache_root>/modules/rio/rio.php"
+
+Ampache Setup
+
+Copy the modules directory to the root of ampache. \ No newline at end of file
diff --git a/contrib/plugins/RioPlayer/RioPlayer.plugin.php b/contrib/plugins/RioPlayer/RioPlayer.plugin.php
new file mode 100644
index 00000000..f6dae7c3
--- /dev/null
+++ b/contrib/plugins/RioPlayer/RioPlayer.plugin.php
@@ -0,0 +1,87 @@
+<?php
+/*
+
+ Copyright (c) 2001 - 2006 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.
+
+*/
+
+class AmpacheRioPlayer {
+
+ var $name ='Rio Player';
+ var $description ='Sets up ampache so a Rio Player can access it';
+ var $url ='';
+ var $version ='000001';
+ var $min_ampache ='333001';
+ var $max_ampache ='333005';
+
+ /**
+ * Constructor
+ * This function does nothing...
+ */
+ function PluginRioPlayer() {
+
+ return true;
+
+ } // PluginLastfm
+
+ /**
+ * install
+ * This is a required plugin function it inserts the required preferences
+ * into Ampache
+ */
+ function install() {
+
+ /* We need to insert the new preferences */
+
+ $sql = "INSERT INTO preferences (`name`,`value`,`description`,`level`,`type`,`catagory`) " .
+ "VALUES ('rio_querylimit','3000','Rio Player Query Limit','100','integer','system')";
+ $db_results = mysql_query($sql,dbh());
+
+ $sql = "INSERT INTO preferences (`name`,`value`,`description`,`level`,`type`,`catagory`) " .
+ "VALUES ('rio_track_stats','0','Rio Player Track Stats','100','boolean','system')";
+ $db_results = mysql_query($sql,dbh());
+
+ $sql = "INSERT INTO preferences (`name`,`value`,`description`,`level`,`type`,`catagory`) " .
+ "VALUES ('rio_user','','Rio Player Global User','100','string','system')";
+ $db_results = mysql_query($sql,dbh());
+
+ $sql = "INSERT INTO preferences (`name`,`value`,`description`,`level`,`type`,`catagory`) " .
+ "VALUES ('rio_global_stats','0','Rio Player Group Stats','100','boolean','system')";
+ $db_results = mysql_query($sql,dbh());
+
+ fix_all_users_prefs();
+
+ } // install
+
+ /**
+ * uninstall
+ * This is a required plugin function it removes the required preferences from
+ * the database returning it to its origional form
+ */
+ function uninstall() {
+
+ /* We need to remove the preivously added preferences */
+
+ $sql = "DELETE FROM preferences WHERE name='rio_querylimit' OR name='rio_track_stats' OR name='rio_user' OR name='rio_global_stats'";
+ $db_results = mysql_query($sql,dbh());
+
+ fix_all_users_prefs();
+
+ } // uninstall
+
+} // end AmpacheRioPlayer
+?>
diff --git a/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/all_info b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/all_info
new file mode 100644
index 00000000..b19a3f73
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/all_info
Binary files differ
diff --git a/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/big_title b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/big_title
new file mode 100644
index 00000000..e76ddca3
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/big_title
Binary files differ
diff --git a/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/index b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/index
new file mode 100644
index 00000000..8221cde0
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/index
@@ -0,0 +1,5 @@
+/layout/en_UK/all_info=All Info
+/layout/en_UK/remaining=Remaining Time
+/layout/en_UK/scope=Scope View
+/layout/en_UK/inverse_scope=Inverse Scope
+/layout/en_UK/big_title=Big Title
diff --git a/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/inverse_scope b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/inverse_scope
new file mode 100644
index 00000000..e1429679
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/inverse_scope
Binary files differ
diff --git a/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/remaining b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/remaining
new file mode 100644
index 00000000..20b52bdc
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/remaining
Binary files differ
diff --git a/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/scope b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/scope
new file mode 100644
index 00000000..5b88b074
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/layout/en_UK/scope
Binary files differ
diff --git a/contrib/plugins/RioPlayer/modules/rio/rio.conf b/contrib/plugins/RioPlayer/modules/rio/rio.conf
new file mode 100644
index 00000000..1a9aaa30
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/rio.conf
@@ -0,0 +1,161 @@
+de_private_playlists=0
+
+
+// When doing filtered queries, should we match from the beginning or anywhere?
+ // eg. Should "bea" match "The Beatles" (0) or only "Beatles" (1)
+ // 0=do not filter from beginning
+ // 1=filter from beginning (default)
+
+ filter_from_beginning=1
+
+
+// The reciver can hang or reboot on large queries. Use this setting to specify a Query Limit.
+ // eg. query_limit=3000 to limit large queries to 3000 entries. This is the default.
+ // The breaking point varies possibly depending on specific data returned and usage conditions (that use cache etc.?)
+ // If your rio hangs or reboots on some queries, try lowering this number.
+ // This entry should be an integer > 0
+
+ query_limit=3000
+
+
+
+
+
+// RECEIVER STATS
+// ==============
+ // You can treat each receiver as individual ampache users, or group all receivers into a single user.
+ // All usernames must be valid, existing ampache users.
+ // Basically this tries to make a receiver behave like a specific ampache user, and share stats and ACLs etc. with it.
+
+ // First you have to enable receiver stats.
+ // If disabled, ampache will not track any play stats or impose any sort of filters on the receivers, in
+ // which case the unit is simply levraging ampache's database, but operating outside of ampache itself.
+ //
+ // This will associate a receiver with an ampache user (more details below) and try to associate as many
+ // user specifc preferences as possible including 'most popular' lists, playlists and eventually ACLs etc.
+ // If you set track_receiver_stats=1, then you must configure the rest of this section. Otherwise this section is ignored.
+ // 0=Disable
+ // 1=Enable (default)
+
+ track_receiver_stats=1
+
+
+ // Next you must specify if you want to assocaiate each with specific ampache users as individuals,
+ // or group alll receivers to a single ampache user.
+ // Note that for individual receiver association this to be reliable, your DHCP server should be configured to always
+ // assign a specific IP to a specifc receiver, based on it's MAC address. Otherwise a receiver could have multiple IPs,
+ // or IPs could bounce between receivers, in which case tracking individually will be ineffective.
+ // 0=Track receivers individually (default)
+ // 1=Group receivers together
+
+ group_receivers_together=0
+
+
+ // Next you must specify if you want ampache to automatically create the users to associate the receivers with.
+ // If enabled, then whenever a new unit is seen, an associated ampache user will be created, if it doesn't already exist.
+ // If you have specifed the username in this config file, that username will be used, otherwise a default
+ // username will be created in the form of 'rio-<dotted_ip_address>', like 'rio-192.168.0.10'
+ // If you specified group_receivers_together=1, then a user for the IP '0.0.0.0' will be created.
+ // Automatically created users will have their access level set as 'disabled' and a random password will be set, since
+ // the receiver doesn't actually 'log in'.
+ // If you want to modify the user access level or password you can do so in the ampache web interface as any Admin user.
+ // If you do not enable this setting, then you MUST specify all receivers by IP (or 0.0.0.0) and assign a username,
+ // which you've previously created in ampache.
+ // 0=User will manually create receiver-mappings and manually create those user accounts
+ // 1=Users will be automatically created for new units (default)
+ //
+ // NOTE: If you let users be automatically created, the rest of this setup is easy and can be ignored
+ // otherwise it's up to you to make everything work the way you want.
+ // 0=You have to create ampache users manually for the receivers
+ // 1=Users will be automatically created for you (default)
+
+ automatically_create_users=1
+
+
+ // Finally you specify each of your receivers and an associated ampache username to assign their play stats to.
+ // If you specified automatically_create_users=0, you MUST do this for each receiver.
+ // otherwise you can simply let the system auto-create the users with default usernames.
+ // Remember, if you set group_receivers_together=1, then you MUST create an entry for 0.0.0.0
+ // if you also specified automatically_create_users=0
+ // The format is:
+ // rio_user_<dotted_ip_address_of_receiver>=<ampache_username>
+ //
+ // eg:
+ // rio_user_0.0.0.0=rio (special address for group_receivers_together=1)
+ // rio_user_192.168.0.10=bedroom
+ //
+ // If not user specified, any automatically created username will be 'rio-<dotted_ip_address>'
+ // eg:
+ // 'all receivers if grouped' would be 'rio-0.0.0.0'
+ // '192.168.0.10' would be 'rio-192.168.0.10'
+ //
+ // Note: You can specify an existing ampache user, to update that user's stats as well as inherit it's Most Popular lists etc.
+
+ rio_user_0.0.0.0=rio
+
+
+
+
+
+// DYNAMIC PLAYLISTS
+// -----------------
+// These playlists are generated on the fly based on usage data and newest album info etc. in the
+// same manner as those in the ampache web interface.
+// For each playlist, specify the maximum number of entries you want to be retreived for that list.
+// Setting a particular playlist to 0 removes it from the UI entirely
+// Note that in order to use 'user' spcific playlists, you must have track_receiver_stats=1 above
+
+ playlist_global_most_popular_songs=50
+ playlist_user_most_popular_songs=50
+
+
+ // These playlists contain each track from the album.
+ // The value indicates how many albums you want on the list. You can have up to 100 each.
+
+ // They are preceded by a 3-letter code to indicate what type it is:
+ // new - Newest Albums
+ // gAl - Most Popular Albums (Global)
+ // gAr - Most Popular Artists (Global)
+ // uAl - Most Popular Albums (User)
+ // uAr - Most Popular Artists (User)
+
+ playlist_newest_albums=5
+ playlist_global_most_popular_albums=5
+ playlist_user_most_popular_albums=5
+ playlist_global_most_popular_artists=5
+ playlist_user_most_popular_artists=5
+
+
+ // If you want to print divider lines "-----" between each playlist section,
+ // to make navigating large lists a bit easier, set this:
+ // 0=Don't print divider lines (default)
+ // 1=Print divider lines
+
+ playlist_dividers=1
+
+
+
+
+// FAVORITES
+// ---------
+// You can dynamically populate the "favorites" list, acessible via the "List" button on the receiver's
+// remote control. This list can have up to 100 entries TOTAL, a limitiation of the hardware. Only the first
+// 100 entries will be used.
+// Specify how many of each type you want to appear on the list.
+
+ favorites_newest_albums=5
+ favorites_global_most_popular_albums=5
+ favorites_user_most_popular_albums=5
+ favorites_global_most_popular_artists=5
+ favorites_user_most_popular_artists=5
+
+
+ // If you want to print divider/header lines "---<section>---" between each favorites section, set this:
+ // 0=Don't print divider lines (default)
+ // 1=Print divider lines
+
+ favorites_dividers=1
+
+
+
+
diff --git a/contrib/plugins/RioPlayer/modules/rio/rio.php b/contrib/plugins/RioPlayer/modules/rio/rio.php
new file mode 100644
index 00000000..99f505be
--- /dev/null
+++ b/contrib/plugins/RioPlayer/modules/rio/rio.php
@@ -0,0 +1,1759 @@
+<?php
+
+/*
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ 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.
+
+*/
+
+/*
+
+ Interface file for SonicBlue Rio Receiver integration.
+ Written by Jon C. Hodgson - ampache@jonq.com
+ v0.2 - 2004.08.08
+
+ Note: You can test any query with debug info by simply adding &debug (or ?debug, as appropriate) to the request string
+
+*/
+
+
+ /*
+ ToDo:
+ =====
+ (in no particular order)
+ Access Lists
+ Associate private playlists with current unit if hide_private_playlists=1 and we're tracking play counts
+ Note in docs that Select Music...Playlist...Play All Music does not include "Special" Playlists
+ Allow user-specifed playlist order
+ Loosen query format to handle mixed case params (not really a big deal since receiver's requests are hard-coded)
+
+ */
+
+
+define('NO_SESSION',1);
+
+// We MUST use output buffering to get around Chunked Transfer Encoding
+ob_start();
+
+header('Content-Type: text/plain');
+
+// This is UBER-IMPORTANT! to disable sessions. Make sure this is set BEFORE including init.php
+
+// Ampache Library
+require_once("../../lib/init.php");
+
+
+// ===============================================================================================
+// DECLARE VARIABLES & LOAD CONFIG FILE
+// ===============================================================================================
+
+ // Defaults;
+ $hide_private_playlists=0;
+ $filter_from_beginning=1;
+ $query_limit=3000;
+ $track_receiver_stats=1;
+ $group_receivers_together=0;
+ $automatically_create_users=1;
+ $rio_user['0.0.0.0']='rio';
+ $playlist_global_most_popular_songs=0;
+ $playlist_user_most_popular_songs=0;
+ $playlist_newest_albums=0;
+ $playlist_global_most_popular_albums=0;
+ $playlist_user_most_popular_albums=0;
+ $playlist_global_most_popular_artists=0;
+ $playlist_user_most_popular_artists=0;
+ $favorites_newest_albums=0;
+ $favorites_global_most_popular_albums=0;
+ $favorites_user_most_popular_albums=0;
+ $favorites_global_most_popular_artists=0;
+ $favorites_user_most_popular_artists=0;
+ $favorites_dividers=0;
+ $playlist_dividers=0;
+// $debug=1;
+
+ // Read in config settings
+ $fh = fopen ('rio.conf', 'r') or die('Could not open config file');
+
+ while ($line = fgets($fh))
+ {
+ // Look for known parameters
+ if (preg_match("/^\s*hide_private_playlists\s*=\s*([0|1])\s*($|\/\/)/i",$line,$matches)) {$hide_private_playlists=$matches[1]; }
+ elseif (preg_match("/^\s*filter_from_beginning\s*=\s*([0|1])\s*($|\/\/)/i",$line,$matches)) {$filter_from_beginning=$matches[1]; }
+ elseif (preg_match("/^\s*query_limit\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$query_limit=$matches[1]; }
+ elseif (preg_match("/^\s*track_receiver_stats\s*=\s*([0|1])\s*($|\/\/)/i",$line,$matches)) {$track_receiver_stats=$matches[1]; }
+ elseif (preg_match("/^\s*group_receivers_together\s*=\s*([0|1])\s*($|\/\/)/i",$line,$matches)) {$group_receivers_together=$matches[1]; }
+ elseif (preg_match("/^\s*automatically_create_users\s*=\s*([0|1])\s*($|\/\/)/i",$line,$matches)) {$automatically_create_users=$matches[1]; }
+ elseif (preg_match("/^\s*rio_user_(\d+\.\d+\.\d+\.\d+)\s*=\s*(\S+)\s*($|\/\/)/i",$line,$matches)) {$rio_user[$matches[1]]=$matches[2]; }
+ elseif (preg_match("/^\s*playlist_global_most_popular_songs\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$playlist_global_most_popular_songs=$matches[1]; }
+ elseif (preg_match("/^\s*playlist_user_most_popular_songs\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$playlist_user_most_popular_songs=$matches[1]; }
+ elseif (preg_match("/^\s*playlist_global_most_popular_albums\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$playlist_global_most_popular_albums=$matches[1]; }
+ elseif (preg_match("/^\s*playlist_user_most_popular_albums\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$playlist_user_most_popular_albums=$matches[1]; }
+ elseif (preg_match("/^\s*playlist_global_most_popular_artists\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$playlist_global_most_popular_artists=$matches[1]; }
+ elseif (preg_match("/^\s*playlist_user_most_popular_artists\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$playlist_user_most_popular_artists=$matches[1]; }
+ elseif (preg_match("/^\s*playlist_newest_albums\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$playlist_newest_albums=$matches[1]; }
+ elseif (preg_match("/^\s*favorites_newest_albums\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$favorites_newest_albums=$matches[1]; }
+ elseif (preg_match("/^\s*favorites_global_most_popular_albums\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$favorites_global_most_popular_albums=$matches[1]; }
+ elseif (preg_match("/^\s*favorites_user_most_popular_albums\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$favorites_user_most_popular_albums=$matches[1]; }
+ elseif (preg_match("/^\s*favorites_global_most_popular_artists\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$favorites_global_most_popular_artists=$matches[1]; }
+ elseif (preg_match("/^\s*favorites_user_most_popular_artists\s*=\s*(\d+)\s*($|\/\/)/i",$line,$matches)) {$favorites_user_most_popular_artists=$matches[1]; }
+ elseif (preg_match("/^\s*favorites_dividers\s*=\s*([0|1])\s*($|\/\/)/i",$line,$matches)) {$favorites_dividers=$matches[1]; }
+ elseif (preg_match("/^\s*playlist_dividers\s*=\s*([0|1])\s*($|\/\/)/i",$line,$matches)) {$playlist_dividers=$matches[1]; }
+
+ }
+
+ fclose($fh);
+
+ // Post validation -- Keep people honest
+ // -------------------------------------
+ if ($playlist_newest_albums>100) {$playlist_newest_albums=100;}
+ if ($playlist_global_most_popular_albums>100) {$playlist_global_most_popular_albums=100;}
+ if ($playlist_user_most_popular_albums>100) {$playlist_user_most_popular_albums=100;}
+ if ($playlist_global_most_popular_artists>100) {$playlist_global_most_popular_artists=100;}
+ if ($playlist_user_most_popular_artists>100) {$playlist_user_most_popular_artists=100;}
+
+
+
+ // Amount to add to a track to protect special keyword 100
+ $track_add=101;
+
+ // Amount to add to a playlist to make 0=0x10000000
+ $playlist_add=268435355;
+
+ // Amount to add to a playlist to make 0=0x20000000
+ $special_add=536870811;
+
+ // How far into the song do you count the track as having being played?
+ // This compensates for songs you simply fast forward thru, not tracking them etc.
+ // Each multiple of 32768 is ~1 second, but that totally depends on bitrate etc.
+ $countTrackBytes=32768*30;
+
+
+
+// ================================================================================================
+// Parse URL
+// ================================================================================================
+// Very strict parse rules are enforced, particularly to ensure that we catch unexpected query
+// variances for untested versions of the receiver.arf, expecially community-hacked versions.
+
+
+ // Host
+ $rioIP = $_SERVER['REMOTE_ADDR'];
+
+
+ // Path
+ $path = $_SERVER['SCRIPT_NAME'];
+
+
+ // "content" "list" "query" "results" "tags"
+ if (preg_match("#^/(content|list|query|results|tags)(/(.*)|$)#",$path,$match)) {
+
+ $queryType=$match[1];
+
+ // Validate
+ if (preg_match("#^[0-9A-Fa-f]*$#",$match[3])) {
+
+ // "content" & "list" require a value
+ if (($queryType=="content" || $queryType=="list") && strlen($match[3])==0) {
+ exit ("ERROR: Invalid '$queryType' query options: '$match[3]' -- Should have a value, eg. '/$queryType/1d3f'");
+ }
+
+ // "query" & "results" should have no value
+ elseif (($queryType=="query" || $queryType=="results") && strlen($match[2])!=0) {
+ exit ("ERROR: Invalid '$queryType' query options: '$match[2]' -- Cannot have a value, must be '/$queryType' ");
+ }
+
+ else {
+ $queryOpts=$match[3];
+ }
+
+ } else {
+ exit ("ERROR: Invalid '$queryType' query options: '$match[3]'");
+ }
+ }
+
+
+ // "favourites"
+ elseif (preg_match("#^/favourites(/(.*)|$)#",$path,$match)) {
+ $queryType="favourites";
+
+ // Validate
+ if ($match[2]=="all") {
+ $queryOpts=$match[2];
+ } else {
+ exit ("ERROR: Invalid '$queryType' query options: '$match[2]'");
+ }
+ }
+
+
+ // ERROR
+ else {
+ exit ("ERROR: Invalid query: '$match[1]'");
+ }
+
+
+
+
+
+// ================================================================================================
+// PARSE REQUEST PARAMETERS
+// ================================================================================================
+
+ foreach ($_GET as $var => $val) {
+
+
+ // "artist" "source" "genre" "title"
+ if (preg_match("/^(artist|source|genre|title)$/",$var,$match)) {
+
+ if (isset($queryCategory)) {
+ exit ("ERROR: You cannot specify another Query Category '$match[1]', '$queryCategory' already set.");
+ }
+
+ $queryCategory=$match[1];
+ $queryFilter=$val;
+ }
+
+
+ // "extended"
+ elseif (strtolower($var)=="_extended") {
+
+ if (preg_match("/^[0-2]$/",$val)) {
+ $extended=$val;
+ } elseif ($val=="") {
+ $extended=0;
+ } else {
+ exit ("ERROR: Invalid value '$val' for '$var', must be blank or 0-2.");
+ }
+
+ }
+
+
+ // "shuffle"
+ elseif (strtolower($var)=="shuffle") {
+
+ if (preg_match("/^[0-1]$/",$val)) {
+ $shuffle=$val;
+ } elseif ($val=="") {
+ $shuffle=0;
+ } else {
+ exit ("ERROR: Invalid value '$val' for '$var', must be blank or 0-1.");
+ }
+
+ }
+
+
+ // begin
+ elseif (strtolower($var)=="_begin") {
+
+ if (preg_match("/^([0-9]*)$/",$val,$match)) {
+ if (strlen($match[1])>0) {
+ $begin=intval($match[1]);
+ }
+ } else {
+ exit ("ERROR: Invalid value '$val' for '$var', must be blank or an integer >= 0");
+ }
+
+ }
+
+
+ // end
+ elseif (strtolower($var)=="_end") {
+
+ if (preg_match("/^([0-9]*)$/",$val,$match)) {
+ if (strlen($match[1])>0) {
+ $end=intval($match[1]);
+ }
+ } else {
+ exit ("ERROR: Invalid value '$val' for '$var', must be blank or an integer >= 0");
+ }
+
+ }
+
+
+ // Newest Albums
+ elseif (strtolower($var)=="shoutcast") {
+
+ if (preg_match("#^(\S+)\s*(http://.*)$#",$val,$match)) {
+
+ $favouriteType=$var;
+ $favourite="$match[2]";
+
+ } else {
+
+ //Ignore
+ //exit ("ERROR: Unexpected parameter '$var' with value '$val' -- please remove from query.");
+
+ }
+
+ }
+
+
+ // Debug
+ elseif (strtolower($var)=="debug") {
+ $debug=1;
+ }
+
+
+ // Divider
+ elseif (strtolower($var)=="divider") {
+
+ // Do nothing... no usable response
+ print "Divider\n";
+ header('Content-Length: '.strlen(ob_get_contents()));
+ ob_end_flush();
+ exit;
+
+ }
+
+
+ // ERROR
+ else {
+ exit ("ERROR: Unexpected parameter '$var' with value '$val' -- please remove from query.");
+ }
+
+ }
+
+
+
+
+
+// ================================================================================================
+// ADDITIONAL VALIDATION
+// ================================================================================================
+
+ // begin & end
+ if (strlen($begin)>0 && strlen($end)>0) {
+
+ if ($begin > $end) {
+ exit ("ERROR: _begin '$begin' must be less than _end '$end'");
+ } else {
+ $range=$end-$begin+1;
+ }
+
+ } else {
+
+ // Both need to be set
+ $begin="";
+ $end="";
+
+ }
+
+
+ // Regex Query
+ if (preg_match("/^(\[[0-9A-Za-z]+\])+$/",$queryFilter)) {
+ $queryFilterRegex=1;
+ } else {
+ $queryFilterRegex=0;
+ }
+
+
+ // extended default
+ if ($extended=="") {
+ $extended=0;
+ }
+
+
+ // shuffle default
+ if ($shuffle=="") {
+ $shuffle=0;
+ }
+
+
+ // ampache usernames for each recevier
+ if ($group_receivers_together) {
+ $rio_user_IP="0.0.0.0";
+ } else {
+ $rio_user_IP=$rioIP;
+ }
+
+ if ($rio_user[$rio_user_IP]=="") {$rio_user[$rio_user_IP]="rio-$rioIP";}
+
+ // this unit's ampache user name
+ $ampacheUsername=$rio_user[$rio_user_IP];
+
+ // ampache user id
+ if ($track_receiver_stats) {
+
+ // v.1.1 Change
+ $userID=mysql_fetch_row(mysql_query("SELECT id FROM user WHERE username='$ampacheUsername'"));
+ $ampacheUserID=$userID[0];
+ //v.1.0 Code
+ //$row=mysql_fetch_row(mysql_query("SELECT id FROM user WHERE username='$ampacheUsername'"));
+ //$ampacheUserID=$row[0];
+
+ }
+
+// ================================================================================================
+// DEBUG
+// ================================================================================================
+ if ($debug) {
+
+ print "DEBUG MODE\n==========\n\n";
+
+ $pad=38;
+
+ print "Path: $path\n";
+ print str_repeat("-",$pad+1)."\n";
+ $format="%".$pad."s: %s\n";
+ printf($format,"Rio IP",$rioIP);
+ printf($format,"Query Type",$queryType);
+ printf($format,"Query Options",$queryOpts);
+ printf($format,"Query Category",$queryCategory);
+ printf($format,"Query Filter",$queryFilter);
+ printf($format,"Query Filter Regex",$queryFilterRegex);
+ printf($format,"Extended",$extended);
+ printf($format,"Shuffle",$shuffle);
+ printf($format,"Begin",$begin);
+ printf($format,"End",$end);
+ printf($format,"Range","$range");
+ printf($format,"Favourite Type","$favouriteType");
+ printf($format,"Favourite","$favourite");
+
+ print "\nConfig File Settings:\n";
+ print str_repeat("-",$pad+1)."\n";
+ printf($format,"hide_private_playlists",$hide_private_playlists);
+ printf($format,"filter_from_beginning",$filter_from_beginning);
+ printf($format,"query_limit",$query_limit);
+ printf($format,"track_receiver_stats",$track_receiver_stats);
+ printf($format,"group_receivers_together",$group_receivers_together);
+ printf($format,"automatically_create_users",$automatically_create_users);
+ printf($format,"Rio User IP",$rio_user_IP);
+ printf($format,"Ampache User ID",$ampacheUserID);
+ printf($format,"Ampache Username",$ampacheUsername);
+ foreach ($rio_user as $var => $val) {
+ printf($format,"rio_user_$var (Mapping)",$val);
+ }
+ printf($format,"Print Playlist Dividers",$playlist_dividers);
+ printf($format,"Print Favorites Dividers",$favorites_dividers);
+
+
+
+ /*
+ printf($format,"Playlist: Newest Albums",$playlist_newest_albums);
+ printf($format,"Playlist: Global Most Popular Songs",$playlist_global_most_popular_songs);
+ printf($format,"Playlist: Global Most Popular Albums",$playlist_global_most_popular_albums);
+ printf($format,"Playlist: Global Most Popular Artists",$playlist_global_most_popular_artists);
+ printf($format,"Playlist: User Most Popular Songs",$playlist_user_most_popular_songs);
+ printf($format,"Playlist: User Most Popular Albums",$playlist_user_most_popular_albums);
+ printf($format,"Playlist: User Most Popular Artists",$playlist_user_most_popular_artists);
+
+ printf($format,"Favorites: Newest Albums",$favorites_newest_albums);
+ printf($format,"Favorites: Global Most Popular Albums",$favorites_global_most_popular_albums);
+ printf($format,"Favorites: Global Most Popular Artists",$favorites_global_most_popular_artists);
+ printf($format,"Favorites: User Most Popular Albums",$favorites_user_most_popular_albums);
+ printf($format,"Favorites: User Most Popular Artists",$favorites_user_most_popular_artists);
+ */
+
+ print "\nVariable/Value Pairs\n";
+ print str_repeat("-",$pad+1)."\n";
+ foreach ($_GET as $var => $val) {
+ printf($format,$var,$val);
+ }
+ print "\n\n";
+ }
+
+
+
+
+
+// ================================================================================================
+// PROCESS STATIC REQUESTS (ones that do not require standard DB access)
+// ================================================================================================
+
+
+ // Ampache user integration -- creating users & updating last_seen etc.
+ // --------------------------------------------------------------------
+ if ($queryType=="tags" && $queryOpts=="") {
+ // Since this is only run at startup, we use this section to auto-create the username if unspecified.
+ if ($track_receiver_stats) {
+
+ if ($automatically_create_users && !$debug) {
+
+ // Create a new user if it doesn't exist
+ if ($ampacheUserID=="") {
+
+ if ($debug) { print "\nCreating new ampache user '$ampacheUsername'... ";}
+
+ $user = new User();
+
+ if (!$user->create($ampacheUsername,"Rio Receiver - $rio_user_IP", "" , mt_rand(10000000,99999999) , "disabled")) {
+ if ($debug) { print "FAILED\n\n";}
+ } else {
+ if ($debug) { print "SUCCEEDED\n\n";}
+ }
+
+ }
+ }
+ }
+ }
+
+ // Update last_seen except for content calls for tracks... (too expensive, we'll do that again later in content for just the first request)
+ if ($track_receiver_stats && !($queryType=="content" && rio_isTrack($queryOpts))) {
+
+ mysql_query("UPDATE user SET last_seen='" . time() . "' WHERE username='$ampacheUsername'");
+
+ }
+
+
+
+
+
+ if ($queryType=="tags" && $queryOpts=="") {
+
+ $static=1;
+
+ // Print list of known tags, these map to the KEYs in the tag format of 0-15
+
+ // Key Description Ampache table.field
+ // --- ----------------------- --------------------------------------------------------------------------------
+ print "fid\n"; // 0 File ID (Decimal) song.id converted to FID-decimal or playlist.id
+ print "title\n"; // 1 Title song.title or playlist.name
+ print "artist\n"; // 2 Artist song.artist
+ print "source\n"; // 3 Album song.album
+ print "year\n"; // 4 Year song.year
+ print "comment\n"; // 5 ID3 Comment song.comment
+ print "length\n"; // 6 Filesize (bytes) song.size (mp3s) or #_of_tracks*4 (playlists) or #_of_playlist*4 (/tags/100) <- I dunno why
+ print "type\n"; // 7 Sourcetype ("tune") "tune" (mp3s) or "playlist" (playlist) <-- dunno what else this could be
+ print "path\n"; // 8 File Path song.file
+ print "genre\n"; // 9 Genre song.genre
+ print "bitrate\n"; // 10 Bitrate song.mode vbr=vs cbr=fs & song.bitrate/1024(or 1000?) eg. fs128 vs 193
+ print "playlist\n"; // 11 Playlist name playlist.name ?
+ print "codec\n"; // 12 CODEC (MP3 or WMA) "mp3" <-- base on extension in song.file?
+ print "offset\n"; // 13 Data Offset ??? <-- It doesn't seem to be needed
+ print "duration\n"; // 14 Duration (milliseconds) song.time * 1000
+ print "tracknr\n"; // 15 Track Number song.track
+
+ }
+
+
+ elseif ($queryType=="tags" && $queryOpts==100) {
+
+ $static=1;
+
+ // Special semi-static Query for All Tracks Playlist
+
+ // Get playlist counts
+ if ($hide_private_playlists) {
+
+ $sql2="SELECT COUNT(*) FROM playlist WHERE playlist.type='public'";
+
+ } else {
+
+ $sql2="SELECT COUNT(*) FROM playlist";
+ }
+
+
+ // Process query 2 & populate variables
+ $row = mysql_fetch_row(mysql_query($sql2));
+
+ $length=$row[0]*4; // Don't ask me why, it just is.
+
+ // LET'S OUTPUT SOME DWORDS!
+
+ print rio_tagdata(0,"256");
+ print rio_tagdata(1,"All Tracks");
+ print rio_tagdata(7,"playlist");
+ print rio_tagdata(6,$length);
+ print pack("C",255); // EOF
+
+ } elseif ($queryType=="favourites") {
+
+ // Handle staticly for now
+ $static=1;
+
+ // Don't put an entry > 99 or the unit will freak out
+
+ //This special entry prevents the rest of the list from being displayed
+ //print "99:Whachatalkingaboutwillis\n";
+
+ // If you pad the URL with 36 characters you can hide the URL from the UI
+ //print "0:artist=\n";
+ //print "1:fave&artist=sting\n";
+ //print "5:shoutcast=label http://sc2.magnatune.com:8008/listen.pls\n";
+ //print "6:http://sc2.magnatune.com:8008/listen.pls=shoutcast2\n";
+
+
+ // FAVORITES
+ // ---------
+
+ $index=0;
+
+ if ($favorites_newest_albums) {
+
+ if ($favorites_dividers) {
+ print "$index:divider=----".$favorites_newest_albums." Newest Albums----\n";
+ $index++;
+ }
+
+ $sql="SELECT DISTINCT album.name FROM song,album WHERE song.album=album.id ORDER BY song.addition_time DESC LIMIT $favorites_newest_albums";
+ $db_results = mysql_query($sql);
+
+ while (($row=mysql_fetch_row($db_results)) && $index < 100) {
+
+ print "$index:source=$row[0]\n";
+ $index++;
+ }
+
+ }
+
+ if ($favorites_global_most_popular_albums) {
+
+ if ($favorites_dividers) {
+ print "$index:divider=---".$favorites_global_most_popular_albums." Fav Albums (G)----\n";
+ $index++;
+ }
+
+ $sql="SELECT album.name,sum(object_count.count) AS total_count FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $favorites_global_most_popular_albums";
+ $db_results = mysql_query($sql);
+
+ while (($row=mysql_fetch_row($db_results)) && $index < 100) {
+
+ print "$index:source=$row[0]\n";
+ $index++;
+ }
+
+ }
+
+ if ($favorites_user_most_popular_albums) {
+
+ if ($favorites_dividers) {
+ print "$index:divider=---".$favorites_user_most_popular_albums." Fav Albums (U)----\n";
+ $index++;
+ }
+
+ $sql="SELECT album.name FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $favorites_user_most_popular_albums";
+ $db_results = mysql_query($sql);
+
+ while (($row=mysql_fetch_row($db_results)) && $index < 100) {
+
+ print "$index:source=$row[0]\n";
+ $index++;
+ }
+
+ }
+
+ if ($favorites_global_most_popular_artists) {
+
+ if ($favorites_dividers) {
+ print "$index:divider=---".$favorites_global_most_popular_artists." Fav Artists (G)----\n";
+ $index++;
+ }
+
+ $sql="SELECT artist.name,sum(object_count.count) AS total_count FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $favorites_global_most_popular_artists";
+ $db_results = mysql_query($sql);
+
+ while (($row=mysql_fetch_row($db_results)) && $index < 100) {
+
+ print "$index:artist=$row[0]\n";
+ $index++;
+ }
+
+ }
+
+ if ($favorites_user_most_popular_artists) {
+
+ if ($favorites_dividers) {
+ print "$index:divider=---".$favorites_user_most_popular_artists." Fav Artists (U)----\n";
+ $index++;
+ }
+
+ $sql="SELECT artist.name FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $favorites_user_most_popular_artists";
+ $db_results = mysql_query($sql);
+
+ while (($row=mysql_fetch_row($db_results)) && $index < 100) {
+
+ print "$index:artist=$row[0]\n";
+ $index++;
+ }
+
+ }
+
+ // Print a footer if there were previous entries
+ if ($favorites_dividers && $index>0) {
+ print "$index:divider=-----------------------\n";
+ $index++;
+ }
+
+
+
+ } elseif ($queryType=="tags" && rio_isSpecial($queryOpts)) {
+
+ // Special Playlists
+ $static=1;
+
+ $special=rio_fid2special($queryOpts);
+
+ if ($special==1) {$title="Popular Songs (Global)";}
+ elseif ($special==2) {$title="Popular Songs (User)";}
+ elseif ($special>=100 && $special<200) {$num=$special-99;$title="Newest Album $num";}
+ elseif ($special>=200 && $special<300) {$num=$special-199;$title="Popular Albums $num (Global)";}
+ elseif ($special>=300 && $special<400) {$num=$special-299;$title="Popular Albums $num (User)" ;}
+ elseif ($special>=400 && $special<500) {$num=$special-399;$title="Popular Artists $num (Global)";}
+ elseif ($special>=500 && $special<600) {$num=$special-499;$title="Popular Artists $num (User)";}
+ else {$title="";} // Including special=0 (dividers)
+
+
+
+ $length=0; // This doesn't seem to be needed either (should be # of entries *4)
+
+ // LET'S OUTPUT SOME DWORDS!
+ print rio_tagdata(0,hexdec($queryOpts));
+ print rio_tagdata(1,$title);
+ print rio_tagdata(7,"playlist");
+ print rio_tagdata(6,$length);
+ print pack("C",255); // EOF
+
+ } elseif ($queryType=="content" && $queryOpts==20000000) {
+
+ // Divider -- Do nothing
+ $static=1;
+
+ //print rio_track2fid($row[0])."=T$row[1]\n";
+
+ }
+
+
+ if ($static) {
+ // Send the Content-Length header to disable Chunked Transfer Encoding
+ header('Content-Length: '.strlen(ob_get_contents()));
+
+ // This is where the page actually gets written
+ ob_end_flush();
+
+ exit;
+ }
+
+
+
+
+
+// ================================================================================================
+// BUILD SQL QUERY
+// ================================================================================================
+
+ // query
+ if ($queryType=="query") {
+
+ // This ORDER BY only affects the display in the UI. This does not affect play order!
+
+ if ($queryCategory=='title') {
+ $select="title";
+ $from="song";
+ $orderby="RAND()"; // Since we have to limit this query anyway, might as well randomize it
+ } elseif ($queryCategory=='artist') {
+ $select="name";
+ $from="artist";
+ $orderby=$select;
+ } elseif ($queryCategory=='source') {
+ $select="name";
+ $from="album";
+ $orderby=$select;
+ } elseif ($queryCategory=='genre') {
+ $select="name";
+ $from="genre";
+ $orderby=$select;
+ }
+
+ $where=$select;
+
+
+ } elseif ($queryType=="results") {
+
+ if ($favouriteType=="shoutcast") {
+
+ //testing
+ $queryCategory="artist";
+ $queryFilter="Sting";
+ //exit;
+ }
+
+
+ // This ORDER BY sorting does not affect UI list, but does affects playback order
+
+ $select="song.id,song.title,song.size";
+ $from="song";
+
+ if ($queryCategory=="title") { // Verifed query results as complete
+
+ $where="song.title";
+ $orderby="RAND()"; // Since we limit the list anyway
+ //$orderby="song.artist,song.album,song.track,song.file";
+
+ } elseif ($queryCategory=="artist") { // Verifed query results as complete
+
+ $where="artist.name";
+ $leftjoin="artist ON song.artist=artist.id LEFT JOIN album ON song.album=album.id";
+ $orderby="artist.name,album.name,song.track,song.file";
+
+ } elseif ($queryCategory=="source") { // Verifed query results as complete
+
+ $where="album.name";
+ $leftjoin="album ON song.album=album.id";
+ $orderby="album.name,song.track,song.file";
+
+ } elseif ($queryCategory=="genre") { // Verifed query results as complete
+
+ $where="genre.name";
+ $leftjoin="genre ON song.genre=genre.id LEFT JOIN artist ON song.artist=artist.id LEFT JOIN album ON song.album=album.id";
+ $orderby="genre.name,artist.name,album.name,song.track,song.file";
+
+ }
+
+ } elseif ($queryType=="tags") {
+
+ if (rio_isTrack($queryOpts)) {
+
+ // Oh my, this is gonna be fun...
+
+ $select="song.title,artist.name,album.name,song.year,song_ext_data.comment,song.size,song.file,song.genre,song.bitrate,song.mode,song.time,song.track";
+ $from="song,song_ext_data";
+ $leftjoin="artist ON song.artist=artist.id LEFT JOIN album on song.album=album.id";
+ $where="song.id";
+ $queryFilter=rio_fid2track($queryOpts); // actual Track ID
+ $range=1;
+
+ } elseif (rio_isPlaylist($queryOpts)) {
+ // /tags/<PlaylistID>
+ // ------------------
+ // Select Music...Playlist...<PlaylistID>
+ // Returns extended info redarding that playlist
+ // /content/<playlistID> is also called at this time
+
+ $select="name";
+ $from="playlist";
+ $where="id";
+ $queryFilter=rio_fid2playlist($queryOpts);
+ $range=1;
+
+ }
+
+ } elseif ($queryType=="content") {
+
+ if ($queryOpts==100) {
+ // Select Music...Playlist
+ // Returns a list of all playlists
+
+ $select="id,name";
+ $from="playlist";
+
+ // Hide private playlists if param is set
+ if ($hide_private_playlists) {
+ $where="type";
+ $queryFilter="public";
+ }
+
+ } elseif (rio_isTrack($queryOpts)) {
+
+ $select="file";
+ $from="song";
+ $where="id";
+ $queryFilter=rio_fid2track($queryOpts); // actual Track ID
+ $range=1;
+
+ } elseif (rio_isPlaylist($queryOpts)) {
+
+ // content/<PlaylistID>?_extended=1
+ // --------------------------------
+ // Select Music...Playlist...<PlaylistID>
+ // Returns a list of tracks for the specific playlist
+ // /tags/<playlistID> is also called at this time
+
+ $select="song.id,song.title";
+ $from="song";
+ $leftjoin="playlist_data ON song.id=playlist_data.song";
+ $where="playlist_data.playlist";
+ $queryFilter=rio_fid2playlist($queryOpts); // actual Playlist ID
+ $orderby="playlist_data.track";
+
+ } elseif (rio_isSpecial($queryOpts)) {
+
+ // Special Playlists
+
+ $special=rio_fid2special($queryOpts);
+
+ // Global Most Popular Songs
+ if ($special==1) {
+ $sql="SELECT song.id,song.title,sum(object_count.count) AS total_count FROM object_count LEFT JOIN song ON object_count.object_id=song.id WHERE object_count.object_type='song' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $playlist_global_most_popular_songs";
+ }
+
+ // User Most Popular Songs
+ elseif ($special==2) {
+ $sql="SELECT song.id,song.title FROM object_count LEFT JOIN song ON object_count.object_id=song.id LEFT JOIN user ON object_count.userid=user.id WHERE object_count.object_type='song' AND user.username='$ampacheUsername' ORDER BY object_count.count DESC LIMIT $playlist_user_most_popular_songs";
+ }
+
+ // Newest Albums
+ elseif ($special >= 100 && $special < 200) {
+
+ $num=$special-100;
+
+ // Get album id
+ $sql="SELECT DISTINCT album.id FROM song,album WHERE song.album=album.id ORDER BY song.addition_time DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.title FROM song LEFT JOIN album ON song.album=album.id WHERE album.id='$row[0]' ORDER BY song.track,song.file";
+
+ }
+
+ // Global Most Popular Albums
+ elseif ($special >= 200 && $special < 300) {
+
+ $num=$special-200;
+
+ // Get album id
+ $sql="SELECT album.id,sum(object_count.count) AS total_count FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.title FROM song LEFT JOIN album ON song.album=album.id WHERE album.id='$row[0]' ORDER BY song.track,song.file";
+
+ }
+
+ // User Most Popular Albums
+ elseif ($special >= 300 && $special < 400) {
+
+ $num=$special-300;
+
+ // Get album id
+ $sql="SELECT album.id FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.title FROM song LEFT JOIN album ON song.album=album.id WHERE album.id='$row[0]' ORDER BY song.track,song.file";
+
+ }
+
+ // Global Most Popular Artists
+ elseif ($special >= 400 && $special < 500) {
+
+ $num=$special-400;
+
+ // Get artist id
+ $sql="SELECT artist.id,sum(object_count.count) AS total_count FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.title FROM song LEFT JOIN artist ON song.artist=artist.id LEFT JOIN album ON song.album=album.id WHERE artist.id='$row[0]' ORDER BY album.name,song.track,song.file";
+
+ }
+
+ // User Most Popular Artists
+ elseif ($special >= 500 && $special < 600) {
+
+ $num=$special-500;
+
+ // Get artist id
+ $sql="SELECT artist.id FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $favorites_user_most_popular_artists";
+
+ $row= mysql_fetch_row(mysql_query($sql));
+ $sql="SELECT song.id,song.title FROM song LEFT JOIN artist ON song.artist=artist.id LEFT JOIN album ON song.album=album.id WHERE artist.id='$row[0]' ORDER BY album.name,song.track,song.file";
+
+ } else {
+ exit("ERROR: Unexpected Special Query '$special'\n");
+ }
+
+
+ }
+
+ } elseif ($queryType=="list") {
+
+ $select="song.id,song.size";
+
+ if ($queryOpts==100) {
+ // /list/100?shuffle=<0|1>&_extended=2
+ // -----------------------------------
+ // Selct Music...Playlist...Play All Music
+ // Returns a list of all songs in all playlist
+
+ if ($hide_private_playlists) {
+
+ $from="song,playlist_data,playlist";
+ $where="song.id";
+ $queryFilter="playlist_data.song AND playlist.id=playlist_data.playlist AND playlist.type='public'";
+
+ } else {
+
+ $from="song,playlist_data";
+ $where="song.id";
+ $queryFilter="playlist_data.song";
+ }
+
+ $orderby="playlist_data.playlist,playlist_data.track";
+
+ } elseif (rio_isTrack($queryOpts)) {
+
+ $from="song";
+ $where="id";
+ $queryFilter=rio_fid2track($queryOpts); // actual Track ID
+
+ } elseif (rio_isPlaylist($queryOpts)) {
+ // /list/<PlaylistID>?shuffle=<0|1>&_extended=2
+ // -----------------------------------
+ // Selct Music...Playlist...<PlaylistID>...Play All Music
+ // Returns a list of songs in the playlist
+
+ $from="song";
+ $leftjoin="playlist_data ON song.id=playlist_data.song";
+ $where="playlist_data.playlist";
+ $queryFilter=rio_fid2playlist($queryOpts); // actual Playlist ID
+ $orderby="playlist_data.track";
+
+ } elseif (rio_isSpecial($queryOpts)) {
+
+ // Special Playlists
+
+ $special=rio_fid2special($queryOpts);
+
+ // Global Most Popular Songs
+ if ($special==1) {
+
+ $sql="SELECT song.id,song.size,sum(object_count.count) AS total_count FROM object_count LEFT JOIN song ON object_count.object_id=song.id WHERE object_count.object_type='song' GROUP BY object_count.object_id";
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND() ";
+ } else {
+ $sql.=" ORDER BY total_count ";
+ }
+ $sql.="DESC LIMIT $playlist_global_most_popular_songs";
+ }
+
+ // User Most Popular Songs
+ elseif ($special==2) {
+
+ $sql="SELECT song.id,song.size FROM object_count LEFT JOIN song ON object_count.object_id=song.id LEFT JOIN user ON object_count.userid=user.id WHERE object_count.object_type='song' AND user.username='$ampacheUsername'";
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND() ";
+ } else {
+ $sql.=" ORDER BY object_count.count ";
+ }
+ $sql.="DESC LIMIT $playlist_user_most_popular_songs";
+ }
+
+ // Newest Albums
+ elseif ($special >= 100 && $special < 200) {
+
+ $num=$special-100;
+
+ // Get album id
+ $sql="SELECT DISTINCT album.id FROM song,album WHERE song.album=album.id ORDER BY song.addition_time DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.size FROM song LEFT JOIN album on song.album=album.id WHERE album.id='$row[0]'";
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND() ";
+ } else {
+ $sql.=" ORDER BY song.track,song.file ";
+ }
+
+ }
+
+ // Global Most Popular Albums
+ elseif ($special >= 200 && $special < 300) {
+
+ $num=$special-200;
+
+ // Get album id
+ $sql="SELECT album.id,sum(object_count.count) AS total_count FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.size FROM song LEFT JOIN album on song.album=album.id WHERE album.id='$row[0]'";
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND() ";
+ } else {
+ $sql.=" ORDER BY song.track,song.file ";
+ }
+
+ }
+
+ // Global Most Popular Albums
+ elseif ($special >= 300 && $special < 400) {
+
+ $num=$special-300;
+
+ $sql="SELECT album.id FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.size FROM song LEFT JOIN album on song.album=album.id WHERE album.id='$row[0]'";
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND() ";
+ } else {
+ $sql.=" ORDER BY song.track,song.file ";
+ }
+
+ }
+
+ // Global Most Popular Artists
+ elseif ($special >= 400 && $special < 500) {
+
+ $num=$special-400;
+
+ // Get artist id
+ $sql="SELECT artist.id,sum(object_count.count) AS total_count FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $num,1";
+ $row= mysql_fetch_row(mysql_query($sql));
+
+ $sql="SELECT song.id,song.size FROM song LEFT JOIN artist ON song.artist=artist.id LEFT JOIN album ON song.album=album.id WHERE artist.id='$row[0]'";
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND() ";
+ } else {
+ $sql.=" ORDER BY album.name,song.track,song.file ";
+ }
+
+ }
+
+ // User Most Popular Artists
+ elseif ($special >= 500 && $special < 600) {
+
+ $num=$special-500;
+
+ // Get artist id
+ $sql="SELECT artist.id FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $favorites_user_most_popular_artists";
+
+ $row= mysql_fetch_row(mysql_query($sql));
+ $sql="SELECT song.id,song.size FROM song LEFT JOIN artist ON song.artist=artist.id LEFT JOIN album ON song.album=album.id WHERE artist.id='$row[0]'";
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND() ";
+ } else {
+ $sql.=" ORDER BY album.name,song.track,song.file ";
+ }
+
+ } else {
+ exit("ERROR: Unexpected Special Query '$special'\n");
+ }
+
+ }
+
+ }
+
+
+
+
+
+// ================================================================================================
+// BUILD FINAL SQL QUERY
+// ================================================================================================
+
+ if($sql=="") {
+
+ // SELECT
+ $sql="SELECT $select FROM $from";
+
+
+ // LEFT JOIN
+ if ($leftjoin) {
+ $sql.=" LEFT JOIN $leftjoin";
+ }
+
+
+ // WHERE
+ if ($queryFilter!="") {
+
+ $sql.=" WHERE $where";
+
+ if ($queryFilterRegex) {
+ // Must compensate for non alphanumeric characters (basically only matching on alpha numeric chars)
+ // [1abc][8tuv][3def][6mno] should match "-Av%e Mar?ia-"
+ // We do this by prepending each pattern set with '[^0-9a-z]*' (MySQL REGEXP queries are case insensitive)
+ $queryFilter=preg_replace("/(\[[0-9A-Za-z]+\])/","[^0-9a-z]*$1",$queryFilter);
+
+ $sql.=" REGEXP '";
+ if($filter_from_beginning){$sql.="^";}
+ $sql.="$queryFilter'";
+ } else {
+
+ if ($queryType=="query") {
+ $queryFilter=mysql_escape_string($queryFilter);
+ $sql.=" LIKE '";
+ if($filter_from_beginning){$sql.="%";}
+ $sql.="$queryFilter%'";
+ } elseif ($queryType=="list") {
+ $sql.="=$queryFilter";
+ } else {
+ $queryFilter=mysql_escape_string($queryFilter);
+ $sql.="='$queryFilter'";
+ }
+ }
+ }
+
+
+ // ORDER BY
+ if ($shuffle) {
+ $sql.=" ORDER BY RAND()";
+ } elseif ($orderby) {
+ $sql.=" ORDER BY $orderby";
+ }
+
+
+ // LIMIT
+
+ if ($range=="" || $range > $query_limit) {
+
+ // Selective filtering alternative for potentially large queries - I commented it out to limit ALL queries
+ //($queryType=="query" && ($queryCategory=="title" && $range=="") || $range > $query_limit) ||
+ //($queryType=="results" && (($queryCategory=="title" || $queryFilter=="") || $range > $query_limit))
+ //) {
+
+ // Handle catalog length issue
+ // The reciver can hang or reboot on large queries. Use this setting to specify a Query Limit
+ // eg. query_limit=3000 to limit large queries to 3000 entries. This is the default.
+ // The breaking point varies possibly depending on specific data returned and usage conditions (that use cache etc.?)
+ // If your rio hangs or reboots on some queries, try lowering this number.
+
+ // This parameter is set in rio.conf
+
+ if ($debug) {print "NOTE: Query limited to $query_limit to prevent hanging of unit.\n\n";}
+ $range=$query_limit;
+ }
+
+ if ($begin && $range) {
+ $sql.=" LIMIT $begin,$range";
+ } elseif ($range) {
+ $sql.=" LIMIT $range";
+ }
+
+ // We shouldn't be here if there's no SQL query
+ if ($select==""){exit("ERROR: Unexpected Query - Missing SELECT statement\n");}
+
+ }
+
+ // EXECUTE SQL QUERY
+ if ($debug) {print "SQL STATEMENT:\n".str_repeat("-",strlen($sql))."\n$sql\n";}
+
+ $db_results = mysql_query($sql);
+
+ if ($debug) {print "\n\nQUERY RESPONSE:\n===============\n";}
+
+
+
+
+
+
+
+
+
+// ================================================================================================
+// PROCESS QUERY RESULTS
+// ================================================================================================
+
+ // Set begin counter if no specified
+ if($begin==""){$begin=0;}
+
+ $firstRun=1;
+
+ while($row=mysql_fetch_row($db_results)) {
+
+ // query
+ if ($queryType=="query") {
+
+ // select="title" or "name" (depending on query)
+
+ if ($firstRun) {print "matches=\n";}
+
+ // NOTE: The first digit ("1") of the returned output (1,0,0) indicates a count for that record.
+ // In the interest of query speed, i've omitted this code completely.
+ print "$begin=1,0,0:$row[0]\n";
+ $begin++;
+
+ } elseif ($queryType=="results") {
+
+ // select="song.id,song.title,song.size"
+
+ if ($extended=="1") {
+
+ print rio_track2fid($row[0])."=T$row[1]\n";
+
+ } elseif ($extended=="2") {
+
+ print rio_track2pfid($row[0]).pack("L2",$row[2],$mpeg_data_offset);
+
+ } else {
+
+ // The rio never seems to query this, format gotten by querying original server
+ print rio_track2pfid($row[0]).pack("L1",$row[2]);
+
+ }
+
+
+ } elseif ($queryType=="tags") {
+
+ if (rio_isTrack($queryOpts)) {
+
+ // select="song.title,artist.name,album.name,song.year,song.comment,song.size,song.file,song.genre,song.bitrate,song.mode,song.time,song.track"
+
+ // Oh my, this is gonna be fun...
+
+ $title=$row[0];
+ $artist=$row[1];
+ $source=$row[2];
+ $year=$row[3];
+ $comment=$row[4];
+ $length=$row[5];
+ $path=$row[6];
+ $genre=$row[7];
+
+ // Mode
+ if ($row[9]=="vbr") {$mode="vs";}
+ elseif ($row[9]=="cbr") {$mode="fs";}
+ else {$mode="xx";}
+
+ $bitrate=$mode.round($row[8]/1000);
+ $duration=$row[10]*1000;
+ $tracknr=$row[11];
+
+ // Codec
+ if (preg_match("/\.mp3$/i",$path)) {$codec="mp3";}
+ elseif (preg_match("/\.wma$/i",$path)) {$codec="wma";}
+ else {$codec="xxx";}
+
+
+ // LET'S OUTPUT SOME DWORDS!
+
+ print rio_tagdata(0,hexdec($queryOpts));
+ print rio_tagdata(1,$title);
+ print rio_tagdata(2,$artist);
+ print rio_tagdata(3,$source);
+ print rio_tagdata(4,$year);
+ print rio_tagdata(5,$comment);
+ print rio_tagdata(6,$length);
+ print rio_tagdata(7,"tune");
+ print rio_tagdata(8,$path);
+ print rio_tagdata(9,$genre);
+ print rio_tagdata(10,$bitrate);
+ //print rio_tagdata(11,$);$playlist // <--- I still don't know what this is for
+ print rio_tagdata(12,$codec);
+ //print rio_tagdata(13,$);$offset // <--- Not needed
+ print rio_tagdata(14,$duration);
+ print rio_tagdata(15,$tracknr);
+ print pack("C",255); // EOF
+
+ } elseif (rio_isPlaylist($queryOpts)) {
+
+ // select="name"
+
+ // Process query & populate variables
+
+ $title=$row[0];
+
+ $sql2="SELECT COUNT(*) FROM playlist_data WHERE playlist='$playlist'";
+
+ // Process query 2 & populate variables
+ $row = mysql_fetch_row(mysql_query($sql2));
+
+ $length=$row[0]*4; // Don't ask me why, it just is.
+
+ // LET'S OUTPUT SOME DWORDS!
+
+ print rio_tagdata(0,hexdec($queryOpts));
+ print rio_tagdata(1,$title);
+ print rio_tagdata(7,"playlist");
+ print rio_tagdata(6,$length);
+ print pack("C",255); // EOF
+
+ }
+
+ } elseif ($queryType=="content") {
+
+ if ($queryOpts==100) {
+
+ // Add "Special" Playlists to the list
+ if ($firstRun) {
+
+ $line=0;
+
+ // Special Playlists
+
+ // FID 1
+ if ($playlist_global_most_popular_songs) {
+
+ print rio_special2fid(1)."=PPopular Songs (Global)\n";
+ $line++;
+ }
+
+ // FID 2
+ if ($playlist_user_most_popular_songs && $track_receiver_stats) {
+ print rio_special2fid(2)."=PPopular Songs (User)\n";
+ $line++;
+ }
+
+ // FIDs 100 thru 199
+ if ($playlist_newest_albums) {
+
+ if ($playlist_dividers && $line) {print "20000000=P------------------------------\n";}
+
+ $sql="SELECT DISTINCT album.name FROM song,album WHERE song.album=album.id ORDER BY song.addition_time DESC LIMIT $playlist_newest_albums";
+
+
+ $index=100;
+
+ $db_results2 = mysql_query($sql);
+
+ while ($row2=mysql_fetch_row($db_results2)) {
+
+ print rio_special2fid($index)."=Pnew-$row2[0]\n";
+ $index++;
+ $line++;
+ }
+ }
+
+ // FIDs 200 thru 299
+ if ($playlist_global_most_popular_albums) {
+
+ if ($playlist_dividers && $line) {print "20000000=P------------------------------\n";}
+
+ $sql="SELECT album.name,sum(object_count.count) AS total_count FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $favorites_global_most_popular_albums";
+
+ $index=200;
+
+ $db_results2 = mysql_query($sql);
+
+ while ($row2=mysql_fetch_row($db_results2)) {
+
+ print rio_special2fid($index)."=PgAl-$row2[0]\n";
+ $index++;
+ $line++;
+ }
+ }
+
+ // FIDs 300 thru 399
+ if ($playlist_user_most_popular_albums) {
+
+ if ($playlist_dividers && $line) {print "20000000=P------------------------------\n";}
+
+ $sql="SELECT album.name FROM object_count LEFT JOIN album ON object_count.object_id=album.id WHERE object_count.object_type='album' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $favorites_user_most_popular_albums";
+
+ $index=300;
+
+ $db_results2 = mysql_query($sql);
+
+ while ($row2=mysql_fetch_row($db_results2)) {
+
+ print rio_special2fid($index)."=PuAl-$row2[0]\n";
+ $index++;
+ $line++;
+ }
+ }
+
+ // FIDs 400 thru 499
+ if ($playlist_global_most_popular_artists) {
+
+ if ($playlist_dividers && $line) {print "20000000=P------------------------------\n";}
+
+ $sql="SELECT artist.name,sum(object_count.count) AS total_count FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' GROUP BY object_count.object_id ORDER BY total_count DESC LIMIT $favorites_global_most_popular_artists";
+
+ $index=400;
+
+ $db_results2 = mysql_query($sql);
+
+ while ($row2=mysql_fetch_row($db_results2)) {
+
+ print rio_special2fid($index)."=PgAr-$row2[0]\n";
+ $index++;
+ $line++;
+ }
+ }
+
+ // FIDs 500 thru 599
+ if ($playlist_user_most_popular_artists) {
+
+ if ($playlist_dividers && $line) {print "20000000=P------------------------------\n";}
+
+ $sql="SELECT artist.name FROM object_count LEFT JOIN artist ON object_count.object_id=artist.id WHERE object_count.object_type='artist' AND object_count.userid='$ampacheUserID' ORDER BY object_count.count DESC LIMIT $favorites_user_most_popular_artists";
+
+ $index=500;
+
+ $db_results2 = mysql_query($sql);
+
+ while ($row2=mysql_fetch_row($db_results2)) {
+
+ print rio_special2fid($index)."=PuAr-$row2[0]\n";
+ $index++;
+ $line++;
+ }
+ }
+
+ if ($playlist_dividers && $line) {print "20000000=P------------------------------\n";}
+
+ }
+
+
+ // select="id,name"
+ print rio_playlist2fid($row[0])."=P$row[1]\n";
+
+ } elseif (rio_isTrack($queryOpts)) {
+
+ // select="file"
+
+ $file=$row[0];
+
+ // open the file in binary mode
+ $fp = fopen($file, 'rb');
+
+ $filesize=filesize($file);
+ $length=$filesize;
+
+ // Handle range headers a-b only right now
+ if (preg_match("/^bytes=(\d+)-(\d*)$/",$_SERVER['HTTP_RANGE'],$matches)) {
+
+ $isRange=1;
+
+ $start=$matches[1];
+ $end=$matches[2];
+
+ // Stupid Receiver requests data beyond EOF & apache drops the connection and the receiver HANGS...
+ if ($end < $filesize) {
+ $length=$end-$start+1;
+ } else {
+ $length=$filesize-$start;
+ $end=$filesize;
+ }
+
+ }
+
+
+ // Update ampache last_seen and favorites only after a certain number of bytes,
+ // To compensate for fast forwarding to a specific song
+
+ if ($start >= $countTrackBytes && $start < $countTrackBytes+32768) {
+
+ //START 1.1 MODIFICATION
+ // Modified for compatibility with Ampache v.3.2.1
+
+ if ($ampacheUserID!="") {
+
+ // last seen
+ mysql_query("UPDATE user SET last_seen='" . time() . "' WHERE id='$userID[0]'");
+
+ //user stats
+
+ //update_user_stats($ampacheUserID, $queryFilter);
+
+ // Ampache went to user classes so i have to do this...
+ $user = new User($ampacheUsername,$userID[0]);
+ $user->update_stats($queryFilter);
+
+ // END 1.1 MODIFICATION
+
+ }
+
+ }
+
+
+ // open the file in binary mode
+ $fh = fopen($file, 'rb');
+
+ // seek to the offset if Ranged
+ if ($isRange) {fseek($fh,$start);}
+
+ // read in the data
+ $contents = fread($fh,$length);
+
+ // close file
+ fclose($fh);
+
+ // Make sure the output buffer is empty
+ ob_clean();
+
+ // Set Headers
+ header("Content-Type: audio/mpeg");
+ header("Content-Length: ".$length);
+ if ($isRange) {header("Content-Range: bytes ".$start."-"."$end");}
+
+ // print data block
+ print $contents;
+
+ // Flush the output buffer
+ ob_end_flush();
+
+ // End
+ exit;
+
+
+ } elseif (rio_isPlaylist($queryOpts) || rio_isSpecial($queryOpts)) {
+
+ // select="song.id,song.title"
+ print rio_track2fid($row[0])."=T$row[1]\n";
+
+ }
+
+ } elseif ($queryType=="list") {
+
+ // select="song.id,song.size"
+ print rio_track2pfid($row[0]).pack("L2",$row[1],$mpeg_data_offset);
+
+ }
+
+ // Flag to indicate that a loop has already iterated once
+ $firstRun=0;
+
+ } // end while
+
+
+
+
+
+// Send the Content-Length header to disable Chunked Transfer Encoding
+header('Content-Length: '.strlen(ob_get_contents()));
+
+// This is where the page actually gets written
+ob_end_flush();
+
+
+
+
+
+// ================================================================================================
+// FUNCTIONS
+// ================================================================================================
+// These conversion functions compensate for the fact that rio FIDs could be either a track or
+// playlist based on their range, as well as deal with the special '100' FID. It offsets all Track
+// IDs by 101 and offsets all playlist IDs by 268,435,355, chosen simply so that you could tell a
+// playlist by looking at it's hex value. There are also "Special" FIDs, which handle new functionality
+// list dynamic playlists.
+
+
+// A FID can be up to 0xFFFFFFFF
+// ID 0xFID
+// Track 0 = 0x00000065
+// Track 268,435,253 = 0x0FFFFF9a
+// Playlist 0 = 0x10000000
+// Playlist 268,435,354 = 0x1FFFFF9a
+// Special 0 = 0x20000000
+// Special 3,758,096,383 = 0xFFFFFFFF
+
+
+
+
+
+// Track Functions
+// ---------------
+
+function rio_track2pfid ($track) {
+ global $track_add;
+ return pack("L",$track+$track_add);
+}
+
+function rio_pfid2track ($pfid) {
+ global $track_add;
+ $array=unpack("L*",$pfid);
+ return $array[1]-$track_add;
+}
+
+function rio_track2fid ($track) {
+ global $track_add;
+ return dechex($track+$track_add);
+}
+
+function rio_fid2track ($fid) {
+ global $track_add;
+ return hexdec($fid)-$track_add;
+}
+
+
+
+
+
+// Playlist Functions
+// ------------------
+
+function rio_playlist2pfid ($playlist) {
+ global $playlist_add;
+ return rio_track2pfid($playlist+$playlist_add);
+}
+
+function rio_pfid2playlist ($pfid) {
+ global $playlist_add;
+ return rio_pfid2track($pfid)-$playlist_add;
+}
+
+function rio_playlist2fid ($playlist) {
+ global $playlist_add;
+ return rio_track2fid($playlist+$playlist_add);
+}
+
+function rio_fid2playlist ($fid) {
+ global $playlist_add;
+ return rio_fid2track($fid)-$playlist_add;
+}
+
+
+
+
+
+// Special Functions
+// -----------------
+
+function rio_special2pfid ($special) {
+ global $special_add;
+ return rio_track2pfid($special+$special_add);
+}
+
+function rio_pfid2special ($pfid) {
+ global $special_add;
+ return rio_pfid2track($pfid)-$special_add;
+}
+
+function rio_special2fid ($special) {
+ global $special_add;
+ return rio_track2fid($special+$special_add);
+}
+
+function rio_fid2special ($fid) {
+ global $special_add;
+ return rio_fid2track($fid)-$special_add;
+}
+
+
+
+
+
+// FID Identification Functions
+// ----------------------------
+
+function rio_isTrack($fid) {
+ global $playlist_add;
+ return hexdec($fid) < $playlist_add;
+}
+
+function rio_isPlaylist($fid) {
+ global $playlist_add;
+ global $special_add;
+ return (hexdec($fid) >= $playlist_add && hexdec($fid) < $special_add);
+}
+
+function rio_isSpecial($fid) {
+ global $special_add;
+ return hexdec($fid) >= $special_add;
+}
+
+
+
+
+
+// encode tag data
+// ---------------
+
+function rio_tagdata($key,$data) {
+
+ $data=trim($data);
+
+ if ($data=="") {
+
+ return;
+
+ } else {
+
+ return pack("CCa*",$key,strlen($data),$data);
+
+ }
+}
+
+
+
+
+
+?>
diff --git a/contrib/plugins/RioPlayer/ssdp.pl b/contrib/plugins/RioPlayer/ssdp.pl
new file mode 100755
index 00000000..d2023d47
--- /dev/null
+++ b/contrib/plugins/RioPlayer/ssdp.pl
@@ -0,0 +1,105 @@
+#!/usr/bin/perl -T -w
+
+#
+# UDP server for auto-configuration of Dell Audio Radio.
+#
+# Listens on UDP port 21075 for SSDP requests and
+# replies to point the client box to the appropriate
+# server.
+#
+# Typically put this is /etc/ssdp.pl and run it from
+# /etc/rc.d/rc.local (or equiv). This runs as a
+# daemon and consumes few resources. The client box
+# sends out a couple of requests when it powers up
+# and none otherwise.
+#
+
+use strict;
+use POSIX;
+use IO::Socket;
+
+$ENV{PATH} = "/usr/bin:/bin:/usr/local/bin";
+$ENV{BASH_ENV} = "/root/.bashrc";
+
+sub mlog($) {
+ my $msg = shift;
+ system("logger -t SSDP \"$msg\"");
+}
+
+sub mdie($) {
+ my $msg = shift;
+ mlog $msg;
+ exit 1;
+}
+
+sub sig_handle($) {
+ mdie "SSDP server exit on signal";
+}
+
+# $mserve_ip must be a dotted-quad unless you modify the client
+# NFS image to include /etc/resolv.conf.
+#
+my $ssdp_port = 21075;
+my $mserve_ip = "10.60.60.16"; # web and NFS server IP address
+my $mserve_port = "80"; # web server port
+
+#
+# The box makes two different requests. One comes from the kernel
+# during initial booting, the second comes from the player application
+# after the second boot when the player starts.
+#
+# The respones are different for Linux. If there is a port number
+# on the first "linux" request then the client box will use that port
+# for portmapper lookups, which is generally bad when talking to
+# another linux box.
+#
+# The second "player" response includes a port number that indicates
+# the port number to use for HTTP music related requests. I use
+# port 81 and setup a virtual server in Apache to respond to music
+# requests, but you may want to do this differently.
+#
+my $player_request = "^upnp:uuid:1D274DB0-F053-11d3-BF72-0050DA689B2F";
+my $linux_request = "^upnp:uuid:1D274DB1-F053-11d3-BF72-0050DA689B2F";
+
+my (
+ $pid, # PID of server
+ $server, # Handle for server socket
+ $him, # peer making UDP request
+ $datagram, # Packet from client
+);
+
+#
+# Cruft to become a daemon
+#
+$pid = fork;
+exit if $pid;
+mlog "SSDP server started";
+mdie "Could not fork: $!" unless defined($pid);
+POSIX::setsid() or mdie "Cannot start new session: $!";
+$SIG{INT} = \&sig_handle;
+$SIG{TERM} = \&sig_handle;
+$SIG{HUP} = \&sig_handle;
+$0 = "ssdp";
+
+#
+# Get a socket to be a UDP server
+$server = IO::Socket::INET->new(LocalPort => $ssdp_port,
+ Proto => "udp")
+ or mdie "Couldn't be a udp server on port $ssdp_port : $@\n";
+
+#
+# Wait for requests and respond if appropriate
+#
+while ($him = $server->recv($datagram, 256, 0)) {
+ my ($port, $iaddr) = sockaddr_in($server->peername);
+ my $peer = inet_ntoa($iaddr);
+ $datagram =~ s/\n//g;
+ if ($datagram =~ $linux_request) {
+ mlog "Linux request from $peer.";
+ $server->send("http://$mserve_ip/descriptor.xml\n");
+ }
+ if ($datagram =~ $player_request) {
+ mlog "Player request from $peer.";
+ $server->send("http://$mserve_ip:$mserve_port/descriptor.xml\n");
+ }
+}