public static function load($force = false) { set_time_limit(300); Debug::startTimer('Load remote data'); $tracks = array(); $first_page = self::loadPage(1); if (!$first_page) { return FALSE; } $total_pages = (int) $first_page['total_pages']; $tracks[1] = $first_page['tracks']; for ($i = 2; $i <= $total_pages; $i++) { $next_page = self::loadPage($i); if ($next_page) { $tracks[$i] = $next_page['tracks']; } } // Loop through tracks. $new_songs = array(); foreach ($tracks as $page_num => $result) { foreach ((array) $result as $row) { $processed = External::processRemote($row); $processed['hash'] = Song::getSongHash($processed); $new_songs[$processed['hash']] = $processed; } } Debug::endTimer('Load remote data'); return External::import($new_songs, $force); }
public function newAction() { $new_threshold = strtotime('-2 weeks'); $song_adapters = Song::getExternalAdapters(); $new_songs_raw = $this->em->createQuery('SELECT s, ex_eqbeats, ex_btunes, ex_pfm FROM Entity\\Song s LEFT JOIN s.external_eqbeats AS ex_eqbeats LEFT JOIN s.external_bronytunes AS ex_btunes LEFT JOIN s.external_ponyfm AS ex_pfm WHERE (ex_eqbeats.created >= :threshold OR ex_btunes.created >= :threshold OR ex_pfm.created >= :threshold)')->setParameter('threshold', $new_threshold)->getArrayResult(); $new_songs = array(); foreach ($new_songs_raw as $song) { $timestamps = array(); foreach ($song_adapters as $adapter_key => $adapter_class) { $local_key = 'external_' . $adapter_key; if (!empty($song[$local_key])) { $timestamps[] = $song[$local_key]['created']; } } $song['created'] = max($timestamps); $song['filename'] = $song['artist'] . ' - ' . $song['title']; $new_songs[] = $song; } $new_songs = Utilities::irsort($new_songs, 'created'); $this->view->new_songs = $new_songs; }
public function indexAction() { $id = $this->getParam('id'); if (empty($id)) { $this->redirectHome(); } $record = Song::find($id); if (!$record instanceof Song) { throw new \DF\Exception\DisplayOnly('Song not found!'); } $song_info = array(); $song_info['record'] = $record; // Get external provider information. $song_info['external'] = $record->getExternal(); // Get album art and lyrics from all providers. $adapters = Song::getExternalAdapters(); $external_fields = array('lyrics', 'purchase_url', 'description'); foreach ($external_fields as $field_name) { $song_info[$field_name] = NULL; foreach ($adapters as $adapter_name => $adapter_class) { if (!empty($song_info['external'][$adapter_name][$field_name])) { $song_info[$field_name] = $song_info['external'][$adapter_name][$field_name]; break; } } } $song_info['image_url'] = $record->image_url; if (!$song_info['image_url']) { $song_info['image_url'] = \DF\Url::content('images/song_generic.png'); } $song_info['description'] = $this->_cleanUpText($song_info['description']); $song_info['lyrics'] = $this->_cleanUpText($song_info['lyrics']); // Get most recent playback information. $history_raw = $this->em->createQuery(' SELECT sh, st FROM Entity\\SongHistory sh JOIN sh.station st WHERE sh.song_id = :song_id AND st.category IN (:categories) AND sh.timestamp >= :threshold ORDER BY sh.timestamp DESC')->setParameter('song_id', $record->id)->setParameter('categories', array('audio', 'video'))->setParameter('threshold', strtotime('-1 week'))->getArrayResult(); $history = array(); $last_row = NULL; foreach ($history_raw as $i => $row) { if ($last_row && $row['station_id'] == $last_row['station_id']) { $timestamp_diff = abs($row['timestamp'] - $last_row['timestamp']); if ($timestamp_diff < 60) { continue; } } $history[] = $row; $last_row = $row; } $song_info['recent_history'] = $history; // Get requestable locations. $song_info['request_on'] = $this->em->createQuery(' SELECT sm, st FROM Entity\\StationMedia sm JOIN sm.station st WHERE sm.song_id = :song_id GROUP BY sm.station_id')->setParameter('song_id', $record->id)->getArrayResult(); $this->view->song = $song_info; }
public static function import($new_songs, $force = false) { $db_stats = array('skipped' => 0, 'updated' => 0, 'inserted' => 0, 'deleted' => 0); if (empty($new_songs)) { return false; } Debug::startTimer('Import data into database'); $em = self::getEntityManager(); $existing_hashes = self::getHashes(); $existing_ids = self::getIds(); $unused_hashes = $existing_hashes; $song_ids = Song::getIds(); $i = 0; foreach ($new_songs as $song_hash => $processed) { if (!in_array($song_hash, $song_ids)) { Song::getOrCreate($processed); } if (isset($existing_hashes[$song_hash])) { if ($force && $existing_hashes[$song_hash] == $processed['id']) { $db_stats['updated']++; $record = self::find($processed['id']); } else { $db_stats['skipped']++; $record = null; } } else { if (isset($existing_ids[$processed['id']])) { $db_stats['updated']++; $record = self::find($processed['id']); } else { $db_stats['inserted']++; $record = new self(); } } if ($record instanceof self) { $existing_ids[$processed['id']] = $processed['hash']; $existing_hashes[$processed['hash']] = $processed['id']; $record->fromArray($processed); $em->persist($record); } unset($unused_hashes[$song_hash]); $i++; if ($i % 200 == 0) { $em->flush(); $em->clear(); } } $em->flush(); $em->clear(); // Clear out any songs not found. $hashes_remaining = array_keys($unused_hashes); $db_stats['deleted'] = count($hashes_remaining); $em->createQuery('DELETE FROM ' . __CLASS__ . ' e WHERE e.hash IN (:hashes)')->setParameter('hashes', $hashes_remaining)->execute(); Debug::endTimer('Import data into database'); Debug::print_r($db_stats); return $db_stats; }
public function searchAction() { if (!$this->hasParam('q')) { return $this->returnError('No query provided.'); } $q = trim($this->getParam('q')); $results_raw = $this->em->createQuery('SELECT s FROM Entity\\Song s WHERE (s.text LIKE :q OR s.id = :q_exact) ORDER BY s.text ASC')->setParameter('q', '%' . addcslashes($q, "%_") . '%')->setParameter('q_exact', $q)->setMaxResults(50)->getArrayResult(); $results = array(); foreach ($results_raw as $row) { $results[$row['id']] = Song::api($row); } return $this->returnSuccess($results); }
protected static function _querySearch($song) { $base_url = 'https://eqbeats.org/tracks/search/json'; $url = $base_url . '?' . http_build_query(array('q' => $song->artist . ' ' . $song->title, 'client' => 'ponyvillelive')); Debug::log('Query Search: ' . $url); $result = file_get_contents($url); if ($result) { $rows = json_decode($result, TRUE); foreach ($rows as $row) { $song_hash = Song::getSongHash(array('artist' => $row['user']['name'], 'title' => $row['title'])); if (strcmp($song_hash, $song->id) == 0) { return $row; } } } return NULL; }
public static function load($force = false) { set_time_limit(300); Debug::startTimer('Load remote data'); $remote_url = 'https://bronytunes.com/retrieve_songs.php?client_type=ponyvillelive'; $result_raw = @file_get_contents($remote_url); Debug::endTimer('Load remote data'); if ($result_raw) { $result = json_decode($result_raw, TRUE); $new_songs = array(); foreach ((array) $result as $row) { $processed = External::processRemote($row); $processed['hash'] = Song::getSongHash($processed); $new_songs[$processed['hash']] = $processed; } return External::import($new_songs, $force); } return false; }
/** * Process a single audio stream's NowPlaying info. * * @param StationStream $stream * @param Station $station * @return array Structured NowPlaying Data */ public static function processAudioStream(StationStream $stream, Station $station, $force = false) { $current_np_data = (array) $stream->nowplaying_data; // Only process non-default streams on odd-numbered "segments" to improve performance. if (!$stream->is_default && !$force && NOWPLAYING_SEGMENT % 2 == 0 && !empty($current_np_data)) { return $current_np_data; } $np = StationStream::api($stream); $custom_class = Station::getStationClassName($station->name); $custom_adapter = '\\PVL\\RadioAdapter\\' . $custom_class; if (class_exists($custom_adapter)) { $np_adapter = new $custom_adapter($stream, $station); } elseif ($stream->type == "icecast") { $np_adapter = new \PVL\RadioAdapter\IceCast($stream, $station); } elseif ($stream->type == "icebreath") { $np_adapter = new \PVL\RadioAdapter\IceBreath($stream, $station); } elseif ($stream->type == "shoutcast2") { $np_adapter = new \PVL\RadioAdapter\ShoutCast2($stream, $station); } elseif ($stream->type == "shoutcast1") { $np_adapter = new \PVL\RadioAdapter\ShoutCast1($stream, $station); } else { return array(); } Debug::log('Adapter Class: ' . get_class($np_adapter)); $stream_np = $np_adapter->process(); $np = array_merge($np, $stream_np['meta']); $np['listeners'] = $stream_np['listeners']; // Pull from current NP data if song details haven't changed. $current_song_hash = Song::getSongHash($stream_np['current_song']); if (strcmp($current_song_hash, $current_np_data['current_song']['id']) == 0) { $np['current_song'] = $current_np_data['current_song']; $np['song_history'] = $current_np_data['song_history']; } else { if (empty($stream_np['current_song']['text'])) { $np['current_song'] = array(); $np['song_history'] = $station->getRecentHistory($stream); } else { // Register a new item in song history. $np['current_song'] = array(); $np['song_history'] = $station->getRecentHistory($stream); // Determine whether to log this song play for analytics. $log_radio_play = $stream->is_default && $station->category == 'audio'; $song_obj = Song::getOrCreate($stream_np['current_song'], $log_radio_play); $sh_obj = SongHistory::register($song_obj, $station, $stream, $np); // Compose "current_song" object for API. $current_song = Song::api($song_obj); $current_song['sh_id'] = $sh_obj->id; $current_song['score'] = SongVote::getScoreForStation($song_obj, $station); $vote_urls = array(); $vote_functions = array('like', 'dislike', 'clearvote'); foreach ($vote_functions as $vote_function) { $vote_urls[$vote_function] = \PVL\Url::api(array('module' => 'api', 'controller' => 'song', 'action' => $vote_function, 'sh_id' => $sh_obj->id)); } $current_song['vote_urls'] = $vote_urls; $external = $song_obj->getExternal(); if ($external) { $current_song['external'] = $song_obj->getExternal(); } $np['current_song'] = $current_song; } } $stream->nowplaying_data = $np; return $np; }
/** @PrePersist */ public function preSave() { $this->song = Song::getOrCreate(array('text' => $this->artist . ' - ' . $this->title, 'artist' => $this->artist, 'title' => $this->title)); }
public function votesAction() { $threshold = strtotime('-2 weeks'); $votes_raw = $this->em->createQuery('SELECT sv.song_id, SUM(sv.vote) AS vote_total FROM Entity\\SongVote sv WHERE sv.station_id = :station_id AND sv.timestamp >= :threshold GROUP BY sv.song_id')->setParameter('station_id', $this->station->id)->setParameter('threshold', $threshold)->getArrayResult(); $ignored_songs = $this->_getIgnoredSongs(); $votes_raw = array_filter($votes_raw, function ($value) use($ignored_songs) { return !isset($ignored_songs[$value['song_id']]); }); \PVL\Utilities::orderBy($votes_raw, 'vote_total DESC'); $votes = array(); foreach ($votes_raw as $row) { $row['song'] = Song::find($row['song_id']); $votes[] = $row; } $this->view->votes = $votes; }
public function votesAction() { $threshold = strtotime('-1 week'); $votes_raw = $this->em->createQuery('SELECT sv.song_id, SUM(sv.vote) AS vote_total FROM Entity\\SongVote sv WHERE sv.timestamp >= :threshold GROUP BY sv.song_id')->setParameter('threshold', $threshold)->getArrayResult(); Utilities::orderBy($votes_raw, 'vote_total DESC'); $votes = array(); foreach ($votes_raw as $row) { $row['song'] = Song::find($row['song_id']); $votes[] = $row; } $this->view->votes = $votes; }
public function getRecentHistory(StationStream $stream, $num_entries = 5) { $em = self::getEntityManager(); $history = $em->createQuery('SELECT sh, s FROM Entity\\SongHistory sh JOIN sh.song s WHERE sh.station_id = :station_id AND sh.stream_id = :stream_id ORDER BY sh.id DESC')->setParameter('station_id', $this->id)->setParameter('stream_id', $stream->id)->setMaxResults($num_entries)->getArrayResult(); $return = array(); foreach ($history as $sh) { $history = array('played_at' => $sh['timestamp'], 'song' => Song::api($sh['song'])); $return[] = $history; } return $return; }
public static function sync() { if (DF_APPLICATION_ENV !== 'production') { return false; } $db = self::getDatabase(); $em = self::getEntityManager(); $settings = self::getSettings(); // Force correct account settings (enable global unified request system). $account_values = array('allowrequests' => '1', 'autoqueuerequests' => '1', 'requestprobability' => '50', 'requestdelay' => '0', 'emailunknownrequests' => '0'); $db->update('accounts', $account_values, array('expectedstate' => 'up')); // Clear out old logs. $threshold = strtotime('-1 month'); $threshold_date = date('Y-m-d', $threshold) . ' 00:00:00'; $db->executeQuery('DELETE FROM playbackstats_tracks WHERE endtime <= ?', array($threshold_date)); $db->executeQuery('DELETE FROM visitorstats_sessions WHERE endtime <= ?', array($threshold_date)); // Delete old requests still listed as pending. $requesttime = new \DateTime('NOW'); $requesttime->modify('-3 hours'); $requesttime->setTimezone(new \DateTimeZone($settings['timezone'])); $threshold_requests = $requesttime->format('Y-m-d h:i:s'); $db->executeQuery('DELETE FROM playlist_tracks_requests WHERE requesttime <= ?', array($threshold_requests)); // Force playlist enabling for existing pending requests. $request_playlists_raw = $db->fetchAll('SELECT DISTINCT ptr.playlistid AS pid FROM playlist_tracks_requests AS ptr'); foreach ($request_playlists_raw as $pl) { $pl_id = $pl['pid']; $db->update('playlists', array('status' => 'enabled'), array('id' => $pl_id)); } // Preload all station media locally. $stations = $em->createQuery('SELECT s FROM Entity\\Station s WHERE s.requests_enabled = 1')->execute(); foreach ($stations as $station) { $account_id = self::getStationID($station); if (!$account_id) { continue; } // Clear existing items. $existing_ids_raw = $em->createQuery('SELECT sm FROM Entity\\StationMedia sm WHERE sm.station_id = :station_id')->setParameter('station_id', $station->id)->execute(); $existing_records = array(); foreach ($existing_ids_raw as $row) { $existing_records[$row['id']] = $row; } // Find all tracks in active playlists. $new_records_raw = self::fetchTracks($station); $records_by_hash = array(); foreach ($new_records_raw as $track_info) { if ($track_info['length'] < 60) { continue; } if ($track_info['playlist_status'] !== 'enabled') { continue; } $row = array('id' => $track_info['id'], 'title' => $track_info['title'], 'artist' => $track_info['artist'], 'album' => $track_info['album'], 'length' => $track_info['length']); $song_hash = Song::getSongHash(array('text' => $row['artist'] . ' - ' . $row['title'], 'artist' => $row['artist'], 'title' => $row['title'])); $records_by_hash[$song_hash] = $row; } $new_records = array(); foreach ($records_by_hash as $row) { $new_records[$row['id']] = $row; } // Reconcile differences. $existing_guids = array_keys($existing_records); $new_guids = array_keys($new_records); $guids_to_delete = array_diff($existing_guids, $new_guids); if ($guids_to_delete) { foreach ($guids_to_delete as $guid) { $record = $existing_records[$guid]; $em->remove($record); } } $guids_to_add = array_diff($new_guids, $existing_guids); if ($guids_to_add) { foreach ($guids_to_add as $guid) { $record = new StationMedia(); $record->station = $station; $record->fromArray($new_records[$guid]); $em->persist($record); } } $em->flush(); } return true; }
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'); }