diff --git a/3rd_party/pypo/api_clients/api_client.py b/3rd_party/pypo/api_clients/api_client.py
index db36c3871..e69ead13e 100644
--- a/3rd_party/pypo/api_clients/api_client.py
+++ b/3rd_party/pypo/api_clients/api_client.py
@@ -164,6 +164,7 @@ class CampcasterApiClient(ApiClientInterface):
# Construct the URL
export_url = self.config["base_url"] + self.config["api_base"] + self.config["export_url"]
+ logger.debug("Exporting schedule using URL: "+export_url)
# Insert the start and end times into the URL
export_url = export_url.replace('%%api_key%%', self.config["api_key"])
diff --git a/3rd_party/pypo/pypo_cli.py b/3rd_party/pypo/pypo_cli.py
index c341156e2..b572608ec 100755
--- a/3rd_party/pypo/pypo_cli.py
+++ b/3rd_party/pypo/pypo_cli.py
@@ -373,21 +373,21 @@ class Playout:
"""
logger = logging.getLogger()
for media in playlist['medias']:
- logger.debug("found track at %s", media['uri'])
+ logger.debug("Processing track %s", media['uri'])
try:
- src = media['uri']
-
if str(media['cue_in']) == '0' and str(media['cue_out']) == '0':
+ logger.debug('No cue in/out detected for this file')
dst = "%s%s/%s.mp3" % (self.cache_dir, str(pkey), str(media['id']))
do_cue = False
else:
+ logger.debug('Cue in/out detected')
dst = "%s%s/%s_cue_%s-%s.mp3" % \
(self.cache_dir, str(pkey), str(media['id']), str(float(media['cue_in']) / 1000), str(float(media['cue_out']) / 1000))
do_cue = True
# check if it is a remote file, if yes download
- if src[0:4] == 'http':
+ if media['uri'][0:4] == 'http':
self.handle_remote_file(media, dst, do_cue)
else:
# Assume local file
@@ -405,7 +405,7 @@ class Playout:
pl_entry = 'annotate:export_source="%s",media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s":%s' % \
(str(media['export_source']), media['id'], 0, str(float(media['fade_in']) / 1000), str(float(media['fade_out']) / 1000), dst)
- print pl_entry
+ logger.debug(pl_entry)
"""
Tracks are only added to the playlist if they are accessible
@@ -417,7 +417,7 @@ class Playout:
logger.debug("everything ok, adding %s to playlist", pl_entry)
else:
- print 'zero-file: ' + dst + ' from ' + src
+ print 'zero-file: ' + dst + ' from ' + media['uri']
logger.warning("zero-size file - skiping %s. will not add it to playlist", dst)
else:
@@ -433,8 +433,8 @@ class Playout:
if os.path.isfile(dst):
logger.debug("file already in cache: %s", dst)
else:
- logger.debug("try to download %s", src)
- api_client.get_media(src, dst)
+ logger.debug("try to download %s", media['uri'])
+ self.api_client.get_media(media['uri'], dst)
else:
if os.path.isfile(dst):
@@ -442,13 +442,13 @@ class Playout:
print 'cached'
else:
- logger.debug("try to download and cue %s", src)
+ logger.debug("try to download and cue %s", media['uri'])
- print '***'
+ #print '***'
dst_tmp = self.tmp_dir + "".join([random.choice(string.letters) for i in xrange(10)]) + '.mp3'
- print dst_tmp
- print '***'
- api_client.get_media(src, dst_tmp)
+ #print dst_tmp
+ #print '***'
+ self.api_client.get_media(media['uri'], dst_tmp)
# cue
print "STARTING CUE"
@@ -488,10 +488,10 @@ class Playout:
logger.debug("file already in cache: %s", dst)
else:
- logger.debug("try to copy file to cache %s", src)
+ logger.debug("try to copy file to cache %s", media['uri'])
try:
- shutil.copy(src, dst)
- logger.info("copied %s to %s", src, dst)
+ shutil.copy(media['uri'], dst)
+ logger.info("copied %s to %s", media['uri'], dst)
except Exception, e:
logger.error("%s", e)
else:
@@ -499,7 +499,7 @@ class Playout:
logger.debug("file already in cache: %s", dst)
else:
- logger.debug("try to copy and cue %s", src)
+ logger.debug("try to copy and cue %s", media['uri'])
print '***'
dst_tmp = self.tmp_dir + "".join([random.choice(string.letters) for i in xrange(10)])
@@ -507,8 +507,8 @@ class Playout:
print '***'
try:
- shutil.copy(src, dst_tmp)
- logger.info("copied %s to %s", src, dst_tmp)
+ shutil.copy(media['uri'], dst_tmp)
+ logger.info("copied %s to %s", media['uri'], dst_tmp)
except Exception, e:
logger.error("%s", e)
@@ -537,17 +537,16 @@ class Playout:
def cleanup(self, export_source):
+ """
+ Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
+ and deletes them.
+ """
logger = logging.getLogger()
self.export_source = export_source
self.cache_dir = CACHE_DIR + self.export_source + '/'
self.schedule_file = self.cache_dir + 'schedule'
- """
- Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
- and deletes them.
- """
-
offset = 3600 * int(CACHE_FOR)
now = time.time()
@@ -619,7 +618,7 @@ class Playout:
else:
for pkey in self.schedule:
- logger.debug('found playlist schedulet at: %s', pkey)
+ logger.debug('found playlist scheduled at: %s', pkey)
#if pkey[0:16] == str_tnow:
if pkey[0:16] == str_tcomming:
@@ -674,8 +673,8 @@ class Playout:
self.cache_dir = CACHE_DIR + self.export_source + '/'
self.schedule_file = self.cache_dir + 'schedule'
- # load the shedule from cache
- logger.debug('load shedule from cache')
+ # load the schedule from cache
+ logger.debug('loading schedule from cache...')
try:
schedule_file = open(self.schedule_file, "r")
schedule = pickle.load(schedule_file)
diff --git a/backend/Playlist.php b/backend/Playlist.php
index bc13938b4..3419c1eef 100644
--- a/backend/Playlist.php
+++ b/backend/Playlist.php
@@ -57,16 +57,23 @@ class Playlist {
private $categories = array("dc:title" => "DbName", "dc:creator" => "DbCreator", "dc:description" => "DbDescription", "dcterms:extent" => "length");
+ /**
+ * @param string $p_gunid
+ */
public function __construct($p_gunid=NULL)
{
}
- public static function Insert($p_values)
+ /**
+ * @param array $p_name
+ * The name of the playlist
+ */
+ private static function Insert($p_name = null)
{
// Create the StoredPlaylist object
$storedPlaylist = new Playlist();
- $storedPlaylist->name = isset($p_values['filename']) ? $p_values['filename'] : date("H:i:s");
+ $storedPlaylist->name = !empty($p_name) ? $p_name : date("H:i:s");
$storedPlaylist->mtime = new DateTime("now");
$pl = new CcPlaylist();
@@ -325,8 +332,9 @@ class Playlist {
->findPK($this->id)
->computeLastPosition();
- if(is_null($res))
- return 0;
+ if(is_null($res)) {
+ return 0;
+ }
return $res + 1;
}
@@ -355,8 +363,9 @@ class Playlist {
->findPK($this->id)
->computeLength();
- if(is_null($res))
- return '00:00:00.000000';
+ if(is_null($res)) {
+ return '00:00:00.000000';
+ }
return $res;
}
@@ -370,12 +379,19 @@ class Playlist {
*/
public function create($fname=NULL)
{
- $values = array("filename" => $fname);
- $pl_id = Playlist::Insert($values);
+ $pl_id = Playlist::Insert($fname);
$this->id = $pl_id;
return $this->id;
}
+
+ public static function findPlaylistByName($p_name)
+ {
+ $res = CcPlaylistQuery::create()->findByDbName($p_name);
+ return $res;
+ }
+
+
/**
* Lock playlist for edit
*
diff --git a/backend/Schedule.php b/backend/Schedule.php
index 988014890..2d64e7281 100644
--- a/backend/Schedule.php
+++ b/backend/Schedule.php
@@ -213,11 +213,13 @@ class Schedule {
}
/**
- * Return true if there is nothing in the schedule for the given times.
+ * Return true if there is nothing in the schedule for the given start time
+ * up to the length of time after that.
*
* @param string $p_datetime
+ * In the format YYYY-MM-DD HH:MM:SS.mmmmmm
* @param string $p_length
- *
+ * In the format HH:MM:SS.mmmmmm
* @return boolean|PEAR_Error
*/
public static function isScheduleEmptyInRange($p_datetime, $p_length) {
@@ -268,7 +270,8 @@ class Schedule {
* "start"/"starts" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
* "end"/"ends" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
* "group_id"/"id" (aliases to the same thing)
- * "clip_length" (for playlists only, this is the length of the entire playlist)
+ * "clip_length" (for audio clips this is the length of the audio clip,
+ * for playlists this is the length of the entire playlist)
* "name" (playlist only)
* "creator" (playlist only)
* "file_id" (audioclip only)
@@ -339,18 +342,85 @@ class Schedule {
}
- private static function CcTimeToPypoTime($p_time) {
+ /**
+ * Convert a time string in the format "YYYY-MM-DD HH:mm:SS"
+ * to "YYYY-MM-DD-HH-mm-SS".
+ *
+ * @param string $p_time
+ * @return string
+ */
+ private static function CcTimeToPypoTime($p_time)
+ {
$p_time = substr($p_time, 0, 19);
$p_time = str_replace(" ", "-", $p_time);
$p_time = str_replace(":", "-", $p_time);
return $p_time;
}
- private static function PypoTimeToCcTime($p_time) {
+ /**
+ * Convert a time string in the format "YYYY-MM-DD-HH-mm-SS" to
+ * "YYYY-MM-DD HH:mm:SS".
+ *
+ * @param string $p_time
+ * @return string
+ */
+ private static function PypoTimeToCcTime($p_time)
+ {
$t = explode("-", $p_time);
return $t[0]."-".$t[1]."-".$t[2]." ".$t[3].":".$t[4].":00";
}
+ /**
+ * Converts a time value as a string (with format HH:MM:SS.mmmmmm) to
+ * millisecs.
+ *
+ * @param string $p_time
+ * @return int
+ */
+ private static function WallTimeToMillisecs($p_time)
+ {
+ $t = explode(":", $p_time);
+ $millisecs = 0;
+ if (strpos($t[2], ".")) {
+ $secParts = explode(".", $t[2]);
+ $millisecs = $secParts[1];
+ $millisecs = substr($millisecs, 0, 3);
+ $millisecs = intval($millisecs);
+ $seconds = intval($secParts[0]);
+ } else {
+ $seconds = intval($t[2]);
+ }
+ $ret = $millisecs + ($seconds * 1000) + ($t[1] * 60 * 1000) + ($t[0] * 60 * 60 * 1000);
+ return $ret;
+ }
+
+
+ /**
+ * Compute the difference between two times in the format "HH:MM:SS.mmmmmm".
+ * Note: currently only supports calculating millisec differences.
+ *
+ * @param string $p_time1
+ * @param string $p_time2
+ * @return double
+ */
+ private static function TimeDiff($p_time1, $p_time2)
+ {
+ $parts1 = explode(".", $p_time1);
+ $parts2 = explode(".", $p_time2);
+ $diff = 0;
+ if ( (count($parts1) > 1) && (count($parts2) > 1) ) {
+ $millisec1 = substr($parts1[1], 0, 3);
+ $millisec1 = str_pad($millisec1, 3, "0");
+ $millisec1 = intval($millisec1);
+ $millisec2 = substr($parts2[1], 0, 3);
+ $millisec2 = str_pad($millisec2, 3, "0");
+ $millisec2 = intval($millisec2);
+ $diff = abs(millisec1 - millisec2)/1000;
+ }
+ return $diff;
+ }
+
+
/**
* Export the schedule in json formatted for pypo (the liquidsoap scheduler)
*
@@ -404,14 +474,20 @@ class Schedule {
{
$storedFile = StoredFile::Recall($item["file_id"]);
$uri = $storedFile->getFileUrl();
+
+ // For pypo, a cueout of zero means no cueout
+ $cueOut = "0";
+ if (Schedule::TimeDiff($item["cue_out"], $item["clip_length"]) > 0.001) {
+ $cueOut = Schedule::WallTimeToMillisecs($item["cue_out"]);
+ }
$medias[] = array(
'id' => $storedFile->getGunid(), //$item["file_id"],
'uri' => $uri,
- 'fade_in' => $item["fade_in"],
- 'fade_out' => $item["fade_out"],
+ 'fade_in' => Schedule::WallTimeToMillisecs($item["fade_in"]),
+ 'fade_out' => Schedule::WallTimeToMillisecs($item["fade_out"]),
'fade_cross' => 0,
- 'cue_in' => $item["cue_in"],
- 'cue_out' => $item["cue_out"],
+ 'cue_in' => Schedule::WallTimeToMillisecs($item["cue_in"]),
+ 'cue_out' => $cueOut
);
}
$playlist['medias'] = $medias;
@@ -425,6 +501,24 @@ class Schedule {
print json_encode($result);
}
+
+ /**
+ * Remove all items from the schedule in the given range.
+ *
+ * @param string $p_start
+ * In the format YYYY-MM-DD HH:MM:SS.nnnnnn
+ * @param string $p_end
+ * In the format YYYY-MM-DD HH:MM:SS.nnnnnn
+ */
+ public static function RemoveItemsInRange($p_start, $p_end)
+ {
+ $items = Schedule::GetItems($p_start, $p_end);
+ foreach ($items as $item) {
+ $scheduleGroup = new ScheduleGroup($item["group_id"]);
+ $scheduleGroup->remove();
+ }
+ }
+
}
?>
\ No newline at end of file
diff --git a/backend/StoredFile.php b/backend/StoredFile.php
index a4f48a8a7..d1313c66d 100644
--- a/backend/StoredFile.php
+++ b/backend/StoredFile.php
@@ -835,6 +835,25 @@ class StoredFile {
}
+ /**
+ * Find and return the first exact match for the original file name
+ * that was used on import.
+ * @param string $p_name
+ */
+ public static function findByOriginalName($p_name)
+ {
+ global $CC_CONFIG, $CC_DBC;
+ $sql = "SELECT id FROM ".$CC_CONFIG["filesTable"]
+ ." WHERE name='".pg_escape_string($p_name)."'";
+ $id = $CC_DBC->getOne($sql);
+ if (is_numeric($id)) {
+ return StoredFile::Recall($id);
+ } else {
+ return NULL;
+ }
+ }
+
+
/**
* Delete and insert media file
*
diff --git a/backend/tests/pdoTest.php b/backend/tests/pdoTest.php
new file mode 100644
index 000000000..3aae4465b
--- /dev/null
+++ b/backend/tests/pdoTest.php
@@ -0,0 +1,22 @@
+= '2010-01-01 00:00:00.000') "
+ ." AND (ends <= (TIMESTAMP '2011-01-01 00:00:00.000' + INTERVAL '01:00:00.123456'))";
+$rows1 = $con->query($sql);
+var_dump($rows1->fetchAll());
+
+$sql2 = "SELECT COUNT(*) FROM cc_playlistcontents";
+$rows2 = $con->query($sql2);
+var_dump($rows2->fetchAll());
+
+$sql3 = "SELECT TIMESTAMP '2011-01-01 00:00:00.000' + INTERVAL '01:00:00.123456'";
+$result3 = $con->query($sql3);
+var_dump($result3->fetchAll());
+
+?>
\ No newline at end of file
diff --git a/backend/tests/pypoTester.php b/backend/tests/pypoTester.php
new file mode 100644
index 000000000..36925e923
--- /dev/null
+++ b/backend/tests/pypoTester.php
@@ -0,0 +1,70 @@
+delete();
+}
+echo "done.\n";
+
+// Create a new playlist
+echo "Creating new playlist '$playlistName'...";
+$pl = new Playlist();
+$pl->create($playlistName);
+
+// Add a media clip
+$mediaFile = StoredFile::findByOriginalName("test10001.mp3");
+if (is_null($mediaFile)) {
+ echo "Adding test audio clip to the database.\n";
+ $v = array("filepath" => __DIR__."/test10001.mp3");
+ $mediaFile = StoredFile::Insert($v);
+}
+$pl->addAudioClip($mediaFile->getId());
+echo "done.\n";
+
+//$pl2 = Playlist::findPlaylistByName("pypo_playlist_test");
+//var_dump($pl2);
+
+// Get current time
+// In the format YYYY-MM-DD HH:MM:SS.nnnnnn
+$startTime = date("Y-m-d H:i:s");
+$endTime = date("Y-m-d H:i:s", time()+(60*60));
+
+echo "Removing everything from the scheduler between $startTime and $endTime...";
+// Scheduler: remove any playlists for the next hour
+Schedule::RemoveItemsInRange($startTime, $endTime);
+// Check for succcess
+$scheduleClear = Schedule::isScheduleEmptyInRange($startTime, "01:00:00");
+if (!$scheduleClear) {
+ echo "\nERROR: Schedule could not be cleared.\n\n";
+ var_dump(Schedule::GetItems($startTime, $endTime));
+ exit;
+}
+echo "done.\n";
+
+// Schedule the playlist for two minutes from now
+echo "Scheduling new playlist...\n";
+$playTime = date("Y-m-d H:i:s", time()+(60*$minutesFromNow));
+$scheduleGroup = new ScheduleGroup();
+$scheduleGroup->add($playTime, null, $pl->getId());
+
+echo " SUCCESS: Playlist scheduled at $playTime\n\n";
+?>
\ No newline at end of file
diff --git a/backend/tests/transTest.php b/backend/tests/transTest.php
index 3a48b1faa..edf04354c 100644
--- a/backend/tests/transTest.php
+++ b/backend/tests/transTest.php
@@ -41,7 +41,7 @@ $values = array(
"gunid" => $gunid,
"filetype" => "audioclip"
);
-$storedFile = $gb->bsPutFile($values);
+$storedFile = StoredFile::Insert($values);
if (PEAR::isError($storedFile)) {
if ($storedFile->getCode()!=GBERR_GUNID) {
echo "ERROR: ".$storedFile->getMessage()."\n";
diff --git a/conf.php b/conf.php
index f43288408..3e98b0dc1 100644
--- a/conf.php
+++ b/conf.php
@@ -41,7 +41,7 @@ $CC_CONFIG = array(
"rootDir" => dirname(__FILE__),
"smartyTemplate" => dirname(__FILE__)."/htmlUI/templates",
"smartyTemplateCompiled" => dirname(__FILE__)."/htmlUI/templates_c",
- 'pearPath' => dirname(__FILE__).'/3rd_party/php/pear',
+ 'pearPath' => dirname(__FILE__).'/3rd_party/php/pear/',
'zendPath' => dirname(__FILE__).'/3rd_party/php/Zend',
'phingPath' => dirname(__FILE__).'/3rd_party/php/phing',
'LogPath' => dirname(__FILE__).'/3rd_party/php/Log',
@@ -166,6 +166,22 @@ set_include_path('.'.PATH_SEPARATOR.$CC_CONFIG['pearPath']
.PATH_SEPARATOR.$CC_CONFIG['zendPath']
.PATH_SEPARATOR.$old_include_path);
+require_once('DB.php');
+
+// Connect to the database
+$CC_DBC = DB::connect($CC_CONFIG['dsn']);
+if (PEAR::isError($CC_DBC)) {
+ echo "*** conf.php ***
";
+ echo "Could not connect to database. Your current configuration is:
";
+ echo "
";
+ echo "Host name: | ".$CC_CONFIG['dsn']['hostspec']." |
";
+ echo "Database name: | ".$CC_CONFIG['dsn']['database']." |
";
+ echo "User name: | ".$CC_CONFIG['dsn']['username']." |
";
+ echo "
";
+ exit;
+}
+$CC_DBC->setFetchMode(DB_FETCHMODE_ASSOC);
+
// Check that all the required directories exist.
//foreach (array('storageDir', 'bufferDir', 'transDir', 'accessDir', 'cronDir') as $d) {
// $test = file_exists($CC_CONFIG[$d]);
diff --git a/htmlUI/ui_browser_init.php b/htmlUI/ui_browser_init.php
index a78c072cb..f9bc2f897 100644
--- a/htmlUI/ui_browser_init.php
+++ b/htmlUI/ui_browser_init.php
@@ -8,7 +8,7 @@ require_once(dirname(__FILE__).'/ui_handler.class.php');
// often used classes ###############################################
require_once(dirname(__FILE__).'/../3rd_party/php/propel/runtime/lib/Propel.php');
require_once(dirname(__FILE__).'/../3rd_party/php/Smarty/libs/Smarty.class.php');
-require_once(dirname(__FILE__).'/../3rd_party/php/pear/HTML/QuickForm/Renderer/ArraySmarty.php');
+require_once('HTML/QuickForm/Renderer/ArraySmarty.php');
require_once(dirname(__FILE__).'/ui_scratchpad.class.php');
require_once(dirname(__FILE__).'/ui_search.class.php');
require_once(dirname(__FILE__).'/ui_browse.class.php');
diff --git a/htmlUI/ui_conf.php b/htmlUI/ui_conf.php
index 8ffd88ceb..1d2ebd32a 100644
--- a/htmlUI/ui_conf.php
+++ b/htmlUI/ui_conf.php
@@ -11,6 +11,9 @@ define('UI_ERROR', TRUE);
// parts of the application do not read in this file.
$WHITE_SCREEN_OF_DEATH = false;
+if ($WHITE_SCREEN_OF_DEATH) {
+ echo "ui_conf.php: start
";
+}
if (UI_DEBUG) {
error_reporting(E_ALL);
}
@@ -173,29 +176,10 @@ if ($WHITE_SCREEN_OF_DEATH) {
echo __FILE__.':line '.__LINE__.": Loaded GreenBox
";
}
require_once(dirname(__FILE__).'/formmask/generic.inc.php');
-require_once(dirname(__FILE__).'/../3rd_party/php/pear/DB.php');
-require_once(dirname(__FILE__).'/../3rd_party/php/pear/HTML/QuickForm.php');
-
-
-// Connect to the database
-$CC_DBC = DB::connect($CC_CONFIG['dsn'], TRUE);
-if (PEAR::isError($CC_DBC)) {
- echo "Could not connect to database. Your current configuration is:
";
- echo "";
- echo "Host name: | ".$CC_CONFIG['dsn']['hostspec']." |
";
- echo "Database name: | ".$CC_CONFIG['dsn']['database']." |
";
- echo "User name: | ".$CC_CONFIG['dsn']['username']." |
";
- echo "
";
- exit;
-}
-
-if ($WHITE_SCREEN_OF_DEATH) {
- echo __FILE__.':line '.__LINE__.": Connected to database
";
-}
-$CC_DBC->setFetchMode(DB_FETCHMODE_ASSOC);
+require_once('HTML/QuickForm.php');
//PEAR::setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_WARNING);
//PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'errCallBack');
PEAR::setErrorHandling(PEAR_ERROR_RETURN);
//PEAR::setErrorHandling(PEAR_ERROR_PRINT);
-?>
+?>
\ No newline at end of file