public static function generate() { set_time_limit(60); // Fix DF\URL // prefixing. \DF\Url::forceSchemePrefix(true); $nowplaying = self::loadNowPlaying(); // Post statistics to official record (legacy for duplication, for now) // Analytics::post($nowplaying['api']); // Post statistics to InfluxDB. $influx = self::getInflux(); $influx->setDatabase('pvlive_stations'); $active_shortcodes = Station::getShortNameLookup(); $total_overall = 0; foreach ($nowplaying['api'] as $short_code => $info) { $listeners = (int) $info['listeners']['current']; $station_id = $info['station']['id']; if (isset($active_shortcodes[$short_code])) { $total_overall += $listeners; } $influx->insert('station.' . $station_id . '.listeners', ['value' => $listeners]); } $influx->insert('all.listeners', ['value' => $total_overall]); // Clear any records that are not audio/video category. $api_categories = array('audio', 'video'); foreach ($nowplaying['api'] as $station_shortcode => $station_info) { if (!in_array($station_info['station']['category'], $api_categories)) { unset($nowplaying['api'][$station_shortcode]); unset($nowplaying['legacy'][$station_shortcode]); } } // Generate PVL legacy nowplaying file. $nowplaying_feed = json_encode($nowplaying['legacy'], JSON_UNESCAPED_SLASHES); $pvl_file_path = \PVL\Service\AmazonS3::path('api/nowplaying.json'); @file_put_contents($pvl_file_path, $nowplaying_feed); $legacy_file_path = DF_INCLUDE_STATIC . '/api/nowplaying.json'; @file_put_contents($legacy_file_path, $nowplaying_feed); // Generate PVL API cache. $np_api = $nowplaying['api']; foreach ($np_api as $station => $np_info) { $np_api[$station]['cache'] = 'hit'; } Cache::save($np_api, 'api_nowplaying_data', array('nowplaying'), 60); foreach ($np_api as $station => $np_info) { $np_api[$station]['cache'] = 'flatfile'; } // Generate PVL API nowplaying file. $file_path_api = \PVL\Service\AmazonS3::path('api/nowplaying_api.json'); $nowplaying_api = json_encode(array('status' => 'success', 'result' => $np_api), JSON_UNESCAPED_SLASHES); @file_put_contents($file_path_api, $nowplaying_api); // Push to live-update service. PvlNode::push('nowplaying', $nowplaying['api']); return $pvl_file_path; }
/** * 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(); }
/** * Send notifications for new podcast episodes. * * @param \Phalcon\DiInterface $di * @throws \DF\Exception */ public static function _runPodcastEpisodes(\Phalcon\DiInterface $di, $force = false) { $em = $di->get('em'); $start_threshold = time() - 86400 * 7; $end_threshold = time(); $podcast_episodes = $em->createQuery('SELECT pe, p FROM Entity\\PodcastEpisode pe JOIN pe.podcast p WHERE pe.timestamp BETWEEN :start AND :end AND pe.is_notified = 0 AND pe.is_active = 1 AND p.is_approved = 1 ORDER BY pe.timestamp DESC')->setParameter('start', $start_threshold)->setParameter('end', $end_threshold)->setMaxResults(1)->execute(); if ($podcast_episodes) { $episode = $podcast_episodes[0]; $podcast = $episode->podcast; $podcast_name = $podcast->name; if ($podcast->is_adult) { $podcast_name = '[18+] ' . $podcast_name; } $title = \DF\Utilities::truncateText($episode->title, 110 - strlen($podcast_name) - 6); $tweet = $podcast_name . ': "' . $title . '"'; PvlNode::push('podcast.new_episode', array('episode' => PodcastEpisode::api($episode), 'podcast' => Podcast::api($podcast, false))); $image_url = NULL; if ($podcast->banner_url) { $image_url = \PVL\Service\AmazonS3::path($podcast->banner_url); } // Special handling of podcast YT videos. if (stristr($episode->web_url, 'youtube.com') !== false) { $image_url = NULL; } self::notify($tweet, $episode->getLocalUrl('twitter'), $image_url); // Set all episodes of the same podcast to be notified, to prevent spam. $em->createQuery('UPDATE Entity\\PodcastEpisode pe SET pe.is_notified=1 WHERE pe.podcast_id = :podcast_id')->setParameter('podcast_id', $podcast->id)->execute(); } }