SchedulerService.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. <?php
  2. class Application_Service_SchedulerService
  3. {
  4. private $con;
  5. private $fileInfo = array(
  6. "id" => "",
  7. "cliplength" => "",
  8. "cuein" => "00:00:00",
  9. "cueout" => "00:00:00",
  10. "fadein" => "00:00:00",
  11. "fadeout" => "00:00:00",
  12. "sched_id" => null,
  13. "type" => 0 //default type of '0' to represent files. type '1' represents a webstream
  14. );
  15. private $epochNow;
  16. private $nowDT;
  17. private $currentUser;
  18. private $checkUserPermissions = true;
  19. public function __construct()
  20. {
  21. $this->con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME);
  22. //subtracting one because sometimes when we cancel a track, we set its end time
  23. //to epochNow and then send the new schedule to pypo. Sometimes the currently cancelled
  24. //track can still be included in the new schedule because it may have a few ms left to play.
  25. //subtracting 1 second from epochNow resolves this issue.
  26. $this->epochNow = microtime(true)-1;
  27. $this->nowDT = DateTime::createFromFormat("U.u", $this->epochNow, new DateTimeZone("UTC"));
  28. if ($this->nowDT === false) {
  29. // DateTime::createFromFormat does not support millisecond string formatting in PHP 5.3.2 (Ubuntu 10.04).
  30. // In PHP 5.3.3 (Ubuntu 10.10), this has been fixed.
  31. $this->nowDT = DateTime::createFromFormat("U", time(), new DateTimeZone("UTC"));
  32. }
  33. $user_service = new Application_Service_UserService();
  34. $this->currentUser = $user_service->getCurrentUser();
  35. }
  36. /**
  37. *
  38. * Applies the show start difference to any scheduled items
  39. *
  40. * @param $instanceIds
  41. * @param $diff (integer, difference between unix epoch in seconds)
  42. */
  43. public static function updateScheduleStartTime($instanceIds, $diff)
  44. {
  45. $con = Propel::getConnection();
  46. if (count($instanceIds) > 0) {
  47. $showIdList = implode(",", $instanceIds);
  48. $ccSchedules = CcScheduleQuery::create()
  49. ->filterByDbInstanceId($instanceIds, Criteria::IN)
  50. ->find($con);
  51. $interval = new DateInterval("PT".abs($diff)."S");
  52. if ($diff < 0) {
  53. $interval->invert = 1;
  54. }
  55. foreach ($ccSchedules as $ccSchedule) {
  56. $start = $ccSchedule->getDbStarts(null);
  57. $newStart = $start->add($interval);
  58. $end = $ccSchedule->getDbEnds(null);
  59. $newEnd = $end->add($interval);
  60. $ccSchedule
  61. ->setDbStarts($newStart)
  62. ->setDbEnds($newEnd)
  63. ->save($con);
  64. }
  65. }
  66. }
  67. /**
  68. *
  69. * Removes any time gaps in shows
  70. *
  71. * @param array $schedIds schedule ids to exclude
  72. */
  73. public function removeGaps($showId, $schedIds=null)
  74. {
  75. $ccShowInstances = CcShowInstancesQuery::create()->filterByDbShowId($showId)->find();
  76. foreach ($ccShowInstances as $instance) {
  77. Logging::info("Removing gaps from show instance #".$instance->getDbId());
  78. //DateTime object
  79. $itemStart = $instance->getDbStarts(null);
  80. $ccScheduleItems = CcScheduleQuery::create()
  81. ->filterByDbInstanceId($instance->getDbId())
  82. ->filterByDbId($schedIds, Criteria::NOT_IN)
  83. ->orderByDbStarts()
  84. ->find();
  85. foreach ($ccScheduleItems as $ccSchedule) {
  86. //DateTime object
  87. $itemEnd = $this->findEndTime($itemStart, $ccSchedule->getDbClipLength());
  88. $ccSchedule->setDbStarts($itemStart)
  89. ->setDbEnds($itemEnd);
  90. $itemStart = $itemEnd;
  91. }
  92. $ccScheduleItems->save();
  93. }
  94. }
  95. /**
  96. *
  97. * Enter description here ...
  98. * @param DateTime $instanceStart
  99. * @param string $clipLength
  100. */
  101. private static function findEndTime($instanceStart, $clipLength)
  102. {
  103. $startEpoch = $instanceStart->format("U.u");
  104. $durationSeconds = Application_Common_DateHelper::playlistTimeToSeconds($clipLength);
  105. //add two float numbers to 6 subsecond precision
  106. //DateTime::createFromFormat("U.u") will have a problem if there is no decimal in the resulting number.
  107. $endEpoch = bcadd($startEpoch , (string) $durationSeconds, 6);
  108. $dt = DateTime::createFromFormat("U.u", $endEpoch, new DateTimeZone("UTC"));
  109. if ($dt === false) {
  110. //PHP 5.3.2 problem
  111. $dt = DateTime::createFromFormat("U", intval($endEpoch), new DateTimeZone("UTC"));
  112. }
  113. return $dt;
  114. }
  115. private static function findTimeDifference($p_startDT, $p_seconds)
  116. {
  117. $startEpoch = $p_startDT->format("U.u");
  118. //add two float numbers to 6 subsecond precision
  119. //DateTime::createFromFormat("U.u") will have a problem if there is no decimal in the resulting number.
  120. $newEpoch = bcsub($startEpoch , (string) $p_seconds, 6);
  121. $dt = DateTime::createFromFormat("U.u", $newEpoch, new DateTimeZone("UTC"));
  122. if ($dt === false) {
  123. //PHP 5.3.2 problem
  124. $dt = DateTime::createFromFormat("U", intval($newEpoch), new DateTimeZone("UTC"));
  125. }
  126. return $dt;
  127. }
  128. /**
  129. *
  130. * Gets a copy of the linked show's schedule from cc_schedule table
  131. *
  132. * If $instanceId is not null, we use that variable to grab the linked
  133. * show's schedule from cc_schedule table. (This is likely to be the case
  134. * if a user has edited a show and changed it from un-linked to linked, in
  135. * which case we copy the show content from the show instance that was clicked
  136. * on to edit the show in the calendar.) Otherwise the schedule is taken
  137. * from the most recent show instance that existed before new show
  138. * instances were created. (This is likely to be the case when a user edits a
  139. * show and a new repeat show day is added (i.e. mondays), or moves forward in the
  140. * calendar triggering show creation)
  141. *
  142. * @param integer $showId
  143. * @param array $instancsIdsToFill
  144. * @param integer $instanceId
  145. */
  146. private static function getLinkedShowSchedule($showId, $instancsIdsToFill, $instanceId)
  147. {
  148. $con = Propel::getConnection();
  149. if (is_null($instanceId)) {
  150. $showsPopulatedUntil = Application_Model_Preference::GetShowsPopulatedUntil();
  151. $showInstanceWithMostRecentSchedule = CcShowInstancesQuery::create()
  152. ->filterByDbShowId($showId)
  153. ->filterByDbStarts($showsPopulatedUntil->format("Y-m-d H:i:s"), Criteria::LESS_THAN)
  154. ->filterByDbId($instancsIdsToFill, Criteria::NOT_IN)
  155. ->orderByDbStarts(Criteria::DESC)
  156. ->limit(1)
  157. ->findOne();
  158. if (is_null($showInstanceWithMostRecentSchedule)) {
  159. return null;
  160. }
  161. $instanceId = $showInstanceWithMostRecentSchedule->getDbId();
  162. }
  163. $linkedShowSchedule_sql = $con->prepare(
  164. "select * from cc_schedule where instance_id = :instance_id ".
  165. "order by starts");
  166. $linkedShowSchedule_sql->bindParam(':instance_id', $instanceId);
  167. $linkedShowSchedule_sql->execute();
  168. return $linkedShowSchedule_sql->fetchAll();
  169. }
  170. /**
  171. *
  172. * This function gets called after new linked show_instances are created, or after
  173. * a show has been edited and went from being un-linked to linked.
  174. * It fills the new show instances' schedules.
  175. *
  176. * @param CcShow_type $ccShow
  177. * @param array $instanceIdsToFill ids of the new linked cc_show_instances that
  178. * need their schedules filled
  179. */
  180. public static function fillLinkedInstances($ccShow, $instanceIdsToFill, $instanceId=null)
  181. {
  182. //Get the "template" schedule for the linked show (contents of the linked show) that will be
  183. //copied into to all the new show instances.
  184. $linkedShowSchedule = self::getLinkedShowSchedule($ccShow->getDbId(), $instanceIdsToFill, $instanceId);
  185. //get time_filled so we can update cc_show_instances
  186. if (!empty($linkedShowSchedule)) {
  187. $timeFilled_sql = "SELECT time_filled FROM cc_show_instances ".
  188. "WHERE id = {$linkedShowSchedule[0]["instance_id"]}";
  189. $timeFilled = Application_Common_Database::prepareAndExecute(
  190. $timeFilled_sql, array(), Application_Common_Database::COLUMN);
  191. } else {
  192. //We probably shouldn't return here because the code below will
  193. //set this on each empty show instance...
  194. $timeFilled = "00:00:00";
  195. }
  196. $values = array();
  197. $con = Propel::getConnection();
  198. //Here we begin to fill the new show instances (as specified by $instanceIdsToFill)
  199. //with content from $linkedShowSchedule.
  200. try {
  201. $con->beginTransaction();
  202. foreach ($instanceIdsToFill as $id)
  203. {
  204. //Start by clearing the show instance that needs to be filling. This ensure
  205. //we're not going to get in trouble in case there's an programming error somewhere else.
  206. self::clearShowInstanceContents($id);
  207. // Now fill the show instance with the same content that $linkedShowSchedule has.
  208. $instanceStart_sql = "SELECT starts FROM cc_show_instances " .
  209. "WHERE id = {$id} " . "ORDER BY starts";
  210. //What's tricky here is that when we copy the content, we have to adjust
  211. //the start and end times of each track so they're inside the new show instance's time slot.
  212. $nextStartDT = new DateTime(
  213. Application_Common_Database::prepareAndExecute(
  214. $instanceStart_sql, array(),
  215. Application_Common_Database::COLUMN),
  216. new DateTimeZone("UTC"));
  217. $defaultCrossfadeDuration = Application_Model_Preference::GetDefaultCrossfadeDuration();
  218. unset($values);
  219. $values = array();
  220. foreach ($linkedShowSchedule as $item) {
  221. $endTimeDT = self::findEndTime($nextStartDT,
  222. $item["clip_length"]);
  223. if (is_null($item["file_id"])) {
  224. $item["file_id"] = "null";
  225. }
  226. if (is_null($item["stream_id"])) {
  227. $item["stream_id"] = "null";
  228. }
  229. $values[] = "(" . "'{$nextStartDT->format("Y-m-d H:i:s")}', " .
  230. "'{$endTimeDT->format("Y-m-d H:i:s")}', " .
  231. "'{$item["clip_length"]}', " .
  232. "'{$item["fade_in"]}', " . "'{$item["fade_out"]}', " .
  233. "'{$item["cue_in"]}', " . "'{$item["cue_out"]}', " .
  234. "{$item["file_id"]}, " . "{$item["stream_id"]}, " .
  235. "{$id}, " . "{$item["position"]})";
  236. $nextStartDT = self::findTimeDifference($endTimeDT,
  237. $defaultCrossfadeDuration);
  238. } //foreach show item
  239. if (!empty($values)) {
  240. $insert_sql = "INSERT INTO cc_schedule (starts, ends, ".
  241. "clip_length, fade_in, fade_out, cue_in, cue_out, ".
  242. "file_id, stream_id, instance_id, position) VALUES ".
  243. implode($values, ",");
  244. Application_Common_Database::prepareAndExecute(
  245. $insert_sql, array(), Application_Common_Database::EXECUTE);
  246. }
  247. //update cc_schedule status column
  248. $instance = CcShowInstancesQuery::create()->findPk($id);
  249. $instance->updateScheduleStatus($con);
  250. } //foreach linked instance
  251. //update time_filled and last_scheduled in cc_show_instances
  252. $now = gmdate("Y-m-d H:i:s");
  253. $whereClause = new Criteria();
  254. $whereClause->add(CcShowInstancesPeer::ID, $instanceIdsToFill, Criteria::IN);
  255. $updateClause = new Criteria();
  256. $updateClause->add(CcShowInstancesPeer::TIME_FILLED, $timeFilled);
  257. $updateClause->add(CcShowInstancesPeer::LAST_SCHEDULED, $now);
  258. BasePeer::doUpdate($whereClause, $updateClause, $con);
  259. $con->commit();
  260. Logging::info("finished fill");
  261. } catch (Exception $e) {
  262. $con->rollback();
  263. Logging::info("Error filling linked shows: ".$e->getMessage());
  264. exit();
  265. }
  266. }
  267. public static function fillPreservedLinkedShowContent($ccShow, $showStamp)
  268. {
  269. $item = $showStamp->getFirst();
  270. $timeFilled = $item->getCcShowInstances()->getDbTimeFilled();
  271. foreach ($ccShow->getCcShowInstancess() as $ccShowInstance) {
  272. $ccSchedules = CcScheduleQuery::create()
  273. ->filterByDbInstanceId($ccShowInstance->getDbId())
  274. ->find();
  275. if ($ccSchedules->isEmpty()) {
  276. $nextStartDT = $ccShowInstance->getDbStarts(null);
  277. foreach ($showStamp as $item) {
  278. $endTimeDT = self::findEndTime($nextStartDT, $item->getDbClipLength());
  279. $ccSchedule = new CcSchedule();
  280. $ccSchedule
  281. ->setDbStarts($nextStartDT)
  282. ->setDbEnds($endTimeDT)
  283. ->setDbFileId($item->getDbFileId())
  284. ->setDbStreamId($item->getDbStreamId())
  285. ->setDbClipLength($item->getDbClipLength())
  286. ->setDbFadeIn($item->getDbFadeIn())
  287. ->setDbFadeOut($item->getDbFadeOut())
  288. ->setDbCuein($item->getDbCueIn())
  289. ->setDbCueOut($item->getDbCueOut())
  290. ->setDbInstanceId($ccShowInstance->getDbId())
  291. ->setDbPosition($item->getDbPosition())
  292. ->save();
  293. $nextStartDT = self::findTimeDifference($endTimeDT,
  294. Application_Model_Preference::GetDefaultCrossfadeDuration());
  295. } //foreach show item
  296. $ccShowInstance
  297. ->setDbTimeFilled($timeFilled)
  298. ->setDbLastScheduled(gmdate("Y-m-d H:i:s"))
  299. ->save();
  300. }
  301. }
  302. }
  303. /** Clears a show instance's schedule (which is actually clearing cc_schedule during that show instance's time slot.) */
  304. private static function clearShowInstanceContents($instanceId)
  305. {
  306. //Application_Common_Database::prepareAndExecute($delete_sql, array(), Application_Common_Database::EXECUTE);
  307. $con = Propel::getConnection();
  308. $query = $con->prepare("DELETE FROM cc_schedule WHERE instance_id = :instance_id");
  309. $query->bindParam(':instance_id', $instanceId);
  310. $query->execute();
  311. }
  312. /*
  313. private static function replaceInstanceContentCheck($currentShowStamp, $showStamp, $instance_id)
  314. {
  315. $counter = 0;
  316. $erraseShow = false;
  317. if (count($currentShowStamp) != count($showStamp)) {
  318. $erraseShow = true;
  319. } else {
  320. foreach ($showStamp as $item) {
  321. if ($item["file_id"] != $currentShowStamp[$counter]["file_id"] ||
  322. $item["stream_id"] != $currentShowStamp[$counter]["stream_id"]) {
  323. $erraseShow = true;
  324. break;
  325. //CcScheduleQuery::create()
  326. // ->filterByDbInstanceId($ccShowInstance->getDbId())
  327. // ->delete();
  328. }
  329. $counter += 1;
  330. }
  331. }
  332. if ($erraseShow) {
  333. $delete_sql = "DELETE FROM cc_schedule ".
  334. "WHERE instance_id = :instance_id";
  335. Application_Common_Database::prepareAndExecute(
  336. $delete_sql, array(), Application_Common_Database::EXECUTE);
  337. return true;
  338. }
  339. //If we get here, the content in the show instance is the same
  340. // as what we want to replace it with, so we can leave as is
  341. return false;
  342. }*/
  343. public function emptyShowContent($instanceId)
  344. {
  345. try {
  346. $ccShowInstance = CcShowInstancesQuery::create()->findPk($instanceId);
  347. $instances = array();
  348. $instanceIds = array();
  349. if ($ccShowInstance->getCcShow()->isLinked()) {
  350. foreach ($ccShowInstance->getCcShow()->getFutureCcShowInstancess() as $instance) {
  351. $instanceIds[] = $instance->getDbId();
  352. $instances[] = $instance;
  353. }
  354. } else {
  355. $instanceIds[] = $ccShowInstance->getDbId();
  356. $instances[] = $ccShowInstance;
  357. }
  358. /* Get the file ids of the tracks we are about to delete
  359. * from cc_schedule. We need these so we can update the
  360. * is_scheduled flag in cc_files
  361. */
  362. $ccSchedules = CcScheduleQuery::create()
  363. ->filterByDbInstanceId($instanceIds, Criteria::IN)
  364. ->setDistinct(CcSchedulePeer::FILE_ID)
  365. ->find();
  366. $fileIds = array();
  367. foreach ($ccSchedules as $ccSchedule) {
  368. $fileIds[] = $ccSchedule->getDbFileId();
  369. }
  370. /* Clear out the schedule */
  371. CcScheduleQuery::create()
  372. ->filterByDbInstanceId($instanceIds, Criteria::IN)
  373. ->delete();
  374. /* Now that the schedule has been cleared we need to make
  375. * sure we do not update the is_scheduled flag for tracks
  376. * that are scheduled in other shows
  377. */
  378. $futureScheduledFiles = Application_Model_Schedule::getAllFutureScheduledFiles();
  379. foreach ($fileIds as $k => $v) {
  380. if (in_array($v, $futureScheduledFiles)) {
  381. unset($fileIds[$k]);
  382. }
  383. }
  384. $selectCriteria = new Criteria();
  385. $selectCriteria->add(CcFilesPeer::ID, $fileIds, Criteria::IN);
  386. $updateCriteria = new Criteria();
  387. $updateCriteria->add(CcFilesPeer::IS_SCHEDULED, false);
  388. BasePeer::doUpdate($selectCriteria, $updateCriteria, Propel::getConnection());
  389. Application_Model_RabbitMq::PushSchedule();
  390. $con = Propel::getConnection(CcShowInstancesPeer::DATABASE_NAME);
  391. foreach ($instances as $instance) {
  392. $instance->updateDbTimeFilled($con);
  393. }
  394. return true;
  395. } catch (Exception $e) {
  396. Logging::info($e->getMessage());
  397. return false;
  398. }
  399. }
  400. /*
  401. * TODO in the future this should probably support webstreams.
  402. */
  403. public function updateFutureIsScheduled($scheduleId, $status)
  404. {
  405. $sched = CcScheduleQuery::create()->findPk($scheduleId);
  406. $redraw = false;
  407. if (isset($sched)) {
  408. $fileId = $sched->getDbFileId();
  409. if (isset($fileId)) {
  410. $redraw = Application_Model_StoredFile::setIsScheduled($fileId, $status);
  411. }
  412. }
  413. return $redraw;
  414. }
  415. }