Merge branch 'devel' of dev.sourcefabric.org:airtime into devel

This commit is contained in:
Martin Konecny 2012-08-21 16:55:17 -04:00
commit 15eae0698b
12 changed files with 215 additions and 147 deletions

View File

@ -125,6 +125,8 @@ class PlaylistController extends Zend_Controller_Action
private function playlistNoPermission($p_type) private function playlistNoPermission($p_type)
{ {
$this->view->error = "You don't have permission to delete selected {$p_type}(s)."; $this->view->error = "You don't have permission to delete selected {$p_type}(s).";
$this->changePlaylist(null, $p_type);
$this->createFullResponse(null);
} }
private function playlistUnknownError($e) private function playlistUnknownError($e)
@ -221,6 +223,8 @@ class PlaylistController extends Zend_Controller_Action
$this->createFullResponse($obj); $this->createFullResponse($obj);
} catch (PlaylistNoPermissionException $e) { } catch (PlaylistNoPermissionException $e) {
$this->playlistNoPermission($type); $this->playlistNoPermission($type);
} catch (BlockNoPermissionException $e) {
$this->playlistNoPermission($type);
} catch (PlaylistNotFoundException $e) { } catch (PlaylistNotFoundException $e) {
$this->playlistNotFound($type); $this->playlistNotFound($type);
} catch (Exception $e) { } catch (Exception $e) {

View File

@ -192,7 +192,7 @@ class Application_Model_Block implements Application_Model_LibraryEditable
$files = array(); $files = array();
$sql = <<<"EOT" $sql = <<<"EOT"
SELECT pc.id as id, pc.position, pc.cliplength as length, pc.cuein, pc.cueout, pc.fadein, pc.fadeout, bl.type, SELECT pc.id as id, pc.position, pc.cliplength as length, pc.cuein, pc.cueout, pc.fadein, pc.fadeout, bl.type, f.length as orig_length,
f.id as item_id, f.track_title, f.artist_name as creator, f.file_exists as exists, f.filepath as path FROM cc_blockcontents AS pc f.id as item_id, f.track_title, f.artist_name as creator, f.file_exists as exists, f.filepath as path FROM cc_blockcontents AS pc
LEFT JOIN cc_files AS f ON pc.file_id=f.id LEFT JOIN cc_files AS f ON pc.file_id=f.id
LEFT JOIN cc_block AS bl ON pc.block_id = bl.id LEFT JOIN cc_block AS bl ON pc.block_id = bl.id
@ -220,6 +220,10 @@ EOT;
$fades = $this->getFadeInfo($row['position']); $fades = $this->getFadeInfo($row['position']);
$row['fadein'] = $fades[0]; $row['fadein'] = $fades[0];
$row['fadeout'] = $fades[1]; $row['fadeout'] = $fades[1];
//format original length
$formatter = new LengthFormatter($row['orig_length']);
$row['orig_length'] = $formatter->format();
} }
return $rows; return $rows;
@ -871,11 +875,20 @@ EOT;
*/ */
public static function deleteBlocks($p_ids, $p_userId) public static function deleteBlocks($p_ids, $p_userId)
{ {
$leftOver = self::blocksNotOwnedByUser($p_ids, $p_userId); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
if (count($leftOver) == 0) { $user = new Application_Model_User($userInfo->id);
CcBlockQuery::create()->findPKs($p_ids)->delete(); $isAdminOrPM = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER));
if (!$isAdminOrPM) {
$leftOver = self::blocksNotOwnedByUser($p_ids, $p_userId);
if (count($leftOver) == 0) {
CcBlockQuery::create()->findPKs($p_ids)->delete();
} else {
throw new BlockNoPermissionException;
}
} else { } else {
throw new BlockNoPermissionException; CcBlockQuery::create()->findPKs($p_ids)->delete();
} }
} }

View File

@ -90,17 +90,21 @@ class Application_Model_Datatables
// we need to go over all items and fix length for playlist // we need to go over all items and fix length for playlist
// in case the playlist contains dynamic block // in case the playlist contains dynamic block
foreach ($results as &$r) { foreach ($results as &$r) {
if ($r['ftype'] == 'playlist') { //this function is also called for Manage Users so in
$pl = new Application_Model_Playlist($r['id']); //this case there will be no 'ftype'
$r['length'] = $pl->getLength(); if (isset($r['ftype'])) {
} else if ($r['ftype'] == "block") { if ($r['ftype'] == 'playlist') {
$bl = new Application_Model_Block($r['id']); $pl = new Application_Model_Playlist($r['id']);
if ($bl->isStatic()) { $r['length'] = $pl->getLength();
$r['bl_type'] = 'static'; } else if ($r['ftype'] == "block") {
} else { $bl = new Application_Model_Block($r['id']);
$r['bl_type'] = 'dynamic'; if ($bl->isStatic()) {
$r['bl_type'] = 'static';
} else {
$r['bl_type'] = 'dynamic';
}
$r['length'] = $bl->getLength();
} }
$r['length'] = $bl->getLength();
} }
} }
} catch (Exception $e) { } catch (Exception $e) {

View File

@ -171,7 +171,8 @@ class Application_Model_Playlist implements Application_Model_LibraryEditable
f.track_title, f.track_title,
f.artist_name AS creator, f.artist_name AS creator,
f.file_exists AS EXISTS, f.file_exists AS EXISTS,
f.filepath AS path f.filepath AS path,
f.length AS orig_length
FROM cc_playlistcontents AS pc FROM cc_playlistcontents AS pc
JOIN cc_files AS f ON pc.file_id=f.id JOIN cc_files AS f ON pc.file_id=f.id
WHERE pc.playlist_id = {$this->id} WHERE pc.playlist_id = {$this->id}
@ -188,7 +189,8 @@ class Application_Model_Playlist implements Application_Model_LibraryEditable
(ws.name || ': ' || ws.url) AS title, (ws.name || ': ' || ws.url) AS title,
sub.login AS creator, sub.login AS creator,
't'::boolean AS EXISTS, 't'::boolean AS EXISTS,
ws.url AS path ws.url AS path,
ws.length AS orig_length
FROM cc_playlistcontents AS pc FROM cc_playlistcontents AS pc
JOIN cc_webstream AS ws ON pc.stream_id=ws.id JOIN cc_webstream AS ws ON pc.stream_id=ws.id
LEFT JOIN cc_subjs AS sub ON sub.id = ws.creator_id LEFT JOIN cc_subjs AS sub ON sub.id = ws.creator_id
@ -206,7 +208,8 @@ class Application_Model_Playlist implements Application_Model_LibraryEditable
bl.name AS title, bl.name AS title,
sbj.login AS creator, sbj.login AS creator,
't'::boolean AS EXISTS, 't'::boolean AS EXISTS,
NULL::text AS path NULL::text AS path,
bl.length AS orig_length
FROM cc_playlistcontents AS pc FROM cc_playlistcontents AS pc
JOIN cc_block AS bl ON pc.block_id=bl.id JOIN cc_block AS bl ON pc.block_id=bl.id
JOIN cc_subjs AS sbj ON bl.creator_id=sbj.id JOIN cc_subjs AS sbj ON bl.creator_id=sbj.id
@ -240,6 +243,10 @@ SQL;
$fades = $this->getFadeInfo($row['position']); $fades = $this->getFadeInfo($row['position']);
$row['fadein'] = $fades[0]; $row['fadein'] = $fades[0];
$row['fadeout'] = $fades[1]; $row['fadeout'] = $fades[1];
//format original length
$formatter = new LengthFormatter($row['orig_length']);
$row['orig_length'] = $formatter->format();
} }
return $rows; return $rows;
@ -867,11 +874,19 @@ SQL;
*/ */
public static function deletePlaylists($p_ids, $p_userId) public static function deletePlaylists($p_ids, $p_userId)
{ {
$leftOver = self::playlistsNotOwnedByUser($p_ids, $p_userId); $userInfo = Zend_Auth::getInstance()->getStorage()->read();
if (count($leftOver) == 0) { $user = new Application_Model_User($userInfo->id);
CcPlaylistQuery::create()->findPKs($p_ids)->delete(); $isAdminOrPM = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER));
if (!$isAdminOrPM) {
$leftOver = self::playlistsNotOwnedByUser($p_ids, $p_userId);
if (count($leftOver) == 0) {
CcPlaylistQuery::create()->findPKs($p_ids)->delete();
} else {
throw new PlaylistNoPermissionException;
}
} else { } else {
throw new PlaylistNoPermissionException; CcPlaylistQuery::create()->findPKs($p_ids)->delete();
} }
} }

View File

@ -1549,7 +1549,7 @@ class Application_Model_Show
$sql = $sql." AND ({$exclude})"; $sql = $sql." AND ({$exclude})";
} }
$result = $con->query($sql); $result = $con->query($sql)->fetchAll();
return $result; return $result;
} }

View File

@ -55,7 +55,7 @@ if ($item['type'] == 2) {
'id' => $item["id"], 'id' => $item["id"],
'cueIn' => $item['cuein'], 'cueIn' => $item['cuein'],
'cueOut' => $item['cueout'], 'cueOut' => $item['cueout'],
'origLength' => $item['length'])); ?> 'origLength' => $item['orig_length'])); ?>
</div> </div>
<?php }?> <?php }?>

View File

@ -59,4 +59,6 @@ class EventContractor(Loggable):
try: del self.store[evt.path] try: del self.store[evt.path]
except Exception as e: except Exception as e:
self.unexpected_exception(e) self.unexpected_exception(e)
self.logger.info("Unregistering. Left: '%d'" % len(self.store.keys())) # the next line is commented out because it clutters up logging real
# bad
#self.logger.info("Unregistering. Left: '%d'" % len(self.store.keys()))

View File

@ -105,10 +105,38 @@ class Metadata(Loggable):
# little bit messy. Some of the handling is in m.m.pure while the rest is # little bit messy. Some of the handling is in m.m.pure while the rest is
# here. Also interface is not very consistent # here. Also interface is not very consistent
@staticmethod
def airtime_dict(d):
"""
Converts mutagen dictionary 'd' into airtime dictionary
"""
temp_dict = {}
for m_key, m_val in d.iteritems():
# TODO : some files have multiple fields for the same metadata.
# genre is one example. In that case mutagen will return a list
# of values
assign_val = m_val[0] if isinstance(m_val, list) else m_val
temp_dict[ m_key ] = assign_val
airtime_dictionary = {}
for muta_k, muta_v in temp_dict.iteritems():
# We must check if we can actually translate the mutagen key into
# an airtime key before doing the conversion
if muta_k in mutagen2airtime:
airtime_key = mutagen2airtime[muta_k]
# Apply truncation in the case where airtime_key is in our
# truncation table
muta_v = \
truncate_to_length(muta_v, truncate_table[airtime_key])\
if airtime_key in truncate_table else muta_v
airtime_dictionary[ airtime_key ] = muta_v
return airtime_dictionary
@staticmethod @staticmethod
def write_unsafe(path,md): def write_unsafe(path,md):
""" """
Writes 'md' metadata into 'path' through mutagen Writes 'md' metadata into 'path' through mutagen. Converts all
dictionary values to strings because mutagen will not write anything
else
""" """
if not os.path.exists(path): raise BadSongFile(path) if not os.path.exists(path): raise BadSongFile(path)
song_file = mutagen.File(path, easy=True) song_file = mutagen.File(path, easy=True)
@ -128,37 +156,13 @@ class Metadata(Loggable):
self.path = fpath self.path = fpath
# TODO : Simplify the way all of these rules are handled right not it's # TODO : Simplify the way all of these rules are handled right not it's
# extremely unclear and needs to be refactored. # extremely unclear and needs to be refactored.
metadata = {} self.__metadata = Metadata.airtime_dict(full_mutagen)
# Load only the metadata avilable in mutagen into metdata
for k,v in full_mutagen.iteritems():
# Special handling of attributes here
if isinstance(v, list):
# TODO : some files have multiple fields for the same metadata.
# genre is one example. In that case mutagen will return a list
# of values
metadata[k] = v[0]
#if len(v) == 1: metadata[k] = v[0]
#else: raise Exception("Unknown mutagen %s:%s" % (k,str(v)))
else: metadata[k] = v
self.__metadata = {}
# Start populating a dictionary of airtime metadata in __metadata
for muta_k, muta_v in metadata.iteritems():
# We must check if we can actually translate the mutagen key into
# an airtime key before doing the conversion
if muta_k in mutagen2airtime:
airtime_key = mutagen2airtime[muta_k]
# Apply truncation in the case where airtime_key is in our
# truncation table
muta_v = \
truncate_to_length(muta_v, truncate_table[airtime_key])\
if airtime_key in truncate_table else muta_v
self.__metadata[ airtime_key ] = muta_v
# Now we extra the special values that are calculated from the mutagen # Now we extra the special values that are calculated from the mutagen
# object itself: # object itself:
for special_key,f in airtime_special.iteritems(): for special_key,f in airtime_special.iteritems():
new_val = f(full_mutagen) new_val = f(full_mutagen)
if new_val is not None: if new_val is not None:
self.__metadata[special_key] = f(full_mutagen) self.__metadata[special_key] = new_val
# Finally, we "normalize" all the metadata here: # Finally, we "normalize" all the metadata here:
self.__metadata = mmp.normalized_metadata(self.__metadata, fpath) self.__metadata = mmp.normalized_metadata(self.__metadata, fpath)
# Now we must load the md5: # Now we must load the md5:

View File

@ -2,6 +2,7 @@
import copy import copy
import os import os
import shutil import shutil
import re
import sys import sys
import hashlib import hashlib
import locale import locale
@ -228,34 +229,33 @@ def normalized_metadata(md, original_path):
# Specific rules that are applied in a per attribute basis # Specific rules that are applied in a per attribute basis
format_rules = { format_rules = {
'MDATA_KEY_TRACKNUMBER' : parse_int, 'MDATA_KEY_TRACKNUMBER' : parse_int,
'MDATA_KEY_BITRATE' : lambda x: str(int(x) / 1000) + "kbps",
'MDATA_KEY_FILEPATH' : lambda x: os.path.normpath(x), 'MDATA_KEY_FILEPATH' : lambda x: os.path.normpath(x),
'MDATA_KEY_MIME' : lambda x: x.replace('-','/'), 'MDATA_KEY_MIME' : lambda x: x.replace('-','/'),
'MDATA_KEY_BPM' : lambda x: x[0:8], 'MDATA_KEY_BPM' : lambda x: x[0:8],
} }
new_md = remove_whitespace(new_md) new_md = remove_whitespace(new_md) # remove whitespace fields
# Format all the fields in format_rules
new_md = apply_rules_dict(new_md, format_rules) new_md = apply_rules_dict(new_md, format_rules)
new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_TITLE'], # set filetype to audioclip by default
default=no_extension_basename(original_path))
new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_CREATOR',
'MDATA_KEY_SOURCE'], default=u'')
new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_FTYPE'], new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_FTYPE'],
default=u'audioclip') default=u'audioclip')
# In the case where the creator is 'Airtime Show Recorder' we would like to
# format the MDATA_KEY_TITLE slightly differently # Try to parse bpm but delete the whole key if that fails
# Note: I don't know why I'm doing a unicode string comparison here if 'MDATA_KEY_BPM' in new_md:
# that part is copied from the original code new_md['MDATA_KEY_BPM'] = parse_int(new_md['MDATA_KEY_BPM'])
if new_md['MDATA_KEY_BPM'] is None:
del new_md['MDATA_KEY_BPM']
if is_airtime_recorded(new_md): if is_airtime_recorded(new_md):
hour,minute,second,name = md['MDATA_KEY_TITLE'].split("-",3) hour,minute,second,name = new_md['MDATA_KEY_TITLE'].split("-",3)
# We assume that MDATA_KEY_YEAR is always given for airtime recorded
# shows
new_md['MDATA_KEY_TITLE'] = u'%s-%s-%s:%s:%s' % \ new_md['MDATA_KEY_TITLE'] = u'%s-%s-%s:%s:%s' % \
(name, new_md['MDATA_KEY_YEAR'], hour, minute, second) (name, new_md['MDATA_KEY_YEAR'], hour, minute, second)
# IMPORTANT: in the original code. MDATA_KEY_FILEPATH would also else:
# be set to the original path of the file for airtime recorded shows # Read title from filename if it does not exist
# (before it was "organized"). We will skip this procedure for now new_md = default_to(dictionary=new_md, keys=['MDATA_KEY_TITLE'],
# because it's not clear why it was done default=no_extension_basename(original_path))
return new_md return new_md
def organized_path(old_path, root_path, orig_md): def organized_path(old_path, root_path, orig_md):
@ -274,15 +274,26 @@ def organized_path(old_path, root_path, orig_md):
if key in dictionary: return len(dictionary[key]) == 0 if key in dictionary: return len(dictionary[key]) == 0
else: return True else: return True
# We set some metadata elements to a default "unknown" value because we use # We set some metadata elements to a default "unknown" value because we use
# these fields to create a path hence they cannot be empty # these fields to create a path hence they cannot be empty Here "normal"
# means normalized only for organized path
# MDATA_KEY_BITRATE is in bytes/second i.e. (256000) we want to turn this
# into 254kbps
normal_md = default_to_f(orig_md, path_md, unicode_unknown, default_f) normal_md = default_to_f(orig_md, path_md, unicode_unknown, default_f)
if normal_md['MDATA_KEY_BITRATE']:
formatted = str(int(normal_md['MDATA_KEY_BITRATE']) / 1000)
normal_md['MDATA_KEY_BITRATE'] = formatted + 'kbps'
else: normal_md['MDATA_KEY_BITRATE'] = unicode_unknown
if is_airtime_recorded(normal_md): if is_airtime_recorded(normal_md):
fname = u'%s-%s-%s.%s' % ( normal_md['MDATA_KEY_YEAR'], title_re = re.match("(?P<show>\w+)-(?P<date>\d+-\d+-\d+-\d+:\d+:\d+)$",
normal_md['MDATA_KEY_TITLE'], normal_md['MDATA_KEY_TITLE'])
normal_md['MDATA_KEY_BITRATE'], ext ) show_name, = title_re.group('show'),
yyyy, mm, _ = normal_md['MDATA_KEY_YEAR'].split('-',3) date = title_re.group('date').replace(':','-')
path = os.path.join(root_path, yyyy, mm) yyyy, mm, _ = normal_md['MDATA_KEY_YEAR'].split('-',2)
filepath = os.path.join(path,fname) fname_base = '%s-%s-%s.%s' % \
(date, show_name, normal_md['MDATA_KEY_BITRATE'], ext)
filepath = os.path.join(root_path, yyyy, mm, fname_base)
elif len(normal_md['MDATA_KEY_TRACKNUMBER']) == 0: elif len(normal_md['MDATA_KEY_TRACKNUMBER']) == 0:
fname = u'%s-%s.%s' % (normal_md['MDATA_KEY_TITLE'], fname = u'%s-%s.%s' % (normal_md['MDATA_KEY_TITLE'],
normal_md['MDATA_KEY_BITRATE'], ext) normal_md['MDATA_KEY_BITRATE'], ext)

View File

@ -127,6 +127,8 @@ def main(global_config, api_client_config, log_config,
tt = ToucherThread(path=config['index_path'], tt = ToucherThread(path=config['index_path'],
interval=int(config['touch_interval'])) interval=int(config['touch_interval']))
apiclient.register_component('media-monitor')
pyi = manager.pyinotify() pyi = manager.pyinotify()
pyi.loop() pyi.loop()

View File

@ -2,6 +2,7 @@
import unittest import unittest
import os import os
import media.monitor.pure as mmp import media.monitor.pure as mmp
from media.monitor.metadata import Metadata
class TestMMP(unittest.TestCase): class TestMMP(unittest.TestCase):
def setUp(self): def setUp(self):
@ -34,23 +35,32 @@ class TestMMP(unittest.TestCase):
for k in def_keys: self.assertEqual( sd[k], 'DEF' ) for k in def_keys: self.assertEqual( sd[k], 'DEF' )
def test_normalized_metadata(self): def test_normalized_metadata(self):
normal = mmp.normalized_metadata(self.md1,"") # Recorded show test first
self.assertTrue(hasattr(normal['MDATA_KEY_CREATOR'],'startswith')) orig = Metadata.airtime_dict({
self.assertTrue('MDATA_KEY_CREATOR' in normal) 'date': [u'2012-08-21'],
self.assertTrue('MDATA_KEY_SOURCE' in normal) 'tracknumber': [u'2'],
'title': [u'11-29-00-record'],
'artist': [u'Airtime Show Recorder']
})
orga = Metadata.airtime_dict({
'date': [u'2012-08-21'],
'tracknumber': [u'2'],
'artist': [u'Airtime Show Recorder'],
'title': [u'record-2012-08-21-11:29:00']
})
orga['MDATA_KEY_FTYPE'] = u'audioclip'
orig['MDATA_KEY_BITRATE'] = u'256000'
orga['MDATA_KEY_BITRATE'] = u'256kbps'
def test_organized_path(self): old_path = "/home/rudi/recorded/2012-08-21-11:29:00.ogg"
o_path = '/home/rudi/throwaway/ACDC_-_Back_In_Black-sample-64kbps.ogg' normalized = mmp.normalized_metadata(orig, old_path)
normal = mmp.normalized_metadata(self.md1,o_path) print(normalized)
og = mmp.organized_path(o_path, self.assertEqual( orga, normalized )
'/home/rudi/throwaway/fucking_around/watch/',
normal) organized_base_name = "2012-08-21-11-29-00-record-256kbps.ogg"
real_path1 = \ base = "/srv/airtime/stor/"
u'/home/rudi/throwaway/fucking_around/watch/unknown/unknown/ACDC_-_Back_In_Black-sample-64kbps-64kbps.ogg' organized_path = mmp.organized_path(old_path,base, normalized)
self.assertTrue( 'unknown' in og, True ) self.assertEqual(os.path.basename(organized_path), organized_base_name)
self.assertEqual( og, real_path1 ) # TODO : fix this failure
# for recorded it should be something like this
# ./recorded/2012/07/2012-07-09-17-55-00-Untitled Show-256kbps.ogg
def test_file_md5(self): def test_file_md5(self):
p = os.path.realpath(__file__) p = os.path.realpath(__file__)

View File

@ -9,6 +9,7 @@ import sys
import pytz import pytz
import signal import signal
import math import math
import traceback
from configobj import ConfigObj from configobj import ConfigObj
@ -55,16 +56,16 @@ class ShowRecorder(Thread):
def __init__ (self, show_instance, show_name, filelength, start_time): def __init__ (self, show_instance, show_name, filelength, start_time):
Thread.__init__(self) Thread.__init__(self)
self.logger = logging.getLogger('recorder') self.logger = logging.getLogger('recorder')
self.api_client = api_client(self.logger) self.api_client = api_client(self.logger)
self.filelength = filelength self.filelength = filelength
self.start_time = start_time self.start_time = start_time
self.show_instance = show_instance self.show_instance = show_instance
self.show_name = show_name self.show_name = show_name
self.p = None self.p = None
def record_show(self): def record_show(self):
length = str(self.filelength) + ".0" length = str(self.filelength) + ".0"
filename = self.start_time filename = self.start_time
filename = filename.replace(" ", "-") filename = filename.replace(" ", "-")
@ -73,16 +74,18 @@ class ShowRecorder(Thread):
else: else:
filetype = "ogg"; filetype = "ogg";
filepath = "%s%s.%s" % (config["base_recorded_files"], filename, filetype) joined_path = os.path.join(config["base_recorded_files"], filename)
filepath = "%s.%s" % (joined_path, filetype)
br = config["record_bitrate"] br = config["record_bitrate"]
sr = config["record_samplerate"] sr = config["record_samplerate"]
c = config["record_channels"] c = config["record_channels"]
ss = config["record_sample_size"] ss = config["record_sample_size"]
#-f:16,2,44100 #-f:16,2,44100
#-b:256 #-b:256
command = "ecasound -f:%s,%s,%s -i alsa -o %s,%s000 -t:%s" % (ss, c, sr, filepath, br, length) command = "ecasound -f:%s,%s,%s -i alsa -o %s,%s000 -t:%s" % \
(ss, c, sr, filepath, br, length)
args = command.split(" ") args = command.split(" ")
self.logger.info("starting record") self.logger.info("starting record")
@ -144,17 +147,16 @@ class ShowRecorder(Thread):
artist = "Airtime Show Recorder" artist = "Airtime Show Recorder"
#set some metadata for our file daemon #set some metadata for our file daemon
recorded_file = mutagen.File(filepath, easy=True) recorded_file = mutagen.File(filepath, easy = True)
recorded_file['title'] = name recorded_file['title'] = name
recorded_file['artist'] = artist recorded_file['artist'] = artist
recorded_file['date'] = md[0] recorded_file['date'] = md[0]
#recorded_file['date'] = md[0].split("-")[0] #recorded_file['date'] = md[0].split("-")[0]
#You cannot pass ints into the metadata of a file. Even tracknumber needs to be a string #You cannot pass ints into the metadata of a file. Even tracknumber needs to be a string
recorded_file['tracknumber'] = unicode(self.show_instance) recorded_file['tracknumber'] = unicode(self.show_instance)
recorded_file.save() recorded_file.save()
except Exception, e: except Exception, e:
import traceback
top = traceback.format_exc() top = traceback.format_exc()
self.logger.error('Exception: %s', e) self.logger.error('Exception: %s', e)
self.logger.error("traceback: %s", top) self.logger.error("traceback: %s", top)
@ -180,20 +182,20 @@ class ShowRecorder(Thread):
class Recorder(Thread): class Recorder(Thread):
def __init__(self, q): def __init__(self, q):
Thread.__init__(self) Thread.__init__(self)
self.logger = logging.getLogger('recorder') self.logger = logging.getLogger('recorder')
self.api_client = api_client(self.logger) self.api_client = api_client(self.logger)
self.api_client.register_component("show-recorder") self.sr = None
self.sr = None
self.shows_to_record = {} self.shows_to_record = {}
self.server_timezone = '' self.server_timezone = ''
self.queue = q self.queue = q
self.loops = 0
self.api_client.register_component("show-recorder")
self.logger.info("RecorderFetch: init complete") self.logger.info("RecorderFetch: init complete")
self.loops = 0
def handle_message(self): def handle_message(self):
if not self.queue.empty(): if not self.queue.empty():
message = self.queue.get() message = self.queue.get()
msg = json.loads(message) msg = json.loads(message)
command = msg["event_type"] command = msg["event_type"]
self.logger.info("Received msg from Pypo Message Handler: %s", msg) self.logger.info("Received msg from Pypo Message Handler: %s", msg)
if command == 'cancel_recording': if command == 'cancel_recording':
@ -212,8 +214,8 @@ class Recorder(Thread):
shows = m['shows'] shows = m['shows']
for show in shows: for show in shows:
show_starts = getDateTimeObj(show[u'starts']) show_starts = getDateTimeObj(show[u'starts'])
show_end = getDateTimeObj(show[u'ends']) show_end = getDateTimeObj(show[u'ends'])
time_delta = show_end - show_starts time_delta = show_end - show_starts
temp_shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']] temp_shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']]
self.shows_to_record = temp_shows_to_record self.shows_to_record = temp_shows_to_record
@ -223,10 +225,10 @@ class Recorder(Thread):
tnow = datetime.datetime.utcnow() tnow = datetime.datetime.utcnow()
sorted_show_keys = sorted(self.shows_to_record.keys()) sorted_show_keys = sorted(self.shows_to_record.keys())
start_time = sorted_show_keys[0] start_time = sorted_show_keys[0]
next_show = getDateTimeObj(start_time) next_show = getDateTimeObj(start_time)
delta = next_show - tnow delta = next_show - tnow
s = '%s.%s' % (delta.seconds, delta.microseconds) s = '%s.%s' % (delta.seconds, delta.microseconds)
out = float(s) out = float(s)
@ -237,36 +239,35 @@ class Recorder(Thread):
return out return out
def start_record(self): def start_record(self):
if len(self.shows_to_record) != 0: if len(self.shows_to_record) == 0: return None
try: try:
delta = self.get_time_till_next_show() delta = self.get_time_till_next_show()
if delta < 5: if delta < 5:
self.logger.debug("sleeping %s seconds until show", delta) self.logger.debug("sleeping %s seconds until show", delta)
time.sleep(delta) time.sleep(delta)
sorted_show_keys = sorted(self.shows_to_record.keys()) sorted_show_keys = sorted(self.shows_to_record.keys())
start_time = sorted_show_keys[0] start_time = sorted_show_keys[0]
show_length = self.shows_to_record[start_time][0] show_length = self.shows_to_record[start_time][0]
show_instance = self.shows_to_record[start_time][1] show_instance = self.shows_to_record[start_time][1]
show_name = self.shows_to_record[start_time][2] show_name = self.shows_to_record[start_time][2]
server_timezone = self.shows_to_record[start_time][3] server_timezone = self.shows_to_record[start_time][3]
T = pytz.timezone(server_timezone) T = pytz.timezone(server_timezone)
start_time_on_UTC = getDateTimeObj(start_time) start_time_on_UTC = getDateTimeObj(start_time)
start_time_on_server = start_time_on_UTC.replace(tzinfo=pytz.utc).astimezone(T) start_time_on_server = start_time_on_UTC.replace(tzinfo=pytz.utc).astimezone(T)
start_time_formatted = '%(year)d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(sec)02d' % \ start_time_formatted = '%(year)d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(sec)02d' % \
{'year': start_time_on_server.year, 'month': start_time_on_server.month, 'day': start_time_on_server.day, \ {'year': start_time_on_server.year, 'month': start_time_on_server.month, 'day': start_time_on_server.day, \
'hour': start_time_on_server.hour, 'min': start_time_on_server.minute, 'sec': start_time_on_server.second} 'hour': start_time_on_server.hour, 'min': start_time_on_server.minute, 'sec': start_time_on_server.second}
self.sr = ShowRecorder(show_instance, show_name, show_length.seconds, start_time_formatted) self.sr = ShowRecorder(show_instance, show_name, show_length.seconds, start_time_formatted)
self.sr.start() self.sr.start()
#remove show from shows to record. #remove show from shows to record.
del self.shows_to_record[start_time] del self.shows_to_record[start_time]
#self.time_till_next_show = self.get_time_till_next_show() #self.time_till_next_show = self.get_time_till_next_show()
except Exception, e : except Exception, e :
import traceback top = traceback.format_exc()
top = traceback.format_exc() self.logger.error('Exception: %s', e)
self.logger.error('Exception: %s', e) self.logger.error("traceback: %s", top)
self.logger.error("traceback: %s", top)
""" """
Main loop of the thread: Main loop of the thread:
@ -284,6 +285,7 @@ class Recorder(Thread):
self.process_recorder_schedule(temp) self.process_recorder_schedule(temp)
self.logger.info("Bootstrap recorder schedule received: %s", temp) self.logger.info("Bootstrap recorder schedule received: %s", temp)
except Exception, e: except Exception, e:
self.logger.error( traceback.format_exc() )
self.logger.error(e) self.logger.error(e)
self.logger.info("Bootstrap complete: got initial copy of the schedule") self.logger.info("Bootstrap complete: got initial copy of the schedule")
@ -305,14 +307,15 @@ class Recorder(Thread):
self.process_recorder_schedule(temp) self.process_recorder_schedule(temp)
self.logger.info("updated recorder schedule received: %s", temp) self.logger.info("updated recorder schedule received: %s", temp)
except Exception, e: except Exception, e:
self.logger.error( traceback.format_exc() )
self.logger.error(e) self.logger.error(e)
try: self.handle_message() try: self.handle_message()
except Exception, e: except Exception, e:
self.logger.error( traceback.format_exc() )
self.logger.error('Pypo Recorder Exception: %s', e) self.logger.error('Pypo Recorder Exception: %s', e)
time.sleep(PUSH_INTERVAL) time.sleep(PUSH_INTERVAL)
self.loops += 1 self.loops += 1
except Exception, e : except Exception, e :
import traceback
top = traceback.format_exc() top = traceback.format_exc()
self.logger.error('Exception: %s', e) self.logger.error('Exception: %s', e)
self.logger.error("traceback: %s", top) self.logger.error("traceback: %s", top)