diff --git a/api/app/Http/Controllers/ProgramController.php b/api/app/Http/Controllers/ProgramController.php
index 3fd535b..c5d6802 100644
--- a/api/app/Http/Controllers/ProgramController.php
+++ b/api/app/Http/Controllers/ProgramController.php
@@ -18,7 +18,7 @@ class ProgramController extends Controller
((`schedule`.`enddate` IS NULL) OR (`schedule`.`enddate` >= :startdate)) AND
`schedule`.`active` = 1 AND
`schedule`.`final` = 1
- ORDER BY `schedule`.`priority` DESC, MOD(`startday` + 6, 7) + 1, `schedule`.`starttime` ASC
+ ORDER BY `schedule`.`priority` DESC, `schedule`.`startday` ASC, `schedule`.`starttime` ASC
QUERY;
/**
@@ -30,6 +30,153 @@ QUERY;
{
//
}
+
+ public function addToSchedule(&$schedule, $start, $end, $program) {
+ if($start == $end) {
+ return;
+ }
+
+ $last = count($schedule) ? $schedule[count($schedule) - 1] : null;
+ if($last && ($last['program'] == $program) && ($last['end'] == $start))
+ {
+ // Merge with the previous entry
+ $last['end'] = $end;
+ }
+ else
+ {
+ $schedule[] = ['start' => clone $start, 'end' => clone $end, 'program' => new \Model\Program($program)];
+ }
+ }
+
+
+ public function createSchedule(\DateTimeImmutable $start, \DateTimeImmutable $end, $DEBUG = false) {
+ $startDay = $start->format('N');
+ $endDay = $end->format('N');
+ $schedule = [];
+
+ $START = 1; $END = -1;
+ $WEEK = new \DateInterval('P7D');
+ $WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" /* == 0 and 7 */];
+ $scheduleItems = app('db')->select(self::$SCHEDULE_SQL,
+ ['enddate' => $end->format('Y-m-d'), 'startdate' => $start->format('Y-m-d')]);
+ $scheduleChanges = new \SplPriorityQueue();
+ $active = [];
+ foreach($scheduleItems as $item) {
+ // Find the start time and end time of the last occurrence on or before the start time
+ $itemStart = new \DateTime("last " . $WEEKDAYS[$item->startday] . " " . $start->format('Y-m-d') . " " . $item->starttime);
+ $itemEnd = new \DateTime("last " . $WEEKDAYS[$item->endday] . " " . $start->format('Y-m-d') . " " . $item->endtime);
+ if($itemEnd <= $itemStart) {
+ $itemEnd->add($WEEK);
+ }
+
+ // If the first occurrence is completely before the period of interest, look at the next week
+ while($itemEnd < $start) {
+ $itemStart->add($WEEK);
+ $itemEnd->add($WEEK);
+ }
+
+ // Create a list in which all start and end times are separate entries
+ $scheduleChanges->insert( [$START, $itemStart, $item], [-$itemStart->getTimestamp(), -$START] );
+ $scheduleChanges->insert( [$END, $itemEnd, $item], [-$itemEnd->getTimestamp(), -$END] );
+
+ // Also keep track of all priorities seen
+ $active[$item->priority] = null;
+ }
+
+ if($DEBUG) {
+ print "
Schedule changes - initial state
";
+ foreach(clone $scheduleChanges as $c) {
+ print "- {$c[1]->format('d-m-Y H:i')}: " . ($c[0] == $START ? 'Start' : 'Einde') . " {$c[2]->name} {$c[2]->suffix}
";
+ }
+ print "
";
+ }
+
+ // Go through all programs and keep going until we have one that starts after the end date.
+ $activeProgram = null;
+ $activeProgramStart = null;
+ while(true) {
+ $item = $scheduleChanges->extract();
+
+ if($DEBUG) print "{$item[1]->format('d-m-Y H:i')}: {$item[2]->name} " . ($item[0] == $START ? "begint" : "eindigt") . "
";
+
+ // If we passed the end time, we are (almost) done
+ if($item[1] > $end) {
+ if($DEBUG) { print "Reached end at {$item[1]->format('d-m-Y H:i')} ({$item[2]->name}).
"; }
+ break;
+ }
+
+ $priority = $item[2]->priority;
+ if($item[0] == $START) {
+ if($active[$priority] != null) {
+ print( "Error:" . $item[2]->name . ' starts at ' . $item[1]->format('d-m-Y H:i') . ' but ' . $active[$priority]->name . ' is still active then.' );
+ print ""; var_dump($active); print "
";
+ }
+
+ // When a program starts, it becomes the active program in its layer
+ $active[$priority] = $item[2];
+
+ // In addition, if it has higher priority than the currently active program, it replaces that.
+ if($activeProgram == null || $priority < $activeProgram->priority) {
+ if(($activeProgram != null) && ($item[1] > $start)) {
+ if($DEBUG) print "{$activeProgramStart->format('d-m-Y H:i')} -- {$item[1]->format('d-m-Y H:i')}: {$activeProgram->name} (reason: {$item[2]->name} starts).
";
+ $this->addToSchedule($schedule, $activeProgramStart, $item[1], $activeProgram);
+ }
+
+ $activeProgramStart = ($item[1] < $start) ? $start : clone $item[1];
+ $activeProgram = $item[2];
+ }
+
+ } else if($item[0] == $END) {
+ if($active[$priority] == null) {
+ print( "Error:" . $item[2]->name . ' ends at ' . $item[1]->format('d-m-Y H:i') . ' but no program is active at that priority and timestamp.');
+ print ""; var_dump($active); print "
";
+ }
+
+ // When the program ends, its layer becomes inactive.
+ $active[$priority] = null;
+
+ // In addition, when the ending program was the active program, we need to schedule it and find the new currently active program
+ if($activeProgram == $item[2]) {
+ if($DEBUG) print "{$activeProgramStart->format('d-m-Y H:i')} -- {$item[1]->format('d-m-Y H:i')}: {$activeProgram->name} (reason: {$item[2]->name} ends).
";
+ $this->addToSchedule($schedule, $activeProgramStart, $item[1], $activeProgram);
+
+ $activeProgram = null;
+ foreach($active as $newPriority => $newActive) {
+ if(($newActive != null) && ($activeProgram == null || $newPriority < $activeProgram->priority)) {
+ $activeProgram = $newActive;
+ }
+ }
+
+ if($activeProgram != null) {
+ $activeProgramStart = clone $item[1];
+ }
+
+ if($DEBUG) print " New active program is " . ($activeProgram ? $activeProgram->name : "---") . "
";
+ }
+ } else {
+ return response()->abort(500, "Invalid item type: expected START ($START) or END ($END), but got {$item[0]}.");
+ }
+
+ // The item will recur in a week
+ $item[1]->add($WEEK);
+ $scheduleChanges->insert($item, [-$item[1]->getTimestamp(), -$item[0]]);
+ }
+
+ if(($activeProgram != null) && ($activeProgramStart != $end)) {
+ if($DEBUG) print "{$activeProgramStart->format('d-m-Y H:i')} -- {$end->format('d-m-Y H:i')}: {$activeProgram->name} (reason: schedule finished).
";
+ $this->addToSchedule($schedule, $activeProgramStart, $end, $activeProgram);
+ }
+
+ return ['startdate' => $start, 'enddate' => $end, 'schedule' => $schedule];
+ }
+
+ public function testSchedule($from, $to)
+ {
+ $start = new \DateTimeImmutable(urldecode($from));
+ $end = new \DateTimeImmutable(urldecode($to));
+ $this->createSchedule($start, $end, true);
+ }
+
/**
* Details over een specifiek programma, inclusief schema en podcasts
@@ -51,88 +198,14 @@ QUERY;
AND `user_emailaddresses`.`preference` = 0
AND `programs_users`.`active` = 1
ORDER BY `name`", ['program' => $program->id]);
+
foreach($hosts as $host) {
$program->hosts[] = new \Model\ProgramHost($host);
}
- $schedule = app('db')->select(<< $program->id] );
- $recentSchedule = $this->recent($schedule);
- $program->schedule = $recentSchedule;
-
return response()->json($program);
}
- private function recent($schedule) {
- $now = new \DateTimeImmutable();
- $ONE_DAY = \DateInterval::createFromDateString('1 day');
- $ONE_WEEK = \DateInterval::createFromDateString('1 week');
- $recent = [];
- $next = [];
-
- // Vind voor elke regel in de uitzendmomenten de eerstvolgende en twee meest recente.
- $isCurrent = false;
- foreach($schedule as $broadcast) {
- $broadcast->startdate = ($broadcast->startdate == null) ? null : new \DateTimeImmutable($broadcast->startdate);
- $broadcast->enddate = ($broadcast->enddate == null) ? null : new \DateTimeImmutable($broadcast->enddate);
- $startTijd = new \DateTimeImmutable($broadcast->starttime);
- $endDate = ($broadcast->enddate == null) ? $now : $broadcast->enddate;
- $endDate = $endDate->setTime($startTijd->format('H'), $startTijd->format('i'), $startTijd->format('s'));
- $endDateWeekday = $endDate->format('N');
-
- // Zoek de laatste uitzending voor de einddatum (of voor het huidige tijdstip), op de juiste weekdag
- while($endDateWeekday != $broadcast->startday) {
- $endDate = $endDate->sub($ONE_DAY);
- if(--$endDateWeekday < 0) { $endDateWeekday += 7; }
- }
-
- $eindTijd = new \DateTimeImmutable($broadcast->endtime);
- $endOfBroadcast = $endDate->setTime($eindTijd->format('H'), $eindTijd->format('i'), $eindTijd->format('s'));
- if($endOfBroadcast < $endDate) { $endOfBroadcast = $endOfBroadcast->add($ONE_DAY); }
-
- if($isInProgress = (($endDate <= $now) && ($endOfBroadcast > $now))) {
- $isCurrent = true;
- }
-
- // Als de uitzending niet al afgelopen is, telt hij als volgende.
- // Dit gebeurt als de uitzending later vandaag is of nu loopt.
- if($endOfBroadcast >= $now) {
- $endDate = $endDate->sub($ONE_WEEK);
- $endOfBroadcast = $endOfBroadcast->sub($ONE_WEEK);
- }
-
- if((($broadcast->startdate == null) || ($broadcast->startdate <= $endDate))
- && (($broadcast->enddate == null) || ($broadcast->enddate >= $endDate->setTime(0,0,0)))) {
- $recent[] = ['time' => $endDate, 'suffix' => $broadcast->suffix, 'isSpecial' => ($broadcast->priority < 2)];
- }
-
-
- $previous = $endDate->sub($ONE_WEEK);
- if((($broadcast->startdate == null) || ($broadcast->startdate <= $previous))
- && (($broadcast->enddate == null) || ($broadcast->enddate >= $previous->setTime(0,0,0)))) {
- $recent[] = ['time' => $previous, 'suffix' => $broadcast->suffix, 'isSpecial' => ($broadcast->priority < 2)];
- }
-
- $following = $endDate->add($ONE_WEEK);
- if(($broadcast->enddate == null) || ($broadcast->enddate->setTime(23,59,59) > $following)) {
- if($following < $now) {
- $isCurrent = true;
- }
-
- $next[] = ['time' => $following, 'suffix' => $broadcast->suffix, 'isSpecial' => ($broadcast->priority < 2)];
- }
- }
-
- sort($next);
- rsort($recent);
- return ['recent' => array_reverse(array_splice($recent, 0, 2)), 'next' => count($next) ? $next[0] : null, 'iscurrent' => $isCurrent];
- }
-
/**
* Programmas nu en straks (24 uur vooruit)
*/
@@ -153,109 +226,4 @@ QUERY
return response()->json($this->createSchedule($start, $einde));
}
-
- private function createSchedule($start, $einde) {
- if(!defined('PROGRAMMA_START')) {
- define('PROGRAMMA_START', 1);
- define('PROGRAMMA_EINDE', -1);
-
- define('PW_TIJDSTIP', 0);
- define('PW_TYPE', 1);
- define('PW_PROGRAMMA', 2);
- }
-
- $oneWeek = \DateInterval::createFromDateString('1 week');
-
- $programmas = app('db')->select(self::$SCHEDULE_SQL,
- ['enddate' => $einde->format('Y-m-d'), 'startdate' => $start->format('Y-m-d')]);
- $programmaInfo = [];
-
- // Maak een lijstje van alle start en eindtijden en welk programma dan begint/eindigt
- $startEinde = [];
- $startMaandag = $start->modify('Monday this week');
- foreach($programmas as $programma) {
- if($programma->suffix) $programma->name .= ' ' . $programma->suffix;
- $programmaInfo[$programma->scheduleid] = new \Model\Program($programma);
-
- $programmaStart = $startMaandag->add(\DateInterval::createFromDateString(($programma->startday - 1) % 7 . ' days'));
- $programmaEinde = $startMaandag->add(\DateInterval::createFromDateString(($programma->endday - 1) % 7 . ' days'));
-
- $startTijd = new \DateTime($programma->starttime);
- $eindTijd = new \DateTime($programma->endtime);
-
- $programmaStart = $programmaStart->setTime($startTijd->format('H'), $startTijd->format('i'), $startTijd->format('s'));
- $programmaEinde = $programmaEinde->setTime($eindTijd->format('H'), $eindTijd->format('i'), $eindTijd->format('s'));
-
- if($programmaEinde <= $programmaStart) {
- $programmaEinde = $programmaEinde->add($oneWeek);
- }
-
- if($programmaEinde < $start) {
- $programmaStart = $programmaStart->add($oneWeek);
- $programmaEinde = $programmaEinde->add($oneWeek);
- }
-
- while($programmaStart < $einde) {
- if((($programma->startdate == null) || ($programmaStart >= new \DateTime($programma->startdate)))
- && (($programma->enddate == null) || ($programmaStart <= (new \DateTimeImmutable($programma->enddate))->setTime(23,59,59)))
- && (($programmaEinde >= $start) && ($programmaStart <= $einde)))
- {
- $startEinde[] = [$programmaStart, PROGRAMMA_START, $programma->scheduleid];
- $startEinde[] = [$programmaEinde, PROGRAMMA_EINDE, $programma->scheduleid];
- }
-
- $programmaStart = $programmaStart->add($oneWeek);
- $programmaEinde = $programmaEinde->add($oneWeek);
- }
- }
-
- array_multisort($startEinde);
-
- $actieveProgrammas = [null, null, null, null];
- $schema = [];
- $fouten = [];
- $index = -1;
- foreach($startEinde as $programmaWissel) {
- $scheduleId = $programmaWissel[PW_PROGRAMMA];
- $programma = $programmaInfo[$scheduleId];
- $tijdstip = $programmaWissel[PW_TIJDSTIP];
- if($programmaWissel[PW_TIJDSTIP] < $start) { $tijdstip = $start; }
- if($programmaWissel[PW_TIJDSTIP] > $einde) { $tijdstip = $einde; }
- if($tijdstip == $einde) { break; }
-
- if($programmaWissel[PW_TYPE] == PROGRAMMA_START) {
- if($actieveProgrammas[$programma->priority] != null) {
- $fouten[] = [$tijdstip, "Begin van {$programma->name} maar {$actieveProgrammas[$programma->priority]->name} is nog actief."];
- }
-
- $actieveProgrammas[$programma->priority] = $scheduleId;
- for($prio = $programma->priority; $prio >= 0; --$prio) {
- if($actieveProgrammas[$prio] != null) {
- if(($index == -1) || ($schema[$index]['starttime'] != $tijdstip)) { $index++; }
- $schema[$index] = ['starttime' => $tijdstip, 'endtime' => null, 'program' => $programmaInfo[$actieveProgrammas[$prio]]];
- break;
- }
- }
- } else /*if($programmaWissel[PW_TYPE] == PROGRAMMA_EINDE)*/ {
- $actieveProgrammas[$programma->priority] = null;
- if(($index == -1) || ($schema[$index]['program'] != null)) { $index++; }
- $schema[$index] = ['starttime' => $tijdstip, 'program' => null];
- for($prio = $programma->priority; $prio < count($actieveProgrammas); ++$prio) {
- if($actieveProgrammas[$prio] != null) {
- $schema[$index] = ['starttime' => $tijdstip, 'endtime' => null, 'program' => $programmaInfo[$actieveProgrammas[$prio]]];
- break;
- }
- }
- }
- }
-
- for($i = 1; $i <= count($schema); $i++) {
- $eindTijd = ($i == count($schema)) ? $einde : $schema[$i]['starttime'];
- $schema[$i - 1]['endtime'] = $eindTijd;
- //$programma = $schema[$i-1]['program'];
- //print $schema[$i-1]['starttime']->format('D d-m-Y, H:i:s') . " - " . $eindTijd->format('D d-m-Y, H:i:s') . ": " . ($programma != null ? $programma->name : "NULL") . "\n";
- }
-
- return ['startdate' => $start, 'enddate' => $einde, 'schedule' => $schema, 'errors' => $fouten];
- }
}
diff --git a/api/routes/web.php b/api/routes/web.php
index 9222f13..4919dde 100644
--- a/api/routes/web.php
+++ b/api/routes/web.php
@@ -32,7 +32,9 @@ $app->get('podcast/stream/{id:\d+}/{title}', 'PodcastController@stream' );
$app->get('programma/schema/nustraks', 'ProgramController@comingup' );
$app->get('programma/schema/week[/{shiftWeeks:-?\d+}]', 'ProgramController@schedule' );
-$app->get('/programma/details/{id:\d+}', 'ProgramController@details' );
+$app->get('programma/details/{id:\d+}', 'ProgramController@details' );
+
+$app->get('programma/schema/test/{from}/{to}', 'ProgramController@testSchedule' );
// live/onair
// live/stream
diff --git a/common/classes/Program.php b/common/classes/Program.php
index 358bba4..3878af3 100644
--- a/common/classes/Program.php
+++ b/common/classes/Program.php
@@ -9,12 +9,16 @@ class Program extends Model {
public $email;
public $priority;
- // TODO: Implementeren
public $hosts;
public $schedule;
public function __construct($data) {
parent::__construct($data);
+
+ if(isset($data->suffix) && $data->suffix) {
+ $this->name .= ' ' . $data->suffix;
+ }
+
if($this->schedule && $this->schedule->next && $this->schedule->next->time) {
parent::ConvertToDateTime($this->schedule->next->time);
}
@@ -31,4 +35,8 @@ class Program extends Model {
$this->url = "/{$this->id}/" . parent::url_slug($this->name);
}
+
+ public function isSpecial() {
+ return ($this->priority < 2);
+ }
}