123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257 |
- <?php
- class Application_Model_Scheduler
- {
- 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 $user;
- private $crossfadeDuration;
- private $applyCrossfades = true;
- 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"));
- }
- $this->user = Application_Model_User::getCurrentUser();
- $this->crossfadeDuration = Application_Model_Preference::GetDefaultCrossfadeDuration();
- }
- public function setCheckUserPermissions($value)
- {
- $this->checkUserPermissions = $value;
- }
- private function validateItemMove($itemsToMove, $destination)
- {
- $destinationInstanceId = $destination["instance"];
- $destinationCcShowInstance = CcShowInstancesQuery::create()
- ->findPk($destinationInstanceId);
- $isDestinationLinked = $destinationCcShowInstance->getCcShow()->isLinked();
- foreach ($itemsToMove as $itemToMove) {
- $sourceInstanceId = $itemToMove["instance"];
- $ccShowInstance = CcShowInstancesQuery::create()
- ->findPk($sourceInstanceId);
- //does the item being moved belong to a linked show
- $isSourceLinked = $ccShowInstance->getCcShow()->isLinked();
- if ($isDestinationLinked && !$isSourceLinked) {
- throw new Exception("Cannot move items into linked shows");
- } elseif (!$isDestinationLinked && $isSourceLinked) {
- throw new Exception("Cannot move items out of linked shows");
- } elseif ($isSourceLinked && $sourceInstanceId != $destinationInstanceId) {
- throw new Exception(_("Cannot move items out of linked shows"));
- }
- }
- }
- /*
- * make sure any incoming requests for scheduling are ligit.
- *
- * @param array $items, an array containing pks of cc_schedule items.
- */
- private function validateRequest($items, $addAction=false)
- {
- //$items is where tracks get inserted (they are schedule locations)
- $nowEpoch = floatval($this->nowDT->format("U.u"));
- $schedInfo = array();
- $instanceInfo = array();
- for ($i = 0; $i < count($items); $i++) {
- $id = $items[$i]["id"];
- //could be added to the beginning of a show, which sends id = 0;
- if ($id > 0) {
- //schedule_id of where we are inserting after?
- $schedInfo[$id] = $items[$i]["instance"];
- }
- //format is instance_id => timestamp
- $instanceInfo[$items[$i]["instance"]] = $items[$i]["timestamp"];
- }
- if (count($instanceInfo) === 0) {
- throw new Exception("Invalid Request.");
- }
- $schedIds = array();
- if (count($schedInfo) > 0) {
- $schedIds = array_keys($schedInfo);
- }
- $schedItems = CcScheduleQuery::create()->findPKs($schedIds, $this->con);
- $instanceIds = array_keys($instanceInfo);
- $showInstances = CcShowInstancesQuery::create()->findPKs($instanceIds, $this->con);
- //an item has been deleted
- if (count($schedIds) !== count($schedItems)) {
- throw new OutDatedScheduleException(_("The schedule you're viewing is out of date! (sched mismatch)"));
- }
- //a show has been deleted
- if (count($instanceIds) !== count($showInstances)) {
- throw new OutDatedScheduleException(_("The schedule you're viewing is out of date! (instance mismatch)"));
- }
- foreach ($schedItems as $schedItem) {
- $id = $schedItem->getDbId();
- $instance = $schedItem->getCcShowInstances($this->con);
- if (intval($schedInfo[$id]) !== $instance->getDbId()) {
- throw new OutDatedScheduleException(_("The schedule you're viewing is out of date!"));
- }
- }
- foreach ($showInstances as $instance) {
- $id = $instance->getDbId();
- $show = $instance->getCcShow($this->con);
- if ($this->checkUserPermissions && $this->user->canSchedule($show->getDbId()) === false) {
- throw new Exception(sprintf(_("You are not allowed to schedule show %s."), $show->getDbName()));
- }
- if ($instance->getDbRecord()) {
- throw new Exception(_("You cannot add files to recording shows."));
- }
- $showEndEpoch = floatval($instance->getDbEnds("U.u"));
- if ($showEndEpoch < $nowEpoch) {
- throw new OutDatedScheduleException(sprintf(_("The show %s is over and cannot be scheduled."), $show->getDbName()));
- }
- $ts = intval($instanceInfo[$id]);
- $lastSchedTs = intval($instance->getDbLastScheduled("U")) ? : 0;
- if ($ts < $lastSchedTs) {
- Logging::info("ts {$ts} last sched {$lastSchedTs}");
- throw new OutDatedScheduleException(sprintf(_("The show %s has been previously updated!"), $show->getDbName()));
- }
- /*
- * Does the afterItem belong to a show that is linked AND
- * currently playing?
- * If yes, throw an exception
- */
- if ($addAction) {
- $ccShow = $instance->getCcShow();
- if ($ccShow->isLinked()) {
- //get all the linked shows instances and check if
- //any of them are currently playing
- $ccShowInstances = $ccShow->getCcShowInstancess();
- $timeNowUTC = gmdate("Y-m-d H:i:s");
- foreach ($ccShowInstances as $ccShowInstance) {
- if ($ccShowInstance->getDbStarts() <= $timeNowUTC &&
- $ccShowInstance->getDbEnds() > $timeNowUTC) {
- throw new Exception(_("Content in linked shows must be scheduled before or after any one is broadcasted"));
- }
- }
- }
- }
- }
- }
-
- private function validateMediaItems($mediaItems)
- {
- foreach ($mediaItems as $mediaItem)
- {
- $id = $mediaItem["id"];
- if ($mediaItem["type"] === "playlist")
- {
- $playlist = new Application_Model_Playlist($id, $this->con);
- if ($playlist->containsMissingFiles()) {
- throw new Exception(_("Cannot schedule a playlist that contains missing files."));
- }
- }
- }
- return true;
- }
- /*
- * @param $id
- * @param $type
- *
- * @return $files
- */
- private function retrieveMediaFiles($id, $type)
- {
- $files = array();
- if ($type === "audioclip") {
- $file = CcFilesQuery::create()->findPK($id, $this->con);
- if (is_null($file) || !$file->visible()) {
- throw new Exception(_("A selected File does not exist!"));
- } else {
- $data = $this->fileInfo;
- $data["id"] = $id;
- $cuein = Application_Common_DateHelper::playlistTimeToSeconds($file->getDbCuein());
- $cueout = Application_Common_DateHelper::playlistTimeToSeconds($file->getDbCueout());
- $row_length = Application_Common_DateHelper::secondsToPlaylistTime($cueout - $cuein);
- $data["cliplength"] = $row_length;
- $data["cuein"] = $file->getDbCuein();
- $data["cueout"] = $file->getDbCueout();
- //fade is in format SS.uuuuuu
- $data["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
- $data["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
- $files[] = $data;
- }
- } elseif ($type === "playlist") {
- $pl = new Application_Model_Playlist($id);
- $contents = $pl->getContents();
- foreach ($contents as $plItem) {
- if ($plItem['type'] == 0) {
- $data["id"] = $plItem['item_id'];
- $data["cliplength"] = $plItem['length'];
- $data["cuein"] = $plItem['cuein'];
- $data["cueout"] = $plItem['cueout'];
- $data["fadein"] = $plItem['fadein'];
- $data["fadeout"] = $plItem['fadeout'];
- $data["type"] = 0;
- $files[] = $data;
- } elseif ($plItem['type'] == 1) {
- $data["id"] = $plItem['item_id'];
- $data["cliplength"] = $plItem['length'];
- $data["cuein"] = $plItem['cuein'];
- $data["cueout"] = $plItem['cueout'];
- $data["fadein"] = "00.500000";//$plItem['fadein'];
- $data["fadeout"] = "00.500000";//$plItem['fadeout'];
- $data["type"] = 1;
- $files[] = $data;
- } elseif ($plItem['type'] == 2) {
- // if it's a block
- $bl = new Application_Model_Block($plItem['item_id']);
- if ($bl->isStatic()) {
- foreach ($bl->getContents() as $track) {
- $data["id"] = $track['item_id'];
- $data["cliplength"] = $track['length'];
- $data["cuein"] = $track['cuein'];
- $data["cueout"] = $track['cueout'];
- $data["fadein"] = $track['fadein'];
- $data["fadeout"] = $track['fadeout'];
- $data["type"] = 0;
- $files[] = $data;
- }
- } else {
- $defaultFadeIn = Application_Model_Preference::GetDefaultFadeIn();
- $defaultFadeOut = Application_Model_Preference::GetDefaultFadeOut();
- $dynamicFiles = $bl->getListOfFilesUnderLimit();
- foreach ($dynamicFiles as $f) {
- $fileId = $f['id'];
- $file = CcFilesQuery::create()->findPk($fileId);
- if (isset($file) && $file->visible()) {
- $data["id"] = $file->getDbId();
- $data["cuein"] = $file->getDbCuein();
- $data["cueout"] = $file->getDbCueout();
- $cuein = Application_Common_DateHelper::calculateLengthInSeconds($data["cuein"]);
- $cueout = Application_Common_DateHelper::calculateLengthInSeconds($data["cueout"]);
- $data["cliplength"] = Application_Common_DateHelper::secondsToPlaylistTime($cueout - $cuein);
- //fade is in format SS.uuuuuu
- $data["fadein"] = $defaultFadeIn;
- $data["fadeout"] = $defaultFadeOut;
- $data["type"] = 0;
- $files[] = $data;
- }
- }
- }
- }
- }
- } elseif ($type == "stream") {
- //need to return
- $stream = CcWebstreamQuery::create()->findPK($id, $this->con);
- if (is_null($stream) /* || !$file->visible() */) {
- throw new Exception(_("A selected File does not exist!"));
- } else {
- $data = $this->fileInfo;
- $data["id"] = $id;
- $data["cliplength"] = $stream->getDbLength();
- $data["cueout"] = $stream->getDbLength();
- $data["type"] = 1;
- //fade is in format SS.uuuuuu
- $data["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
- $data["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
- $files[] = $data;
- }
- } elseif ($type == "block") {
- $bl = new Application_Model_Block($id);
- if ($bl->isStatic()) {
- foreach ($bl->getContents() as $track) {
- $data["id"] = $track['item_id'];
- $data["cliplength"] = $track['length'];
- $data["cuein"] = $track['cuein'];
- $data["cueout"] = $track['cueout'];
- $data["fadein"] = $track['fadein'];
- $data["fadeout"] = $track['fadeout'];
- $data["type"] = 0;
- $files[] = $data;
- }
- } else {
- $defaultFadeIn = Application_Model_Preference::GetDefaultFadeIn();
- $defaultFadeOut = Application_Model_Preference::GetDefaultFadeOut();
- $dynamicFiles = $bl->getListOfFilesUnderLimit();
- foreach ($dynamicFiles as $f) {
- $fileId = $f['id'];
- $file = CcFilesQuery::create()->findPk($fileId);
- if (isset($file) && $file->visible()) {
- $data["id"] = $file->getDbId();
- $data["cuein"] = $file->getDbCuein();
- $data["cueout"] = $file->getDbCueout();
- $cuein = Application_Common_DateHelper::calculateLengthInSeconds($data["cuein"]);
- $cueout = Application_Common_DateHelper::calculateLengthInSeconds($data["cueout"]);
- $data["cliplength"] = Application_Common_DateHelper::secondsToPlaylistTime($cueout - $cuein);
- //fade is in format SS.uuuuuu
- $data["fadein"] = $defaultFadeIn;
- $data["fadeout"] = $defaultFadeOut;
- $data["type"] = 0;
- $files[] = $data;
- }
- }
- }
- }
- return $files;
- }
- /*
- * @param DateTime startDT in UTC
- * @param string duration
- * in format H:i:s.u (could be more that 24 hours)
- *
- * @return DateTime endDT in UTC
- */
- private 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;
- }
- /*
- * @param DateTime startDT in UTC
- * @param string duration
- * in format H:i:s.u (could be more that 24 hours)
- *
- * @return DateTime endDT in UTC
- */
- private function findEndTime($p_startDT, $p_duration)
- {
- $startEpoch = $p_startDT->format("U.u");
- $durationSeconds = Application_Common_DateHelper::playlistTimeToSeconds($p_duration);
- //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 function findNextStartTime($DT, $instanceId)
- {
- $sEpoch = $DT->format("U.u");
- $nEpoch = $this->epochNow;
- //check for if the show has started.
- if (bccomp( $nEpoch , $sEpoch , 6) === 1) {
- $this->applyCrossfades = false;
- //need some kind of placeholder for cc_schedule.
- //playout_status will be -1.
- $nextDT = $this->nowDT;
- $length = bcsub($nEpoch , $sEpoch , 6);
- $cliplength = Application_Common_DateHelper::secondsToPlaylistTime($length);
- //fillers are for only storing a chunk of time space that has already passed.
- $filler = new CcSchedule();
- $filler->setDbStarts($DT)
- ->setDbEnds($this->nowDT)
- ->setDbClipLength($cliplength)
- ->setDbCueIn('00:00:00')
- ->setDbCueOut('00:00:00')
- ->setDbPlayoutStatus(-1)
- ->setDbInstanceId($instanceId)
- ->save($this->con);
- } else {
- $nextDT = $DT;
- }
- return $nextDT;
- }
- /*
- * @param int $showInstance
- * This function recalculates the start/end times of items in a gapless show to
- * account for crossfade durations.
- */
- private function calculateCrossfades($instanceId)
- {
- Logging::info("adjusting start, end times of scheduled items to account for crossfades show instance #".$instanceId);
- $sql = "SELECT * FROM cc_show_instances ".
- "WHERE id = {$instanceId}";
- $instance = Application_Common_Database::prepareAndExecute(
- $sql, array(), Application_Common_Database::SINGLE);
- if (is_null($instance)) {
- throw new OutDatedScheduleException(_("The schedule you're viewing is out of date!"));
- }
- $itemStartDT = new DateTime($instance["starts"], new DateTimeZone("UTC"));
- $itemEndDT = null;
- $schedule_sql = "SELECT * FROM cc_schedule ".
- "WHERE instance_id = {$instanceId} ".
- "ORDER BY starts";
- $schedule = Application_Common_Database::prepareAndExecute($schedule_sql);
- foreach ($schedule as $item) {
- $itemEndDT = $this->findEndTime($itemStartDT, $item["clip_length"]);
- $update_sql = "UPDATE cc_schedule SET ".
- "starts = '{$itemStartDT->format("Y-m-d H:i:s.u")}', ".
- "ends = '{$itemEndDT->format("Y-m-d H:i:s.u")}' ".
- "WHERE id = {$item["id"]}";
- Application_Common_Database::prepareAndExecute(
- $update_sql, array(), Application_Common_Database::EXECUTE);
- $itemStartDT = $this->findTimeDifference($itemEndDT, $this->crossfadeDuration);
- }
- }
- /*
- * @param int $showInstance
- * @param array $exclude
- * ids of sched items to remove from the calulation.
- * This function squeezes all items of a show together so that
- * there are no gaps between them.
- */
- public function removeGaps($showInstance, $exclude=null)
- {
- Logging::info("removing gaps from show instance #".$showInstance);
- $instance = CcShowInstancesQuery::create()->findPK($showInstance, $this->con);
- if (is_null($instance)) {
- throw new OutDatedScheduleException(_("The schedule you're viewing is out of date!"));
- }
- $itemStartDT = $instance->getDbStarts(null);
- $schedule = CcScheduleQuery::create()
- ->filterByDbInstanceId($showInstance)
- ->filterByDbId($exclude, Criteria::NOT_IN)
- ->orderByDbStarts()
- ->find($this->con);
- foreach ($schedule as $item) {
- $itemEndDT = $this->findEndTime($itemStartDT, $item->getDbClipLength());
- $item->setDbStarts($itemStartDT)
- ->setDbEnds($itemEndDT);
- $itemStartDT = $itemEndDT;
- }
- $schedule->save($this->con);
- }
- /**
- *
- * Enter description here ...
- * @param $scheduleItems
- * cc_schedule items, where the items get inserted after
- * @param $filesToInsert
- * array of schedule item info, what gets inserted into cc_schedule
- * @param $adjustSched
- */
- private function insertAfter($scheduleItems, $mediaItems, $filesToInsert=null, $adjustSched=true, $moveAction=false)
- {
- try {
- // temporary fix for CC-5665
- set_time_limit(180);
- $affectedShowInstances = array();
- //dont want to recalculate times for moved items
- //only moved items have a sched_id
- $excludeIds = array();
- $startProfile = microtime(true);
- $temp = array();
- $instance = null;
- /* Items in shows are ordered by position number. We need to know
- * the position when adding/moving items in linked shows so they are
- * added or moved in the correct position
- */
- $pos = 0;
- $linked = false;
- foreach ($scheduleItems as $schedule) {
- //reset
- $this->applyCrossfades = true;
- $id = intval($schedule["id"]);
- /* Find out if the show where the cursor position (where an item will
- * be inserted) is located is linked or not. If the show is linked,
- * we need to make sure there isn't another cursor selection in one of it's
- * linked shows. If there is that will cause a duplication, in the least,
- * of inserted items
- */
- if ($id != 0) {
- $schedule_sql = "SELECT * FROM cc_schedule WHERE id = ".$id;
- $ccSchedule = Application_Common_Database::prepareAndExecute(
- $schedule_sql, array(), Application_Common_Database::SINGLE);
- $show_sql = "SELECT * FROM cc_show WHERE id IN (".
- "SELECT show_id FROM cc_show_instances WHERE id = ".$ccSchedule["instance_id"].")";
- $ccShow = Application_Common_Database::prepareAndExecute(
- $show_sql, array(), Application_Common_Database::SINGLE);
- $linked = $ccShow["linked"];
- if ($linked) {
- $unique = $ccShow["id"] . $ccSchedule["position"];
- if (!in_array($unique, $temp)) {
- $temp[] = $unique;
- } else {
- continue;
- }
- }
- } else {
- $show_sql = "SELECT * FROM cc_show WHERE id IN (".
- "SELECT show_id FROM cc_show_instances WHERE id = ".$schedule["instance"].")";
- $ccShow = Application_Common_Database::prepareAndExecute(
- $show_sql, array(), Application_Common_Database::SINGLE);
- $linked = $ccShow["linked"];
- if ($linked) {
- $unique = $ccShow["id"] . "a";
- if (!in_array($unique, $temp)) {
- $temp[] = $unique;
- } else {
- continue;
- }
- }
- }
- /* If the show where the cursor position is located is linked
- * we need to insert the items for each linked instance belonging
- * to that show
- */
- if ($linked) {
- $instances = CcShowInstancesQuery::create()
- ->filterByDbShowId($ccShow["id"])
- ->filterByDbStarts(gmdate("Y-m-d H:i:s"), Criteria::GREATER_THAN)
- ->find();
- } else {
- $instances = CcShowInstancesQuery::create()
- ->filterByDbId($schedule["instance"])
- ->find();
- }
- $excludePositions = array();
- foreach($instances as &$instance) {
- //reset
- $this->applyCrossfades = true;
- //$instanceId = $instance["id"];
- $instanceId = $instance->getDbId();
- if ($id !== 0) {
- /* We use the selected cursor's position to find the same
- * positions in every other linked instance
- */
- $pos = $ccSchedule["position"];
- $linkedItem_sql = "SELECT ends FROM cc_schedule ".
- "WHERE instance_id = {$instanceId} ".
- "AND position = {$pos} ".
- "AND playout_status != -1";
- $linkedItemEnds = Application_Common_Database::prepareAndExecute(
- $linkedItem_sql, array(), Application_Common_Database::COLUMN);
- if (!$linkedItemEnds) {
- //With dynamic smart blocks there may be different number of items in
- //each show. In case the position does not exist we need to select
- //the end time of the last position
- $maxPos_sql = "SELECT max(position) from cc_schedule ".
- "WHERE instance_id = {$instanceId}";
- $pos = Application_Common_Database::prepareAndExecute(
- $maxPos_sql, array(), Application_Common_Database::COLUMN);
- //show instance has no scheduled tracks
- if (empty($pos)) {
- $pos = 0;
- $nextStartDT = new DateTime($instance->getDbStarts(), new DateTimeZone("UTC"));
- } else {
- $linkedItem_sql = "SELECT ends FROM cc_schedule ".
- "WHERE instance_id = {$instanceId} ".
- "AND position = {$pos} ".
- "AND playout_status != -1";
- $linkedItemEnds = Application_Common_Database::prepareAndExecute(
- $linkedItem_sql, array(), Application_Common_Database::COLUMN);
- $nextStartDT = $this->findNextStartTime(
- new DateTime($linkedItemEnds, new DateTimeZone("UTC")),
- $instanceId);
- }
- } else {
- $nextStartDT = $this->findNextStartTime(
- new DateTime($linkedItemEnds, new DateTimeZone("UTC")),
- $instanceId);
- $pos++;
- }
- //$pos++;
- }
- //selected empty row to add after
- else {
- $showStartDT = new DateTime($instance->getDbStarts(), new DateTimeZone("UTC"));
- $nextStartDT = $this->findNextStartTime($showStartDT, $instanceId);
- //first item in show so start position counter at 0
- $pos = 0;
- /* Show is empty so we don't need to calculate crossfades
- * for the first inserted item
- */
- $this->applyCrossfades = false;
- }
- if (!in_array($instanceId, $affectedShowInstances)) {
- $affectedShowInstances[] = $instanceId;
- }
- /*
- * $adjustSched is true if there are schedule items
- * following the item just inserted, per show instance
- */
- if ($adjustSched === true) {
- $pstart = microtime(true);
- if ($this->applyCrossfades) {
- $initalStartDT = clone $this->findTimeDifference(
- $nextStartDT, $this->crossfadeDuration);
- } else {
- $initalStartDT = clone $nextStartDT;
- }
- $pend = microtime(true);
- Logging::debug("finding all following items.");
- Logging::debug(floatval($pend) - floatval($pstart));
- }
- if (is_null($filesToInsert)) {
- $filesToInsert = array();
- foreach ($mediaItems as $media) {
- $filesToInsert = array_merge($filesToInsert,
- $this->retrieveMediaFiles($media["id"], $media["type"]));
- }
- }
- $doInsert = false;
- $doUpdate = false;
- $values = array();
- //array that stores the cc_file ids so we can update the is_scheduled flag
- $fileIds = array();
- foreach ($filesToInsert as &$file) {
- //item existed previously and is being moved.
- //need to keep same id for resources if we want REST.
- if (isset($file['sched_id'])) {
- $adjustFromDT = clone $nextStartDT;
- $doUpdate = true;
- $movedItem_sql = "SELECT * FROM cc_schedule ".
- "WHERE id = ".$file["sched_id"];
- $sched = Application_Common_Database::prepareAndExecute(
- $movedItem_sql, array(), Application_Common_Database::SINGLE);
- /* We need to keep a record of the original positon a track
- * is being moved from so we can use it to retrieve the correct
- * items in linked instances
- */
- if (!isset($originalPosition)) {
- $originalPosition = $sched["position"];
- }
- /* If we are moving an item in a linked show we need to get
- * the relative item to move in each instance. We know what the
- * relative item is by its position
- */
- if ($linked) {
- $movedItem_sql = "SELECT * FROM cc_schedule ".
- "WHERE position = {$originalPosition} ".
- "AND instance_id = {$instanceId}";
- $sched = Application_Common_Database::prepareAndExecute(
- $movedItem_sql, array(), Application_Common_Database::SINGLE);
- }
- /* If we don't find a schedule item it means the linked
- * shows have a different amount of items (dyanmic block)
- * and we should skip the item move for this show instance
- */
- if (!$sched) {
- continue;
- }
- $excludeIds[] = intval($sched["id"]);
- $file["cliplength"] = $sched["clip_length"];
- $file["cuein"] = $sched["cue_in"];
- $file["cueout"] = $sched["cue_out"];
- $file["fadein"] = $sched["fade_in"];
- $file["fadeout"] = $sched["fade_out"];
- } else {
- $doInsert = true;
- }
- // default fades are in seconds
- // we need to convert to '00:00:00' format
- $file['fadein'] = Application_Common_DateHelper::secondsToPlaylistTime($file['fadein']);
- $file['fadeout'] = Application_Common_DateHelper::secondsToPlaylistTime($file['fadeout']);
- switch ($file["type"]) {
- case 0:
- $fileId = $file["id"];
- $streamId = "null";
- $fileIds[] = $fileId;
- break;
- case 1:
- $streamId = $file["id"];
- $fileId = "null";
- break;
- default: break;
- }
- if ($this->applyCrossfades) {
- $nextStartDT = $this->findTimeDifference($nextStartDT,
- $this->crossfadeDuration);
- $endTimeDT = $this->findEndTime($nextStartDT, $file['cliplength']);
- $endTimeDT = $this->findTimeDifference($endTimeDT, $this->crossfadeDuration);
- /* Set it to false because the rest of the crossfades
- * will be applied after we insert each item
- */
- $this->applyCrossfades = false;
- }
- $endTimeDT = $this->findEndTime($nextStartDT, $file['cliplength']);
- if ($doInsert) {
- $values[] = "(".
- "'{$nextStartDT->format("Y-m-d H:i:s.u")}', ".
- "'{$endTimeDT->format("Y-m-d H:i:s.u")}', ".
- "'{$file["cuein"]}', ".
- "'{$file["cueout"]}', ".
- "'{$file["fadein"]}', ".
- "'{$file["fadeout"]}', ".
- "'{$file["cliplength"]}', ".
- "{$pos}, ".
- "{$instanceId}, ".
- "{$fileId}, ".
- "{$streamId})";
- } elseif ($doUpdate) {
- $update_sql = "UPDATE cc_schedule SET ".
- "starts = '{$nextStartDT->format("Y-m-d H:i:s.u")}', ".
- "ends = '{$endTimeDT->format("Y-m-d H:i:s.u")}', ".
- "cue_in = '{$file["cuein"]}', ".
- "cue_out = '{$file["cueout"]}', ".
- "fade_in = '{$file["fadein"]}', ".
- "fade_out = '{$file["fadeout"]}', ".
- "clip_length = '{$file["cliplength"]}', ".
- "position = {$pos}, ".
- "instance_id = {$instanceId} ".
- "WHERE id = {$sched["id"]}";
- Application_Common_Database::prepareAndExecute(
- $update_sql, array(), Application_Common_Database::EXECUTE);
- }
- $nextStartDT = $this->findTimeDifference($endTimeDT, $this->crossfadeDuration);
- $pos++;
- }//all files have been inserted/moved
- if ($doInsert) {
- $insert_sql = "INSERT INTO cc_schedule ".
- "(starts, ends, cue_in, cue_out, fade_in, fade_out, ".
- "clip_length, position, instance_id, file_id, stream_id) VALUES ".
- implode($values, ",")." RETURNING id";
- $stmt = $this->con->prepare($insert_sql);
- if ($stmt->execute()) {
- foreach ($stmt->fetchAll() as $row) {
- $excludeIds[] = $row["id"];
- }
- };
- }
- $selectCriteria = new Criteria();
- $selectCriteria->add(CcFilesPeer::ID, $fileIds, Criteria::IN);
- $selectCriteria->addAnd(CcFilesPeer::IS_SCHEDULED, false);
- $updateCriteria = new Criteria();
- $updateCriteria->add(CcFilesPeer::IS_SCHEDULED, true);
- BasePeer::doUpdate($selectCriteria, $updateCriteria, $this->con);
- /* Reset files to insert so we can get a new set of files. We have
- * to do this in case we are inserting a dynamic block
- */
- if (!$moveAction) {
- $filesToInsert = null;
- }
- if ($adjustSched === true) {
- $followingItems_sql = "SELECT * FROM cc_schedule ".
- "WHERE starts >= '{$initalStartDT->format("Y-m-d H:i:s.u")}' ".
- "AND instance_id = {$instanceId} ";
- if (count($excludeIds) > 0) {
- $followingItems_sql .= "AND id NOT IN (". implode($excludeIds, ",").") ";
- }
- $followingItems_sql .= "ORDER BY starts";
- $followingSchedItems = Application_Common_Database::prepareAndExecute(
- $followingItems_sql);
- $pstart = microtime(true);
- //recalculate the start/end times after the inserted items.
- foreach ($followingSchedItems as $item) {
- $endTimeDT = $this->findEndTime($nextStartDT, $item["clip_length"]);
- $endTimeDT = $this->findTimeDifference($endTimeDT, $this->crossfadeDuration);
- $update_sql = "UPDATE cc_schedule SET ".
- "starts = '{$nextStartDT->format("Y-m-d H:i:s.u")}', ".
- "ends = '{$endTimeDT->format("Y-m-d H:i:s.u")}', ".
- "position = {$pos} ".
- "WHERE id = {$item["id"]}";
- Application_Common_Database::prepareAndExecute(
- $update_sql, array(), Application_Common_Database::EXECUTE);
- $nextStartDT = $this->findTimeDifference($endTimeDT, $this->crossfadeDuration);
- $pos++;
- }
- $pend = microtime(true);
- Logging::debug("adjusting all following items.");
- Logging::debug(floatval($pend) - floatval($pstart));
- }
- if ($moveAction) {
- $this->calculateCrossfades($instanceId);
- }
- }//for each instance
- }//for each schedule location
- $endProfile = microtime(true);
- Logging::debug("finished adding scheduled items.");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
- //update the status flag in cc_schedule.
- $instances = CcShowInstancesQuery::create()
- ->filterByPrimaryKeys($affectedShowInstances)
- ->find($this->con);
- $startProfile = microtime(true);
- foreach ($instances as $instance) {
- $instance->updateScheduleStatus($this->con);
- }
- $endProfile = microtime(true);
- Logging::debug("updating show instances status.");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
- $startProfile = microtime(true);
- //update the last scheduled timestamp.
- CcShowInstancesQuery::create()
- ->filterByPrimaryKeys($affectedShowInstances)
- ->update(array('DbLastScheduled' => new DateTime("now", new DateTimeZone("UTC"))), $this->con);
- $endProfile = microtime(true);
- Logging::debug("updating last scheduled timestamp.");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
- } catch (Exception $e) {
- Logging::debug($e->getMessage());
- throw $e;
- }
- }
- private function updateMovedItem()
- {
- }
- private function getInstances($instanceId)
- {
- $ccShowInstance = CcShowInstancesQuery::create()->findPk($instanceId);
- $ccShow = $ccShowInstance->getCcShow();
- if ($ccShow->isLinked()) {
- return $ccShow->getFutureCcShowInstancess();
- } else {
- return array($ccShowInstance);
- }
- }
- /*
- * @param array $scheduleItems (schedule_id and instance_id it belongs to)
- * @param array $mediaItems (file|block|playlist|webstream)
- */
- public function scheduleAfter($scheduleItems, $mediaItems, $adjustSched = true)
- {
- $this->con->beginTransaction();
- try {
- $this->validateMediaItems($mediaItems); //Check for missing files, etc.
- $this->validateRequest($scheduleItems, true);
- /*
- * create array of arrays
- * array of schedule item info
- * (sched_id is the cc_schedule id and is set if an item is being
- * moved because it is already in cc_schedule)
- * [0] = Array(
- * id => 1,
- * cliplength => 00:04:32,
- * cuein => 00:00:00,
- * cueout => 00:04:32,
- * fadein => 00.5,
- * fadeout => 00.5,
- * sched_id => ,
- * type => 0)
- * [1] = Array(
- * id => 2,
- * cliplength => 00:05:07,
- * cuein => 00:00:00,
- * cueout => 00:05:07,
- * fadein => 00.5,
- * fadeout => 00.5,
- * sched_id => ,
- * type => 0)
- */
- $this->insertAfter($scheduleItems, $mediaItems, null, $adjustSched);
- $this->con->commit();
- Application_Model_RabbitMq::PushSchedule();
- } catch (Exception $e) {
- $this->con->rollback();
- throw $e;
- }
- }
- /*
- * @param array $selectedItem
- * @param array $afterItem
- */
- public function moveItem($selectedItems, $afterItems, $adjustSched = true)
- {
- $startProfile = microtime(true);
- $this->con->beginTransaction();
- $this->con->useDebug(true);
- try {
- $this->validateItemMove($selectedItems, $afterItems[0]);
- $this->validateRequest($selectedItems);
- $this->validateRequest($afterItems);
- $endProfile = microtime(true);
- Logging::debug("validating move request took:");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
- $afterInstance = CcShowInstancesQuery::create()->findPK($afterItems[0]["instance"], $this->con);
- //map show instances to cc_schedule primary keys.
- $modifiedMap = array();
- $movedData = array();
- //prepare each of the selected items.
- for ($i = 0; $i < count($selectedItems); $i++) {
- $selected = CcScheduleQuery::create()->findPk($selectedItems[$i]["id"], $this->con);
- $selectedInstance = $selected->getCcShowInstances($this->con);
- $data = $this->fileInfo;
- $data["id"] = $selected->getDbFileId();
- $data["cliplength"] = $selected->getDbClipLength();
- $data["cuein"] = $selected->getDbCueIn();
- $data["cueout"] = $selected->getDbCueOut();
- $data["fadein"] = $selected->getDbFadeIn();
- $data["fadeout"] = $selected->getDbFadeOut();
- $data["sched_id"] = $selected->getDbId();
- $movedData[] = $data;
- //figure out which items must be removed from calculated show times.
- $showInstanceId = $selectedInstance->getDbId();
- $schedId = $selected->getDbId();
- if (isset($modifiedMap[$showInstanceId])) {
- array_push($modifiedMap[$showInstanceId], $schedId);
- } else {
- $modifiedMap[$showInstanceId] = array($schedId);
- }
- }
- //calculate times excluding the to be moved items.
- foreach ($modifiedMap as $instance => $schedIds) {
- $startProfile = microtime(true);
- $this->removeGaps($instance, $schedIds);
- $endProfile = microtime(true);
- Logging::debug("removing gaps from instance $instance:");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
- }
- $startProfile = microtime(true);
- $this->insertAfter($afterItems, null, $movedData, $adjustSched, true);
- $endProfile = microtime(true);
- Logging::debug("inserting after removing gaps.");
- Logging::debug(floatval($endProfile) - floatval($startProfile));
- $modified = array_keys($modifiedMap);
- //need to adjust shows we have moved items from.
- foreach ($modified as $instanceId) {
- $instance = CcShowInstancesQuery::create()->findPK($instanceId, $this->con);
- $instance->updateScheduleStatus($this->con);
- }
- $this->con->useDebug(false);
- $this->con->commit();
- Application_Model_RabbitMq::PushSchedule();
- } catch (Exception $e) {
- $this->con->rollback();
- throw $e;
- }
- }
- public function removeItems($scheduledItems, $adjustSched = true, $cancelShow=false)
- {
- $showInstances = array();
- $this->con->beginTransaction();
- try {
- $this->validateRequest($scheduledItems);
- $scheduledIds = array();
- foreach ($scheduledItems as $item) {
- $scheduledIds[] = $item["id"];
- }
- $removedItems = CcScheduleQuery::create()->findPks($scheduledIds);
- // This array is used to keep track of every show instance that was
- // effected by the track deletion. It will be used later on to
- // remove gaps in the schedule and adjust crossfade times.
- $effectedInstanceIds = array();
- foreach ($removedItems as $removedItem) {
- $instance = $removedItem->getCcShowInstances($this->con);
- $effectedInstanceIds[] = $instance->getDbId();
- //check if instance is linked and if so get the schedule items
- //for all linked instances so we can delete them too
- if (!$cancelShow && $instance->getCcShow()->isLinked()) {
- //returns all linked instances if linked
- $ccShowInstances = $this->getInstances($instance->getDbId());
- $instanceIds = array();
- foreach ($ccShowInstances as $ccShowInstance) {
- $instanceIds[] = $ccShowInstance->getDbId();
- }
- $effectedInstanceIds = array_merge($effectedInstanceIds, $instanceIds);
-
- // Delete the same track, represented by $removedItem, in
- // each linked show instance.
- $itemsToDelete = CcScheduleQuery::create()
- ->filterByDbPosition($removedItem->getDbPosition())
- ->filterByDbInstanceId($instanceIds, Criteria::IN)
- ->filterByDbId($removedItem->getDbId(), Criteria::NOT_EQUAL)
- ->delete($this->con);
- }
-
- //check to truncate the currently playing item instead of deleting it.
- if ($removedItem->isCurrentItem($this->epochNow)) {
- $nEpoch = $this->epochNow;
- $sEpoch = $removedItem->getDbStarts('U.u');
- $length = bcsub($nEpoch , $sEpoch , 6);
- $cliplength = Application_Common_DateHelper::secondsToPlaylistTime($length);
- $cueinSec = Application_Common_DateHelper::playlistTimeToSeconds($removedItem->getDbCueIn());
- $cueOutSec = bcadd($cueinSec , $length, 6);
- $cueout = Application_Common_DateHelper::secondsToPlaylistTime($cueOutSec);
- //Set DbEnds - 1 second because otherwise there can be a timing issue
- //when sending the new schedule to Pypo where Pypo thinks the track is still
- //playing.
- $removedItem->setDbCueOut($cueout)
- ->setDbClipLength($cliplength)
- ->setDbEnds($this->nowDT)
- ->save($this->con);
- } else {
- $removedItem->delete($this->con);
- }
- }
- Application_Model_StoredFile::updatePastFilesIsScheduled();
- if ($adjustSched === true) {
- foreach ($effectedInstanceIds as $instance) {
- $this->removeGaps($instance);
- $this->calculateCrossfades($instance);
- }
- }
- //update the status flag in cc_schedule.
- $instances = CcShowInstancesQuery::create()
- ->filterByPrimaryKeys($effectedInstanceIds)
- ->find($this->con);
- foreach ($instances as $instance) {
- $instance->updateScheduleStatus($this->con);
- $instance->correctSchedulePositions();
- }
- //update the last scheduled timestamp.
- CcShowInstancesQuery::create()
- ->filterByPrimaryKeys($showInstances)
- ->update(array('DbLastScheduled' => new DateTime("now", new DateTimeZone("UTC"))), $this->con);
- $this->con->commit();
- Application_Model_RabbitMq::PushSchedule();
- } catch (Exception $e) {
- $this->con->rollback();
- throw $e;
- }
- }
- /*
- * Used for cancelling the current show instance.
- *
- * @param $p_id id of the show instance to cancel.
- */
- public function cancelShow($p_id)
- {
- $this->con->beginTransaction();
- try {
- $instance = CcShowInstancesQuery::create()->findPK($p_id);
- if (!$instance->getDbRecord()) {
- $items = CcScheduleQuery::create()
- ->filterByDbInstanceId($p_id)
- ->filterByDbEnds($this->nowDT, Criteria::GREATER_THAN)
- ->find($this->con);
- if (count($items) > 0) {
- $remove = array();
- $ts = $this->nowDT->format('U');
- for ($i = 0; $i < count($items); $i++) {
- $remove[$i]["instance"] = $p_id;
- $remove[$i]["timestamp"] = $ts;
- $remove[$i]["id"] = $items[$i]->getDbId();
- }
- $this->removeItems($remove, false, true);
- }
- } else {
- $rebroadcasts = $instance->getCcShowInstancessRelatedByDbId(null, $this->con);
- $rebroadcasts->delete($this->con);
- }
- $instance->setDbEnds($this->nowDT);
- $instance->save($this->con);
- $this->con->commit();
- if ($instance->getDbRecord()) {
- Application_Model_RabbitMq::SendMessageToShowRecorder("cancel_recording");
- }
- } catch (Exception $e) {
- $this->con->rollback();
- throw $e;
- }
- }
- }
- class OutDatedScheduleException extends Exception {}
|