/** * Generate Structured NowPlaying Data * * @param Station $station * @return array Structured NowPlaying Data */ public static function processStation(Station $station) { $em = self::getEntityManager(); $np_old = (array) $station->nowplaying_data; $np = array(); $np['status'] = 'offline'; $np['station'] = Station::api($station); // Remove API-supplied 'streams' item in the wrong place. unset($np['station']['streams']); $listener_totals = array('current' => 0, 'unique' => 0, 'total' => 0); $np['streams'] = array(); foreach ($station->streams as $stream) { if (!$stream->is_active) { continue; } if ($station->category == 'video') { $np_stream = self::processVideoStream($stream, $station); foreach ($listener_totals as $type => $total) { $listener_totals[$type] += $np_stream['meta']['listeners']; } } else { $np_stream = self::processAudioStream($stream, $station); foreach ($np_stream['listeners'] as $type => $count) { $listener_totals[$type] += $count; } } $np['streams'][] = $np_stream; $em->persist($stream); // Merge default info into main array for legacy purposes. if ($np_stream['is_default'] == true) { $np['status'] = $np_stream['status']; $np['station']['stream_url'] = $np_stream['url']; $np['station']['default_stream_id'] = $np_stream['id']; if ($station->category != 'video') { $np['current_song'] = $np_stream['current_song']; $np['song_history'] = $np_stream['song_history']; } } } $np['listeners'] = $listener_totals; // Get currently active event (cached query) $event_current = Schedule::getCurrentEvent($station->id); $event_upcoming = Schedule::getUpcomingEvent($station->id); $np['event'] = Schedule::api($event_current); $np['event_upcoming'] = Schedule::api($event_upcoming); if ($station->category != 'video') { $station->nowplaying_data = array('current_song' => $np['current_song'], 'song_history' => $np['song_history']); $em->persist($station); } $em->flush(); return $np; }
/** * 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 promoteAction() { $event_guid = $this->getParam('event'); $event = Schedule::getRepository()->findOneBy(array('guid' => $event_guid, 'station_id' => $this->station->id)); if (!$event instanceof Schedule) { throw new \DF\Exception\DisplayOnly('Event not found!'); } $banner_url = $this->station->banner_url; if (empty($banner_url)) { throw new \DF\Exception\DisplayOnly('You have not supplied a banner for your station yet! Please visit the "Edit Profile" page to supply a banner image.'); } $event->is_promoted = !$event->is_promoted; $event->save(); $this->alert('<b>Event promotion toggled!</b>', 'green'); return $this->redirectFromHere(array('action' => 'index', 'event' => NULL)); }
protected function _initEvents() { $event_info = Cache::get('pvlive_events'); if (!$event_info) { $events_raw = $this->em->createQuery('SELECT s, st FROM Entity\\Schedule s JOIN s.station st WHERE (s.end_time >= :current AND s.start_time <= :future) ORDER BY s.start_time ASC')->setParameter('current', time())->setParameter('future', strtotime('+4 days'))->getArrayResult(); $all_events = array(); $events_by_day = array(); for ($i = 0; $i < 6; $i++) { $day_timestamp = mktime(0, 0, 1, date('n'), (int) date('j') + $i); $day_date = date('Y-m-d', $day_timestamp); $is_today = $day_date == date('Y-m-d'); $events_by_day[$day_date] = array('day_name' => $is_today ? 'Today' : date('l', $day_timestamp), 'timestamp' => $day_timestamp, 'is_today' => $is_today, 'events' => array()); } foreach ($events_raw as $event) { $event['image_url'] = \PVL\Url::upload(Schedule::getRowImageUrl($event)); $event['status'] = $event['start_time'] <= time() ? 'now' : 'upcoming'; $event['range'] = Schedule::getRangeText($event['start_time'], $event['end_time'], $event['is_all_day']); if ($event['station_id']) { $sid = $event['station_id']; if (isset($this->stations[$sid])) { $station = $this->stations[$sid]; unset($station['nowplaying_data'], $station['streams'], $station['intake_votes']); $event['station'] = $station; } } $all_events[] = $event; for ($i = $event['start_time']; $i <= $event['end_time']; $i += 86400) { $event_date = date('Y-m-d', $i); if (isset($events_by_day[$event_date])) { $events_by_day[$event_date]['events'][] = $event; } } } $event_info = array('all' => $all_events, 'by_day' => $events_by_day); Cache::save($event_info, 'pvlive_events', array(), 60); } $this->view->all_events = $event_info['all']; $this->view->events_by_day = $event_info['by_day']; }
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 function timelineAction() { $stream_id = $this->getParam('stream'); if (!$stream_id) { $default_stream = $this->station->getDefaultStream(); $stream_id = $default_stream->id; } $this->view->stream_id = $stream_id; $songs_played_raw = $this->_getEligibleHistory($stream_id); // Get current events within threshold. $threshold = $songs_played_raw[0]['timestamp']; $events = \Entity\Schedule::getEventsInRange($this->station->id, $threshold, time()); $songs = array(); foreach ($songs_played_raw as $i => $song_row) { if (!isset($songs_played_raw[$i + 1])) { break; } $start_timestamp = $song_row['timestamp']; $song_row['stat_start'] = $song_row['listeners']; if ($i + 1 == count($songs_played_raw)) { $end_timestamp = $start_timestamp; $song_row['stat_end'] = $song_row['stat_start']; } else { $end_timestamp = $songs_played_raw[$i + 1]['timestamp']; $song_row['stat_end'] = $songs_played_raw[$i + 1]['listeners']; } $song_row['stat_delta'] = $song_row['stat_end'] - $song_row['stat_start']; foreach ($events as $event) { if ($event['end_time'] >= $start_timestamp && $event['start_time'] <= $end_timestamp) { $song_row['event'] = $event; break; } } $songs[] = $song_row; } $format = $this->getParam('format', 'html'); if ($format == 'csv') { $this->doNotRender(); $export_all = array(); $export_all[] = array('Date', 'Time', 'Listeners', 'Delta', 'Likes', 'Dislikes', 'Track', 'Artist', 'Event'); foreach ($songs as $song_row) { $export_row = array(date('Y-m-d', $song_row['timestamp']), date('g:ia', $song_row['timestamp']), $song_row['stat_start'], $song_row['stat_delta'], $song_row['score_likes'], $song_row['score_dislikes'], $song_row['song']['title'] ? $song_row['song']['title'] : $song_row['song']['text'], $song_row['song']['artist'], $song_row['event'] ? $song_row['event']['title'] : ''); $export_all[] = $export_row; } \DF\Export::csv($export_all, true, $this->station->getShortName() . '_timeline_' . date('Ymd')); return; } else { $songs = array_reverse($songs); $pager = new \DF\Paginator($songs, $this->getParam('page', 1), 50); $this->view->pager = $pager; } }
public static function _runScheduleItems(\Phalcon\DiInterface $di) { $news_items = array(); $em = $di->get('em'); // Pull promoted schedule items. $events_raw = $em->createQuery('SELECT st, s FROM \\Entity\\Schedule s JOIN s.station st WHERE (s.end_time >= :current AND s.start_time <= :future) AND (st.banner_url != \'\' AND st.banner_url IS NOT NULL) AND s.is_promoted = 1 ORDER BY s.start_time ASC')->setParameter('current', time())->setParameter('future', strtotime('+1 week'))->getArrayResult(); $promoted_stations = array(); foreach ($events_raw as $event) { $station_id = $event['station_id']; if (isset($promoted_stations[$station_id])) { continue; } else { $promoted_stations[$station_id] = true; } $range = Schedule::getRangeText($event['start_time'], $event['end_time'], $event['is_all_day']); $description = array(); $description[] = 'Coming up on ' . $event['station']['name']; $description[] = $range; if (!empty($event['body'])) { $description[] = $event['body']; } // Manually adjust the sorting timestamp for the event if it is in the future. $sort_timestamp = $event['start_time']; if ($sort_timestamp >= time()) { $sort_timestamp = time() - ($sort_timestamp - time()); } $news_items[] = array('id' => 'schedule_' . $event['guid'], 'title' => trim($event['title']), 'source' => 'station', 'body' => implode('<br>', $description), 'image_url' => \PVL\Url::upload($event['station']['banner_url']), 'web_url' => $event['station']['web_url'], 'layout' => 'vertical', 'tags' => array($event['station']['name'], 'Events'), 'sort_timestamp' => $sort_timestamp, 'display_timestamp' => $event['start_time']); break; } return $news_items; }
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()); }