From bae771790ac1d381178c658f33385bdc6f8cd213 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Thu, 23 Jun 2011 21:14:09 +0200 Subject: [PATCH] cc-1799 Human Filesystem editing recording shows so they work with the new filesystem/file upload. --- .../application/controllers/ApiController.php | 29 ++++++++++-- python_apps/api_clients/api_client.py | 22 ++++++++- .../airtimefilemonitor/airtimenotifier.py | 19 +++++--- .../airtimefilemonitor/airtimeprocessevent.py | 30 ++++++++---- python_apps/media-monitor/media-monitor.cfg | 4 +- python_apps/show-recorder/recorder.cfg | 2 +- python_apps/show-recorder/recorder.py | 46 +++++++++++++++---- 7 files changed, 118 insertions(+), 34 deletions(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 700632542..158b679fa 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -9,6 +9,7 @@ class ApiController extends Zend_Controller_Action $context = $this->_helper->getHelper('contextSwitch'); $context->addActionContext('version', 'json') ->addActionContext('recorded-shows', 'json') + ->addActionContext('upload-file', 'json') ->addActionContext('upload-recorded', 'json') ->addActionContext('media-monitor-setup', 'json') ->addActionContext('media-item-status', 'json') @@ -288,6 +289,22 @@ class ApiController extends Zend_Controller_Action } } + public function uploadFileAction() + { + global $CC_CONFIG; + + $api_key = $this->_getParam('api_key'); + if (!in_array($api_key, $CC_CONFIG["apiKey"])) + { + header('HTTP/1.0 401 Unauthorized'); + print 'You are not allowed to access this resource.'; + exit; + } + + $upload_dir = ini_get("upload_tmp_dir"); + StoredFile::uploadFile($upload_dir); + } + public function uploadRecordedAction() { global $CC_CONFIG; @@ -300,12 +317,17 @@ class ApiController extends Zend_Controller_Action exit; } + //this file id is the recording for this show instance. + $show_instance_id = $this->_getParam('showinstanceid'); + $file_id = $this->_getParam('fileid'); + + $this->view->fileid = $file_id; + $this->view->showinstanceid = $show_instance_id; + + /* $showCanceled = false; $show_instance = $this->_getParam('show_instance'); - $upload_dir = ini_get("upload_tmp_dir"); - $file = StoredFile::uploadFile($upload_dir); - $show_name = ""; try { $show_inst = new ShowInstance($show_instance); @@ -357,6 +379,7 @@ class ApiController extends Zend_Controller_Action } $this->view->id = $file->getId(); + */ } public function mediaMonitorSetupAction() { diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index 568b933c0..cd96c3593 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -318,6 +318,7 @@ class AirTimeApiClient(ApiClientInterface): logger.debug(url) url = url.replace("%%api_key%%", self.config["api_key"]) + logger.debug(url) for i in range(0, retries): logger.debug("Upload attempt: %s", i+1) @@ -360,14 +361,15 @@ class AirTimeApiClient(ApiClientInterface): return response - def update_media_metadata(self, md, mode): + def update_media_metadata(self, md, mode, is_record=False): logger = logging.getLogger() response = None try: - url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_media_url"]) + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_media_url"]) url = url.replace("%%api_key%%", self.config["api_key"]) url = url.replace("%%mode%%", mode) + logger.debug(url) data = urllib.urlencode(md) req = urllib2.Request(url, data) @@ -376,6 +378,22 @@ class AirTimeApiClient(ApiClientInterface): response = json.loads(response) logger.info("update media %s", response) + if(is_record): + url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["upload_recorded"]) + logger.debug(url) + url = url.replace("%%api_key%%", self.config["api_key"]) + logger.debug(url) + url = url.replace("%%fileid%%", str(response[u'id'])) + logger.debug(url) + url = url.replace("%%showinstanceid%%", str(md['MDATA_KEY_TRACKNUMBER'])) + logger.debug(url) + + req = urllib2.Request(url) + response = urllib2.urlopen(req).read() + response = json.loads(response) + logger.info("associate recorded %s", response) + + except Exception, e: response = None logger.error("Exception: %s", e) diff --git a/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py b/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py index 0401d7f8d..091c7b3bd 100644 --- a/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py +++ b/python_apps/media-monitor/airtimefilemonitor/airtimenotifier.py @@ -100,19 +100,24 @@ class AirtimeNotifier(Notifier): if file_md is None: mutagen = self.md_manager.get_md_from_file(filepath) md.update(mutagen) - data = md + + if d['is_recorded_show']: + self.api_client.update_media_metadata(md, mode, True) + else: + self.api_client.update_media_metadata(md, mode) + elif (os.path.exists(filepath) and (mode == self.config.MODE_MODIFY)): mutagen = self.md_manager.get_md_from_file(filepath) md.update(mutagen) - data = md + self.api_client.update_media_metadata(md, mode) + elif (mode == self.config.MODE_MOVED): md['MDATA_KEY_MD5'] = self.md_manager.get_md5(filepath) - data = md - elif (mode == self.config.MODE_DELETE): - data = md + self.api_client.update_media_metadata(md, mode) + + elif (mode == self.config.MODE_DELETE): + self.api_client.update_media_metadata(md, mode) - if data is not None: - self.api_client.update_media_metadata(data, mode) def process_file_events(self, queue): diff --git a/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py b/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py index 3c1bfbd7f..7673daee8 100644 --- a/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py +++ b/python_apps/media-monitor/airtimefilemonitor/airtimeprocessevent.py @@ -145,6 +145,8 @@ class AirtimeProcessEvent(ProcessEvent): storage_directory = self.config.storage_directory + is_recorded_show = False + try: #will be in the format .ext file_ext = os.path.splitext(imported_filepath)[1] @@ -165,10 +167,17 @@ class AirtimeProcessEvent(ProcessEvent): md[m] = orig_md[m] filepath = None - if(md['MDATA_KEY_TRACKNUMBER'] == u'unknown'.encode('utf-8')): - filepath = '%s/%s/%s/%s-%s%s' % (storage_directory, md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) + #file is recorded by Airtime + #/srv/airtime/stor/recorded/year/month/year-month-day-time-showname-bitrate.ext + if(md['MDATA_KEY_CREATOR'] == "AIRTIMERECORDERSOURCEFABRIC".encode('utf-8')): + #yyyy-mm-dd-hh-MM-ss + y = orig_md['MDATA_KEY_YEAR'].split("-") + filepath = '%s/%s/%s/%s/%s-%s-%s%s' % (storage_directory, "recorded".encode('utf-8'), y[0], y[1], orig_md['MDATA_KEY_YEAR'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) + is_recorded_show = True + elif(md['MDATA_KEY_TRACKNUMBER'] == u'unknown'.encode('utf-8')): + filepath = '%s/%s/%s/%s/%s-%s%s' % (storage_directory, "imported".encode('utf-8'), md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) else: - filepath = '%s/%s/%s/%s-%s-%s%s' % (storage_directory, md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TRACKNUMBER'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) + filepath = '%s/%s/%s/%s/%s-%s-%s%s' % (storage_directory, "imported".encode('utf-8'), md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TRACKNUMBER'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext) self.logger.info('Created filepath: %s', filepath) filepath = self.create_unique_filename(filepath, imported_filepath) @@ -178,7 +187,7 @@ class AirtimeProcessEvent(ProcessEvent): except Exception, e: self.logger.error('Exception: %s', e) - return filepath, orig_md + return filepath, orig_md, is_recorded_show def process_IN_CREATE(self, event): @@ -194,11 +203,11 @@ class AirtimeProcessEvent(ProcessEvent): if self.is_audio_file(event.pathname): if self.is_parent_directory(event.pathname, storage_directory): self.set_needed_file_permissions(event.pathname, event.dir) - filepath, file_md = self.create_file_path(event.pathname) + filepath, file_md, is_recorded_show = self.create_file_path(event.pathname) self.move_file(event.pathname, filepath) - self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': filepath, 'data': file_md}) + self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': filepath, 'data': file_md, 'is_recorded_show': is_recorded_show}) else: - self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': event.pathname}) + self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': event.pathname, 'is_recorded_show': False}) else: if self.is_parent_directory(event.pathname, storage_directory): @@ -233,13 +242,14 @@ class AirtimeProcessEvent(ProcessEvent): del self.moved_files[event.cookie] self.file_events.put({'filepath': event.pathname, 'mode': self.config.MODE_MOVED}) else: + # show dragged from unwatched folder into a watched folder. storage_directory = self.config.storage_directory if self.is_parent_directory(event.pathname, storage_directory): - filepath, file_md = self.create_file_path(event.pathname) + filepath, file_md, is_recorded_show = self.create_file_path(event.pathname) self.move_file(event.pathname, filepath) - self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': filepath, 'data': file_md}) + self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': filepath, 'data': file_md, 'is_recorded_show': False}) else: - self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': event.pathname}) + self.file_events.put({'mode': self.config.MODE_CREATE, 'filepath': event.pathname, 'is_recorded_show': False}) def process_IN_DELETE(self, event): self.logger.info("%s: %s", event.maskname, event.pathname) diff --git a/python_apps/media-monitor/media-monitor.cfg b/python_apps/media-monitor/media-monitor.cfg index e1f36619f..e5b196f05 100644 --- a/python_apps/media-monitor/media-monitor.cfg +++ b/python_apps/media-monitor/media-monitor.cfg @@ -22,8 +22,8 @@ version_url = 'version/api_key/%%api_key%%' # URL to setup the media monitor media_setup_url = 'media-monitor-setup/format/json/api_key/%%api_key%%' -# URL to check Airtime's status of a file -media_status_url = 'media-item-status/format/json/api_key/%%api_key%%/md5/%%md5%%' +# Tell Airtime the file id associated with a show instance. +upload_recorded = 'upload-recorded/format/json/api_key/%%api_key%%/fileid/%%fileid%%/showinstanceid/%%showinstanceid%%' # URL to tell Airtime to update file's meta data update_media_url = 'reload-metadata/format/json/api_key/%%api_key%%/mode/%%mode%%' diff --git a/python_apps/show-recorder/recorder.cfg b/python_apps/show-recorder/recorder.cfg index 22fcd227c..0079fd017 100644 --- a/python_apps/show-recorder/recorder.cfg +++ b/python_apps/show-recorder/recorder.cfg @@ -26,7 +26,7 @@ version_url = 'version/api_key/%%api_key%%' show_schedule_url = 'recorded-shows/format/json/api_key/%%api_key%%' # URL to upload the recorded show's file to Airtime -upload_file_url = 'upload-recorded/format/json/api_key/%%api_key%%' +upload_file_url = 'upload-file/format/json/api_key/%%api_key%%' #number of retries to upload file if connection problem upload_retries = 3 diff --git a/python_apps/show-recorder/recorder.py b/python_apps/show-recorder/recorder.py index 233f5c8de..0f8a8857d 100644 --- a/python_apps/show-recorder/recorder.py +++ b/python_apps/show-recorder/recorder.py @@ -7,6 +7,7 @@ import time import datetime import os import sys +import shutil from configobj import ConfigObj @@ -17,6 +18,8 @@ import urllib2 from subprocess import Popen from threading import Thread +import mutagen + # For RabbitMQ - to be implemented in the future #from kombu.connection import BrokerConnection #from kombu.messaging import Exchange, Queue, Consumer, Producer @@ -48,13 +51,14 @@ def getDateTimeObj(time): class ShowRecorder(Thread): - def __init__ (self, show_instance, filelength, start_time, filetype): + def __init__ (self, show_instance, show_name, filelength, start_time, filetype): Thread.__init__(self) self.api_client = api_client.api_client_factory(config) self.filelength = filelength self.start_time = start_time self.filetype = filetype self.show_instance = show_instance + self.show_name = show_name self.logger = logging.getLogger('root') self.p = None @@ -70,24 +74,24 @@ class ShowRecorder(Thread): self.logger.info("starting record") self.logger.info("command " + command) - + self.p = Popen(args) - + #blocks at the following line until the child process #quits code = self.p.wait() self.p = None - + self.logger.info("finishing record, return code %s", code) return code, filepath - + def cancel_recording(self): #send signal interrupt (2) self.logger.info("Show manually cancelled!") if (self.p is not None): self.p.terminate() self.p = None - + #if self.p is defined, then the child process ecasound is recording def is_recording(self): return (self.p is not None) @@ -110,6 +114,29 @@ class ShowRecorder(Thread): if code == 0: self.logger.info("Preparing to upload %s" % filepath) + + try: + date = self.start_time + md = date.split(" ") + time = md[1].replace(":", "-") + self.logger.info("time: %s" % time) + + name = time+"-"+self.show_name + name.encode('utf-8') + artist = "AIRTIMERECORDERSOURCEFABRIC".encode('utf-8') + + #set some metadata for our file daemon + recorded_file = mutagen.File(filepath, easy=True) + recorded_file['title'] = name + recorded_file['artist'] = artist + recorded_file['date'] = md[0] + recorded_file['tracknumber'] = self.show_instance + recorded_file.save() + + except Exception, e: + self.logger.error("Exception: %s", e) + + self.upload_file(filepath) else: self.logger.info("problem recording show") @@ -154,8 +181,9 @@ class Record(): show_length = self.shows_to_record[start_time][0] show_instance = self.shows_to_record[start_time][1] + show_name = self.shows_to_record[start_time][2] - self.sr = ShowRecorder(show_instance, show_length.seconds, start_time, filetype="mp3") + self.sr = ShowRecorder(show_instance, show_name, show_length.seconds, start_time, filetype="mp3") self.sr.start() #remove show from shows to record. @@ -170,9 +198,9 @@ class Record(): if self.sr is not None: if not response['is_recording'] and self.sr.is_recording(): self.sr.cancel_recording() - + shows = response[u'shows'] - + if len(shows): self.process_shows(shows) self.check_record()