summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAfterster <afterster@gmail.com>2013-10-31 22:58:06 +0100
committerPaul Arthur <paul.arthur@flowerysong.com>2013-11-05 20:40:13 -0500
commite0f72082a0b6ad9b46f3d8a9e2ede615a6500de1 (patch)
tree92bb0cf8d0ae0442508f6a1080784770be8b2950 /lib
parent76bca9785acd884b6a1984f1830effa2a0d6a2da (diff)
downloadampache-e0f72082a0b6ad9b46f3d8a9e2ede615a6500de1.tar.gz
ampache-e0f72082a0b6ad9b46f3d8a9e2ede615a6500de1.tar.bz2
ampache-e0f72082a0b6ad9b46f3d8a9e2ede615a6500de1.zip
Add Subsonic API
Diffstat (limited to 'lib')
-rw-r--r--lib/class/subsonic_api.class.php1077
-rw-r--r--lib/class/subsonic_xml_data.class.php407
2 files changed, 1484 insertions, 0 deletions
diff --git a/lib/class/subsonic_api.class.php b/lib/class/subsonic_api.class.php
new file mode 100644
index 00000000..4ae1c679
--- /dev/null
+++ b/lib/class/subsonic_api.class.php
@@ -0,0 +1,1077 @@
+<?php
+/* vim:set softtabstop=4 shiftwidth=4 expandtab: */
+/**
+ *
+ * LICENSE: GNU General Public License, version 2 (GPLv2)
+ * Copyright 2001 - 2013 Ampache.org
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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.
+ *
+ */
+
+/**
+ * Subsonic Class
+ *
+ * This class wrap Ampache to Subsonic API functions. See http://www.subsonic.org/pages/api.jsp
+ * These are all static calls.
+ *
+ */
+class Subsonic_Api {
+
+ /**
+ * constructor
+ * This really isn't anything to do here, so it's private
+ */
+ private function __construct() {
+
+ }
+
+ public static function check_version($input, $version = "1.0.0", $addheader = false) {
+ if (version_compare($input['v'], $version) < 0) {
+ ob_end_clean();
+ if ($addheader) header("Content-type: text/xml; charset=" . Config::get('site_charset'));
+ echo Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_APIVERSION_CLIENT)->asXml();
+ exit;
+ }
+ }
+
+ public static function check_parameter($parameter, $addheader = false) {
+ if (empty($parameter)) {
+ ob_end_clean();
+ if ($addheader) header("Content-type: text/xml; charset=" . Config::get('site_charset'));
+ echo Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_MISSINGPARAM)->asXml();
+ exit;
+ }
+ }
+
+ public static function follow_stream($url) {
+ // Stream media, easier to redirect to the dedicated page
+ header("Location: " . $url);
+ }
+
+
+ /**
+ * ping
+ * Simple server ping to test connectivity with the server.
+ * Takes no parameter.
+ */
+ public static function ping($input) {
+ self::check_version($input);
+
+ echo Subsonic_XML_Data::createSuccessResponse()->asXml();
+ }
+
+ /**
+ * getLicense
+ * Get details about the software license. Always return a valid default license.
+ * Takes no parameter.
+ */
+ public static function getlicense($input) {
+ self::check_version($input);
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addLicense($r);
+ echo $r->asXml();
+ }
+
+ /**
+ * getMusicFolders
+ * Get all configured top-level music folders (= ampache catalogs).
+ * Takes no parameter.
+ */
+ public static function getmusicfolders($input) {
+ self::check_version($input);
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addMusicFolders($r, Catalog::get_catalogs());
+ echo $r->asXml();
+ }
+
+ /**
+ * getIndexes
+ * Get an indexed structure of all artists.
+ * Takes optional musicFolderId and optional ifModifiedSince in parameters.
+ */
+ public static function getindexes($input) {
+ self::check_version($input);
+
+ $musicFolderId = $input['musicFolderId'];
+ $ifModifiedSince = $input['ifModifiedSince'];
+
+ $catalogs = array();
+ if (!empty($musicFolderId)) {
+ $catalogs[] = $musicFolderId;
+ } else {
+ $catalogs = Catalog::get_catalogs();
+ }
+
+ $lastmodified = 0;
+ $fcatalogs = array();
+
+ foreach($catalogs as $id) {
+ $clastmodified = 0;
+ $catalog = new Catalog($id);
+
+ if ($catalog->last_update > $clastmodified) $clastmodified = $catalog->last_update;
+ if ($catalog->last_add > $clastmodified) $clastmodified = $catalog->last_add;
+ if ($catalog->last_clean > $clastmodified) $clastmodified = $catalog->last_clean;
+
+ if ($clastmodified > $lastmodified) $lastmodified = $clastmodified;
+ if (!empty($ifModifiedSince) && $clastmodified > $ifModifiedSince) $fcatalogs[] = $id;
+ }
+ if (empty($ifModifiedSince)) $fcatalogs = $catalogs;
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ if (count($fcatalogs) > 0) {
+ $artists = Catalog::get_artists($fcatalogs);
+ Subsonic_XML_Data::addArtistsIndexes($r, $artists, $lastmodified);
+ }
+ echo $r->asXml();
+ }
+
+ /**
+ * getMusicDirectory
+ * Get a list of all files in a music directory.
+ * Takes the directory id in parameters.
+ */
+ public static function getmusicdirectory($input) {
+ self::check_version($input);
+
+ $id = $input['id'];
+ self::check_parameter($id);
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ if (Subsonic_XML_Data::isArtist($id)) {
+ $artist = new Artist(Subsonic_XML_Data::getAmpacheId($id));
+ Subsonic_XML_Data::addArtistDirectory($r, $artist);
+ }
+ else if(Subsonic_XML_Data::isAlbum($id)) {
+ $album = new Album(Subsonic_XML_Data::getAmpacheId($id));
+ Subsonic_XML_Data::addAlbumDirectory($r, $album);
+ }
+ echo $r->asXml();
+ }
+
+ /**
+ * getGenres
+ * Get all genres.
+ * Takes no parameter.
+ */
+ public static function getgenres($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addGenres($r, Tag::get_tags());
+ echo $r->asXml();
+ }
+
+ /**
+ * getArtists
+ * Get all artists.
+ * Takes no parameter.
+ */
+ public static function getartists($input) {
+ self::check_version($input, "1.8.0");
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ $artists = Catalog::get_artists(Catalog::get_catalogs());
+ Subsonic_XML_Data::addArtistsRoot($r, $artists);
+ echo $r->asXml();
+ }
+
+ /**
+ * getArtist
+ * Get details fro an artist, including a list of albums.
+ * Takes the artist id in parameter.
+ */
+ public static function getartist($input) {
+ self::check_version($input, "1.8.0");
+
+ $artistid = $input['id'];
+ self::check_parameter($artistid);
+
+ $artist = new Artist(Subsonic_XML_Data::getAmpacheId($artistid));
+ if (empty($artist->name)) {
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND, "Artist not found.");
+ } else {
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addArtist($r, $artist, true, true);
+ }
+ echo $r->asXml();
+ }
+
+ /**
+ * getAlbum
+ * Get details for an album, including a list of songs.
+ * Takes the album id in parameter.
+ */
+ public static function getalbum($input) {
+ self::check_version($input, "1.8.0");
+
+ $albumid = $input['id'];
+ self::check_parameter($albumid);
+
+ $album = new Album(Subsonic_XML_Data::getAmpacheId($albumid));
+ if (empty($album->name)) {
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND, "Album not found.");
+ } else {
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addAlbum($r, $album, true);
+ }
+
+ echo $r->asXml();
+ }
+
+ /**
+ * getVideos
+ * Get all videos.
+ * Takes no parameter.
+ * Not supported yet.
+ */
+ public static function getvideos($input) {
+ self::check_version($input, "1.8.0");
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addVideos($r);
+ echo $r->asXml();
+ }
+
+ /**
+ * getAlbumList
+ * Get a list of random, newest, highest rated etc. albums.
+ * Takes the list type with optional size and offset in parameters.
+ */
+ public static function getalbumlist($input, $elementName="albumList") {
+ self::check_version($input, "1.2.0");
+
+ $type = $input['type'];
+ self::check_parameter($type);
+
+ $size = $input['size'];
+ $offset = $input['offset'];
+
+ $albums = array();
+ if ($type == "random") {
+ $albums = Album::get_random($size);
+ } else if ($type == "newest") {
+ $albums = Stats::get_newest("album", $size, $offset);
+ } else if ($type == "highest") {
+ $albums = Rating::get_highest("album", $size, $offset);
+ } else if ($type == "frequent") {
+ $albums = Stats::get_top("album", $size, '', $offset);
+ } else if ($type == "recent") {
+ $albums = Stats::get_recent("album", $size, $offset);
+ }
+
+ if (count($albums)) {
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addAlbumList($r, $albums, $elementName);
+ } else {
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ }
+
+ echo $r->asXml();
+ }
+
+ /**
+ * getAlbumList2
+ * See getAlbumList.
+ */
+ public static function getalbumlist2($input) {
+ self::check_version($input, "1.8.0");
+ self::getAlbumList($input, "albumList2");
+ }
+
+ /**
+ * getRandomSongs
+ * Get random songs matching the given criteria.
+ * Takes the optional size, genre, fromYear, toYear and music folder id in parameters.
+ */
+ public static function getrandomsongs($input) {
+ self::check_version($input, "1.2.0");
+
+ $size = $input['size'];
+ if (!$size) $size = 10;
+ $genre = $input['genre'];
+ $fromYear = $input['fromYear'];
+ $toYear = $input['toYear'];
+ $musicFolderId = $input['musicFolderId'];
+
+ $search = array();
+ $search['limit'] = $size;
+ $search['random'] = $size;
+ $search['type'] = "song";
+ $i = 0;
+ if ($genre) {
+ $search['rule_'.$i.'_input'] = $genre;
+ $search['rule_'.$i.'_operator'] = 0;
+ $search['rule_'.$i.''] = "tag";
+ ++$i;
+ }
+ if ($fromYear) {
+ $search['rule_'.$i.'_input'] = $fromYear;
+ $search['rule_'.$i.'_operator'] = 0;
+ $search['rule_'.$i.''] = "year";
+ ++$i;
+ }
+ if ($toYear) {
+ $search['rule_'.$i.'_input'] = $toYear;
+ $search['rule_'.$i.'_operator'] = 1;
+ $search['rule_'.$i.''] = "year";
+ ++$i;
+ }
+ if ($musicFolderId) {
+ if (Subsonic_XML_Data::isArtist($musicFolderId)) {
+ $artist = new Artist(Subsonic_XML_Data::getAmpacheId($musicFolderId));
+ $finput = $artist->name;
+ $ftype = "artist";
+ } else if (Subsonic_XML_Data::isAlbum($musicFolderId)) {
+ $album = new Album(Subsonic_XML_Data::getAmpacheId($musicFolderId));
+ $finput = $album->name;
+ $ftype = "artist";
+ }
+ $search['rule_'.$i.'_input'] = $finput;
+ $search['rule_'.$i.'_operator'] = 4;
+ $search['rule_'.$i.''] = $ftype;
+ ++$i;
+ }
+ $songs = Random::advanced("song", $search);
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addRandomSongs($r, $songs);
+ echo $r->asXml();
+ }
+
+ /**
+ * getSongsByGenre
+ * Get songs in a given genre.
+ * Takes the genre with optional count and offset in parameters.
+ */
+ public static function getsongsbygenre($input) {
+ self::check_version($input, "1.9.0");
+
+ $genre = $input['genre'];
+ self::check_parameter($genre);
+ $count = $input['count'];
+ $offset = $input['offset'];
+
+ $tag = Tag::construct_from_name($genre);
+ if ($tag->id) {
+ $songs = Tag::get_tag_objects("song", $tag->id, $count, $offset);
+ }
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addSongsByGenre($r, $songs);
+ echo $r->asXml();
+ }
+
+ /**
+ * getNowPlaying
+ * Get what is currently being played by all users.
+ * Takes no parameter.
+ */
+ public static function getnowplaying($input) {
+ self::check_version($input);
+
+ $data = Stream::get_now_playing();
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addNowPlaying($r, $data);
+ echo $r->asXml();
+ }
+
+ /**
+ * search2
+ * Get albums, artists and songs matching the given criteria.
+ * Takes query with optional artist count, artist offset, album count, album offset, song count and song offset in parameters.
+ */
+ public static function search2($input, $elementName="searchResult2") {
+ self::check_version($input, "1.2.0");
+
+ $query = $input['query'];
+ self::check_parameter($query);
+
+ $artistCount = $input['artistCount'];
+ $artistOffset = $input['artistOffset'];
+ $albumCount = $input['albumCount'];
+ $albumOffset = $input['albumOffset'];
+ $songCount = $input['songCount'];
+ $songOffset = $input['songOffset'];
+
+ $sartist = array();
+ $sartist['limit'] = $artistCount;
+ if ($artistOffset) $sartist['offset'] = $artistOffset;
+ $sartist['rule_1_input'] = $query;
+ $sartist['rule_1_operator'] = 0;
+ $sartist['rule_1'] = "name";
+ $sartist['type'] = "artist";
+ $artists = Search::run($sartist);
+
+ $salbum = array();
+ $salbum['limit'] = $albumCount;
+ if ($albumOffset) $salbum['offset'] = $albumOffset;
+ $salbum['rule_1_input'] = $query;
+ $salbum['rule_1_operator'] = 0;
+ $salbum['rule_1'] = "title";
+ $salbum['type'] = "album";
+ $albums = Search::run($salbum);
+
+ $ssong = array();
+ $ssong['limit'] = $songCount;
+ if ($songOffset) $ssong['offset'] = $songOffset;
+ $ssong['rule_1_input'] = $query;
+ $ssong['rule_1_operator'] = 0;
+ $ssong['rule_1'] = "anywhere";
+ $ssong['type'] = "song";
+ $songs = Search::run($ssong);
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addSearchResult($r, $artists, $albums, $songs, $elementName);
+ echo $r->asXml();
+ }
+
+ /**
+ * search3
+ * See search2.
+ */
+ public static function search3($input) {
+ self::check_version($input, "1.8.0");
+ self::search2($input, "searchResult3");
+ }
+
+ /**
+ * getPlaylists
+ * Get all playlists a user is allowed to play.
+ * Takes optional user in parameter.
+ */
+ public static function getplaylists($input) {
+ self::check_version($input);
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ $username = $input['username'];
+
+ // Don't allow playlist listing for another user
+ if (empty($username) || $username == $GLOBALS['user']->username) {
+ Subsonic_XML_Data::addPlaylists($r, Playlist::get_playlists());
+ } else {
+ $user = User::get_from_username($username);
+ if ($user->id) {
+ Subsonic_XML_Data::addPlaylists($r, Playlist::get_users($user->id));
+ } else {
+ Subsonic_XML_Data::addPlaylists($r, array());
+ }
+ }
+ echo $r->asXml();
+ }
+
+ /**
+ * getPlaylist
+ * Get the list of files in a saved playlist.
+ * Takes the playlist id in parameters.
+ */
+ public static function getplaylist($input) {
+ self::check_version($input);
+
+ $playlistid = $input['id'];
+ self::check_parameter($playlistid);
+
+ $playlist = new Playlist($playlistid);
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addPlaylist($r, $playlist, true);
+ echo $r->asXml();
+ }
+
+ /**
+ * createPlaylist
+ * Create (or updates) a playlist.
+ * Takes playlist id in parameter if updating, name in parameter if creating and a list of song id for the playlist.
+ */
+ public static function createplaylist($input) {
+ self::check_version($input, "1.2.0");
+
+ $playlistId = $input['playlistId'];
+ $name = $input['name'];
+ $songId = $input['songId'];
+
+ if ($playlistId) {
+ self::_updatePlaylist($playlistId, $name, $songId);
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ } else if (!empty($name)) {
+ $playlistId = Playlist::create($name, 'public');
+ if (count($songId) > 0) {
+ self::_updatePlaylist($playlistId, "", $songId);
+ }
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ } else {
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_MISSINGPARAM);
+ }
+ echo $r->asXml();
+ }
+
+ private static function _updatePlaylist($id, $name, $songsIdToAdd = array(), $songIndexToRemove = array(), $public = true) {
+ $playlist = new Playlist($id);
+
+ $newdata = array();
+ $newdata['name'] = (!empty($name)) ? $name : $playlist->name;
+ $newdata['pl_type'] = ($public) ? "public" : "private";
+ $playlist->update($newdata);
+
+ if (is_array($songsIdToAdd) && count($songsIdToAdd) > 0) {
+ $playlist->add_songs(Subsonic_XML_Data::getAmpacheIds($songsIdToAdd));
+ }
+
+ if (is_array($songIndexToRemove) && count($songIndexToRemove) > 0) {
+ $tracks = Subsonic_XML_Data::getAmpacheIds($songIndexToRemove);
+ foreach ($tracks as $track) {
+ $playlist->delete_track_number($track);
+ }
+ }
+ }
+
+ /**
+ * updatePlaylist
+ * Update a playlist.
+ * Takes playlist id in parameter with optional name, comment, public level and a list of song id to add/remove.
+ */
+ public static function updateplaylist($input) {
+ self::check_version($input, "1.8.0");
+
+ $playlistId = $input['playlistId'];
+ self::check_parameter($playlistId);
+
+ $name = $input['name'];
+ $comment = $input['comment']; // Not supported.
+ $public = boolean($input['public']);
+ echo $public;
+ $songIdToAdd = $input['songIdToAdd'];
+ $songIndexToRemove = $input['songIndexToRemove'];
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ echo $r->asXml();
+ }
+
+ /**
+ * deletePlaylist
+ * Delete a saved playlist.
+ * Takes playlist id in parameter.
+ */
+ public static function deleteplaylist($input) {
+ self::check_version($input, "1.2.0");
+
+ $playlistId = $input['playlistId'];
+ self::check_parameter($playlistId);
+
+ $playlist = new Playlist($playlistId);
+ $playlist->delete();
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ echo $r->asXml();
+ }
+
+ /**
+ * stream
+ * Streams a given media file.
+ * Takes the file id in parameter with optional max bit rate, file format, time offset, size and estimate content length option.
+ */
+ public static function stream($input) {
+ self::check_version($input, "1.0.0", true);
+
+ $fileid = $input['id'];
+ self::check_parameter($fileid, true);
+
+ $maxBitRate = $input['maxBitRate']; // Not supported.
+ $format = $input['format']; // mp3, flv or raw. Not supported.
+ $timeOffset = $input['timeOffset']; // For video streaming. Not supported.
+ $size = $input['size']; // For video streaming. Not supported.
+ $maxBitRate = $input['maxBitRate']; // For video streaming. Not supported.
+ $estimateContentLength = $input['estimateContentLength']; // Not supported.
+
+ $url = Song::play_url(Subsonic_XML_Data::getAmpacheId($fileid));
+ self::follow_stream($url);
+ }
+
+ /**
+ * download
+ * Downloads a given media file.
+ * Takes the file id in parameter.
+ */
+ public static function download($input) {
+ self::check_version($input, "1.0.0", true);
+
+ $fileid = $input['id'];
+ self::check_parameter($fileid, true);
+
+ $url = Song::play_url(Subsonic_XML_Data::getAmpacheId($fileid)) . '&action=download';
+ self::follow_stream($url);
+ }
+
+ /**
+ * hls
+ * Create an HLS playlist.
+ * Takes the file id in parameter with optional max bit rate.
+ */
+ public static function hls($input) {
+ self::check_version($input, "1.8.0", true);
+
+ $fileid = $input['id'];
+ self::check_parameter($fileid, true);
+
+ $bitRate = $input['bitRate']; // Not supported.
+
+ $media = array();
+ $media['object_type'] = 'song';
+ $media['object_id'] = Subsonic_XML_Data::getAmpacheId($fileid);
+
+ $medias = array();
+ $medias[] = $media;
+ $stream = new Stream_Playlist();
+ $stream->add($medias);
+
+ header('Content-Type: application/vnd.apple.mpegurl;');
+ $stream->create_m3u();
+ }
+
+ /**
+ * getCoverArt
+ * Get a cover art image.
+ * Takes the cover art id in parameter.
+ */
+ public static function getcoverart($input) {
+ self::check_version($input, "1.0.0", true);
+
+ $id = $input['id'];
+ self::check_parameter($id, true);
+ $size = $input['size'];
+
+ $art = null;
+ if (Subsonic_XML_Data::isArtist($id)) {
+ $art = new Art(Subsonic_XML_Data::getAmpacheId($id), "artist");
+ } else if (Subsonic_XML_Data::isAlbum($id)) {
+ $art = new Art(Subsonic_XML_Data::getAmpacheId($id), "album");
+ } else if (Subsonic_XML_Data::isSong($id)) {
+ $art = new Art(Subsonic_XML_Data::getAmpacheId($id), "song");
+ }
+
+ if ($art != null) {
+ $art->get_db();
+ if (!$size) {
+ echo $art->raw;
+ } else {
+ $dim = array();
+ $dim['width'] = $size;
+ $dim['height'] = $size;
+ $thumb = $art->get_thumb($dim);
+ echo $thumb['thumb'];
+ }
+ }
+ }
+
+ /**
+ * setRating
+ * Sets the rating for a music file.
+ * Takes the file id and rating in parameters.
+ */
+ public static function setrating($input) {
+ self::check_version($input, "1.6.0");
+
+ $id = $input['id'];
+ self::check_parameter($id);
+ $rating = $input['rating'];
+
+ $robj = null;
+ if (Subsonic_XML_Data::isArtist($id)) {
+ $robj = new Rating(Subsonic_XML_Data::getAmpacheId($id), "artist");
+ } else if (Subsonic_XML_Data::isAlbum($id)) {
+ $robj = new Rating(Subsonic_XML_Data::getAmpacheId($id), "album");
+ } else if (Subsonic_XML_Data::isSong($id)) {
+ $robj = new Rating(Subsonic_XML_Data::getAmpacheId($id), "song");
+ }
+
+ if ($robj != null) {
+ $robj->set_rating($rating);
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ } else {
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND, "Media not found.");
+ }
+
+ echo $r->asXml();
+ }
+
+
+
+ /**** CURRENT UNSUPPORTED FUNCTIONS ****/
+
+ /**
+ * getLyrics
+ * Searches and returns lyrics for a given song.
+ * Takes the optional artist and title in parameters.
+ */
+ public static function getlyrics($input) {
+ self::check_version($input, "1.2.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getStarred
+ * Get starred songs, albums and artists.
+ * Takes no parameter.
+ * Not supported.
+ */
+ public static function getstarred($input, $elementName="starred") {
+ self::check_version($input, "1.8.0");
+
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ Subsonic_XML_Data::addStarred($r, $elementName);
+ echo $r->asXml();
+ }
+
+
+ /**
+ * getStarred2
+ * See getStarred.
+ */
+ public static function getStarred2($input) {
+ self::getStarred($input, "starred2");
+ }
+
+ /**
+ * star
+ * Attaches a star to a song, album or artist.
+ * Takes the optional file id, album id or artist id in parameters.
+ * Not supported.
+ */
+ public static function star($input) {
+ self::check_version($input, "1.8.0");
+
+ // Ignore error
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ echo $r->asXml();
+ }
+
+ /**
+ * unstar
+ * Removes the star from a song, album or artist.
+ * Takes the optional file id, album id or artist id in parameters.
+ * Not supported.
+ */
+ public static function unstar($input) {
+ self::check_version($input, "1.8.0");
+
+ // Ignore error
+ $r = Subsonic_XML_Data::createSuccessResponse();
+ echo $r->asXml();
+ }
+
+ /**
+ * scrobble
+ * Scrobbles a given music file on last.fm.
+ * Takes the file id with optional time and submission parameters.
+ * Not supported.
+ */
+ public static function scrobble($input) {
+ self::check_version($input, "1.5.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getShares
+ * Get information about shared media this user is allowed to manage.
+ * Takes no parameter.
+ * Not supported.
+ */
+ public static function getshares($input) {
+ self::check_version($input, "1.6.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * createShare
+ * Create a public url that can be used by anyone to stream media.
+ * Takes the file id with optional description and expires parameters.
+ * Not supported.
+ */
+ public static function createshare($input) {
+ self::check_version($input, "1.6.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * updateShare
+ * Update the description and/or expiration date for an existing share.
+ * Takes the share id to update with optional description and expires parameters.
+ * Not supported.
+ */
+ public static function updateshare($input) {
+ self::check_version($input, "1.6.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * deleteShare
+ * Delete an existing share.
+ * Takes the share id to delete in parameters.
+ * Not supported.
+ */
+ public static function deleteshare($input) {
+ self::check_version($input, "1.6.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getPodcasts
+ * Get all podcast channels.
+ * Takes the optional includeEpisodes and channel id in parameters
+ * Not supported.
+ */
+ public static function getpodcasts($input) {
+ self::check_version($input, "1.6.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * refreshPodcasts
+ * Request the server to check for new podcast episodes.
+ * Takes no parameters.
+ * Not supported.
+ */
+ public static function refreshpodcasts($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * createPodcastChannel
+ * Add a new podcast channel.
+ * Takes the podcast url in parameter.
+ * Not supported.
+ */
+ public static function createpodcastchannel($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * deletePodcastChannel
+ * Delete an existing podcast channel
+ * Takes the podcast id in parameter.
+ * Not supported.
+ */
+ public static function deletepodcastchannel($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * deletePodcastEpisode
+ * Delete a podcast episode
+ * Takes the podcast episode id in parameter.
+ * Not supported.
+ */
+ public static function deletepodcastepisode($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * downloadPodcastEpisode
+ * Request the server to download a podcast episode
+ * Takes the podcast episode id in parameter.
+ * Not supported.
+ */
+ public static function downloadpodcastepisode($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * jukeboxControl
+ * Control the jukebox.
+ * Takes the action with optional index, offset, song id and volume gain in parameters.
+ * Not supported.
+ */
+ public static function jukeboxcontrol($input) {
+ self::check_version($input, "1.2.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getInternetRadioStations
+ * Get all internet radio stations
+ * Takes no parameter.
+ * Not supported.
+ */
+ public static function getinternetradiostations($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getChatMessages
+ * Get the current chat messages.
+ * Takes no parameter.
+ * Not supported.
+ */
+ public static function getchatmessages($input) {
+ self::check_version($input, "1.2.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * addChatMessages
+ * Add a message to the chat.
+ * Takes the message in parameter.
+ * Not supported.
+ */
+ public static function addchatmessages($input) {
+ self::check_version($input, "1.2.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getUser
+ * Get details about a given user.
+ * Takes the username in parameter.
+ * Not supported.
+ */
+ public static function getuser($input) {
+ self::check_version($input, "1.3.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getUsers
+ * Get details about a given user.
+ * Takes no parameter.
+ * Not supported.
+ */
+ public static function getusers($input) {
+ self::check_version($input, "1.8.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * createUser
+ * Create a new user.
+ * Takes the username, password and email with optional roles in parameters.
+ * Not supported.
+ */
+ public static function createuser($input) {
+ self::check_version($input, "1.1.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * deleteUser
+ * Delete an existing user.
+ * Takes the username in parameter.
+ * Not supported.
+ */
+ public static function deleteuser($input) {
+ self::check_version($input, "1.3.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * changePassword
+ * Change the password of an existing user.
+ * Takes the username with new password in parameters.
+ * Not supported.
+ */
+ public static function changepassword($input) {
+ self::check_version($input, "1.1.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * getBookmarks
+ * Get all user bookmarks.
+ * Takes no parameter.
+ * Not supported.
+ */
+ public static function getbookmarks($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * createBookmark
+ * Creates or updates a bookmark.
+ * Takes the file id and position with optional comment in parameters.
+ * Not supported.
+ */
+ public static function createbookmark($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+
+ /**
+ * deleteBookmark
+ * Delete an existing bookmark.
+ * Takes the file id in parameter.
+ * Not supported.
+ */
+ public static function deletebookmark($input) {
+ self::check_version($input, "1.9.0");
+
+ $r = Subsonic_XML_Data::createError(Subsonic_XML_Data::SSERROR_DATA_NOTFOUND);
+ echo $r->asXml();
+ }
+}
+?>
diff --git a/lib/class/subsonic_xml_data.class.php b/lib/class/subsonic_xml_data.class.php
new file mode 100644
index 00000000..fddd2c1a
--- /dev/null
+++ b/lib/class/subsonic_xml_data.class.php
@@ -0,0 +1,407 @@
+<?php
+/* vim:set softtabstop=4 shiftwidth=4 expandtab: */
+/**
+ *
+ * LICENSE: GNU General Public License, version 2 (GPLv2)
+ * Copyright 2001 - 2013 Ampache.org
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * 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.
+ *
+ */
+
+/**
+ * XML_Data Class
+ *
+ * This class takes care of all of the xml document stuff in Ampache these
+ * are all static calls
+ *
+ */
+class Subsonic_XML_Data {
+
+ const API_VERSION = "1.10.0";
+
+ const SSERROR_GENERIC = 0;
+ const SSERROR_MISSINGPARAM = 10;
+ const SSERROR_APIVERSION_CLIENT = 20;
+ const SSERROR_APIVERSION_SERVER = 30;
+ const SSERROR_BADAUTH = 40;
+ const SSERROR_UNAUTHORIZED = 50;
+ const SSERROR_TRIAL = 60;
+ const SSERROR_DATA_NOTFOUND = 70;
+
+ // Ampache doesn't have a global unique id but each items are unique per category. We use id pattern to identify item category.
+ const AMPACHEID_ARTIST = 100000000;
+ const AMPACHEID_ALBUM = 200000000;
+ const AMPACHEID_SONG = 300000000;
+
+ /**
+ * constructor
+ *
+ * We don't use this, as its really a static class
+ */
+ private function __construct() {
+
+ }
+
+ public static function getArtistId($id) {
+ return $id + Subsonic_XML_Data::AMPACHEID_ARTIST;
+ }
+
+ public static function getAlbumId($id) {
+ return $id + Subsonic_XML_Data::AMPACHEID_ALBUM;
+ }
+
+ public static function getSongId($id) {
+ return $id + Subsonic_XML_Data::AMPACHEID_SONG;
+ }
+
+ public static function getAmpacheId($id) {
+ return ($id % Subsonic_XML_Data::AMPACHEID_ARTIST);
+ }
+
+ public static function getAmpacheIds($ids) {
+ $ampids = array();
+ foreach ($ids as $id) {
+ $ampids[] = self::getAmpacheId($id);
+ }
+ return $ampids;
+ }
+
+ public static function isArtist($id) {
+ return ($id >= Subsonic_XML_Data::AMPACHEID_ARTIST && $id < Subsonic_XML_Data::AMPACHEID_ALBUM);
+ }
+
+ public static function isAlbum($id) {
+ return ($id >= Subsonic_XML_Data::AMPACHEID_ALBUM && $id < Subsonic_XML_Data::AMPACHEID_SONG);
+ }
+
+ public static function isSong($id) {
+ return ($id >= Subsonic_XML_Data::AMPACHEID_SONG);
+ }
+
+ public static function createFailedResponse($version = "") {
+ $response = self::createResponse($version);
+ $response->addAttribute('status', 'failed');
+ return $response;
+ }
+
+ public static function createSuccessResponse($version = "") {
+ $response = self::createResponse($version);
+ $response->addAttribute('status', 'ok');
+ return $response;
+ }
+
+ public static function createResponse($version = "") {
+ if (empty($version)) $version = Subsonic_XML_Data::API_VERSION;
+ $response = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><subsonic-response/>');
+ $response->addAttribute('xmlns', 'http://subsonic.org/restapi');
+ $response->addAttribute('version', $version);
+ return $response;
+ }
+
+ public static function createError($code, $message = "", $version = "") {
+ if (empty($version)) $version = Subsonic_XML_Data::API_VERSION;
+ $response = self::createFailedResponse($version);
+ self::setError($response, $code, $message);
+ return $response;
+ }
+
+ /**
+ * Set error information.
+ *
+ * @param SimpleXMLElement $xml Parent node
+ * @param integer $code Error code
+ * @param string $string Error message
+ */
+ public static function setError($xml, $code, $message = "") {
+
+ $xerr = $xml->addChild('error');
+ $xerr->addAttribute('code', $code);
+
+ if (empty($message)) {
+ switch ($code) {
+ case Subsonic_XML_Data::SSERROR_GENERIC: $message = "A generic error."; break;
+ case Subsonic_XML_Data::SSERROR_MISSINGPARAM: $message = "Required parameter is missing."; break;
+ case Subsonic_XML_Data::SSERROR_APIVERSION_CLIENT: $message = "Incompatible Subsonic REST protocol version. Client must upgrade."; break;
+ case Subsonic_XML_Data::SSERROR_APIVERSION_SERVER: $message = "Incompatible Subsonic REST protocol version. Server must upgrade."; break;
+ case Subsonic_XML_Data::SSERROR_BADAUTH: $message = "Wrong username or password."; break;
+ case Subsonic_XML_Data::SSERROR_UNAUTHORIZED: $message = "User is not authorized for the given operation."; break;
+ case Subsonic_XML_Data::SSERROR_TRIAL: $message = "The trial period for the Subsonic server is over. Please upgrade to Subsonic Premium. Visit subsonic.org for details."; break;
+ case Subsonic_XML_Data::SSERROR_DATA_NOTFOUND: $message = "The requested data was not found."; break;
+ }
+ }
+
+ $xerr->addAttribute("message", $message);
+ }
+
+ public static function addLicense($xml) {
+ $xlic = $xml->addChild('license');
+ $xlic->addAttribute('valid', 'true');
+ $xlic->addAttribute('email', 'webmaster@ampache.org');
+ $xlic->addAttribute('key', 'ABC123DEF');
+ $xlic->addAttribute('date', '2009-09-03T14:46:43');
+ }
+
+ public static function addMusicFolders($xml, $catalogs) {
+ $xfolders = $xml->addChild('musicFolders');
+ foreach($catalogs as $id) {
+ $catalog = new Catalog($id);
+ $xfolder = $xfolders->addChild('musicFolder');
+ $xfolder->addAttribute('id', $id);
+ $xfolder->addAttribute('name', $catalog->name);
+ }
+ }
+
+ public static function addArtistsIndexes($xml, $artists, $lastModified) {
+ $xindexes = $xml->addChild('indexes');
+ $xindexes->addAttribute('lastModified', $lastModified);
+ self::addArtists($xindexes, $artists);
+ }
+
+ public static function addArtistsRoot($xml, $artists) {
+ $xartists = $xml->addChild('artists');
+ self::addArtists($xartists, $artists, true);
+ }
+
+ public static function addArtists($xml, $artists, $extra=false) {
+ $xlastcat = null;
+ $xlastletter = '';
+ foreach ($artists as $artist) {
+ if (strlen($artist->name) > 0) {
+ $letter = strtoupper($artist->name[0]);
+ if ($letter == "X" || $letter == "Y" || $letter == "Z") $letter = "X-Z";
+ else if (!preg_match("/^[A-W]$/", $letter)) $letter = "#";
+
+ if ($letter != $xlastletter) {
+ $xlastletter = $letter;
+ $xlastcat = $xml->addChild('index');
+ $xlastcat->addAttribute('name', $xlastletter);
+ }
+ }
+
+ self::addArtist($xlastcat, $artist, $extra);
+ }
+ }
+
+ public static function addArtist($xml, $artist, $extra=false, $albums=false) {
+ $xartist = $xml->addChild('artist');
+ $xartist->addAttribute('id', self::getArtistId($artist->id));
+ $xartist->addAttribute('name', $artist->name);
+ if ($extra) {
+ //$xartist->addAttribute('coverArt');
+ $xartist->addAttribute('albumCount', count($artist->get_albums()));
+ }
+ if ($albums) {
+ $allalbums = $artist->get_albums();
+ foreach ($allalbums as $id) {
+ $album = new Album($id);
+ self::addAlbum($xartist, $album);
+ }
+ }
+ }
+
+ public static function addAlbumList($xml, $albums, $elementName="albumList") {
+ $xlist = $xml->addChild($elementName);
+ foreach($albums as $id) {
+ $album = new Album($id);
+ self::addAlbum($xlist, $album);
+ }
+ }
+
+ public static function addAlbum($xml, $album, $songs=false, $elementName="album") {
+ $xalbum = $xml->addChild($elementName);
+ $xalbum->addAttribute('id', self::getAlbumId($album->id));
+ $xalbum->addAttribute('name', $album->name);
+ $xalbum->addAttribute('album', $album->name);
+ $xalbum->addAttribute('title', self::formatAlbum($album));
+ $xalbum->addAttribute('isDir', 'true');
+ $album->format();
+ if ($album->has_art) $xalbum->addAttribute('coverArt', self::getAlbumId($album->id));
+ $xalbum->addAttribute('songCount', $album->song_count);
+ $xalbum->addAttribute('duration', $album->total_duration);
+ $xalbum->addAttribute('artistId', self::getArtistId($album->artist_id));
+ $xalbum->addAttribute('parent', self::getArtistId($album->artist_id));
+ $xalbum->addAttribute('artist', $album->artist_name);
+
+ $rating = new Rating($album->id, "album");
+ $rating_value = $rating->get_average_rating();
+ $xalbum->addAttribute('averageRating', ($rating_value) ? $rating_value : 0);
+
+ if ($songs) {
+ $allsongs = $album->get_songs();
+ foreach ($allsongs as $id) {
+ $song = new Song($id);
+ self::addSong($xalbum, $song);
+ }
+ }
+ }
+
+ public static function addSong($xml, $song, $elementName='song') {
+ self::createSong($xml, $song, $elementName);
+ }
+
+ public static function createSong($xml, $song, $elementName='song') {
+ $xsong = $xml->addChild($elementName);
+ $xsong->addAttribute('id', self::getSongId($song->id));
+ $xsong->addAttribute('parent', self::getAlbumId($song->album));
+ //$xsong->addAttribute('created', );
+ $xsong->addAttribute('title', $song->title);
+ $xsong->addAttribute('isDir', 'false');
+ $xsong->addAttribute('isVideo', 'false');
+ $xsong->addAttribute('type', 'music');
+ $album = new Album($song->album);
+ $xsong->addAttribute('albumId', self::getAlbumId($album->id));
+ $xsong->addAttribute('album', $album->name);
+ $artist = new Artist($song->artist);
+ $xsong->addAttribute('artistId', self::getArtistId($album->id));
+ $xsong->addAttribute('artist', $artist->name);
+ $xsong->addAttribute('coverArt', self::getAlbumId($album->id));
+ $xsong->addAttribute('duration', $song->time);
+ $xsong->addAttribute('bitRate', intval($song->bitrate / 1000));
+ $xsong->addAttribute('track', $song->track);
+ $xsong->addAttribute('year', $song->year);
+ $tags = Tag::get_object_tags('song', $song->id);
+ if (count($tags) > 0) $xsong->addAttribute('genre', $tags[0]['name']);
+ $xsong->addAttribute('size', $song->size);
+ if ($album->disk > 0) $xsong->addAttribute('discNumber', $album->disk);
+ $xsong->addAttribute('suffix', $song->type);
+ $xsong->addAttribute('contentType', $song->mime);
+ $xsong->addAttribute('path', $song->file);
+
+ //Do we need to support transcodedContentType and transcodedSuffix attributes?
+
+ return $xsong;
+ }
+
+ private static function formatAlbum($album) {
+ return $album->name . " [" . $album->year . "]";
+ }
+
+ public static function addArtistDirectory($xml, $artist) {
+ $xdir = $xml->addChild('directory');
+ $xdir->addAttribute('id', self::getArtistId($artist->id));
+ $xdir->addAttribute('name', $artist->name);
+
+ $allalbums = $artist->get_albums();
+ foreach ($allalbums as $id) {
+ $album = new Album($id);
+ self::addAlbum($xdir, $album, false, "child");
+ }
+ }
+
+ public static function addAlbumDirectory($xml, $album) {
+ $xdir = $xml->addChild('directory');
+ $xdir->addAttribute('id', self::getAlbumId($album->id));
+ $xdir->addAttribute('name', self::formatAlbum($album));
+ $album->format();
+ //$xdir->addAttribute('parent', self::getArtistId($album->artist_id));
+
+ $allsongs = $album->get_songs();
+ foreach ($allsongs as $id) {
+ $song = new Song($id);
+ self::addSong($xdir, $song, "child");
+ }
+ }
+
+ public static function addGenres($xml, $tags) {
+ $xgenres = $xml->addChild('genres');
+
+ foreach($tags as $tag) {
+ $otag = new Tag($tag['id']);
+ $xgenre = $xgenres->addChild('genre', $otag->name);
+ }
+ }
+
+ public static function addVideos($xml) {
+ // Not supported yet
+ $xvideos = $xml->addChild('videos');
+ }
+
+ public static function addPlaylists($xml, $playlists) {
+ $xplaylists = $xml->addChild('playlists');
+ foreach($playlists as $id) {
+ $playlist = new Playlist($id);
+ self::addPlaylist($xplaylists, $playlist);
+ }
+ }
+
+ public static function addPlaylist($xml, $playlist, $songs=false) {
+ $xplaylist = $xml->addChild('playlist');
+ $xplaylist->addAttribute('id', $playlist->id);
+ $xplaylist->addAttribute('name', $playlist->name);
+ $user = new User($playlist->user);
+ $xplaylist->addAttribute('owner', $user->username);
+ $xplaylist->addAttribute('public', ($playlist->type != "private") ? "true" : "false");
+ $xplaylist->addAttribute('created', date("c", $playlist->date));
+ $xplaylist->addAttribute('songCount', $playlist->get_song_count());
+ $xplaylist->addAttribute('duration', $playlist->get_total_duration());
+
+ if ($songs) {
+ $allsongs = $playlist->get_songs();
+ foreach($allsongs as $id) {
+ $song = new Song($id);
+ self::addSong($xplaylist, $song, "entry");
+ }
+ }
+ }
+
+ public static function addRandomSongs($xml, $songs) {
+ $xsongs = $xml->addChild('randomSongs');
+ foreach ($songs as $id) {
+ $song = new Song($id);
+ self::addSong($xsongs, $song);
+ }
+ }
+
+ public static function addSongsByGenre($xml, $songs) {
+ $xsongs = $xml->addChild('songsByGenre');
+ foreach ($songs as $id) {
+ $song = new Song($id);
+ self::addSong($xsongs, $song);
+ }
+ }
+
+ public static function addNowPlaying($xml, $data) {
+ $xplaynow = $xml->addChild('nowPlaying');
+ foreach($data as $d) {
+ $track = self::createSong($xplaynow, $d['media'], "entry");
+ $track->addAttribute('username', $d['client']->username);
+ $track->addAttribute('minutesAgo', intval(time() - ($d['expire'] - Config::get('stream_length')) / 1000));
+ $track->addAttribute('playerId', $d['agent']);
+ }
+ }
+
+ public static function addSearchResult($xml, $artists, $albums, $songs, $elementName = "searchResult2") {
+ $xresult = $xml->addChild($elementName);
+ foreach ($artists as $id) {
+ $artist = new Artist($id);
+ self::addArtist($xresult, $artist);
+ }
+ foreach ($albums as $id) {
+ $album = new Album($id);
+ self::addAlbum($xresult, $album);
+ }
+ foreach ($songs as $id) {
+ $song = new Song($id);
+ self::addSong($xresult, $song);
+ }
+ }
+
+ public static function addStarred($xml, $elementName="starred") {
+ $xstarred = $xml->addChild($elementName);
+ }
+}
+
+?>