From 9c46b0d396d2062d70a1a6b0254050d7963eab95 Mon Sep 17 00:00:00 2001 From: naomiaro Date: Thu, 5 May 2011 10:55:14 -0400 Subject: [PATCH] CC-1799 : Live Studio Playout from media library (pytagsfs) metadata almost working from airtime form -> python daemon. Just need to look into permissions for some stor folders. --- .../controllers/LibraryController.php | 4 ++ airtime_mvc/application/forms/EditAudioMD.php | 52 +++++++++++++- airtime_mvc/application/models/RabbitMq.php | 22 ++++++ python_apps/pytag-fs/MediaMonitor.cfg | 7 ++ python_apps/pytag-fs/MediaMonitor.py | 70 ++++++++++++++++--- 5 files changed, 144 insertions(+), 11 deletions(-) diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index 6d6c70acc..b7e15bd95 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -168,6 +168,10 @@ class LibraryController extends Zend_Controller_Action $formdata = $form->getValues(); $file->replaceDbMetadata($formdata); + $data = $formdata; + $data['filepath'] = $file->getRealFilePath(); + RabbitMq::SendFileMetaData($data); + $this->_helper->redirector('index'); } } diff --git a/airtime_mvc/application/forms/EditAudioMD.php b/airtime_mvc/application/forms/EditAudioMD.php index 6a002cd03..022745e6f 100644 --- a/airtime_mvc/application/forms/EditAudioMD.php +++ b/airtime_mvc/application/forms/EditAudioMD.php @@ -2,6 +2,24 @@ class Application_Form_EditAudioMD extends Zend_Form { + /* + "title": "track_title",\ + "artist": "artist_name",\ + "album": "album_title",\ + "genre": "genre",\ + "mood": "mood",\ + "tracknumber": "track_number",\ + "bpm": "bpm",\ + "organization": "label",\ + "composer": "composer",\ + "encodedby": "encoded_by",\ + "conductor": "conductor",\ + "date": "year",\ + "website": "info_url",\ + "isrc": "isrc_number",\ + "copyright": "copyright",\ + */ + public function init() { @@ -37,6 +55,13 @@ class Application_Form_EditAudioMD extends Zend_Form 'filters' => array('StringTrim') )); + // Add mood field + $this->addElement('text', 'track_number', array( + 'label' => 'Track:', + 'class' => 'input_text', + 'filters' => array('StringTrim') + )); + // Add genre field $this->addElement('text', 'genre', array( 'label' => 'Genre:', @@ -77,9 +102,30 @@ class Application_Form_EditAudioMD extends Zend_Form 'filters' => array('StringTrim') )); - // Add language field - $this->addElement('text', 'language', array( - 'label' => 'Language:', + // Add mood field + $this->addElement('text', 'bpm', array( + 'label' => 'BPM:', + 'class' => 'input_text', + 'filters' => array('StringTrim') + )); + + // Add mood field + $this->addElement('text', 'copyright', array( + 'label' => 'Copyright:', + 'class' => 'input_text', + 'filters' => array('StringTrim') + )); + + // Add mood field + $this->addElement('text', 'isrc_number', array( + 'label' => 'ISRC Number:', + 'class' => 'input_text', + 'filters' => array('StringTrim') + )); + + // Add mood field + $this->addElement('text', 'info_url', array( + 'label' => 'Website:', 'class' => 'input_text', 'filters' => array('StringTrim') )); diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php index 1672dd4e2..971933f20 100644 --- a/airtime_mvc/application/models/RabbitMq.php +++ b/airtime_mvc/application/models/RabbitMq.php @@ -40,5 +40,27 @@ class RabbitMq } } + public static function SendFileMetaData($md) + { + global $CC_CONFIG; + + $conn = new AMQPConnection($CC_CONFIG["rabbitmq"]["host"], + $CC_CONFIG["rabbitmq"]["port"], + $CC_CONFIG["rabbitmq"]["user"], + $CC_CONFIG["rabbitmq"]["password"]); + $channel = $conn->channel(); + $channel->access_request($CC_CONFIG["rabbitmq"]["vhost"], false, false, true, true); + + $EXCHANGE = 'airtime-media-monitor'; + $channel->exchange_declare($EXCHANGE, 'direct', false, true); + + $data = json_encode($md); + $msg = new AMQPMessage($data, array('content_type' => 'text/plain')); + + $channel->basic_publish($msg, $EXCHANGE); + $channel->close(); + $conn->close(); + } + } diff --git a/python_apps/pytag-fs/MediaMonitor.cfg b/python_apps/pytag-fs/MediaMonitor.cfg index 2c22fe656..925bcbdb4 100644 --- a/python_apps/pytag-fs/MediaMonitor.cfg +++ b/python_apps/pytag-fs/MediaMonitor.cfg @@ -21,3 +21,10 @@ version_url = 'version/api_key/%%api_key%%' # URL to tell Airtime to update file's meta data update_media_url = 'reload-metadata/format/json/api_key/%%api_key%%' + +############################################ +# RabbitMQ settings # +############################################ +rabbitmq_host = 'localhost' +rabbitmq_user = 'guest' +rabbitmq_password = 'guest' diff --git a/python_apps/pytag-fs/MediaMonitor.py b/python_apps/pytag-fs/MediaMonitor.py index a0ed37a90..e3104b767 100644 --- a/python_apps/pytag-fs/MediaMonitor.py +++ b/python_apps/pytag-fs/MediaMonitor.py @@ -7,16 +7,19 @@ import datetime import os import sys import hashlib +import json from subprocess import Popen, PIPE, STDOUT from configobj import ConfigObj +import mutagen import pyinotify from pyinotify import WatchManager, Notifier, ProcessEvent -import mutagen - +# For RabbitMQ +from kombu.connection import BrokerConnection +from kombu.messaging import Exchange, Queue, Consumer, Producer from api_clients import api_client # configure logging @@ -38,6 +41,59 @@ list of supported easy tags in mutagen version 1.20 ['albumartistsort', 'musicbrainz_albumstatus', 'lyricist', 'releasecountry', 'date', 'performer', 'musicbrainz_albumartistid', 'composer', 'encodedby', 'tracknumber', 'musicbrainz_albumid', 'album', 'asin', 'musicbrainz_artistid', 'mood', 'copyright', 'author', 'media', 'length', 'version', 'artistsort', 'titlesort', 'discsubtitle', 'website', 'musicip_fingerprint', 'conductor', 'compilation', 'barcode', 'performer:*', 'composersort', 'musicbrainz_discid', 'musicbrainz_albumtype', 'genre', 'isrc', 'discnumber', 'musicbrainz_trmid', 'replaygain_*_gain', 'musicip_puid', 'artist', 'title', 'bpm', 'musicbrainz_trackid', 'arranger', 'albumsort', 'replaygain_*_peak', 'organization'] """ +def checkRabbitMQ(notifier): + try: + notifier.connection.drain_events(timeout=5) + except Exception, e: + logger = logging.getLogger('root') + logger.info("%s", e) + +class AirtimeNotifier(Notifier): + + def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, threshold=0, timeout=None): + Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, threshold, timeout) + + self.airtime2mutagen = {\ + "track_title": "title",\ + "artist_name": "artist",\ + "album_title": "album",\ + "genre": "genre",\ + "mood": "mood",\ + "track_number": "tracknumber",\ + "bpm": "bpm",\ + "label": "organization",\ + "composer": "composer",\ + "encoded_by": "encodedby",\ + "conductor": "conductor",\ + "year": "date",\ + "info_url": "website",\ + "isrc_number": "isrc",\ + "copyright": "copyright",\ + } + + schedule_exchange = Exchange("airtime-media-monitor", "direct", durable=True, auto_delete=True) + schedule_queue = Queue("media-monitor", exchange=schedule_exchange, key="filesystem") + self.connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], "/") + channel = self.connection.channel() + consumer = Consumer(channel, schedule_queue) + consumer.register_callback(self.handle_message) + consumer.consume() + + def handle_message(self, body, message): + # ACK the message to take it off the queue + message.ack() + + logger = logging.getLogger('root') + logger.info("Received md from RabbitMQ: " + body) + + m = json.loads(message.body) + airtime_file = mutagen.File(m['filepath'], easy=True) + del m['filepath'] + for key in m.keys() : + airtime_file[self.airtime2mutagen[key]] = m[key] + + airtime_file.save() + class MediaMonitor(ProcessEvent): def my_init(self): @@ -69,7 +125,7 @@ class MediaMonitor(ProcessEvent): def process_IN_CREATE(self, event): if not event.dir : #This is a newly imported file. - print "%s: %s%s" % (event.maskname, event.path, event.name) + print "%s: %s" % (event.maskname, event.pathname) #event.path : /srv/airtime/stor/bd2 #event.name : bd2aa73b58d9c8abcced989621846e99.mp3 @@ -98,12 +154,10 @@ class MediaMonitor(ProcessEvent): print "%s: path: %s name: %s" % (event.maskname, event.path, event.name) def process_default(self, event): - print "%s: %s%s" % (event.maskname, event.path, event.name) + print "%s: %s" % (event.maskname, event.pathname) if __name__ == '__main__': - print 'Media Monitor' - try: # watched events mask = pyinotify.IN_CREATE | pyinotify.IN_MODIFY @@ -111,9 +165,9 @@ if __name__ == '__main__': wm = WatchManager() wdd = wm.add_watch('/srv/airtime/stor', mask, rec=True, auto_add=True) - notifier = Notifier(wm, MediaMonitor(), read_freq=10, timeout=1) + notifier = AirtimeNotifier(wm, MediaMonitor(), read_freq=10, timeout=1) notifier.coalesce_events() - notifier.loop() + notifier.loop(callback=checkRabbitMQ) except KeyboardInterrupt: notifier.stop()