public static function api($row_raw) { if (empty($row_raw)) { return array(); } if ($row_raw instanceof self) { $row_raw = $row_raw->toArray(); } $row = array('id' => (int) $row_raw['id'], 'station_id' => (int) $row_raw['station_id'], 'guid' => $row_raw['guid'], 'start_time' => (int) $row_raw['start_time'], 'end_time' => (int) $row_raw['end_time'], 'is_all_day' => (bool) $row_raw['is_all_day'], 'title' => $row_raw['title'], 'location' => $row_raw['location'], 'body' => $row_raw['body'], 'banner_url' => $row_raw['banner_url'], 'web_url' => $row_raw['web_url'], 'range' => $row_raw['range'] ?: self::getRangeText($row_raw['start_time'], $row_raw['end_time'], $row_raw['is_all_day']), 'image_url' => \PVL\Url::upload(self::getRowImageUrl($row_raw))); if (isset($row_raw['minutes_until'])) { $row['minutes_until'] = (int) $row_raw['minutes_until']; } // Add station shortcode. if (isset($row_raw['station'])) { $row['station'] = Station::api($row_raw['station']); $shortcode = Station::getStationShortName($row_raw['station']['name']); $row['station_shortcode'] = $shortcode; } return $row; }
public static function api($row_obj, $include_episodes = TRUE) { if ($row_obj instanceof self) { $row = $row_obj->toArray(); $row['stations'] = array(); if ($row_obj->stations) { foreach ($row_obj->stations as $station) { $row['stations'][] = Station::api($station); } } $row['episodes'] = array(); if ($include_episodes && $row_obj->episodes) { foreach ($row_obj->episodes as $episode) { $row['episodes'][] = $episode; } } } else { $row = $row_obj; if (isset($row['stations'])) { $stations_raw = array(); foreach ($row['stations'] as $station) { $stations_raw[] = Station::api($station); } $row['stations'] = $stations_raw; } else { $row['stations'] = array(); } if (!isset($row['episodes'])) { $row['episodes'] = array(); } } $api_row = array('id' => (int) $row['id'], 'name' => $row['name'], 'country' => $row['country'], 'description' => $row['description'], 'image_url' => \PVL\Url::upload(self::getArtistImage($row['image_url'])), 'banner_url' => \PVL\Url::upload($row['banner_url']), 'stations' => (array) $row['stations'], 'is_adult' => (bool) $row['is_adult']); if ($include_episodes) { $api_row['episodes'] = array(); $i = 1; foreach ((array) $row['episodes'] as $ep) { if (is_int($include_episodes) && $i > $include_episodes) { break; } $api_row['episodes'][] = PodcastEpisode::api($ep); $i++; } } $social_types = array_keys(self::getSocialTypes()); foreach ($social_types as $type_key) { $api_row[$type_key] = $row[$type_key]; } return $api_row; }
public function indexAction() { $urls = $this->em->createQuery('SELECT su, s FROM Entity\\ShortUrl su LEFT JOIN su.station s ORDER BY su.station_id, su.short_url ASC')->getArrayResult(); $global_custom_urls = array(); $station_custom_urls = array(); foreach ($urls as $url) { if ($url['station']) { $station_custom_urls[] = $url; } else { $global_custom_urls[] = $url; } } $this->view->station_custom_urls = $station_custom_urls; $this->view->global_custom_urls = $global_custom_urls; // Auto-Generated Station URLs. $station_details = Station::getShortNameLookup(); $station_categories = Station::getCategories(); $station_urls = array(); foreach ($station_details as $short_name => $station) { $station['url'] = ShortUrl::getFullUrl($short_name); $station['icon'] = $station_categories[$station['category']]['icon']; $station_urls[$short_name] = $station; } $this->view->station_urls = $station_urls; // Auto-Generated Convention Archive URLs $convention_details = Convention::getShortNameLookup(); $convention_urls = array(); foreach ($convention_details as $short_name => $convention) { $convention['url'] = ShortUrl::getFullUrl($short_name); $convention_urls[$short_name] = $convention; } $this->view->convention_urls = $convention_urls; }
public function viewAction() { $id = $this->getParam('id'); $station = Station::find($id); if ($station->is_active) { throw new \DF\Exception\DisplayOnly('This station has already been reviewed and is active.'); } $station_form = new \DF\Form($this->module_config['frontend']->forms->submit_station); $station_form->populate($station->toArray()); $form = new \DF\Form($this->current_module_config->forms->vote); if ($_POST && $form->isValid($_POST)) { $data = $form->getValues(); $data['name'] = $this->vote_name; $intake_votes = (array) $station->intake_votes; if ($data['decision'] == 'Abstain') { unset($intake_votes[$this->vote_hash]); } else { $intake_votes[$this->vote_hash] = $data; } $station->intake_votes = $intake_votes; $station->save(); $this->alert('Your vote has been submitted. Thank you for your feedback.', 'green'); $this->redirectFromHere(array('action' => 'index', 'id' => NULL)); return; } $this->view->station_form = $station_form; $this->view->form = $form; }
/** * Send notifications for new station events. * * @param \Phalcon\DiInterface $di * @param bool $force * @throws \DF\Exception */ public static function _runStationEvents(\Phalcon\DiInterface $di, $force = false) { $notify_minutes = 15; $em = $di->get('em'); $start_threshold = time(); $end_threshold = time() + 60 * $notify_minutes; $schedule_items = $em->createQuery('SELECT s, st FROM Entity\\Schedule s JOIN s.station st WHERE s.start_time >= :start AND s.start_time <= :end AND s.is_notified = 0')->setParameter('start', $start_threshold)->setParameter('end', $end_threshold)->setMaxResults(1)->execute(); if ($schedule_items) { $schedule_item = $schedule_items[0]; $station = $schedule_item->station; if ($station->twitter_url) { $twitter_handle = '@' . array_pop(explode('/', $station->twitter_url)); } else { $twitter_handle = $station->name; } $tweet = 'Tune in to ' . $schedule_item->title . ' in ' . $notify_minutes . ' minutes on ' . $twitter_handle . '!'; $tweet_url = $station->getShortUrl(); PvlNode::push('schedule.event_upcoming', array('event' => Schedule::api($schedule_item), 'station' => Station::api($station))); $image_url = NULL; if ($schedule_item->banner_url) { $image_url = $schedule_item->banner_url; } else { if ($station->banner_url) { $image_url = \PVL\Service\AmazonS3::path($station->banner_url); } } self::notify($tweet, $tweet_url, $image_url, $force); $schedule_item->is_notified = true; $schedule_item->save(); } }
public function fetchAction() { $this->doNotRender(); // Allow AJAX retrieval. $this->response->setHeader('Access-Control-Allow-Origin', '*'); $all_stations = Station::fetchArray(); $stations = array(); foreach ($all_stations as $station_info) { $stations[$station_info['short_name']] = $station_info; } $station_shortcode = $this->getParam('station', 'all'); // All parameters required for normal pull. if (!$this->hasParam('start') || !$this->hasParam('end')) { return $this->response->setJsonContent(array()); } $timestamps = array('start' => strtotime($this->getParam('start') . ' 00:00:00'), 'end' => strtotime($this->getParam('end') . ' 23:59:59') + 1); if ($station_shortcode != "all") { $station = $stations[$station_shortcode]; $events_raw = $this->em->createQuery('SELECT s, st FROM Entity\\Schedule s LEFT JOIN s.station st WHERE (s.station_id = :sid) AND (s.start_time <= :end AND s.end_time >= :start) ORDER BY s.start_time ASC')->setParameter('sid', $station['id'])->setParameter('start', $timestamps['start'])->setParameter('end', $timestamps['end'])->getArrayResult(); } else { $station = NULL; $events_raw = $this->em->createQuery('SELECT s, st FROM Entity\\Schedule s LEFT JOIN s.station st WHERE (s.start_time <= :end AND s.end_time >= :start) ORDER BY s.start_time ASC')->setParameter('start', $timestamps['start'])->setParameter('end', $timestamps['end'])->getArrayResult(); } $events = array(); foreach ((array) $events_raw as $event) { if (!$station) { $event['title'] = $event['station']['name'] . ":\n" . $event['title']; } $events[] = array('id' => $event['guid'], 'title' => $event['title'], 'allDay' => $event['is_all_day'] ? true : false, 'start' => date(\DateTime::ISO8601, $event['start_time']), 'end' => date(\DateTime::ISO8601, $event['end_time']), 'url' => $event['web_url']); } return $this->response->setJsonContent($events); }
public function deleteAction() { $record = Record::find($this->getParam('id')); if ($record) { $record->delete(); } $this->alert('Record deleted.', 'green'); $this->redirectFromHere(array('action' => 'index', 'id' => NULL, 'csrf' => NULL)); }
public static function showSpecialEventsMode() { if (Settings::getSetting('special_event', 0) == 1) { return true; } elseif (Settings::getSetting('special_event_if_stream_active', 0) == 1) { $stream = Station::getRepository()->findOneBy(array('name' => 'PVL Special Events')); if ($stream instanceof Station) { return $stream->isPlaying(); } else { return false; } } else { return false; } }
public function testAction() { $this->doNotRender(); set_time_limit(0); ini_set('memory_limit', '-1'); Debug::setEchoMode(); // -------- START HERE -------- // \PVL\CentovaCast::sync(); Debug::log('CCast Sync Complete'); $station = \Entity\Station::getRepository()->findOneBy(array('name' => 'PonyvilleFM')); $tracks = \PVL\CentovaCast::fetchTracks($station); Debug::print_r($tracks); // -------- END HERE -------- // Debug::log('Done!'); }
public function indexAction() { $default_station = 'PVL Presents (Video)'; $stations_covering = array('PVL Presents (Video)' => 'Video Stream', 'PVL Presents (Radio)' => 'Radio Stream'); $categories = array('event' => array('name' => 'Live Event Coverage', 'icon' => 'icon-star', 'stations' => array())); $all_stations = Station::fetchArray(); $stations_by_name = array(); foreach ($all_stations as $station) { $name = $station['name']; $stations_by_name[$name] = $station; if (isset($stations_covering[$name])) { $station['category'] = 'event'; $station['nickname'] = $stations_covering[$name]; $categories['event']['stations'][] = $station; } } $this->view->categories = $categories; $this->view->station_id = $stations_by_name[$default_station]['id']; $this->view->autoplay = true; }
/** * Static Functions */ public static function post($nowplaying) { $em = self::getEntityManager(); $active_shortcodes = Station::getShortNameLookup(); $total_overall = 0; foreach ($nowplaying as $short_code => $info) { $listeners = (int) $info['listeners']['current']; $station_id = $info['station']['id']; if (isset($active_shortcodes[$short_code])) { $total_overall += $listeners; } $record = new self(); $record->fromArray(array('station_id' => $station_id, 'type' => 'second', 'timestamp' => time(), 'number_min' => $listeners, 'number_max' => $listeners, 'number_avg' => $listeners)); $em->persist($record); } // Create "overall" statistic. $record = new self(); $record->fromArray(array('type' => 'second', 'timestamp' => time(), 'number_min' => $total_overall, 'number_max' => $total_overall, 'number_avg' => $total_overall)); $em->persist($record); $em->flush(); }
public function indexAction() { $urls = $this->em->createQuery('SELECT su FROM Entity\\ShortUrl su WHERE su.station_id = :station_id ORDER BY su.timestamp ASC')->setParameter('station_id', $this->station->id)->execute(); $this->view->urls = $urls; // Auto-Generated Station URLs. $station_details = Station::getShortNameLookup(); $station_categories = Station::getCategories(); $station_urls = array(); foreach ($station_details as $short_name => $station) { $station['url'] = ShortUrl::getFullUrl($short_name); $station['icon'] = $station_categories[$station['category']]['icon']; $station_urls[$short_name] = $station; } $this->view->station_urls = $station_urls; // Auto-Generated Convention Archive URLs $convention_details = Convention::getShortNameLookup(); $convention_urls = array(); foreach ($convention_details as $short_name => $convention) { $convention['url'] = ShortUrl::getFullUrl($short_name); $convention_urls[$short_name] = $convention; } $this->view->convention_urls = $convention_urls; }
protected function preDispatch() { parent::preDispatch(); $this->forceSecure(); $user = $this->auth->getLoggedInUser(); // Compile list of visible stations. $all_stations = Station::fetchAll(); $stations = array(); foreach ($all_stations as $station) { if ($station->canManage($user)) { $stations[$station->id] = $station; } } $this->stations = $stations; $this->view->stations = $stations; // Assign a station if one is selected. if ($this->hasParam('station')) { $station_id = (int) $this->getParam('station'); if (isset($stations[$station_id])) { $this->station = $stations[$station_id]; $this->view->station = $this->station; $this->view->hide_title = true; } else { throw new \DF\Exception\PermissionDenied(); } } else { if (count($this->stations) == 1) { // Convenience auto-redirect for single-station admins. $this->redirectFromHere(array('station' => key($this->stations))); return false; } } // Force a redirect to the "Select" page if no station ID is specified. if (!$this->station) { throw new \DF\Exception\PermissionDenied(); } }
public function indexAction() { // Get calendar name. $short_names = Station::getShortNameLookup(); $station_shortcode = $this->getParam('station', 'all'); if ($station_shortcode != "all") { $station = $short_names[$station_shortcode]; $calendar_name = $station['name']; } else { $calendar_name = 'Ponyville Live!'; } // Get timestamp boundaries. if ($this->hasParam('month')) { $show = $this->getParam('month'); $calendar = new \DF\Calendar($show); $timestamps = $calendar->getTimestamps(); $start_timestamp = $timestamps['start']; $end_timestamp = $timestamps['end']; $use_cache = true; $cache_name = 'month_' . $show; $calendar_name .= ' - ' . date('F Y', $timestamps['mid']); } elseif ($this->hasParam('start')) { $start_timestamp = (int) $this->getParam('start'); $end_timestamp = (int) $this->getParam('end'); $use_cache = false; $cache_name = null; // $cache_name = 'range_'.$start_timestamp.'_'.$end_timestamp; $calendar_name .= ' - ' . date('F j, Y', $start_timestamp) . ' to ' . date('F j, Y', $end_timestamp); } else { $start_timestamp = time(); $end_timestamp = time() + 86400 * 30; $use_cache = true; $cache_name = 'upcoming'; $calendar_name .= ' - Upcoming'; } // Load from cache or regenerate. if ($use_cache) { $cache_name = 'api_sched_' . $station_shortcode . '_' . $cache_name; $events = \DF\Cache::get($cache_name); } else { $events = null; } if (!$events) { if ($station_shortcode != "all") { $station = $short_names[$station_shortcode]; $events_raw = $this->em->createQuery('SELECT s FROM Entity\\Schedule s WHERE (s.station_id = :sid) AND (s.start_time <= :end AND s.end_time >= :start) ORDER BY s.start_time ASC')->setParameter('sid', $station['id'])->setParameter('start', $start_timestamp)->setParameter('end', $end_timestamp)->getArrayResult(); } else { $events_raw = $this->em->createQuery('SELECT s, st FROM Entity\\Schedule s LEFT JOIN s.station st WHERE (s.start_time <= :end AND s.end_time >= :start) ORDER BY s.start_time ASC')->setParameter('start', $start_timestamp)->setParameter('end', $end_timestamp)->getArrayResult(); } $events = array(); foreach ((array) $events_raw as $event) { $events[] = Schedule::api($event); } if ($use_cache) { \DF\Cache::save($events, $cache_name, array(), 300); } } $format = strtolower($this->getParam('format', 'json')); switch ($format) { case "ics": case "ical": return $this->_printCalendar($events, $calendar_name, $cache_name); break; case "json": default: return $this->returnSuccess($events); break; } }
public static function run($force_run = false) { $di = \Phalcon\Di::getDefault(); $em = $di->get('em'); $config = $di->get('config'); // Set up Google Client. $gclient_api_key = $config->apis->google_apis_key; $gclient_app_name = $config->application->name; if (empty($gclient_api_key)) { return null; } $gclient = new \Google_Client(); $gclient->setApplicationName($gclient_app_name); $gclient->setDeveloperKey($gclient_api_key); $gcal = new \Google_Service_Calendar($gclient); // Prevent running repeatedly in too short of a time (avoid API limits). $last_run = Settings::getSetting('schedule_manager_last_run', 0); if ($last_run > time() - 60 && !$force_run) { return null; } $schedule_items = array(); $schedule_records = array(); $stations = $em->createQuery('SELECT s FROM Entity\\Station s WHERE (s.gcal_url IS NOT NULL AND s.gcal_url != \'\') AND s.is_active = 1')->getArrayResult(); $active_stations = Utilities::ipull($stations, 'id'); // Clear all invalid station records. $em->createQuery('DELETE FROM Entity\\Schedule s WHERE (s.station_id IS NOT NULL) AND (s.station_id NOT IN (:station_ids))')->setParameter('station_ids', $active_stations)->execute(); foreach ($stations as $station) { if ($station['gcal_url']) { $schedule_items[] = array('name' => $station['name'], 'url' => $station['gcal_url'], 'type' => 'station', 'station_id' => $station['id'], 'image_url' => \DF\Url::content($station['image_url'])); } } Debug::startTimer('Get Calendar Records'); // Time boundaries for calendar entries. $threshold_start = date(\DateTime::RFC3339, strtotime('-1 week')); $threshold_end = date(\DateTime::RFC3339, strtotime('+1 year')); foreach ($schedule_items as $item) { // Get the "calendar_id" from the URL provided by the user. $orig_url_parts = parse_url($item['url']); $url_path_parts = explode('/', $orig_url_parts['path']); $calendar_id = urldecode($url_path_parts[3]); if (empty($calendar_id)) { continue; } // Call the external Google Calendar client. try { $all_events = $gcal->events->listEvents($calendar_id, array('timeMin' => $threshold_start, 'timeMax' => $threshold_end, 'singleEvents' => 'true', 'orderBy' => 'startTime', 'maxResults' => '300')); } catch (\Exception $e) { continue; } // Process each individual event. foreach ($all_events as $event_orig) { $title = $event_orig->summary; $body = $event_orig->description; $location = $event_orig->location; $web_url = $event_orig->htmlLink; $banner_url = null; $is_all_day = false; $start_time_obj = $event_orig->start; if ($start_time_obj->date) { $is_all_day = true; $start_time = strtotime($start_time_obj->date . ' 00:00:00'); } else { $start_time = strtotime($start_time_obj->dateTime); } $end_time_obj = $event_orig->end; if ($end_time_obj->date) { $is_all_day = true; $end_time = strtotime($end_time_obj->date . ' 00:00:00'); } elseif ($end_time_obj) { $end_time = strtotime($end_time_obj->dateTime); } else { $end_time = $start_time; } // Detect URLs for link. if ($body && !$web_url) { preg_match('@((https?://)?([-\\w]+\\.[-\\w\\.]+)+\\w(:\\d+)?(/([-\\w/_\\.]*(\\?\\S+)?)?)*)@', $body, $urls); if (count($urls) > 0) { $web_url = $urls[0]; } } // Detect URLs for photo. if ($location) { preg_match('@((https?://)?([-\\w]+\\.[-\\w\\.]+)+\\w(:\\d+)?(/([-\\w/_\\.]*(\\?\\S+)?)?)*)@', $location, $urls); if (count($urls) > 0) { $banner_url = $urls[0]; } } $guid = md5(implode('|', array($event_orig->id, $start_time, $end_time, $title, $location))); $schedule_record = array('guid' => $guid, 'type' => $item['type'], 'start_time' => $start_time, 'end_time' => $end_time, 'is_all_day' => $is_all_day, 'title' => $title, 'location' => $location, 'body' => \DF\Utilities::truncateText(strip_tags($body), 300), 'banner_url' => $banner_url, 'web_url' => $web_url); \PVL\Debug::print_r($schedule_record); $schedule_records[$item['station_id']][$guid] = $schedule_record; } } Debug::endTimer('Get Calendar Records'); if (count($schedule_records) == 0) { Debug::log('Error: No calendar records loaded'); return; } // Add/Remove all differential records. Debug::startTimer('Sync DB Records'); foreach ($schedule_records as $station_id => $station_records) { $station = Station::find($station_id); if ($station_id == 0) { $existing_guids_raw = $em->createQuery('SELECT s.guid FROM Entity\\Schedule s WHERE s.station_id IS NULL')->getArrayResult(); } else { $existing_guids_raw = $em->createQuery('SELECT s.guid FROM Entity\\Schedule s WHERE s.station_id = :sid')->setParameter('sid', $station_id)->getArrayResult(); } $existing_guids = array(); foreach ($existing_guids_raw as $i) { $existing_guids[] = $i['guid']; } $new_guids = array_keys($station_records); $guids_to_delete = array_diff($existing_guids, $new_guids); if ($guids_to_delete) { $em->createQuery('DELETE FROM Entity\\Schedule s WHERE s.guid IN (:guids)')->setParameter('guids', $guids_to_delete)->execute(); } $guids_to_add = array_diff($new_guids, $existing_guids); if ($guids_to_add) { foreach ($guids_to_add as $guid) { $schedule_record = $station_records[$guid]; $record = new Schedule(); $record->station = $station; $record->fromArray($schedule_record); $em->persist($record); } } $em->flush(); $em->clear(); } Debug::endTimer('Sync DB Records'); Settings::setSetting('schedule_manager_last_run', time()); }
<?php /** * Settings form. */ return array('form' => array('method' => 'post', 'groups' => array('flags' => array('legend' => 'Site Content Display Flags', 'elements' => array('special_event_if_stream_active' => array('radio', array('label' => 'Automatically Enable Special Events when PVL Stream Live', 'description' => 'This will cause the special event code below to automatically preempt the homepage rotator if any stream is active on the PVL Special Events channel.', 'multiOptions' => array(0 => 'No', 1 => 'Yes'), 'default' => 1)), 'special_event' => array('radio', array('label' => 'Special Event - Manual Override', 'description' => 'Causes an advertisement to tune in to the stream to preempt any other rotating images on the homepage.', 'multiOptions' => array(0 => 'No', 1 => 'Yes'), 'default' => 0)), 'special_event_embed_code' => array('textarea', array('label' => 'Special Event - Left Panel Embed Code', 'class' => 'full-width full-height input-code', 'spellcheck' => 'false')), 'special_event_chat_code' => array('textarea', array('label' => 'Special Event - Right Panel Chat', 'class' => 'full-width double-height input-code', 'spellcheck' => 'false')), 'special_event_station_id' => array('select', array('label' => 'Special Event - Default Station', 'description' => 'Configure a station to autoplay on the homepage when loaded. This should only be used when a radio station should be shown instead of the video stream (uncommon).', 'multiOptions' => \Entity\Station::fetchSelect(TRUE))), 'footer_message' => array('textarea', array('label' => 'Footer Custom Message to Visitors', 'class' => 'full-width double-height input-code', 'spellcheck' => 'false')))), 'misc' => array('legend' => 'Miscellaneous Items', 'elements' => array('notes' => array('textarea', array('label' => 'Scratch Area', 'description' => 'The code and other items pasted here aren\'t displayed anywhere in the system, but are useful for storing notes related to this page, including other embed codes.', 'class' => 'full-width double-height input-code', 'spellcheck' => 'false')))), 'submit' => array('legend' => '', 'elements' => array('submit' => array('submit', array('type' => 'submit', 'label' => 'Save Changes', 'helper' => 'formButton', 'class' => 'ui-button')))))));
public static function stationUrls() { $urls = array(); $short_names = Station::getShortNameLookup(); foreach ($short_names as $short_name => $record) { $urls[$short_name] = \DF\Url::route(array('module' => 'default', 'controller' => 'index', 'action' => 'index', 'id' => $record['id'], 'autoplay' => 'true')); } return $urls; }
public function playlistAction() { $this->doNotRender(); if ($this->station) { $stations = array($this->station); } else { $all_stations = Station::getStationsInCategories(); $stations = $all_stations['audio']['stations']; } $format = strtolower($this->getParam('format', 'pls')); switch ($format) { // M3U Playlist Format case "m3u": $m3u_lines = array(); $m3u_lines[] = '#EXTM3U'; $i = 0; foreach ($stations as $station) { foreach ($station['streams'] as $stream) { if (!$stream['is_active']) { continue; } $m3u_lines[] = '#EXTINF:' . $i . ',PVL! ' . $station['name'] . ': ' . $stream['name']; $m3u_lines[] = $stream['stream_url']; $i++; } } $m3u_file = implode("\r\n", $m3u_lines); header('Content-Type: audio/x-mpegurl'); header('Content-Disposition: attachment; filename="pvl_stations.m3u"'); echo $m3u_file; break; // Euro Truck Simulator 2 // Euro Truck Simulator 2 case "ets": $ets_lines = array(); $ets_i = 0; foreach ($stations as $station) { foreach ($station['streams'] as $stream) { if (!$stream['is_active'] || !$stream['is_default']) { continue; } $ets_line = array(str_replace('|', '', $stream['stream_url']), str_replace('|', '', $station['name']), str_replace('|', '', $station['genre']), $station['country'] ? strtoupper($station['country']) : 'EN', 128, 1); $ets_lines[] = ' stream_data[' . $ets_i . ']: "' . implode('|', $ets_line) . '"'; $ets_i++; } } $ets_file = "SiiNunit\n{\nlive_stream_def : _nameless.0662.83F8 {\n"; $ets_file .= " stream_data: " . count($ets_lines) . "\n"; $ets_file .= implode("\n", $ets_lines); $ets_file .= "\n}\n\n}"; header('Content-Type: text/plain'); header('Content-Disposition: attachment; filename="live_streams.sii"'); echo $ets_file; break; // PLS Playlist Format // PLS Playlist Format case "pls": default: $output = array(); $output[] = '[playlist]'; $output[] = 'NumberOfEntries=' . count($stations); $i = 1; foreach ($stations as $station) { foreach ($station['streams'] as $stream) { if (!$stream['is_active']) { continue; } $output[] = 'File' . $i . '=' . $stream['stream_url']; $output[] = 'Title' . $i . '=PVL! ' . $station['name'] . ': ' . $stream['name']; $output[] = 'Length' . $i . '=-1'; $output[] = 'Version=2'; $i++; } } header('Content-Type: audio/x-scpls'); header('Content-Disposition: attachment; filename="pvl_stations.pls"'); echo implode("\r\n", $output); break; } }
protected function _initStations() { $this->view->station_id = $station_id = $this->getParam('id', NULL); $this->view->volume = $this->hasParam('volume') ? (int) $this->getParam('volume') : NULL; $this->categories = Station::getCategories(); $stations_raw = Station::fetchArray(); // Limit to a single station if requested. if ($station_id && $this->getParam('showonlystation', false) == 'true') { foreach ($stations_raw as $station) { if ($station['id'] == $station_id) { $stations_raw = array($station); break; } } } $this->stations = array(); foreach ($stations_raw as $station) { // Build multi-stream directory. $streams = array(); $current_stream_id = NULL; foreach ((array) $station['streams'] as $stream) { if (!$stream['hidden_from_player'] && $stream['is_active']) { if ($stream['is_default']) { $station['default_stream_id'] = $stream['id']; $current_stream_id = $stream['id']; } $streams[$stream['id']] = $stream; } } // Pull from user preferences to potentially override defaults. $default_streams = (array) \PVL\Customization::get('stream_defaults'); if (isset($default_streams[$station['id']])) { $stream_id = (int) $default_streams[$station['id']]; if (isset($streams[$stream_id])) { $current_stream_id = $stream_id; } } $station['current_stream_id'] = $current_stream_id; $station['streams'] = $streams; // Only show stations with at least one usable stream. if (count($streams) > 0) { $this->stations[$station['id']] = $station; } } foreach ($this->stations as $station) { if (isset($this->categories[$station['category']])) { $this->categories[$station['category']]['stations'][] = $station; } } $this->view->stations = $this->stations; $this->view->categories = $this->categories; }
<?php return array('method' => 'post', 'enctype' => 'multipart/form-data', 'groups' => array('profile' => array('legend' => 'Basic Details', 'elements' => array('name' => array('text', array('label' => 'Name', 'class' => 'half-width', 'required' => true)), 'description' => array('textarea', array('label' => 'Podcast Description', 'description' => 'Tell us about what you do in the pony community, what projects you\'ve worked with, or how you want to contribute in the future.', 'class' => 'full-width half-height')), 'country' => array('select', array('label' => 'Country of Broadcast', 'multiOptions' => \PVL\Internationalization::getCountryLookup(), 'default' => '')), 'web_url' => array('text', array('label' => 'Web Site Address', 'class' => 'half-width', 'validators' => array('WebAddress'))), 'contact_email' => array('text', array('label' => 'Contact E-mail Address', 'description' => 'Include to show an e-mail link for the station on the "Contact Us" page.', 'validators' => array('EmailAddress'), 'class' => 'half-width')), 'image_url' => array('image', array('label' => 'Avatar (150x150 PNG)', 'description' => 'This is the small image that appears on your profile. Images should be under 150x150px in size. Larger images will automatically be scaled.')), 'banner_url' => array('image', array('label' => 'Promotional Banner (600x300 PNG)', 'description' => 'This image will be shown in the header rotator when new episodes are posted. Images should be 600x300.')), 'stations' => array('multiCheckbox', array('label' => 'Airs on Station(s)', 'description' => 'Select the station(s) that this podcast broadcasts on.', 'multiOptions' => \Entity\Station::fetchSelect())), 'is_adult' => array('radio', array('label' => 'Contains Adult (18+) Content', 'description' => 'If this podcast contains any content that may be considered "R-rated", or suitable only for adults 18 years or older, please select "Yes" below to indicate this on public pages.', 'multiOptions' => array(0 => 'No', 1 => 'Yes'), 'default' => 0)), 'always_use_banner_url' => array('radio', array('label' => 'Always Use Promotional Banner for New Episode Promotion', 'description' => 'When promoting an individual episode, if PVL is able to pull an individual thumbnail for the video, it will use this for promoting the episode instead of the one you supply above. To force the banner URL above to always be used, select "Yes" here.', 'multiOptions' => array(0 => 'No', 1 => 'Yes'), 'default' => 0)))), 'social' => array('legend' => 'Social Networking Links', 'description' => ' <b>Note: Updating these links does not automatically update the source of your podcast episodes.</b> To do this, visit the "Syndication Sources" page in the Podcast Center.<br> All fields are optional. Most of the time, your web address for these services will match the format shown in the field. ', 'elements' => array('rss_url' => array('text', array('label' => 'RSS Feed Address', 'class' => 'half-width', 'validators' => array('WebAddress'))), 'twitter_url' => array('text', array('label' => 'Twitter Address', 'class' => 'half-width', 'validators' => array('WebAddress'), 'placeholder' => 'http://www.twitter.com/YourUsername')), 'tumblr_url' => array('text', array('label' => 'Tumblr Address', 'class' => 'half-width', 'validators' => array('WebAddress'), 'placeholder' => 'http://YourUsername.tumblr.com')), 'facebook_url' => array('text', array('label' => 'Facebook Address', 'class' => 'half-width', 'validators' => array('WebAddress'), 'placeholder' => 'http://www.facebook.com/YourUserName')), 'youtube_url' => array('text', array('label' => 'YouTube Address', 'class' => 'half-width', 'validators' => array('WebAddress'), 'placeholder' => 'http://www.youtube.com/YourUsername')), 'soundcloud_url' => array('text', array('label' => 'SoundCloud Address', 'class' => 'half-width', 'validators' => array('WebAddress'), 'placeholder' => 'http://www.soundcloud.com/YourUsername')), 'deviantart_url' => array('text', array('label' => 'DeviantArt Address', 'class' => 'half-width', 'validators' => array('WebAddress'), 'placeholder' => 'http://YourUsername.deviantart.com')), 'livestream_url' => array('text', array('label' => 'LiveStream Address', 'class' => 'half-width', 'validators' => array('WebAddress'), 'placeholder' => 'http://livestream.com/username')))), 'admin' => array('legend' => 'Administrator Details', 'elements' => array('is_approved' => array('radio', array('label' => 'Is Approved', 'multiOptions' => array(0 => 'No', 1 => 'Yes'), 'default' => 1)))), 'submit_grp' => array('elements' => array('submit' => array('submit', array('type' => 'submit', 'label' => 'Save Changes', 'helper' => 'formButton', 'class' => 'ui-button'))))));
/** * Process a single video stream's NowPlaying info. * * @param StationStream $stream * @param Station $station * @return array Structured NowPlaying Data */ public static function processVideoStream(StationStream $stream, Station $station, $force = false) { $current_np_data = (array) $stream->nowplaying_data; if (!$force && NOWPLAYING_SEGMENT % 2 == 0 && !empty($current_np_data)) { return $current_np_data; } // Process stream. $custom_class = Station::getStationClassName($station->name); $custom_adapter = '\\PVL\\VideoAdapter\\' . $custom_class; $np = StationStream::api($stream); if (class_exists($custom_adapter)) { $np_adapter = new $custom_adapter($stream, $station); $stream_np = $np_adapter->process(); } else { $adapters = array(new \PVL\VideoAdapter\Livestream($stream, $station), new \PVL\VideoAdapter\TwitchTv($stream, $station), new \PVL\VideoAdapter\UStream($stream, $station), new \PVL\VideoAdapter\StreamUp($stream, $station)); foreach ($adapters as $np_adapter) { if ($np_adapter->canHandle()) { $stream_np = $np_adapter->process(); break; } } } if (!empty($stream_np)) { $np = array_merge($np, $stream_np); $np['status'] = isset($np['meta']['status']) ? $np['meta']['status'] : 'offline'; Debug::log('Adapter Class: ' . get_class($np_adapter)); Debug::print_r($np); } else { $np['on_air'] = array('text' => 'Stream Offline'); $np['meta'] = array('status' => 'offline', 'listeners' => 0); $np['status'] = 'offline'; } $stream->nowplaying_data = $np; return $np; }
public function songconfirmAction() { // Handle files submitted directly to page. $ignore_files = (int) $this->getParam('ignore_files'); $request = $this->di->get('request'); if ($request->hasFiles() && !$ignore_files) { $this->_processSongUpload(); } // Validate song identifier token. $token = $this->_getSongHashToken(); if (!$this->_validateSongHash($token)) { return $this->redirectFromHere(array('action' => 'song')); } // Check that any stations were selected if (!$this->hasParam('stations')) { throw new \DF\Exception\DisplayOnly('You did not specify any stations!'); } // Check for uploaded songs. $temp_dir_name = 'song_uploads'; $temp_dir = DF_INCLUDE_TEMP . DIRECTORY_SEPARATOR . $temp_dir_name; $all_files = glob($temp_dir . DIRECTORY_SEPARATOR . $token . '*.mp3'); if (empty($all_files)) { throw new \DF\Exception\DisplayOnly('No files were uploaded!'); } $songs = array(); $getId3 = new GetId3(); $getId3->encoding = 'UTF-8'; foreach ($all_files as $song_file_base) { $song_file_path = $temp_dir . DIRECTORY_SEPARATOR . basename($song_file_base); // Attempt to analyze the MP3. $audio = $getId3->analyze($song_file_path); if (isset($audio['error'])) { @unlink($song_file_path); throw new \DF\Exception\DisplayOnly(sprintf('Error at reading audio properties with GetId3: %s.', $audio['error'][0])); } if (isset($audio['tags']['id3v1']['title'])) { $song_data = array('title' => $audio['tags']['id3v1']['title'][0], 'artist' => $audio['tags']['id3v1']['artist'][0]); } elseif (isset($audio['tags']['id3v2']['title'])) { $song_data = array('title' => $audio['tags']['id3v2']['title'][0], 'artist' => $audio['tags']['id3v2']['artist'][0]); } else { @unlink($song_file_path); continue; } // Check if existing submission exists. $song = Song::getOrCreate($song_data); $existing_submission = SongSubmission::getRepository()->findOneBy(array('hash' => $song->id)); if ($existing_submission instanceof SongSubmission) { @unlink($song_file_path); continue; } // Create record in database. $metadata = array('File Format' => strtoupper($audio['fileformat']), 'Play Time' => $audio['playtime_string'], 'Bitrate' => round($audio['audio']['bitrate'] / 1024) . 'kbps', 'Bitrate Mode' => strtoupper($audio['audio']['bitrate_mode']), 'Channels' => $audio['audio']['channels'], 'Sample Rate' => $audio['audio']['sample_rate']); $record = new SongSubmission(); $record->song = $song; $auth = $this->di->get('auth'); $record->user = $auth->getLoggedInUser(); $record->title = $song_data['title']; $record->artist = $song_data['artist']; $record->song_metadata = $metadata; $record->stations = $this->getParam('stations'); $song_download_url = $record->uploadSong($song_file_path); $record->save(); // Append information to e-mail to stations. $song_row = array('Download URL' => '<a href="' . $song_download_url . '" target="_blank">' . $song_download_url . '</a>', 'Title' => $song_data['title'], 'Artist' => $song_data['artist']) + $metadata; $songs[] = $song_row; } if (!empty($songs)) { // Notify all existing managers. $network_administrators = Action::getUsersWithAction('administer all'); $email_to = Utilities::ipull($network_administrators, 'email'); // Pull list of station managers for the specified stations. $station_managers = array(); $short_names = Station::getShortNameLookup(); foreach ($this->getParam('stations') as $station_key) { if (isset($short_names[$station_key])) { $station_id = $short_names[$station_key]['id']; $station = Station::find($station_id); foreach ($station->managers as $manager) { $station_managers[] = $manager->email; } } } $email_to = array_merge($email_to, $station_managers); // Trigger e-mail notice. if (!empty($email_to)) { \DF\Messenger::send(array('to' => $email_to, 'subject' => 'New Song(s) Submitted to Station', 'template' => 'newsong', 'vars' => array('songs' => $songs))); } } // Have to manually call view because a view was already rendered (e-mail was sent). // TODO: Fix this. It's dumb. $this->view->songs = $songs; return $this->view->render('submit', 'songconfirm'); }
/** * Main display. */ public function indexAction() { // Inject all stations. $stations = \Entity\Station::fetchAll(); $this->view->stations = $stations; // Inject all podcasts. $podcasts = \Entity\Podcast::fetchAll(); $this->view->podcasts = $podcasts; // Pull cached statistic charts if available. $metrics = \DF\Cache::get('admin_metrics'); if (!$metrics) { // Statistics by day. $influx = $this->di->get('influx'); $station_averages = array(); $network_data = array('PVL Network' => array('ranges' => array(), 'averages' => array())); $daily_stats = $influx->setDatabase('pvlive_stations')->query('SELECT * FROM /1d.*/ WHERE time > now() - 180d', 'm'); foreach ($daily_stats as $stat_series => $stat_rows) { $series_split = explode('.', $stat_series); if ($series_split[1] == 'all') { $network_name = 'PVL Network'; foreach ($stat_rows as $stat_row) { $network_data[$network_name]['ranges'][$stat_row['time']] = array($stat_row['time'], $stat_row['min'], $stat_row['max']); $network_data[$network_name]['averages'][$stat_row['time']] = array($stat_row['time'], round($stat_row['value'], 2)); } } else { $station_id = $series_split[2]; foreach ($stat_rows as $stat_row) { $station_averages[$station_id][$stat_row['time']] = array($stat_row['time'], round($stat_row['value'], 2)); } } } $network_metrics = array(); foreach ($network_data as $network_name => $data_charts) { if (isset($data_charts['ranges'])) { $metric_row = new \stdClass(); $metric_row->name = $network_name . ' Listener Range'; $metric_row->type = 'arearange'; ksort($data_charts['ranges']); $metric_row->data = array_values($data_charts['ranges']); $network_metrics[] = $metric_row; } if (isset($data_charts['averages'])) { $metric_row = new \stdClass(); $metric_row->name = $network_name . ' Daily Average'; $metric_row->type = 'spline'; ksort($data_charts['averages']); $metric_row->data = array_values($data_charts['averages']); $network_metrics[] = $metric_row; } } $station_metrics = array(); foreach ($stations as $station) { $station_id = $station['id']; if (isset($station_averages[$station_id])) { $series_obj = new \stdClass(); $series_obj->name = $station['name']; $series_obj->type = 'spline'; ksort($station_averages[$station_id]); $series_obj->data = array_values($station_averages[$station_id]); $station_metrics[] = $series_obj; } } // Podcast and API Call Metrics $analytics_raw = $influx->setDatabase('pvlive_analytics')->query('SELECT * FROM /1h.*/', 'm'); $raw_metrics = array(); foreach ($analytics_raw as $series_name => $series_stats) { $series_parts = explode('.', $series_name); if ($series_parts[1] == 'api_calls') { $metric_section = 'api'; } else { $metric_section = 'podcast'; } foreach ($series_stats as $stat_row) { if (!isset($raw_metrics[$metric_section][$stat_row['time']])) { $raw_metrics[$metric_section][$stat_row['time']] = 0; } $raw_metrics[$metric_section][$stat_row['time']] += $stat_row['count']; } } // Reformat for highcharts. $refined_metrics = array(); foreach ($raw_metrics as $metric_type => $metric_rows) { ksort($metric_rows); foreach ($metric_rows as $row_timestamp => $row_value) { $refined_metrics[$metric_type][] = array($row_timestamp, $row_value); } } // API call object $series_obj = new \stdClass(); $series_obj->name = 'API Calls'; $series_obj->type = 'spline'; $series_obj->data = $refined_metrics['api']; $api_metrics = array($series_obj); // Podcast object $series_obj = new \stdClass(); $series_obj->name = 'Podcast Clicks'; $series_obj->type = 'spline'; $series_obj->data = $refined_metrics['podcast']; $podcast_metrics = array($series_obj); $metrics = array('network' => json_encode($network_metrics), 'station' => json_encode($station_metrics), 'api' => json_encode($api_metrics), 'podcast' => json_encode($podcast_metrics)); \DF\Cache::save($metrics, 'admin_metrics', array(), 600); } $this->view->metrics = $metrics; // Synchronization statuses if ($this->acl->isAllowed('administer all')) { $this->view->sync_times = \PVL\SyncManager::getSyncTimes(); } // PVLNode service stats. $this->view->pvlnode_stats = \PVL\Service\PvlNode::fetch(); }
<?php return array('method' => 'post', 'enctype' => 'multipart/form-data', 'groups' => array('profile' => array('legend' => 'Profile Information', 'elements' => array('name' => array('text', array('label' => 'Station Name', 'class' => 'half-width', 'required' => true)), 'genre' => array('text', array('label' => 'Station Genre', 'description' => 'Listed underneath the station in the player.')), 'description' => array('textarea', array('label' => 'Station Description', 'class' => 'full-width full-height')), 'country' => array('select', array('label' => 'Country of Broadcast', 'multiOptions' => \PVL\Internationalization::getCountryLookup(), 'default' => '')), 'image_url' => array('image', array('label' => 'Upload New Station Avatar (150x150 PNG)', 'description' => 'To replace the existing icon associated with this station, upload a new one using the file browser below. Icons should be 150x150px in dimension.')), 'banner_url' => array('image', array('label' => 'Upload New Promotional Banner (600x300 PNG)', 'description' => 'This image will be shown in the header rotator when events are promoted. Images should be 600x300.')))), 'contact' => array('legend' => 'Contact Information', 'elements' => array('web_url' => array('text', array('label' => 'Web URL', 'description' => 'Include full address (with http://).', 'class' => 'half-width')), 'contact_email' => array('text', array('label' => 'Contact E-mail Address', 'description' => 'Include to show an e-mail link for the station on the "Contact Us" page.', 'validators' => array('EmailAddress'), 'class' => 'half-width')), 'twitter_url' => array('text', array('label' => 'Twitter URL', 'description' => 'Include full address of the station\'s Twitter account (with http://).', 'class' => 'half-width')), 'facebook_url' => array('text', array('label' => 'Facebook URL', 'description' => 'Optional: This will be included in the "Contact Us" page if provided.', 'class' => 'half-width')), 'tumblr_url' => array('text', array('label' => 'Tumblr URL', 'description' => 'Optional: This will be included in the "Contact Us" page if provided.', 'class' => 'half-width')), 'gcal_url' => array('text', array('label' => 'Google Calendar XML Feed URL', 'description' => 'This URL can be retrieved by visiting Google Calendar, hovering over the station\'s calendar on the left sidebar, clicking the dropdown menu, then "Calendar Settings". From the settings page, click the "XML" link inside the Calendar Address area. Include full address of the feed (ending in /basic or /full) (with http://).', 'class' => 'half-width')), 'irc' => array('text', array('label' => 'IRC Channel Name', 'description' => 'Include hash tag: #channelname')))), 'admin' => array('legend' => 'Administrator Settings', 'elements' => array('category' => array('radio', array('label' => 'Station Category', 'multiOptions' => \Entity\Station::getCategorySelect(), 'required' => true)), 'affiliation' => array('radio', array('label' => 'PVL Affiliation Level', 'multiOptions' => \Entity\Station::getAffiliationSelect())), 'is_active' => array('radio', array('label' => 'Is Active', 'description' => 'Is visible in the PVL network player interface.', 'multiOptions' => array(1 => 'Yes', 0 => 'No'), 'default' => 1)), 'weight' => array('text', array('label' => 'Sort Order', 'description' => 'Lower numbers appear higher on the list of stations.')), 'requests_enabled' => array('radio', array('label' => 'Enable Request System', 'description' => 'Enable the "Submit Request" button under this station.', 'multiOptions' => array(1 => 'Yes', 0 => 'No'), 'default' => 0)), 'requests_ccast_username' => array('text', array('label' => 'Request System CentovaCast Account Name', 'description' => 'Account username in the CentovaCast system, if requests are enabled.', 'class' => 'half-width')), 'requests_external_url' => array('text', array('label' => 'External URL for Third-Party Request System', 'description' => 'If the station is using a non-CentovaCast request system, enter the URL for it below.', 'class' => 'half-width')), 'admin_notes' => array('textarea', array('label' => 'Administration Notes', 'description' => 'These notes are only visible by network administration.', 'class' => 'full-width half-height')), 'station_notes' => array('textarea', array('label' => 'Station Notes', 'description' => 'These notes are visible/editable by station owners.', 'class' => 'full-width half-height')))), 'submit_grp' => array('elements' => array('submit' => array('submit', array('type' => 'submit', 'label' => 'Save Changes', 'helper' => 'formButton', 'class' => 'ui-button'))))));
public function listAction() { $category = $this->getParam('category', 'all'); if ($category == 'all') { $stations_raw = Station::fetchArray(); } else { $cats = Station::getStationsInCategories(); if (!isset($cats[$category])) { return $this->returnError('Category not found.'); } $stations_raw = $cats[$category]['stations']; } $stations = array(); foreach ($stations_raw as $row) { $stations[] = Station::api($row); } return $this->returnSuccess($stations); }