libretime/backend/M3uPlaylist.php

352 lines
12 KiB
PHP

<?
define('INDCH', ' ');
/**
* M3uPlaylist class
*
* @package Campcaster
* @subpackage StorageServer
* @copyright 2010 Sourcefabric O.P.S.
* @license http://www.gnu.org/licenses/gpl.txt
*/
class M3uPlaylist {
/**
* Parse M3U file or string
*
* @param string $data
* local path to M3U file or M3U string
* @param string $loc
* location: 'file'|'string'
* @return array
* reference, parse result tree (or PEAR::error)
*/
function &parse($data='', $loc='file')
{
switch ($loc) {
case "file":
if (!is_file($data)) {
return PEAR::raiseError(
"M3uPlaylist::parse: file not found ($data)"
);
}
if (!is_readable($data)) {
return PEAR::raiseError(
"M3uPlaylist::parse: can't read file ($data)"
);
}
$data = file_get_contents($data);
case "string":
$arr = preg_split("|\n#EXTINF: |", $data);
if ($arr[0] != '#EXTM3U') {
return PEAR::raiseError(
"M3uPlaylist::parse: invalid M3U header"
);
}
array_shift($arr);
break;
default:
return PEAR::raiseError(
"M3uPlaylist::parse: unsupported data location ($loc)"
);
}
return $arr;
}
/**
* Import M3U file to storage
*
* @param GreenBox $gb
* @param string $aPath
* absolute path part of imported file (e.g. /home/user/campcaster)
* @param string $rPath
* relative path/filename part of imported file
* (e.g. playlists/playlist_1.smil)
* @param array $gunids
* hash relation from filenames to gunids
* @param string $plid
* playlist gunid
* @param int $subjid
* local subject (user) id (id of user doing the import)
* @return Playlist
*/
function import(&$gb, $aPath, $rPath, &$gunids, $plid, $subjid=NULL)
{
$path = realpath("$aPath/$rPath");
if (FALSE === $path) {
return PEAR::raiseError(
"M3uPlaylist::import: file doesn't exist ($aPath/$rPath)"
);
}
$arr = M3uPlaylist::parse($path);
if (PEAR::isError($arr)) {
return $arr;
}
require_once("Playlist.php");
$pl =& Playlist::create($gb, $plid, "imported_M3U");
if (PEAR::isError($pl)) {
return $pl;
}
$r = $pl->lock($gb, $subjid);
if (PEAR::isError($r)) {
return $r;
}
foreach ($arr as $i => $it) {
list($md, $uri) = preg_split("|\n|", $it);
list($length, $title) = preg_split("|, *|", $md);
// $gunid = StoredFile::CreateGunid();
$gunid = ( isset($gunids[basename($uri)]) ? $gunids[basename($uri)] : NULL);
$acId = BasicStor::IdFromGunid($gunid);
if (PEAR::isError($acId)) {
return $acId;
}
$length = Playlist::secondsToPlaylistTime($length);
$offset = '???';
if (preg_match("|\.([a-zA-Z0-9]+)$|", $uri, $va)) {
switch (strtolower($ext = $va[1])) {
case "lspl":
case "xml":
case "smil":
case "m3u":
$acId = $gb->bsImportPlaylistRaw($gunid,
$aPath, $uri, $ext, $gunids, $subjid);
if (PEAR::isError($acId)) {
break;
}
//no break!
default:
if (is_null($gunid)) {
return PEAR::raiseError(
"M3uPlaylist::import: no gunid");
}
$r = $pl->addAudioClip($acId);
if (PEAR::isError($r)) {
return $r;
}
}
}
}
$r = $pl->unlock($gb);
if (PEAR::isError($r)) {
return $r;
}
return $pl;
}
/**
* Import M3U file to storage
*
* @param GreenBox $gb
* @param string $data
* local path to M3U file
* @return string
* XML playlist in Campcaster playlist format
*/
function convert2lspl(&$gb, $data)
{
$arr = M3uPlaylist::parse($data);
if (PEAR::isError($arr)) {
return $arr;
}
$ind = '';
$ind2 = $ind.INDCH;
$ind3 = $ind2.INDCH;
$res = '';
foreach ($arr as $i => $it) {
list($md, $uri) = preg_split("|\n|", $it);
list($length, $title) = preg_split("|, *|", $md);
$gunid = StoredFile::CreateGunid();
$gunid2 = StoredFile::CreateGunid();
$length = Playlist::secondsToPlaylistTime($length);
$offset = '???';
$clipStart = '???';
$clipEnd = '???';
$clipLength = '???';
$uri_h = preg_replace("|--|", "&#2d;&#2d;", htmlspecialchars("$uri"));
if (preg_match("|\.([a-zA-Z0-9]+)$|", $uri, $va)) {
switch (strtolower($ext = $va[1])) {
case "lspl":
case "xml":
case "smil":
case "m3u":
$acOrPl = "$ind3<playlist id=\"$gunid2\" ".
"playlength=\"$length\" title=\"$title\"/> ".
"<!-- $uri_h -->\n";
break;
default:
$acOrPl = "$ind3<audioClip id=\"$gunid2\" ".
"playlength=\"$length\" title=\"$title\"/> ".
"<!-- $uri_h -->\n";
break;
}
}
$res .= "$ind2<playlistElement id=\"$gunid\" relativeOffset=\"$offset\" clipStart=\"$clipStart\" clipEnd=\"$clipEnd\" clipLength=\"$clipLength\">\n".
$acOrPl.
"$ind2</playlistElement>\n";
}
$res = "$ind<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n".
"$ind<playlist id=\"$gunid\" playlength=\"$playlength\" title=\"\">\n".
"$ind2<metadata/>\n".
"$res".
"$ind</playlist>\n";
return $res;
}
} // class M3uPlaylist
/**
* @package Campcaster
* @subpackage StorageServer
* @copyright 2010 Sourcefabric O.P.S.
* @license http://www.gnu.org/licenses/gpl.txt
*/
class M3uPlaylistBodyElement {
function convert2lspl(&$tree, $ind='')
{
$ind2 = $ind.INDCH;
if ($tree->name != 'body') {
return PEAR::raiseError("M3uPlaylist::parse: body tag expected");
}
if (isset($tree->children[1])) {
return PEAR::raiseError(sprintf(
"M3uPlaylist::parse: unexpected tag %s in tag body",
$tree->children[1]->name
));
}
$res = M3uPlaylistParElement::convert2lspl($tree->children[0], $ind2);
if (PEAR::isError($res)) {
return $res;
}
$gunid = StoredFile::CreateGunid();
$playlength = '???'; // ***
$res = "$ind<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n".
"$ind<playlist id=\"$gunid\" playlength=\"$playlength\" title=\"\">\n".
"$ind2<metadata/>\n".
"$res".
"$ind</playlist>\n";
return $res;
}
}
/**
* @package Campcaster
* @subpackage StorageServer
* @copyright 2010 Sourcefabric O.P.S.
* @license http://www.gnu.org/licenses/gpl.txt
*/
class M3uPlaylistParElement {
function convert2lspl(&$tree, $ind='')
{
if ($tree->name != 'par') {
return PEAR::raiseError("M3uPlaylist::parse: par tag expected");
}
$res = '';
foreach ($tree->children as $i => $ch) {
$ch =& $tree->children[$i];
$r = M3uPlaylistAudioElement::convert2lspl($ch, $ind.INDCH);
if (PEAR::isError($r)) {
return $r;
}
$res .= $r;
}
return $res;
}
}
/**
* @package Campcaster
* @subpackage StorageServer
* @copyright 2010 Sourcefabric O.P.S.
* @license http://www.gnu.org/licenses/gpl.txt
*/
class M3uPlaylistAudioElement {
function convert2lspl(&$tree, $ind='')
{
$ind2 = $ind.INDCH;
if ($tree->name != 'audio') {
return PEAR::raiseError("M3uPlaylist::parse: audio tag expected");
}
if (isset($tree->children[2])) {
return PEAR::raiseError(sprintf(
"M3uPlaylist::parse: unexpected tag %s in tag audio",
$tree->children[2]->name
));
}
$res = ''; $fadeIn = 0; $fadeOut = 0;
foreach ($tree->children as $i => $ch) {
$ch =& $tree->children[$i];
$r = M3uPlaylistAnimateElement::convert2lspl($ch, $ind2);
if (PEAR::isError($r)) {
return $r;
}
switch ($r['type']) {
case "fadeIn":
$fadeIn = $r['val'];
break;
case "fadeOut":
$fadeOut = $r['val'];
break;
}
}
if ($fadeIn > 0 || $fadeOut > 0) {
$fadeIn = Playlist::secondsToPlaylistTime($fadeIn);
$fadeOut = Playlist::secondsToPlaylistTime($fadeOut);
$fInfo = "$ind2<fadeInfo fadeIn=\"$fadeIn\" fadeOut=\"$fadeOut\"/>\n";
} else {
$fInfo = '';
}
$plElGunid = StoredFile::CreateGunid();
$aGunid = StoredFile::CreateGunid();
$title = basename($tree->attrs['src']->val);
$offset = Playlist::secondsToPlaylistTime($tree->attrs['begin']->val);
$playlength = '???'; # ***
$res = "$ind<playlistElement id=\"$plElGunid\" relativeOffset=\"$offset\">\n".
"$ind2<audioClip id=\"$aGunid\" playlength=\"$playlength\" title=\"$title\"/>\n".
$fInfo.
"$ind</playlistElement>\n";
return $res;
}
} // class M3uPlaylistAudioElement
/**
* @package Campcaster
* @subpackage StorageServer
* @copyright 2010 Sourcefabric O.P.S.
* @license http://www.gnu.org/licenses/gpl.txt
*/
class M3uPlaylistAnimateElement {
function convert2lspl(&$tree, $ind='') {
if ($tree->name != 'animate') {
return PEAR::raiseError("M3uPlaylist::parse: animate tag expected");
}
if ($tree->attrs['attributeName']->val == 'soundLevel' &&
$tree->attrs['from']->val == '0%' &&
$tree->attrs['to']->val == '100%' &&
$tree->attrs['calcMode']->val == 'linear' &&
$tree->attrs['fill']->val == 'freeze' &&
$tree->attrs['begin']->val == '0s' &&
preg_match("|^([0-9.]+)s$|", $tree->attrs['end']->val, $va)
) {
return array('type'=>'fadeIn', 'val'=>$va[1]);
}
if ($tree->attrs['attributeName']->val == 'soundLevel' &&
$tree->attrs['from']->val == '100%' &&
$tree->attrs['to']->val == '0%' &&
$tree->attrs['calcMode']->val == 'linear' &&
$tree->attrs['fill']->val == 'freeze' &&
preg_match("|^([0-9.]+)s$|", $tree->attrs['begin']->val, $vaBegin) &&
preg_match("|^([0-9.]+)s$|", $tree->attrs['end']->val, $vaEnd)
) {
return array('type'=>'fadeOut', 'val'=>($vaEnd[1] - $vaBegin[1]));
}
return PEAR::raiseError(
"M3uPlaylistAnimateElement::convert2lspl: animate parameters too general"
);
}
}
?>