sintonia/airtime_mvc/application/services/SchedulerService.php
drigato 520387fcad CC-5898: Future repeating shows may not get generated and/or filled with content
Create and fill show instances when pypo requests the schedule
Changed getCcShowInstancess function to return all instances
Changed some function calls to retrieve only show instances scheduled in the future
2014-08-16 11:05:55 -04:00

414 lines
16 KiB
PHP

<?php
class Application_Service_SchedulerService
{
private $con;
private $fileInfo = array(
"id" => "",
"cliplength" => "",
"cuein" => "00:00:00",
"cueout" => "00:00:00",
"fadein" => "00:00:00",
"fadeout" => "00:00:00",
"sched_id" => null,
"type" => 0 //default type of '0' to represent files. type '1' represents a webstream
);
private $epochNow;
private $nowDT;
private $currentUser;
private $checkUserPermissions = true;
public function __construct()
{
$this->con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME);
//subtracting one because sometimes when we cancel a track, we set its end time
//to epochNow and then send the new schedule to pypo. Sometimes the currently cancelled
//track can still be included in the new schedule because it may have a few ms left to play.
//subtracting 1 second from epochNow resolves this issue.
$this->epochNow = microtime(true)-1;
$this->nowDT = DateTime::createFromFormat("U.u", $this->epochNow, new DateTimeZone("UTC"));
if ($this->nowDT === false) {
// DateTime::createFromFormat does not support millisecond string formatting in PHP 5.3.2 (Ubuntu 10.04).
// In PHP 5.3.3 (Ubuntu 10.10), this has been fixed.
$this->nowDT = DateTime::createFromFormat("U", time(), new DateTimeZone("UTC"));
}
$user_service = new Application_Service_UserService();
$this->currentUser = $user_service->getCurrentUser();
}
/**
*
* Applies the show start difference to any scheduled items
*
* @param $instanceIds
* @param $diff (integer, difference between unix epoch in seconds)
*/
public static function updateScheduleStartTime($instanceIds, $diff)
{
$con = Propel::getConnection();
if (count($instanceIds) > 0) {
$showIdList = implode(",", $instanceIds);
$ccSchedules = CcScheduleQuery::create()
->filterByDbInstanceId($instanceIds, Criteria::IN)
->find($con);
$interval = new DateInterval("PT".abs($diff)."S");
if ($diff < 0) {
$interval->invert = 1;
}
foreach ($ccSchedules as $ccSchedule) {
$start = $ccSchedule->getDbStarts(null);
$newStart = $start->add($interval);
$end = $ccSchedule->getDbEnds(null);
$newEnd = $end->add($interval);
$ccSchedule
->setDbStarts($newStart)
->setDbEnds($newEnd)
->save($con);
}
}
}
/**
*
* Removes any time gaps in shows
*
* @param array $schedIds schedule ids to exclude
*/
public function removeGaps($showId, $schedIds=null)
{
$ccShowInstances = CcShowInstancesQuery::create()->filterByDbShowId($showId)->find();
foreach ($ccShowInstances as $instance) {
Logging::info("Removing gaps from show instance #".$instance->getDbId());
//DateTime object
$itemStart = $instance->getDbStarts(null);
$ccScheduleItems = CcScheduleQuery::create()
->filterByDbInstanceId($instance->getDbId())
->filterByDbId($schedIds, Criteria::NOT_IN)
->orderByDbStarts()
->find();
foreach ($ccScheduleItems as $ccSchedule) {
//DateTime object
$itemEnd = $this->findEndTime($itemStart, $ccSchedule->getDbClipLength());
$ccSchedule->setDbStarts($itemStart)
->setDbEnds($itemEnd);
$itemStart = $itemEnd;
}
$ccScheduleItems->save();
}
}
/**
*
* Enter description here ...
* @param DateTime $instanceStart
* @param string $clipLength
*/
private static function findEndTime($instanceStart, $clipLength)
{
$startEpoch = $instanceStart->format("U.u");
$durationSeconds = Application_Common_DateHelper::playlistTimeToSeconds($clipLength);
//add two float numbers to 6 subsecond precision
//DateTime::createFromFormat("U.u") will have a problem if there is no decimal in the resulting number.
$endEpoch = bcadd($startEpoch , (string) $durationSeconds, 6);
$dt = DateTime::createFromFormat("U.u", $endEpoch, new DateTimeZone("UTC"));
if ($dt === false) {
//PHP 5.3.2 problem
$dt = DateTime::createFromFormat("U", intval($endEpoch), new DateTimeZone("UTC"));
}
return $dt;
}
private static function findTimeDifference($p_startDT, $p_seconds)
{
$startEpoch = $p_startDT->format("U.u");
//add two float numbers to 6 subsecond precision
//DateTime::createFromFormat("U.u") will have a problem if there is no decimal in the resulting number.
$newEpoch = bcsub($startEpoch , (string) $p_seconds, 6);
$dt = DateTime::createFromFormat("U.u", $newEpoch, new DateTimeZone("UTC"));
if ($dt === false) {
//PHP 5.3.2 problem
$dt = DateTime::createFromFormat("U", intval($newEpoch), new DateTimeZone("UTC"));
}
return $dt;
}
public static function fillNewLinkedInstances($ccShow)
{
/* First check if any linked instances have content
* If all instances are empty then we don't need to fill
* any other instances with content
*/
$instanceIds = $ccShow->getInstanceIds();
if (count($instanceIds) == 0) {
return;
}
$schedule_sql = "SELECT * FROM cc_schedule ".
"WHERE instance_id IN (".implode($instanceIds, ",").")";
$ccSchedules = Application_Common_Database::prepareAndExecute(
$schedule_sql);
if (count($ccSchedules) > 0) {
/* Find the show contents of just one of the instances. It doesn't
* matter which instance we use since all the content is the same
*/
$ccSchedule = $ccSchedules[0];
$showStamp_sql = "SELECT * FROM cc_schedule ".
"WHERE instance_id = {$ccSchedule["instance_id"]} ".
"ORDER BY starts";
$showStamp = Application_Common_Database::prepareAndExecute(
$showStamp_sql);
//get time_filled so we can update cc_show_instances
$timeFilled_sql = "SELECT time_filled FROM cc_show_instances ".
"WHERE id = {$ccSchedule["instance_id"]}";
$timeFilled = Application_Common_Database::prepareAndExecute(
$timeFilled_sql, array(), Application_Common_Database::COLUMN);
//need to find out which linked instances are empty
$values = array();
foreach ($instanceIds as $id) {
$instanceSched_sql = "SELECT * FROM cc_schedule ".
"WHERE instance_id = {$id} ".
"ORDER by starts";
$ccSchedules = Application_Common_Database::prepareAndExecute(
$instanceSched_sql);
/* If the show instance is empty OR it has different content than
* the first instance, we need to fill/replace with the show stamp
* (The show stamp is taken from the first show instance's content)
*/
if (count($ccSchedules) < 1 ||
self::replaceInstanceContentCheck($ccSchedules, $showStamp)) {
$instanceStart_sql = "SELECT starts FROM cc_show_instances ".
"WHERE id = {$id} ".
"ORDER BY starts";
$nextStartDT = new DateTime(
Application_Common_Database::prepareAndExecute(
$instanceStart_sql, array(), Application_Common_Database::COLUMN),
new DateTimeZone("UTC"));
foreach ($showStamp as $item) {
$endTimeDT = self::findEndTime($nextStartDT, $item["clip_length"]);
if (is_null($item["file_id"])) {
$item["file_id"] = "null";
}
if (is_null($item["stream_id"])) {
$item["stream_id"] = "null";
}
$values[] = "(".
"'{$nextStartDT->format("Y-m-d H:i:s")}', ".
"'{$endTimeDT->format("Y-m-d H:i:s")}', ".
"'{$item["clip_length"]}', ".
"'{$item["fade_in"]}', ".
"'{$item["fade_out"]}', ".
"'{$item["cue_in"]}', ".
"'{$item["cue_out"]}', ".
"{$item["file_id"]}, ".
"{$item["stream_id"]}, ".
"{$id}, ".
"{$item["position"]})";
$nextStartDT = self::findTimeDifference($endTimeDT,
Application_Model_Preference::GetDefaultCrossfadeDuration());
} //foreach show item
}
} //foreach linked instance
if (!empty($values)) {
$insert_sql = "INSERT INTO cc_schedule (starts, ends, ".
"clip_length, fade_in, fade_out, cue_in, cue_out, ".
"file_id, stream_id, instance_id, position) VALUES ".
implode($values, ",");
Application_Common_Database::prepareAndExecute(
$insert_sql, array(), Application_Common_Database::EXECUTE);
//update time_filled in cc_show_instances
$now = gmdate("Y-m-d H:i:s");
$update_sql = "UPDATE cc_show_instances SET ".
"time_filled = '{$timeFilled}', ".
"last_scheduled = '{$now}' ".
"WHERE show_id = {$ccShow->getDbId()}";
Application_Common_Database::prepareAndExecute(
$update_sql, array(), Application_Common_Database::EXECUTE);
}
} //if at least one linked instance has content
}
public static function fillPreservedLinkedShowContent($ccShow, $showStamp)
{
$item = $showStamp->getFirst();
$timeFilled = $item->getCcShowInstances()->getDbTimeFilled();
foreach ($ccShow->getCcShowInstancess() as $ccShowInstance) {
$ccSchedules = CcScheduleQuery::create()
->filterByDbInstanceId($ccShowInstance->getDbId())
->find();
if ($ccSchedules->isEmpty()) {
$nextStartDT = $ccShowInstance->getDbStarts(null);
foreach ($showStamp as $item) {
$endTimeDT = self::findEndTime($nextStartDT, $item->getDbClipLength());
$ccSchedule = new CcSchedule();
$ccSchedule
->setDbStarts($nextStartDT)
->setDbEnds($endTimeDT)
->setDbFileId($item->getDbFileId())
->setDbStreamId($item->getDbStreamId())
->setDbClipLength($item->getDbClipLength())
->setDbFadeIn($item->getDbFadeIn())
->setDbFadeOut($item->getDbFadeOut())
->setDbCuein($item->getDbCueIn())
->setDbCueOut($item->getDbCueOut())
->setDbInstanceId($ccShowInstance->getDbId())
->setDbPosition($item->getDbPosition())
->save();
$nextStartDT = self::findTimeDifference($endTimeDT,
Application_Model_Preference::GetDefaultCrossfadeDuration());
} //foreach show item
$ccShowInstance
->setDbTimeFilled($timeFilled)
->setDbLastScheduled(gmdate("Y-m-d H:i:s"))
->save();
}
}
}
private static function replaceInstanceContentCheck($currentShowStamp, $showStamp)
{
$counter = 0;
foreach ($showStamp as $item) {
if ($item["file_id"] != $currentShowStamp[$counter]["file_id"] ||
$item["stream_id"] != $currentShowStamp[$counter]["stream_id"]) {
/*CcScheduleQuery::create()
->filterByDbInstanceId($ccShowInstance->getDbId())
->delete();*/
$delete_sql = "DELETE FROM cc_schedule ".
"WHERE instance_id = {$currentShowStamp[$counter]["instance_id"]}";
Application_Common_Database::prepareAndExecute(
$delete_sql, array(), Application_Common_Database::EXECUTE);
return true;
}
$counter += 1;
}
/* If we get here, the content in the show instance is the same
* as what we want to replace it with, so we can leave as is
*/
return false;
}
public function emptyShowContent($instanceId)
{
try {
$ccShowInstance = CcShowInstancesQuery::create()->findPk($instanceId);
$instances = array();
$instanceIds = array();
if ($ccShowInstance->getCcShow()->isLinked()) {
foreach ($ccShowInstance->getCcShow()->getFutureCcShowInstancess() as $instance) {
$instanceIds[] = $instance->getDbId();
$instances[] = $instance;
}
} else {
$instanceIds[] = $ccShowInstance->getDbId();
$instances[] = $ccShowInstance;
}
/* Get the file ids of the tracks we are about to delete
* from cc_schedule. We need these so we can update the
* is_scheduled flag in cc_files
*/
$ccSchedules = CcScheduleQuery::create()
->filterByDbInstanceId($instanceIds, Criteria::IN)
->setDistinct(CcSchedulePeer::FILE_ID)
->find();
$fileIds = array();
foreach ($ccSchedules as $ccSchedule) {
$fileIds[] = $ccSchedule->getDbFileId();
}
/* Clear out the schedule */
CcScheduleQuery::create()
->filterByDbInstanceId($instanceIds, Criteria::IN)
->delete();
/* Now that the schedule has been cleared we need to make
* sure we do not update the is_scheduled flag for tracks
* that are scheduled in other shows
*/
$futureScheduledFiles = Application_Model_Schedule::getAllFutureScheduledFiles();
foreach ($fileIds as $k => $v) {
if (in_array($v, $futureScheduledFiles)) {
unset($fileIds[$k]);
}
}
$selectCriteria = new Criteria();
$selectCriteria->add(CcFilesPeer::ID, $fileIds, Criteria::IN);
$updateCriteria = new Criteria();
$updateCriteria->add(CcFilesPeer::IS_SCHEDULED, false);
BasePeer::doUpdate($selectCriteria, $updateCriteria, Propel::getConnection());
Application_Model_RabbitMq::PushSchedule();
$con = Propel::getConnection(CcShowInstancesPeer::DATABASE_NAME);
foreach ($instances as $instance) {
$instance->updateDbTimeFilled($con);
}
return true;
} catch (Exception $e) {
Logging::info($e->getMessage());
return false;
}
}
/*
* TODO in the future this should probably support webstreams.
*/
public function updateFutureIsScheduled($scheduleId, $status)
{
$sched = CcScheduleQuery::create()->findPk($scheduleId);
$redraw = false;
if (isset($sched)) {
$fileId = $sched->getDbFileId();
if (isset($fileId)) {
$redraw = Application_Model_StoredFile::setIsScheduled($fileId, $status);
}
}
return $redraw;
}
}