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.
This commit is contained in:
parent
5be039d349
commit
a3d7c5e70b
|
@ -168,6 +168,10 @@ class LibraryController extends Zend_Controller_Action
|
||||||
$formdata = $form->getValues();
|
$formdata = $form->getValues();
|
||||||
$file->replaceDbMetadata($formdata);
|
$file->replaceDbMetadata($formdata);
|
||||||
|
|
||||||
|
$data = $formdata;
|
||||||
|
$data['filepath'] = $file->getRealFilePath();
|
||||||
|
RabbitMq::SendFileMetaData($data);
|
||||||
|
|
||||||
$this->_helper->redirector('index');
|
$this->_helper->redirector('index');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,24 @@
|
||||||
|
|
||||||
class Application_Form_EditAudioMD extends Zend_Form
|
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()
|
public function init()
|
||||||
{
|
{
|
||||||
|
@ -37,6 +55,13 @@ class Application_Form_EditAudioMD extends Zend_Form
|
||||||
'filters' => array('StringTrim')
|
'filters' => array('StringTrim')
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Add mood field
|
||||||
|
$this->addElement('text', 'track_number', array(
|
||||||
|
'label' => 'Track:',
|
||||||
|
'class' => 'input_text',
|
||||||
|
'filters' => array('StringTrim')
|
||||||
|
));
|
||||||
|
|
||||||
// Add genre field
|
// Add genre field
|
||||||
$this->addElement('text', 'genre', array(
|
$this->addElement('text', 'genre', array(
|
||||||
'label' => 'Genre:',
|
'label' => 'Genre:',
|
||||||
|
@ -77,9 +102,30 @@ class Application_Form_EditAudioMD extends Zend_Form
|
||||||
'filters' => array('StringTrim')
|
'filters' => array('StringTrim')
|
||||||
));
|
));
|
||||||
|
|
||||||
// Add language field
|
// Add mood field
|
||||||
$this->addElement('text', 'language', array(
|
$this->addElement('text', 'bpm', array(
|
||||||
'label' => 'Language:',
|
'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',
|
'class' => 'input_text',
|
||||||
'filters' => array('StringTrim')
|
'filters' => array('StringTrim')
|
||||||
));
|
));
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,3 +21,10 @@ version_url = 'version/api_key/%%api_key%%'
|
||||||
|
|
||||||
# URL to tell Airtime to update file's meta data
|
# URL to tell Airtime to update file's meta data
|
||||||
update_media_url = 'reload-metadata/format/json/api_key/%%api_key%%'
|
update_media_url = 'reload-metadata/format/json/api_key/%%api_key%%'
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# RabbitMQ settings #
|
||||||
|
############################################
|
||||||
|
rabbitmq_host = 'localhost'
|
||||||
|
rabbitmq_user = 'guest'
|
||||||
|
rabbitmq_password = 'guest'
|
||||||
|
|
|
@ -7,16 +7,19 @@ import datetime
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
|
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
from configobj import ConfigObj
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
import mutagen
|
||||||
import pyinotify
|
import pyinotify
|
||||||
from pyinotify import WatchManager, Notifier, ProcessEvent
|
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
|
from api_clients import api_client
|
||||||
|
|
||||||
# configure logging
|
# 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']
|
['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):
|
class MediaMonitor(ProcessEvent):
|
||||||
|
|
||||||
def my_init(self):
|
def my_init(self):
|
||||||
|
@ -69,7 +125,7 @@ class MediaMonitor(ProcessEvent):
|
||||||
def process_IN_CREATE(self, event):
|
def process_IN_CREATE(self, event):
|
||||||
if not event.dir :
|
if not event.dir :
|
||||||
#This is a newly imported file.
|
#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.path : /srv/airtime/stor/bd2
|
||||||
#event.name : bd2aa73b58d9c8abcced989621846e99.mp3
|
#event.name : bd2aa73b58d9c8abcced989621846e99.mp3
|
||||||
|
@ -98,12 +154,10 @@ class MediaMonitor(ProcessEvent):
|
||||||
print "%s: path: %s name: %s" % (event.maskname, event.path, event.name)
|
print "%s: path: %s name: %s" % (event.maskname, event.path, event.name)
|
||||||
|
|
||||||
def process_default(self, event):
|
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__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
print 'Media Monitor'
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# watched events
|
# watched events
|
||||||
mask = pyinotify.IN_CREATE | pyinotify.IN_MODIFY
|
mask = pyinotify.IN_CREATE | pyinotify.IN_MODIFY
|
||||||
|
@ -111,9 +165,9 @@ if __name__ == '__main__':
|
||||||
wm = WatchManager()
|
wm = WatchManager()
|
||||||
wdd = wm.add_watch('/srv/airtime/stor', mask, rec=True, auto_add=True)
|
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.coalesce_events()
|
||||||
notifier.loop()
|
notifier.loop(callback=checkRabbitMQ)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
notifier.stop()
|
notifier.stop()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue