2
/* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */
7
* LICENSE: GNU General Public License, version 2 (GPLv2)
8
* Copyright (c) 2001 - 2011 Ampache.org All Rights Reserved
10
* This program is free software; you can redistribute it and/or
11
* modify it under the terms of the GNU General Public License
12
* as published by the Free Software Foundation; version 2
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program; if not, write to the Free Software
22
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
* @copyright 2001 - 2011 Ampache.org
26
* @license http://opensource.org/licenses/gpl-2.0 GPLv2
27
* @link http://www.ampache.org/
33
* This class takes care of all of the xml document stuff in Ampache these
34
* are all static calls
37
* @copyright 2001 - 2011 Ampache.org
38
* @license http://opensource.org/licenses/gpl-2.0 GPLv2
39
* @link http://www.ampache.org/
43
// This is added so that we don't pop any webservers
44
private static $limit = '5000';
45
private static $offset = '0';
46
private static $type = '';
51
* We don't use this, as its really a static class
53
private function __construct() {
62
* This takes an int and changes the offset
64
* @param integer $offset (description here...)
67
public static function set_offset($offset) {
69
$offset = intval($offset);
70
self::$offset = $offset;
77
* This sets the limit for any ampache transactions
79
* @param integer $limit (description here...)
82
public static function set_limit($limit) {
84
if (!$limit) { return false; }
86
$limit = intval($limit);
87
self::$limit = $limit;
94
* This sets the type of xmlData we are working on
96
* @param string $type xmlData type
99
public static function set_type($type) {
101
if (!in_array($type,array('rss','xspf','itunes'))) { return false; }
110
* This generates a standard XML Error message
111
* nothing fancy here...
113
* @param integer $code Error code
114
* @param string $string Error message
115
* @return string return error message xml
117
public static function error($code,$string) {
119
$string = self::_header() . "\t<error code=\"$code\"><![CDATA[$string]]></error>" . self::_footer();
127
* This takes two values, first the key second the string
129
* @param string $key (description here...)
130
* @param string $string xml data
131
* @return string return xml
133
public static function single_string($key,$string) {
135
$final = self::_header() . "\t<$key><![CDATA[$string]]></$key>" . self::_footer();
144
* This returns the header
147
* @return string return xml
149
public static function header() {
151
return self::_header();
158
* This returns the footer
161
* @return string return xml
163
public static function footer() {
165
return self::_footer();
172
* This returns the formatted 'tags' string for an xml document
175
private static function tags_string($tags) {
179
if (is_array($tags)) {
181
foreach ($tags as $tag_id => $data) {
182
$tag = new Tag($tag_id);
183
$string .= "\t<tag id=\"" . $tag->id .
184
'" count="' . count($data['users']) .
185
'"><![CDATA[' . $tag->name . "]]></tag>\n";
196
* This will build an xml document from a key'd array,
198
* @param array $array (description here...)
199
* @param boolean $callback (description here...)
200
* @return string return xml
202
public static function keyed_array($array,$callback='') {
207
foreach ($array as $key=>$value) {
209
// See if the key has attributes
210
if (is_array($value) AND isset($value['<attributes>'])) {
211
$attribute = ' ' . $value['<attributes>'];
212
$key = $value['value'];
215
// If it's an array, run again
216
if (is_array($value)) {
217
$value = self::keyed_array($value,1);
218
$string .= "<$key$attribute>\n$value\n</$key>\n";
221
$string .= "\t<$key$attribute><![CDATA[$value]]></$key>\n";
227
$string = self::_header() . $string . self::_footer();
237
* This returns tags to the user, in a pretty xml document with the information
239
* @param array $tags (description here...)
240
* @return string return xml
242
public static function tags($tags) {
244
if (count($tags) > self::$limit OR self::$offset > 0) {
245
$tags = array_splice($tags,self::$offset,self::$limit);
250
foreach ($tags as $tag_id) {
251
$tag = new Tag($tag_id);
252
$counts = $tag->count();
253
$string .= "<tag id=\"$tag_id\">\n" .
254
"\t<name><![CDATA[$tag->name]]></name>\n" .
255
"\t<albums>" . intval($counts['album']) . "</albums>\n" .
256
"\t<artists>" . intval($counts['artist']) . "</artists>\n" .
257
"\t<songs>" . intval($counts['song']) . "</songs>\n" .
258
"\t<videos>" . intval($counts['video']) . "</videos>\n" .
259
"\t<playlists>" . intval($count['playlist']) . "</playlists>\n" .
260
"\t<stream>" . intval($count['live_stream']) . "</stream>\n" .
264
$final = self::_header() . $string . self::_footer();
273
* This takes an array of artists and then returns a pretty xml document with the information
276
* @param array $artists (description here...)
277
* @return string return xml
279
public static function artists($artists) {
281
if (count($artists) > self::$limit OR self::$offset > 0) {
282
$artists = array_splice($artists,self::$offset,self::$limit);
287
Rating::build_cache('artist',$artists);
289
foreach ($artists as $artist_id) {
290
$artist = new Artist($artist_id);
293
$rating = new Rating($artist_id,'artist');
294
$tag_string = self::tags_string($artist->tags);
296
$string .= "<artist id=\"$artist->id\">\n" .
297
"\t<name><![CDATA[$artist->f_full_name]]></name>\n" .
299
"\t<albums>$artist->albums</albums>\n" .
300
"\t<songs>$artist->songs</songs>\n" .
301
"\t<preciserating>" . $rating->preciserating . "</preciserating>\n" .
302
"\t<rating>" . $rating->rating . "</rating>\n" .
304
} // end foreach artists
306
$final = self::_header() . $string . self::_footer();
315
* This echos out a standard albums XML document, it pays attention to the limit
317
* @param array $albums (description here...)
318
* @return string return xml
320
public static function albums($albums) {
322
if (count($albums) > self::$limit OR self::$offset > 0) {
323
$albums = array_splice($albums,self::$offset,self::$limit);
326
Rating::build_cache('album',$albums);
328
foreach ($albums as $album_id) {
329
$album = new Album($album_id);
332
$rating = new Rating($album_id,'album');
334
// Build the Art URL, include session
335
$art_url = Config::get('web_path') . '/image.php?id=' . $album->id . '&auth=' . scrub_out($_REQUEST['auth']);
337
$string .= "<album id=\"$album->id\">\n" .
338
"\t<name><![CDATA[$album->name]]></name>\n";
340
// Do a little check for artist stuff
341
if ($album->artist_count != 1) {
342
$string .= "\t<artist id=\"0\"><![CDATA[Various]]></artist>\n";
345
$string .= "\t<artist id=\"$album->artist_id\"><![CDATA[$album->artist_name]]></artist>\n";
348
$string .= "\t<year>$album->year</year>\n" .
349
"\t<tracks>$album->song_count</tracks>\n" .
350
"\t<disk>$album->disk</disk>\n" .
351
self::tags_string($album->tags) .
352
"\t<art><![CDATA[$art_url]]></art>\n" .
353
"\t<preciserating>" . $rating->preciserating . "</preciserating>\n" .
354
"\t<rating>" . $rating->rating . "</rating>\n" .
358
$final = self::_header() . $string . self::_footer();
367
* This takes an array of playlist ids and then returns a nice pretty XML document
369
* @param array $playlists (description here...)
370
* @return string return xml
372
public static function playlists($playlists) {
374
if (count($playlists) > self::$limit OR self::$offset > 0) {
375
$playlists = array_slice($playlists,self::$offset,self::$limit);
380
// Foreach the playlist ids
381
foreach ($playlists as $playlist_id) {
382
$playlist = new Playlist($playlist_id);
384
$item_total = $playlist->get_song_count();
386
// Build this element
387
$string .= "<playlist id=\"$playlist->id\">\n" .
388
"\t<name><![CDATA[$playlist->name]]></name>\n" .
389
"\t<owner><![CDATA[$playlist->f_user]]></owner>\n" .
390
"\t<items>$item_total</items>\n" .
391
"\t<type>$playlist->type</type>\n" .
397
// Build the final and then send her off
398
$final = self::_header() . $string . self::_footer();
407
* This returns an xml document from an array of song ids spiffy isn't it!
409
* @param array $songs (description here...)
410
* @return string return xml
412
public static function songs($songs) {
414
if (count($songs) > self::$limit OR self::$offset > 0) {
415
$songs = array_slice($songs,self::$offset,self::$limit);
418
Song::build_cache($songs);
419
Stream::set_session($_REQUEST['auth']);
422
foreach ($songs as $song_id) {
423
$song = new Song($song_id);
425
// If the song id is invalid/null
426
if (!$song->id) { continue; }
428
$tag_string = self::tags_string(Tag::get_top_tags('song', $song_id));
429
$rating = new Rating($song_id, 'song');
430
$art_url = Art::url($song->album, 'album', $_REQUEST['auth']);
432
$string .= "<song id=\"$song->id\">\n" .
433
"\t<title><![CDATA[$song->title]]></title>\n" .
434
"\t<artist id=\"" . $song->artist .
435
'"><![CDATA[' . $song->get_artist_name() .
437
"\t<album id=\"" . $song->album .
438
'"><![CDATA[' . $song->get_album_name().
441
"\t<track>$song->track</track>\n" .
442
"\t<time>$song->time</time>\n" .
443
"\t<year>$song->year</year>\n" .
444
"\t<bitrate>$song->bitrate</bitrate>\n".
445
"\t<mode>$song->mode</mode>\n".
446
"\t<mime>$song->mime</mime>\n" .
447
"\t<url><![CDATA[" . Song::play_url($song->id) . "]]></url>\n" .
448
"\t<size>$song->size</size>\n".
449
"\t<mbid>$song->mbid</mbid>\n".
450
"\t<album_mbid>$song->album_mbid</album_mbid>\n".
451
"\t<artist_mbid>$song->artist_mbid</artist_mbid>\n".
452
"\t<art><![CDATA[" . $art_url . "]]></art>\n" .
453
"\t<preciserating>" . $rating->preciserating . "</preciserating>\n" .
454
"\t<rating>" . $rating->rating . "</rating>\n" .
459
$final = self::_header() . $string . self::_footer();
468
* This builds the xml document for displaying video objects
470
* @param array $videos (description here...)
471
* @return string return xml
473
public static function videos($videos) {
475
if (count($videos) > self::$limit OR self::$offset > 0) {
476
$videos = array_slice($videos,self::$offset,self::$limit);
481
foreach ($videos as $video_id) {
482
$video = new Video($video_id);
485
$string .= "<video id=\"$video->id\">\n" .
486
"\t<title><![CDATA[$video->title]]></title>\n" .
487
"\t<mime><![CDATA[$video->mime]]></mime>\n" .
488
"\t<resolution>$video->f_resolution</resolution>\n" .
489
"\t<size>$video->size</size>\n" .
490
self::tags_string($video->tags) .
491
"\t<url><![CDATA[" . Video::play_url($video->id) . "]]></url>\n" .
496
$final = self::_header() . $string . self::_footer();
505
* This handles creating an xml document for democratic items, this can be a little complicated
506
* due to the votes and all of that
508
* @param array $object_ids Object IDs
509
* @return string return xml
511
public static function democratic($object_ids=array()) {
513
if (!is_array($object_ids)) { $object_ids = array(); }
515
$democratic = Democratic::get_current_playlist();
519
foreach ($object_ids as $row_id=>$data) {
520
$song = new $data['object_type']($data['object_id']);
523
//FIXME: This is duplicate code and so wrong, functions need to be improved
526
$tag = new Tag($song->tags['0']);
527
$song->genre = $tag->id;
528
$song->f_genre = $tag->name;
530
$tag_string = self::tags_string($song->tags);
532
$rating = new Rating($song_id,'song');
534
$art_url = Art::url($song->album, 'album', $_REQUEST['auth']);
536
$string .= "<song id=\"$song->id\">\n" .
537
"\t<title><![CDATA[$song->title]]></title>\n" .
538
"\t<artist id=\"$song->artist\"><![CDATA[$song->f_artist_full]]></artist>\n" .
539
"\t<album id=\"$song->album\"><![CDATA[$song->f_album_full]]></album>\n" .
540
"\t<genre id=\"$song->genre\"><![CDATA[$song->f_genre]]></genre>\n" .
542
"\t<track>$song->track</track>\n" .
543
"\t<time>$song->time</time>\n" .
544
"\t<mime>$song->mime</mime>\n" .
545
"\t<url><![CDATA[" . Song::play_url($song->id) . "]]></url>\n" .
546
"\t<size>$song->size</size>\n" .
547
"\t<art><![CDATA[" . $art_url . "]]></art>\n" .
548
"\t<preciserating>" . $rating->preciserating . "</preciserating>\n" .
549
"\t<rating>" . $rating->rating . "</rating>\n" .
550
"\t<vote>" . $democratic->get_vote($row_id) . "</vote>\n" .
555
$final = self::_header() . $string . self::_footer();
564
* (description here...)
566
* @param array $data (descriptiong here...)
567
* @param string $title RSS feed title
568
* @param string $description (not use yet?)
569
* @param string $date publish date
570
* @return string RSS feed xml
572
public static function rss_feed($data,$title,$description,$date) {
574
$string = "\t<title>$title</title>\n\t<link>" . Config::get('web_path') . "</link>\n\t" .
575
"<pubDate>" . date("r",$date) . "</pubDate>\n";
577
// Pass it to the keyed array xml function
578
foreach ($data as $item) {
579
// We need to enclose it in an item tag
580
$string .= self::keyed_array(array('item'=>$item),1);
583
$final = self::_header() . $string . self::_footer();
592
* this returns a standard header, there are a few types
593
* so we allow them to pass a type if they want to
595
* @return string Header xml tag.
597
private static function _header() {
599
switch (self::$type) {
601
$header = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" .
602
"<playlist version = \"1\" xmlns=\"http://xspf.org/ns/0/\">\n " .
603
"<title>Ampache XSPF Playlist</title>\n" .
604
"<creator>" . scrub_out(Config::get('site_title')) . "</creator>\n" .
605
"<annotation>" . scrub_out(Config::get('site_title')) . "</annotation>\n" .
606
"<info>". Config::get('web_path') ."</info>\n" .
610
$header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" .
611
"<!-- XML Generated by Ampache v." . Config::get('version') . " -->\n";
612
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"\n" .
613
"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" .
614
"<plist version=\"1.0\">\n" .
616
" <key>Major Version</key><integer>1</integer>\n" .
617
" <key>Minor Version</key><integer>1</integer>\n" .
618
" <key>Application Version</key><string>7.0.2</string>\n" .
619
" <key>Features</key><integer>1</integer>\n" .
620
" <key>Show Content Ratings</key><true/>\n" .
621
" <key>Tracks</key>\n" .
625
$header = "<?xml version=\"1.0\" encoding=\"" . Config::get('site_charset') . "\" ?>\n " .
626
"<!-- RSS Generated by Ampache v." . Config::get('version') . " on " . date("r",time()) . "-->\n" .
627
"<rss version=\"2.0\">\n<channel>\n";
630
$header = "<?xml version=\"1.0\" encoding=\"" . Config::get('site_charset') . "\" ?>\n<root>\n";
641
* this returns the footer for this document, these are pretty boring
643
* @return string Footer xml tag.
645
private static function _footer() {
647
switch (self::$type) {
649
$footer = "\t\t</dict>\t\n</dict>\n</plist>\n";
652
$footer = "</trackList>\n</playlist>\n";
655
$footer = "\n</channel>\n</rss>\n";
658
$footer = "\n</root>\n";
660
} // end switch on type