diff --git a/airtime_mvc/application/configs/conf.php b/airtime_mvc/application/configs/conf.php index dc3e03a2b..fcdece4bc 100644 --- a/airtime_mvc/application/configs/conf.php +++ b/airtime_mvc/application/configs/conf.php @@ -62,6 +62,8 @@ class Config { $CC_CONFIG['baseUrl'] = $values['general']['base_url']; $CC_CONFIG['basePort'] = $values['general']['base_port']; + $CC_CONFIG['cache_ahead_hours'] = $values['general']['cache_ahead_hours']; + // Database config $CC_CONFIG['dsn']['username'] = $values['database']['dbuser']; $CC_CONFIG['dsn']['password'] = $values['database']['dbpass']; diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index d32f2ad30..5a0d53cd9 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -150,8 +150,11 @@ class ApiController extends Zend_Controller_Action //user clicks play button for track and downloads it. header('Content-Disposition: inline; filename="'.$file_base_name.'"'); } - - $this->smartReadFile($filepath, 'audio/'.$ext); + if ($ext === 'mp3'){ + $this->smartReadFile($filepath, 'audio/mpeg'); + } else { + $this->smartReadFile($filepath, 'audio/'.$ext); + } exit; }else{ header ("HTTP/1.1 404 Not Found"); diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index d13cd678d..607592217 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -232,6 +232,9 @@ class PreferenceController extends Zend_Controller_Action $this->view->statusMsg = "
Stream Setting Updated.
"; } } + + $this->view->confirm_pypo_restart_text = "Updating settings will temporarily interrupt any currently playing shows. Click \'OK\' to continue."; + $this->view->num_stream = $num_of_stream; $this->view->enable_stream_conf = Application_Model_Preference::GetEnableStreamConf(); $this->view->form = $form; diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index 57c285835..583f97f49 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -778,15 +778,13 @@ class ScheduleController extends Zend_Controller_Action try { $showInstance = new Application_Model_ShowInstance($showInstanceId); - } - catch(Exception $e){ + } catch(Exception $e) { $this->view->show_error = true; return false; } $show = new Application_Model_Show($showInstance->getShowId()); $show->cancelShow($showInstance->getShowInstanceStart()); - $this->view->show_id = $showInstance->getShowId(); } } @@ -806,8 +804,8 @@ class ScheduleController extends Zend_Controller_Action } $showInstance->clearShow(); $showInstance->delete(); - // send 'cancel-current-show' command to pypo - Application_Model_RabbitMq::SendMessageToPypo("cancel_current_show", array()); + + Application_Model_RabbitMq::PushSchedule(); } } diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index b924ee4c0..204d5d983 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -64,14 +64,16 @@ class Application_Model_Schedule { $utcTimeNow = $date->getUtcTimestamp(); $shows = Application_Model_Show::getPrevCurrentNext($utcTimeNow); + $previousShowID = count($shows['previousShow'])>0?$shows['previousShow'][0]['id']:null; $currentShowID = count($shows['currentShow'])>0?$shows['currentShow'][0]['id']:null; - $results = Application_Model_Schedule::GetPrevCurrentNext($currentShowID, $utcTimeNow); + $nextShowID = count($shows['nextShow'])>0?$shows['nextShow'][0]['id']:null; + $results = Application_Model_Schedule::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcTimeNow); $range = array("env"=>APPLICATION_ENV, "schedulerTime"=>$timeNow, - "previous"=>isset($results['previous'])?$results['previous']:(count($shows['previousShow'])>0?$shows['previousShow'][0]:null), - "current"=>isset($results['current'])?$results['current']:null, - "next"=> isset($results['next'])?$results['next']:(count($shows['nextShow'])>0?$shows['nextShow'][0]:null), + "previous"=>$results['previous'] !=null?$results['previous']:(count($shows['previousShow'])>0?$shows['previousShow'][0]:null), + "current"=>$results['current'] !=null?$results['current']:null, + "next"=> $results['next'] !=null?$results['next']:(count($shows['nextShow'])>0?$shows['nextShow'][0]:null), "currentShow"=>$shows['currentShow'], "nextShow"=>$shows['nextShow'], "timezone"=> date("T"), @@ -88,19 +90,39 @@ class Application_Model_Schedule { * show types are not found through this mechanism a call is made to the old way of querying * the database to find the track info. **/ - public static function GetPrevCurrentNext($p_currentShowID, $p_timeNow) + public static function GetPrevCurrentNext($p_previousShowID, $p_currentShowID, $p_nextShowID, $p_timeNow) { + if ($p_previousShowID == null && $p_currentShowID == null && $p_nextShowID == null) + return; + global $CC_CONFIG, $CC_DBC; - - if (!isset($p_currentShowID)) { - return array(); - } - $sql = "Select ft.artist_name, ft.track_title, st.starts as starts, st.ends as ends, st.media_item_played as media_item_played + $sql = 'Select ft.artist_name, ft.track_title, st.starts as starts, st.ends as ends, st.media_item_played as media_item_played FROM cc_schedule st LEFT JOIN cc_files ft ON st.file_id = ft.id - WHERE st.instance_id = '$p_currentShowID' AND st.playout_status > 0 - ORDER BY st.starts"; + WHERE '; + + if (isset($p_previousShowID)){ + if (isset($p_nextShowID) || isset($p_currentShowID)) + $sql .= '('; + $sql .= 'st.instance_id = '.$p_previousShowID; + } + if ($p_currentShowID != null){ + if ($p_previousShowID != null) + $sql .= ' OR '; + else if($p_nextShowID != null) + $sql .= '('; + $sql .= 'st.instance_id = '.$p_currentShowID; + } + if ($p_nextShowID != null) { + if ($p_previousShowID != null || $p_currentShowID != null) + $sql .= ' OR '; + $sql .= 'st.instance_id = '.$p_nextShowID; + if($p_previousShowID != null || $p_currentShowID != null) + $sql .= ')'; + } else if($p_previousShowID != null && $p_currentShowID != null) + $sql .= ')'; + + $sql .= ' AND st.playout_status > 0 ORDER BY st.starts'; - //Logging::log($sql); $rows = $CC_DBC->GetAll($sql); $numberOfRows = count($rows); @@ -587,7 +609,17 @@ class Application_Model_Schedule { } if (is_null($p_fromDateTime)) { $t2 = new DateTime("@".time()); - $t2->add(new DateInterval("PT30M")); + + $cache_ahead_hours = $CC_CONFIG["cache_ahead_hours"]; + + if (is_numeric($cache_ahead_hours)){ + //make sure we are not dealing with a float + $cache_ahead_hours = intval($cache_ahead_hours); + } else { + $cache_ahead_hours = 1; + } + + $t2->add(new DateInterval("PT".$cache_ahead_hours."H")); $range_end = $t2->format("Y-m-d H:i:s"); } else { $range_end = Application_Model_Schedule::PypoTimeToAirtimeTime($p_toDateTime); diff --git a/airtime_mvc/application/models/Show.php b/airtime_mvc/application/models/Show.php index afd9255b7..762ace161 100644 --- a/airtime_mvc/application/models/Show.php +++ b/airtime_mvc/application/models/Show.php @@ -199,9 +199,6 @@ class Application_Model_Show { ->filterByDbShowId($this->_showId) ->update(array('DbLastShow' => $timeinfo[0])); - //$sql = "DELETE FROM cc_show_instances - // WHERE starts >= '{$day_timestamp}' AND show_id = {$this->_showId}"; - $sql = "UPDATE cc_show_instances SET modified_instance = TRUE WHERE starts >= '{$day_timestamp}' AND show_id = {$this->_showId}"; @@ -1762,7 +1759,9 @@ class Application_Model_Show { //Find the show that is within the current time. if ((strtotime($rows[$i]['starts']) <= $timeNowAsMillis) && (strtotime($rows[$i]['ends']) >= $timeNowAsMillis)){ if ( $i - 1 >= 0){ - $results['previousShow'][0] = array("name"=>$rows[$i-1]['name'], + $results['previousShow'][0] = array( + "id"=>$rows[$i-1]['id'], + "name"=>$rows[$i-1]['name'], "start_timestamp"=>$rows[$i-1]['start_timestamp'], "end_timestamp"=>$rows[$i-1]['end_timestamp'], "starts"=>$rows[$i-1]['starts'], @@ -1772,7 +1771,9 @@ class Application_Model_Show { $results['currentShow'][0] = $rows[$i]; if ( isset($rows[$i+1])){ - $results['nextShow'][0] = array("name"=>$rows[$i+1]['name'], + $results['nextShow'][0] = array( + "id"=>$rows[$i+1]['id'], + "name"=>$rows[$i+1]['name'], "start_timestamp"=>$rows[$i+1]['start_timestamp'], "end_timestamp"=>$rows[$i+1]['end_timestamp'], "starts"=>$rows[$i+1]['starts'], @@ -1787,7 +1788,9 @@ class Application_Model_Show { } //if we hit this we know we've gone to far and can stop looping. if (strtotime($rows[$i]['starts']) > $timeNowAsMillis) { - $results['nextShow'][0] = array("name"=>$rows[$i]['name'], + $results['nextShow'][0] = array( + "id"=>$rows[$i]['id'], + "name"=>$rows[$i]['name'], "start_timestamp"=>$rows[$i]['start_timestamp'], "end_timestamp"=>$rows[$i]['end_timestamp'], "starts"=>$rows[$i]['starts'], @@ -1798,7 +1801,9 @@ class Application_Model_Show { //If we didn't find a a current show because the time didn't fit we may still have //found a previous show so use it. if (count($results['previousShow']) == 0 && isset($previousShowIndex)) { - $results['previousShow'][0] = array("name"=>$rows[$previousShowIndex]['name'], + $results['previousShow'][0] = array( + "id"=>$rows[$previousShowIndex]['id'], + "name"=>$rows[$previousShowIndex]['name'], "start_timestamp"=>$rows[$previousShowIndex]['start_timestamp'], "end_timestamp"=>$rows[$previousShowIndex]['end_timestamp'], "starts"=>$rows[$previousShowIndex]['starts'], diff --git a/airtime_mvc/application/views/scripts/preference/stream-setting.phtml b/airtime_mvc/application/views/scripts/preference/stream-setting.phtml index 58332bde8..ed88f61e0 100644 --- a/airtime_mvc/application/views/scripts/preference/stream-setting.phtml +++ b/airtime_mvc/application/views/scripts/preference/stream-setting.phtml @@ -1,7 +1,7 @@

enable_stream_conf == "true"){?>style="float:left">Stream Settings

enable_stream_conf == "true"){?> -
+
@@ -83,4 +83,4 @@
- \ No newline at end of file + diff --git a/airtime_mvc/build/airtime.conf b/airtime_mvc/build/airtime.conf index b11e672ce..2c36648ed 100644 --- a/airtime_mvc/build/airtime.conf +++ b/airtime_mvc/build/airtime.conf @@ -18,6 +18,10 @@ airtime_dir = x base_url = localhost base_port = 80 +#How many hours ahead of time should Airtime playout engine (PYPO) +#cache scheduled media files. +cache_ahead_hours = 1 + [soundcloud] connection_retries = 3 time_between_retries = 60 diff --git a/airtime_mvc/public/js/jplayer/preview_jplayer.js b/airtime_mvc/public/js/jplayer/preview_jplayer.js index 2f6c2bd6f..e954a74b5 100644 --- a/airtime_mvc/public/js/jplayer/preview_jplayer.js +++ b/airtime_mvc/public/js/jplayer/preview_jplayer.js @@ -8,16 +8,22 @@ var _idToPostionLookUp; */ $(document).ready(function(){ + if (useFlash()) + mySupplied = "oga, mp3, m4v"; + else + mySupplied = "oga, mp3"; + _playlist_jplayer = new jPlayerPlaylist({ jPlayer: "#jquery_jplayer_1", cssSelectorAncestor: "#jp_container_1" },[], //array of songs will be filled with below's json call { swfPath: "/js/jplayer", - //supplied: "mp3,oga", + supplied:mySupplied, wmode: "window" }); + $.jPlayer.timeFormat.showHour = true; var audioFileID = $('.audioFileID').text(); @@ -35,6 +41,10 @@ $(document).ready(function(){ } }); +function useFlash() { + console.log(navigator.userAgent); + return navigator.userAgent.toLowerCase().match('firefox'); +} /** * Sets up the jPlayerPlaylist to play. * - Get the playlist info based on the playlistID give. @@ -122,7 +132,7 @@ function play(p_playlistIndex){ function playOne(p_audioFileID) { var playlist = new Array(); var fileExtensioin = p_audioFileID.split('.').pop(); - + console.log(p_audioFileID); if (fileExtensioin === 'mp3') { media = {title: $('.audioFileTitle').text() !== 'null' ?$('.audioFileTitle').text():"", artist: $('.audioFileArtist').text() !== 'null' ?$('.audioFileArtist').text():"", @@ -135,7 +145,7 @@ function playOne(p_audioFileID) { }; } _playlist_jplayer.option("autoPlay", true); - + console.log(media); playlist[0] = media; //_playlist_jplayer.setPlaylist(playlist); --if I use this the player will call _init on the setPlaylist and on the ready _playlist_jplayer._initPlaylist(playlist); diff --git a/dev_tools/compare_cc_files_to_fs.py b/dev_tools/compare_cc_files_to_fs.py new file mode 100644 index 000000000..3f35fc033 --- /dev/null +++ b/dev_tools/compare_cc_files_to_fs.py @@ -0,0 +1,131 @@ +import os +import time +import shutil +import sys +import logging + +from configobj import ConfigObj +from subprocess import Popen, PIPE +from api_clients import api_client as apc + +""" +The purpose of this script is that you can run it, and it will compare what the database has to what your filesystem +has. It will then report if there are any differences. It will *NOT* make any changes, unlike media-monitor which uses +similar code when it starts up (but then makes changes if something is different) +""" + + +class AirtimeMediaMonitorBootstrap(): + + """AirtimeMediaMonitorBootstrap constructor + + Keyword Arguments: + logger -- reference to the media-monitor logging facility + pe -- reference to an instance of ProcessEvent + api_clients -- reference of api_clients to communicate with airtime-server + """ + def __init__(self): + config = ConfigObj('/etc/airtime/media-monitor.cfg') + self.api_client = apc.api_client_factory(config) + + """ + try: + logging.config.fileConfig("logging.cfg") + except Exception, e: + print 'Error configuring logging: ', e + sys.exit(1) + """ + + self.logger = logging.getLogger() + self.logger.info("Adding %s on watch list...", "xxx") + + self.scan() + + """On bootup we want to scan all directories and look for files that + weren't there or files that changed before media-monitor process + went offline. + """ + def scan(self): + directories = self.get_list_of_watched_dirs(); + + self.logger.info("watched directories found: %s", directories) + + for id, dir in directories.iteritems(): + self.logger.debug("%s, %s", id, dir) + #CHANGED!!! + #self.sync_database_to_filesystem(id, api_client.encode_to(dir, "utf-8")) + self.sync_database_to_filesystem(id, dir) + + """Gets a list of files that the Airtime database knows for a specific directory. + You need to provide the directory's row ID, which is obtained when calling + get_list_of_watched_dirs function. + dir_id -- row id of the directory in the cc_watched_dirs database table + """ + def list_db_files(self, dir_id): + return self.api_client.list_all_db_files(dir_id) + + """ + returns the path and the database row id for this path for all watched directories. Also + returns the Stor directory, which can be identified by its row id (always has value of "1") + """ + def get_list_of_watched_dirs(self): + json = self.api_client.list_all_watched_dirs() + return json["dirs"] + + def scan_dir_for_existing_files(self, dir): + command = 'find "%s" -type f -iname "*.ogg" -o -iname "*.mp3" -readable' % dir.replace('"', '\\"') + self.logger.debug(command) + #CHANGED!! + stdout = self.exec_command(command).decode("UTF-8") + + return stdout.splitlines() + + def exec_command(self, command): + p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE) + stdout, stderr = p.communicate() + if p.returncode != 0: + self.logger.warn("command \n%s\n return with a non-zero return value", command) + self.logger.error(stderr) + return stdout + + """ + This function takes in a path name provided by the database (and its corresponding row id) + and reads the list of files in the local file system. Its purpose is to discover which files + exist on the file system but not in the database and vice versa, as well as which files have + been modified since the database was last updated. In each case, this method will call an + appropiate method to ensure that the database actually represents the filesystem. + dir_id -- row id of the directory in the cc_watched_dirs database table + dir -- pathname of the directory + """ + def sync_database_to_filesystem(self, dir_id, dir): + """ + set to hold new and/or modified files. We use a set to make it ok if files are added + twice. This is because some of the tests for new files return result sets that are not + mutually exclusive from each other. + """ + db_known_files_set = set() + + files = self.list_db_files(dir_id) + for file in files['files']: + db_known_files_set.add(file) + + existing_files = self.scan_dir_for_existing_files(dir) + + existing_files_set = set() + for file_path in existing_files: + if len(file_path.strip(" \n")) > 0: + existing_files_set.add(file_path[len(dir):]) + + + deleted_files_set = db_known_files_set - existing_files_set + new_files_set = existing_files_set - db_known_files_set + + + print ("DB Known files: \n%s\n\n"%len(db_known_files_set)) + print ("FS Known files: \n%s\n\n"%len(existing_files_set)) + + print ("Deleted files: \n%s\n\n"%deleted_files_set) + print ("New files: \n%s\n\n"%new_files_set) + +if __name__ == "__main__": + AirtimeMediaMonitorBootstrap() diff --git a/dev_tools/fabric/fab_liquidsoap_compile.cfg b/dev_tools/fabric/fab_liquidsoap_compile.cfg new file mode 100644 index 000000000..bf6d462d8 --- /dev/null +++ b/dev_tools/fabric/fab_liquidsoap_compile.cfg @@ -0,0 +1,2 @@ +[main] +liquidsoap_tar_url = http://dl.dropbox.com/u/256410/airtime/liquidsoap.tar.gz diff --git a/dev_tools/fabric/fab_liquidsoap_compile.py b/dev_tools/fabric/fab_liquidsoap_compile.py index 68064395d..06ca05730 100644 --- a/dev_tools/fabric/fab_liquidsoap_compile.py +++ b/dev_tools/fabric/fab_liquidsoap_compile.py @@ -10,6 +10,8 @@ import sys from fabric.api import * from fabric.contrib.files import comment, sed, append +from ConfigParser import ConfigParser + from xml.dom.minidom import parse from xml.dom.minidom import Node from xml.dom.minidom import Element @@ -157,7 +159,13 @@ def debian_squeeze_64(fresh_os=True): def compile_liquidsoap(filename="liquidsoap"): - + + config = ConfigParser() + config.readfp(open('fab_liquidsoap_compile.cfg')) + url = config.get('main', 'liquidsoap_tar_url') + + print "Will get liquidsoap from " + url + do_sudo('apt-get update') do_sudo('apt-get upgrade -y --force-yes') do_sudo('apt-get install -y --force-yes ocaml-findlib libao-ocaml-dev libportaudio-ocaml-dev ' + \ @@ -171,14 +179,15 @@ def compile_liquidsoap(filename="liquidsoap"): do_run('mkdir -p %s' % root) tmpPath = do_local("mktemp", capture=True) - do_run('wget %s -O %s' % ('https://downloads.sourceforge.net/project/savonet/liquidsoap/1.0.0/liquidsoap-1.0.0-full.tar.bz2', tmpPath)) - do_run('mv %s %s/liquidsoap-1.0.0-full.tar.bz2' % (tmpPath, root)) - do_run('cd %s && bunzip2 liquidsoap-1.0.0-full.tar.bz2 && tar xf liquidsoap-1.0.0-full.tar' % root) + do_run('wget %s -O %s' % (url, tmpPath)) + do_run('mv %s %s/liquidsoap.tar.gz' % (tmpPath, root)) + do_run('cd %s && tar xzf liquidsoap.tar.gz' % root) - do_run('cd %s/liquidsoap-1.0.0-full && cp PACKAGES.minimal PACKAGES' % root) - sed('%s/liquidsoap-1.0.0-full/PACKAGES' % root, '#ocaml-portaudio', 'ocaml-portaudio') - sed('%s/liquidsoap-1.0.0-full/PACKAGES' % root, '#ocaml-alsa', 'ocaml-alsa') - sed('%s/liquidsoap-1.0.0-full/PACKAGES' % root, '#ocaml-pulseaudio', 'ocaml-pulseaudio') - do_run('cd %s/liquidsoap-1.0.0-full && ./configure' % root) - do_run('cd %s/liquidsoap-1.0.0-full && make' % root) - get('%s/liquidsoap-1.0.0-full/liquidsoap-1.0.0/src/liquidsoap' % root, filename) + do_run('cd %s/savonet && cp PACKAGES.minimal PACKAGES' % root) + sed('%s/savonet/PACKAGES' % root, '#ocaml-portaudio', 'ocaml-portaudio') + sed('%s/savonet/PACKAGES' % root, '#ocaml-alsa', 'ocaml-alsa') + sed('%s/savonet/PACKAGES' % root, '#ocaml-pulseaudio', 'ocaml-pulseaudio') + do_run('cd %s/savonet && ./bootstrap' % root) + do_run('cd %s/savonet && ./configure' % root) + do_run('cd %s/savonet && make' % root) + get('%s/savonet/liquidsoap/src/liquidsoap' % root, filename) diff --git a/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py b/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py index 55b8856ff..d23651955 100644 --- a/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py +++ b/python_apps/media-monitor/airtimefilemonitor/airtimemediamonitorbootstrap.py @@ -42,7 +42,7 @@ class AirtimeMediaMonitorBootstrap(): for id, dir in directories.iteritems(): self.logger.debug("%s, %s", id, dir) - self.sync_database_to_filesystem(id, api_client.encode_to(dir, "utf-8")) + self.sync_database_to_filesystem(id, dir) """Gets a list of files that the Airtime database knows for a specific directory. You need to provide the directory's row ID, which is obtained when calling @@ -82,7 +82,7 @@ class AirtimeMediaMonitorBootstrap(): db_known_files_set = set() files = self.list_db_files(dir_id) for file in files['files']: - db_known_files_set.add(api_client.encode_to(file, 'utf-8')) + db_known_files_set.add(file) new_files = self.mmc.scan_dir_for_new_files(dir) @@ -123,9 +123,6 @@ class AirtimeMediaMonitorBootstrap(): new_files_set = all_files_set - db_known_files_set modified_files_set = new_and_modified_files - new_files_set - #NAOMI: Please comment out the "Known files" line, if you find the bug. - #it is for debugging purposes only (Too much data will be written to log). -mk - #self.logger.info("Known files: \n%s\n\n"%db_known_files_set) self.logger.info("Deleted files: \n%s\n\n"%deleted_files_set) self.logger.info("New files: \n%s\n\n"%new_files_set) self.logger.info("Modified files: \n%s\n\n"%modified_files_set) diff --git a/python_apps/media-monitor/airtimefilemonitor/mediamonitorcommon.py b/python_apps/media-monitor/airtimefilemonitor/mediamonitorcommon.py index 8927a9c4c..f526f2616 100644 --- a/python_apps/media-monitor/airtimefilemonitor/mediamonitorcommon.py +++ b/python_apps/media-monitor/airtimefilemonitor/mediamonitorcommon.py @@ -251,12 +251,29 @@ class MediaMonitorCommon: if p.returncode != 0: self.logger.warn("command \n%s\n return with a non-zero return value", command) self.logger.error(stderr) + + try: + """ + File name charset encoding is UTF-8. + """ + stdout = stdout.decode("UTF-8") + except Exception, e: + self.logger.error("Could not decode %s using UTF-8" % stdout) + return stdout def scan_dir_for_new_files(self, dir): command = 'find "%s" -type f -iname "*.ogg" -o -iname "*.mp3" -readable' % dir.replace('"', '\\"') self.logger.debug(command) - stdout = self.exec_command(command) + stdout = self.exec_command(command).decode("UTF-8") + + try: + """ + File name charset encoding is UTF-8. + """ + stdout = stdout.decode("UTF-8") + except Exception, e: + self.logger.error("Could not decode %s using UTF-8" % stdout) return stdout.splitlines() diff --git a/python_apps/pypo/__init__.py b/python_apps/pypo/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python_apps/pypo/pypo-notify.py b/python_apps/pypo/pypo-notify.py index 0df1abab5..ebd8e0f5b 100644 --- a/python_apps/pypo/pypo-notify.py +++ b/python_apps/pypo/pypo-notify.py @@ -95,7 +95,7 @@ class Notify: logger.debug("Response: "+json.dumps(response)) def notify_source_status(self, source_name, status): - logger = logging.getLogger() + logger = logging.getLogger("notify") logger.debug('#################################################') logger.debug('# Calling server to update source status #') diff --git a/python_apps/pypo/pypocli.py b/python_apps/pypo/pypocli.py index 9eeb15a6f..6a9649d5a 100644 --- a/python_apps/pypo/pypocli.py +++ b/python_apps/pypo/pypocli.py @@ -5,13 +5,14 @@ Python part of radio playout (pypo) import time from optparse import * import sys -import os import signal import logging import logging.config import logging.handlers from Queue import Queue +from threading import Lock + from pypopush import PypoPush from pypofetch import PypoFetch from pypofile import PypoFile @@ -125,6 +126,8 @@ if __name__ == '__main__': recorder_q = Queue() pypoPush_q = Queue() + telnet_lock = Lock() + """ This queue is shared between pypo-fetch and pypo-file, where pypo-file is the receiver. Pypo-fetch will send every schedule it gets to pypo-file @@ -141,11 +144,11 @@ if __name__ == '__main__': pfile.daemon = True pfile.start() - pf = PypoFetch(pypoFetch_q, pypoPush_q, media_q) + pf = PypoFetch(pypoFetch_q, pypoPush_q, media_q, telnet_lock) pf.daemon = True pf.start() - pp = PypoPush(pypoPush_q) + pp = PypoPush(pypoPush_q, telnet_lock) pp.daemon = True pp.start() diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py index c7634b878..7189f5ca9 100644 --- a/python_apps/pypo/pypofetch.py +++ b/python_apps/pypo/pypofetch.py @@ -1,22 +1,13 @@ import os import sys import time -import calendar import logging import logging.config import shutil -import random -import string import json import telnetlib -import math import copy from threading import Thread -from subprocess import Popen, PIPE -from datetime import datetime -from datetime import timedelta -from Queue import Empty -import filecmp from api_clients import api_client @@ -38,13 +29,15 @@ except Exception, e: sys.exit() class PypoFetch(Thread): - def __init__(self, pypoFetch_q, pypoPush_q, media_q): + def __init__(self, pypoFetch_q, pypoPush_q, media_q, telnet_lock): Thread.__init__(self) self.api_client = api_client.api_client_factory(config) self.fetch_queue = pypoFetch_q self.push_queue = pypoPush_q self.media_prepare_queue = media_q + self.telnet_lock = telnet_lock + self.logger = logging.getLogger(); self.cache_dir = os.path.join(config["cache_dir"], "scheduler") @@ -79,7 +72,7 @@ class PypoFetch(Thread): if command == 'update_schedule': self.schedule_data = m['schedule'] - self.process_schedule(self.schedule_data, False) + self.process_schedule(self.schedule_data) elif command == 'update_stream_setting': self.logger.info("Updating stream setting...") self.regenerateLiquidsoapConf(m['setting']) @@ -89,9 +82,6 @@ class PypoFetch(Thread): elif command == 'update_station_name': self.logger.info("Updating station name...") self.update_liquidsoap_station_name(m['station_name']) - elif command == 'cancel_current_show': - self.logger.info("Cancel current show command received...") - self.stop_current_show() elif command == 'switch_source': self.logger.info("switch_on_source show command received...") self.switch_source(m['sourcename'], m['status']) @@ -113,14 +103,16 @@ class PypoFetch(Thread): elif(sourcename == "live_dj"): command += "live_dj_harbor.kick\n" + self.telnet_lock.acquire() try: tn = telnetlib.Telnet(LS_HOST, LS_PORT) tn.write(command) tn.write('exit\n') tn.read_all() except Exception, e: - self.logger.debug(e) - self.logger.debug('Could not connect to liquidsoap') + self.logger.error(str(e)) + finally: + self.telnet_lock.release() def switch_source(self, sourcename, status): self.logger.debug('Switching source: %s to "%s" status', sourcename, status) @@ -137,14 +129,16 @@ class PypoFetch(Thread): else: command += "stop\n" + self.telnet_lock.acquire() try: tn = telnetlib.Telnet(LS_HOST, LS_PORT) tn.write(command) tn.write('exit\n') tn.read_all() except Exception, e: - self.logger.debug(e) - self.logger.debug('Could not connect to liquidsoap') + self.logger.error(str(e)) + finally: + self.telnet_lock.release() """ This check current switch status from Airtime and update the status @@ -155,18 +149,7 @@ class PypoFetch(Thread): self.logger.debug('switch_status:%s',switch_status) for k, v in switch_status['status'].iteritems(): self.switch_source(k, v) - - def stop_current_show(self): - self.logger.debug('Notifying Liquidsoap to stop playback.') - try: - tn = telnetlib.Telnet(LS_HOST, LS_PORT) - tn.write('source.skip\n') - tn.write('exit\n') - tn.read_all() - except Exception, e: - self.logger.debug(e) - self.logger.debug('Could not connect to liquidsoap') - + def regenerateLiquidsoapConf(self, setting_p): existing = {} # create a temp file @@ -252,19 +235,19 @@ class PypoFetch(Thread): fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n") fh.write("################################################\n") for k, d in setting: - buffer = d[u'keyname'] + " = " + buffer_str = d[u'keyname'] + " = " if(d[u'type'] == 'string'): temp = d[u'value'] if(temp == ""): temp = "" - buffer += "\"" + temp + "\"" + buffer_str += "\"" + temp + "\"" else: temp = d[u'value'] if(temp == ""): temp = "0" - buffer += temp - buffer += "\n" - fh.write(api_client.encode_to(buffer)) + buffer_str += temp + buffer_str += "\n" + fh.write(api_client.encode_to(buffer_str)) fh.write("log_file = \"/var/log/airtime/pypo-liquidsoap/