diff options
Diffstat (limited to 'model')
-rw-r--r-- | model/Relive.php | 24 | ||||
-rw-r--r-- | model/Room.php | 193 | ||||
-rw-r--r-- | model/RoomSelection.php | 52 | ||||
-rw-r--r-- | model/RoomTab.php | 34 | ||||
-rw-r--r-- | model/Schedule.php | 173 | ||||
-rw-r--r-- | model/Stream.php | 174 | ||||
-rw-r--r-- | model/StreamList.php | 55 |
7 files changed, 648 insertions, 57 deletions
diff --git a/model/Relive.php b/model/Relive.php new file mode 100644 index 0000000..3ffdc1b --- /dev/null +++ b/model/Relive.php @@ -0,0 +1,24 @@ +<?php + +class Relive extends ModelBase +{ + function relive_talks() + { + $talks = @file_get_contents(get('OVERVIEW.RELIVE_JSON')); + $talks = utf8_decode($talks); + $talks = (array)json_decode($talks, true); + + usort($talks, function($a, $b) { + $sort = array('live', 'recorded', 'released'); + return array_search($a['status'], $sort) > array_search($b['status'], $sort); + }); + + $talks_by_id = array(); + foreach ($talks as $value) + { + $talks_by_id[$value['id']] = $value; + } + + return $talks_by_id; + } +} diff --git a/model/Room.php b/model/Room.php index c7e51f9..f428d87 100644 --- a/model/Room.php +++ b/model/Room.php @@ -35,11 +35,202 @@ class Room extends ModelBase + public function hasStereo() { + return $this->get('ROOMS.'.$this->getSlug().'.STEREO'); + } + public function hasPreview() { - return get('ROOMS.'.$this->getSlug().'.PREVIEW'); + return $this->get('ROOMS.'.$this->getSlug().'.PREVIEW'); } public function hasSchedule() { return $this->get('ROOMS.'.$this->getSlug().'.SCHEDULE') && $this->has('SCHEDULE'); } + + public function hasFeedback() { + return $this->get('ROOMS.'.$this->getSlug().'.FEEDBACK') && $this->has('FEEDBACK'); + } + + + public function hasTwitter() { + return $this->get('ROOMS.'.$this->getSlug().'.TWITTER') && $this->has('TWITTER'); + } + + public function getTwitterDisplay() { + return sprintf( + $this->get('ROOMS.'.$this->getSlug().'.TWITTER_CONFIG.DISPLAY', $this->get('TWITTER.DISPLAY')), + $this->getSlug() + ); + } + + public function getTwitterUrl() { + return sprintf( + 'https://twitter.com/intent/tweet?text=%s', + rawurlencode($this->getTwitterText()) + ); + } + + public function getTwitterText() { + return sprintf( + $this->get('ROOMS.'.$this->getSlug().'.TWITTER_CONFIG.TEXT', $this->get('TWITTER.TEXT')), + $this->getSlug() + ); + } + + + public function hasIrc() { + return $this->get('ROOMS.'.$this->getSlug().'.IRC') && $this->has('IRC'); + } + + public function getIrcDisplay() { + return sprintf( + $this->get('ROOMS.'.$this->getSlug().'.IRC_CONFIG.DISPLAY', $this->get('IRC.DISPLAY')), + $this->getSlug() + ); + } + + public function getIrcUrl() { + return sprintf( + $this->get('ROOMS.'.$this->getSlug().'.IRC_CONFIG.URL', $this->get('IRC.URL')), + rawurlencode($this->getSlug()) + ); + } + + + public function hasChat() { + return $this->hasTwitter() || $this->hasIrc(); + } + + + public function hasSdVideo() { + return $this->get('ROOMS.'.$this->getSlug().'.SD_VIDEO'); + } + + public function hasHdVideo() { + return $this->get('ROOMS.'.$this->getSlug().'.HD_VIDEO'); + } + + public function hasVideo() { + return $this->hasSdVideo() || $this->hasHdVideo(); + } + + public function hasAudio() { + return $this->get('ROOMS.'.$this->getSlug().'.AUDIO'); + } + + public function hasSlides() { + return $this->get('ROOMS.'.$this->getSlug().'.SLIDES'); + } + + public function hasMusic() { + return $this->get('ROOMS.'.$this->getSlug().'.MUSIC'); + } + + public function hasTranslation() { + return $this->get('ROOMS.'.$this->getSlug().'.TRANSLATION'); + } + + public function getSelectionNames() + { + $selections = array(); + if($this->hasHdVideo()) + $selections[] = 'hd'; + + if($this->hasSdVideo()) + $selections[] = 'sd'; + + if($this->hasSlides()) + $selections[] = 'slides'; + + if($this->hasAudio()) + $selections[] = 'audio'; + + if($this->hasMusic()) + $selections[] = 'music'; + + return $selections; + } + + public function getTabNames() + { + $tabs = array(); + if($this->hasVideo()) + $tabs[] = 'video'; + + if($this->hasSlides()) + $tabs[] = 'slides'; + + if($this->hasAudio()) + $tabs[] = 'audio'; + + if($this->hasMusic()) + $tabs[] = 'music'; + + return $tabs; + } + + public function getSelections() + { + $selections = array(); + foreach($this->getSelectionNames() as $selection) + $selections[$tab] = $this->createSelectionObject($selection); + + return $selections; + } + + public function createSelectionObject($selection) + { + return new RoomSelection($this, $selection); + } + + public function getTabs() + { + $tabs = array(); + foreach($this->getTabNames() as $tab) + $tabs[$tab] = $this->createTabObject($tab); + + return $tabs; + } + + public function createTabObject($tab) + { + return new RoomTab($this, $tab); + } + + public function getVideoResolutions() + { + $res = array(); + if($this->hasHdVideo()) + $res[] = 'hd'; + + if($this->hasSdVideo()) + $res[] = 'sd'; + + return $res; + } + + public function selectStream($selection, $language = 'native') + { + $selections = $this->getSelectionNames(); + + // default page + if(!$selection) + $selection = $selections[0]; + + if(!in_array($selection, $selections)) + throw new NotFoundException('Selection '.$selection.' in Room '.$this->getSlug()); + + if($language == 'translated' && !$this->hasTranslation()) + throw new NotFoundException('Translated Streams of Room '.$this->getSlug()); + + return $this->createStreamObject($selection, $language); + } + + public function createStreamObject($selection, $language = 'native') + { + if($language == 'native' && $this->hasStereo()) + $language = 'stereo'; + + return new Stream($this, $selection, $language); + } } diff --git a/model/RoomSelection.php b/model/RoomSelection.php new file mode 100644 index 0000000..c163321 --- /dev/null +++ b/model/RoomSelection.php @@ -0,0 +1,52 @@ +<?php + +class RoomSelection +{ + public function __construct(Room $room, $selection) + { + $this->room = $room; + $this->selection = $selection; + } + + public function getRoom() + { + return $this->room; + } + + public function getSelection() + { + return $this->selection; + } + + public function getLink() + { + $selection = $this->getRoom()->getSelectionNames(); + if($selection[0] == $this->getSelection()) + return rawurlencode($this->getRoom()->getSlug()).'/'; + + return rawurlencode($this->getRoom()->getSlug()).'/'.rawurlencode($this->getSelection()).'/'; + } + + public function getTranslatedLink() + { + return $this->getLink().'translated/'; + } + + public function getDisplay() + { + switch($this->getSelection()) + { + case 'sd': + case 'hd': + return strtoupper($this->getSelection()); + + default: + return ucfirst($this->getSelection()); + } + } + + public function getTech() + { + return $this->getSelection().'-tech'; + } +} diff --git a/model/RoomTab.php b/model/RoomTab.php new file mode 100644 index 0000000..d4f781b --- /dev/null +++ b/model/RoomTab.php @@ -0,0 +1,34 @@ +<?php + +class RoomTab +{ + public function __construct(Room $room, $tab) + { + $this->room = $room; + $this->tab = $tab; + } + + public function getRoom() + { + return $this->room; + } + + public function getTab() + { + return $this->tab; + } + + public function getLink() + { + $tabs = $this->getRoom()->getTabNames(); + if($tabs[0] == $this->getTab()) + return rawurlencode($this->getRoom()->getSlug()).'/'; + + return rawurlencode($this->getRoom()->getSlug()).'/'.rawurlencode($this->getTab()).'/'; + } + + public function getDisplay() + { + return ucfirst($this->getTab()); + } +} diff --git a/model/Schedule.php b/model/Schedule.php index db940ce..ca4881d 100644 --- a/model/Schedule.php +++ b/model/Schedule.php @@ -5,4 +5,177 @@ class Schedule extends ModelBase public function getSimulationOffset() { return $this->get('SCHEDULE.SIMULATE_OFFSET', 0); } + + private function strtoduration($str) + { + $parts = explode(':', $str); + return ((int)$parts[0] * 60 + (int)$parts[1]) * 60; + } + + function program() + { + if(!has('SCHEDULE')) + return; + + if(has('SCHEDULE.CACHE') && function_exists('apc_fetch')) + { + $program = apc_fetch('SCHEDULE.CACHE'); + if($program) return $program; + } + + + $program = array(); + $opts = array( + 'http' => array( + 'timeout' => 2, + 'user_agent' => 'C3Voc Universal Streaming-Website Backend @ '.$_SERVER['HTTP_HOST'], + ) + ); + $context = stream_context_create($opts); + $schedule = file_get_contents(get('SCHEDULE.URL'), false, $context); + + // failed, give up + if(!$schedule) + return array(); + + $schedule = simplexml_load_string($schedule); + + // re-calculate day-ends + // some schedules have long gaps before the first talk or talks that expand beyond the dayend + // (fiffkon, i look at you) + // so to be on the safer side we calculate our own daystart/end here + foreach($schedule->day as $day) + { + $daystart = PHP_INT_MAX; + $dayend = 0; + + foreach($day->room as $room) + { + foreach($room->event as $event) + { + $start = strtotime((string)$event->date); + $duration = strtoduration((string)$event->duration); + $end = $start + $duration; + + $daystart = min($daystart, $start); + $dayend = max($dayend, $end); + } + } + + $day['start'] = $daystart; + $day['end'] = $dayend; + } + + $dayidx = 0; + foreach($schedule->day as $day) + { + $dayidx++; + $daystart = (int)$day['start']; + $dayend = (int)$day['end']; + + $roomidx = 0; + foreach($day->room as $room) + { + $roomidx++; + $lastend = false; + $name = (string)$room['name']; + if(isset($GLOBALS['CONFIG']['FAHRPLAN_ROOM_MAPPING'][$name])) + $name = $GLOBALS['CONFIG']['FAHRPLAN_ROOM_MAPPING'][$name]; + + foreach($room->event as $event) + { + $start = strtotime((string)$event->date); + $duration = strtoduration((string)$event->duration); + $end = $start + $duration; + + if($lastend && $lastend < $start) + { + // synthesize pause event + $pauseduration = $start - $lastend; + $program[$name][] = array( + 'special' => 'pause', + 'title' => round($pauseduration / 60).' minutes pause', + + 'fstart' => date('c', $lastend), + 'fend' => date('c', $start), + + 'start' => $lastend, + 'end' => $start, + 'duration' => $pauseduration, + ); + } + else if(!$lastend && $daystart < $start) + { + $program[$name][] = array( + 'special' => 'gap', + + 'fstart' => date('c', $daystart), + 'fend' => date('c', $start), + + 'start' => $daystart, + 'end' => $start, + 'duration' => $start - $daystart, + ); + } + + $personnames = array(); + foreach($event->persons->person as $person) + $personnames[] = (string)$person; + + $program[$name][] = array( + 'title' => (string)$event->title, + 'speaker' => implode(', ', $personnames), + + 'fstart' => date('c', $start), + 'fend' => date('c', $end), + + 'start' => $start, + 'end' => $end, + 'duration' => $duration, + ); + + $lastend = $end; + } + + // synthesize daychange event + if(!$lastend) $lastend = $daystart; + if($lastend < $dayend) + { + $program[$name][] = array( + 'special' => 'gap', + + 'fstart' => date('c', $lastend), + 'fend' => date('c', $dayend), + + 'start' => $lastend, + 'end' => $dayend, + 'duration' => $dayend - $lastend, + ); + } + + if($dayidx < count($schedule->day)) + { + $program[$name][] = array( + 'special' => 'daychange', + 'title' => 'Daychange from Day '.$dayidx.' to '.($dayidx+1), + + 'start' => $dayend, + 'end' => (int)$schedule->day[$dayidx]['start'], + 'duration' => 60*60, + ); + } + } + } + + if(has('SCHEDULE.CACHE') && function_exists('apc_store')) + { + apc_store( + 'SCHEDULE.CACHE', + $program, + get('SCHEDULE.CACHE') + ); + } + + return $program; + } } diff --git a/model/Stream.php b/model/Stream.php index a60284a..402dfff 100644 --- a/model/Stream.php +++ b/model/Stream.php @@ -1,5 +1,177 @@ <?php -class Room { +class Stream +{ + public function __construct(Room $room, $selection, $language) + { + $this->room = $room; + $this->selection = $selection; + $this->language = $language; + } + public function getRoom() + { + return $this->room; + } + + public function getSelection() + { + return $this->selection; + } + + public function getLanguage() + { + return $this->language; + } + + public function isTranslated() + { + return $this->getLanguage() == 'translated'; + } + + public function getVideoSize() + { + switch($this->getSelection()) + { + case 'sd': + case 'slides': + return array(1024, 576); + + case 'hd': + return array(1920, 1080); + + default: + return null; + } + } + + public function getVideoWidth() + { + $sz = $this->getVideoSize(); + return $sz[0]; + } + + public function getVideoHeight() + { + $sz = $this->getVideoSize(); + return $sz[1]; + } + + public function getTab() + { + switch($this->getSelection()) + { + case 'sd': + case 'hd': + return 'video'; + + default: + return $this->getSelection(); + } + } + + public function getPlayerType() + { + return $this->getTab(); + } + + public function getDisplay() + { + $display = $this->getRoom()->getDisplay().' '; + switch($this->getSelection()) + { + case 'hd': + $display .= 'FullHD Video'; + break; + + case 'sd': + $display .= 'SD Video'; + break; + + default: + $display .= ucfirst($this->getSelection()); + break; + } + + if($this->isTranslated()) + $display .= ' (Translation)'; + + return $display; + } + + public function getVideoUrl($proto) + { + switch($proto) + { + case 'webm': + return 'http://cdn.c3voc.de/'.rawurlencode($this->getRoom()->getStream()).'_'.rawurlencode($this->getLanguage()).'_'.rawurlencode($this->getSelection()).'.webm'; + + case 'hls': + return 'http://cdn.c3voc.de/hls/'.rawurlencode($this->getRoom()->getStream()).'_'.rawurlencode($this->getLanguage()).'_'.rawurlencode($this->getSelection()).'.m3u8'; + + default: + return null; + } + } + public static function getVideoProtos() + { + return array( + 'webm' => 'WebM', + 'hls' => 'HLS', + ); + } + + public function getSlidesUrl($proto) + { + return $this->getVideoUrl($proto); + } + public static function getSlidesProtos() + { + return Stream::getVideoProtos(); + } + + + public function getAudioUrl($proto) + { + switch($proto) + { + case 'mp3': + return 'http://cdn.c3voc.de/'.rawurlencode($this->getRoom()->getStream()).'_'.rawurlencode($this->getLanguage()).'.mp3'; + + case 'opus': + return 'http://cdn.c3voc.de/'.rawurlencode($this->getRoom()->getStream()).'_'.rawurlencode($this->getLanguage()).'.opus'; + + default: + return null; + } + } + public static function getAudioProtos() + { + return array( + 'mp3' => 'MP3', + 'opus' => 'Opus', + ); + } + + public function getMusicUrl($proto) + { + switch($proto) + { + case 'mp3': + return 'http://cdn.c3voc.de/'.rawurlencode($this->getRoom()->getStream()).'.mp3'; + + case 'opus': + return 'http://cdn.c3voc.de/'.rawurlencode($this->getRoom()->getStream()).'.opus'; + + default: + return null; + } + } + public static function getMusicProtos() + { + return array( + 'mp3' => 'MP3', + 'opus' => 'Opus', + ); + } } diff --git a/model/StreamList.php b/model/StreamList.php deleted file mode 100644 index d203a52..0000000 --- a/model/StreamList.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -class StreamList extends ModelBase implements IteratorAggregate -{ - private $streams = array(); - - public function __construct($slug) - { - if(! $this->has('ROOMS.'.$slug)) - throw new NotFoundException('Room '.$slug); - - $this->slug = $slug; - $this->streams = array(); - - $streams = $this->get("ROOMS.$slug.STREAMS"); - foreach((array)$streams as $stream) { - $this->streams[$stream] = explode('-', $stream); - } - } - - public function getRoomSlug() { - return $this->slug; - } - - public function getRoom() { - return new Room($this->getRoomSlug()); - } - - public function getStreams() { - return array_keys($this->streams); - } - - public function getIterator() { - return new ArrayIterator($this->streams); - } - - - public function hasTranslation() { - } - - public function hasRTMP() { - } - - public function hasHLS() { - } - - public function hasWebM() { - } - - public function hasHD() { - } - - public function hasSD() { - } -} |