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

This commit is contained in:
james 2012-09-19 12:51:13 -04:00
commit b2e452acbb
9 changed files with 112 additions and 46 deletions

View File

@ -396,7 +396,6 @@ SQL;
return $possible_ext; return $possible_ext;
} }
// We fallback to guessing the extension from the mimetype if we // We fallback to guessing the extension from the mimetype if we
// cannot extract it from the file name // cannot extract it from the file name
@ -993,15 +992,18 @@ SQL;
/** /**
* *
* Enter description here ... * Enter description here ...
* @param $dir_id - if this is not provided, it returns all files with full path constructed. * @param $dir_id - if this is not provided, it returns all files with full
* path constructed.
*/ */
public static function listAllFiles($dir_id=null, $all) public static function listAllFiles($dir_id=null, $all)
{ {
$con = Propel::getConnection(); $con = Propel::getConnection();
$sql = "SELECT filepath as fp" $sql = <<<SQL
." FROM CC_FILES as f" SELECT filepath AS fp
." WHERE f.directory = :dir_id"; FROM CC_FILES AS f
WHERE f.directory = :dir_id
SQL;
if (!$all) { if (!$all) {
$sql .= " AND f.file_exists = 'TRUE'"; $sql .= " AND f.file_exists = 'TRUE'";

View File

@ -38,6 +38,7 @@ var AIRTIME = (function(AIRTIME) {
"track_num" : "n", "track_num" : "n",
"year" : "n", "year" : "n",
"owner_id" : "s", "owner_id" : "s",
"info_url" : "s",
"replay_gain" : "n" "replay_gain" : "n"
}; };
@ -1041,6 +1042,7 @@ function addQtipToSCIcons(){
*/ */
function validateAdvancedSearch(divs) { function validateAdvancedSearch(divs) {
var valid = true, var valid = true,
allValid = true,
fieldName, fieldName,
fields, fields,
searchTerm = Array(), searchTerm = Array(),
@ -1053,7 +1055,6 @@ function validateAdvancedSearch(divs) {
searchTerm[0] = ""; searchTerm[0] = "";
searchTerm[1] = ""; searchTerm[1] = "";
$.each(divs, function(i, div){ $.each(divs, function(i, div){
fieldName = $(div).children(':nth-child(2)').attr('id'); fieldName = $(div).children(':nth-child(2)').attr('id');
fields = $(div).children().find('input'); fields = $(div).children().find('input');
@ -1085,6 +1086,7 @@ function validateAdvancedSearch(divs) {
//string fields do not need validation //string fields do not need validation
if (searchTermType !== "s") { if (searchTermType !== "s") {
valid = regExpr.test(searchTerm[i]); valid = regExpr.test(searchTerm[i]);
if (!valid) allValid = false;
} }
addRemoveValidationIcons(valid, $(field)); addRemoveValidationIcons(valid, $(field));
@ -1107,13 +1109,9 @@ function validateAdvancedSearch(divs) {
return false; return false;
} }
}); });
if (!valid) {
return false;
}
}); });
return valid; return allValid;
} }
function addRemoveValidationIcons(valid, field) { function addRemoveValidationIcons(valid, field) {

View File

@ -2,6 +2,45 @@ from media.monitor.log import Loggable
from media.monitor.events import DeleteFile from media.monitor.events import DeleteFile
class EventContractor(Loggable): class EventContractor(Loggable):
def __init__(self):
self.store = {}
def event_registered(self, evt):
"""
returns true if the event is registered which means that there is
another "unpacked" event somewhere out there with the same path
"""
return evt.path in self.store
def get_old_event(self, evt):
"""
get the previously registered event with the same path as 'evt'
"""
return self.store[ evt.path ]
def register(self, evt):
if self.event_registered(evt):
ev_proxy = self.get_old_event(evt)
if ev_proxy.same_event(evt):
ev_proxy.merge_proxy(evt)
return False
# delete overrides any other event
elif evt.is_event(DeleteFile):
ev_proxy.merge_proxy(evt)
return False
else:
ev_proxy.run_hook()
ev_proxy.reset_hook()
self.store[ evt.path ] = evt
evt.set_pack_hook( lambda : self.__unregister(evt) )
return True
def __unregister(self, evt):
del self.store[evt.path]
# Delete this class when done using
class EventContractor2(Loggable):
""" """
This class is responsible for "contracting" events together to ease the This class is responsible for "contracting" events together to ease the
load on airtime. It does this by morphing old events into newer ones load on airtime. It does this by morphing old events into newer ones

View File

@ -42,6 +42,43 @@ class EventRegistry(object):
raise Exception("You can instantiate this class. Must only use class \ raise Exception("You can instantiate this class. Must only use class \
methods") methods")
class EventProxy(object):
"""
A container object for instances of BaseEvent (or it's subclasses) used for
event contractor
"""
def __init__(self, orig_evt):
self.orig_evt = orig_evt
self.evt = orig_evt
self.reset_hook()
if hasattr(orig_evt, 'path'): self.path = orig_evt.path
def set_pack_hook(self, l):
self._pack_hook = l
def reset_hook(self):
self._pack_hook = lambda : None
def run_hook(self):
self._pack_hook()
def safe_pack(self):
self.run_hook()
# make sure that cleanup hook is never called twice for the same event
self.reset_hook()
return self.evt.safe_pack()
def merge_proxy(self, proxy):
self.evt = proxy.evt
def is_event(self, real_event):
return isinstance(self.evt, real_event)
def same_event(self, proxy):
return self.evt.__class__ == proxy.evt.__class__
class HasMetaData(object): class HasMetaData(object):
""" """
Any class that inherits from this class gains the metadata attribute that Any class that inherits from this class gains the metadata attribute that
@ -91,6 +128,9 @@ class BaseEvent(Loggable):
""" """
self._pack_hook = k self._pack_hook = k
def proxify(self):
return EventProxy(self)
# As opposed to unsafe_pack... # As opposed to unsafe_pack...
def safe_pack(self): def safe_pack(self):
""" """

View File

@ -490,7 +490,6 @@ def file_playable(pathname):
command = ("airtime-liquidsoap -c 'output.dummy" + \ command = ("airtime-liquidsoap -c 'output.dummy" + \
"(audio_to_stereo(single(\"%s\")))' > /dev/null 2>&1") % \ "(audio_to_stereo(single(\"%s\")))' > /dev/null 2>&1") % \
pathname.replace("'", "'\\''") pathname.replace("'", "'\\''")
print(command)
return True return True
return_code = subprocess.call(command, shell=True) return_code = subprocess.call(command, shell=True)
return (return_code == 0) return (return_code == 0)

View File

@ -8,6 +8,7 @@ from media.monitor.log import Loggable
from media.monitor.exceptions import BadSongFile from media.monitor.exceptions import BadSongFile
from media.monitor.pure import LazyProperty from media.monitor.pure import LazyProperty
from media.monitor.eventcontractor import EventContractor from media.monitor.eventcontractor import EventContractor
from media.monitor.events import EventProxy
import api_clients.api_client as ac import api_clients.api_client as ac
@ -125,7 +126,7 @@ class WatchSyncer(ReportHandler,Loggable):
try: try:
# If there is a strange bug anywhere in the code the next line # If there is a strange bug anywhere in the code the next line
# should be a suspect # should be a suspect
if self.contractor.register(event): self.push_queue( event ) if self.contractor.register(EventProxy(event)): self.push_queue( event )
#self.push_queue( event ) #self.push_queue( event )
except BadSongFile as e: except BadSongFile as e:
self.fatal_exception("Received bas song file '%s'" % e.path, e) self.fatal_exception("Received bas song file '%s'" % e.path, e)

View File

@ -1,19 +1,15 @@
import shutil
import subprocess
# The tests rely on a lot of absolute paths and other garbage so this file # The tests rely on a lot of absolute paths and other garbage so this file
# configures all of that # configures all of that
music_folder = u'/home/rudi/music' music_folder = u'/home/rudi/music'
o_path = u'/home/rudi/throwaway/ACDC_-_Back_In_Black-sample-64kbps.ogg' o_path = u'/home/rudi/throwaway/ACDC_-_Back_In_Black-sample-64kbps.ogg'
watch_path = u'/home/rudi/throwaway/fucking_around/watch/', watch_path = u'/home/rudi/throwaway/fucking_around/watch/',
real_path1 = u'/home/rudi/throwaway/fucking_around/watch/unknown/unknown/ACDC_-_Back_In_Black-sample-64kbps-64kbps.ogg' real_path1 = u'/home/rudi/throwaway/fucking_around/watch/unknown/unknown/ACDC_-_Back_In_Black-sample-64kbps-64kbps.ogg'
opath = u"/home/rudi/Airtime/python_apps/media-monitor2/tests/" opath = u"/home/rudi/Airtime/python_apps/media-monitor2/tests/"
ppath = u"/home/rudi/Airtime/python_apps/media-monitor2/media/" ppath = u"/home/rudi/Airtime/python_apps/media-monitor2/media/"
sample_config = u'/home/rudi/Airtime/python_apps/media-monitor2/tests/api_client.cfg'
real_config = u'/home/rudi/Airtime/python_apps/media-monitor2/tests/live_client.cfg'
api_client_path = '/etc/airtime/api_client.cfg' api_client_path = '/etc/airtime/api_client.cfg'
# holdover from the time we had a special config for testing
if __name__ == "__main__": sample_config = api_client_path
shutil.copy(api_client_path, real_config) real_config = api_client_path
# TODO : fix this to use liberal permissions
subprocess.call(["chown","rudi",real_config])

View File

@ -9,7 +9,8 @@ import prepare_tests
class TestApiClient(unittest.TestCase): class TestApiClient(unittest.TestCase):
def setUp(self): def setUp(self):
test_path = prepare_tests.real_config test_path = prepare_tests.api_client_path
print("Running from api_config: %s" % test_path)
if not os.path.exists(test_path): if not os.path.exists(test_path):
print("path for config does not exist: '%s' % test_path") print("path for config does not exist: '%s' % test_path")
# TODO : is there a cleaner way to exit the unit testing? # TODO : is there a cleaner way to exit the unit testing?

View File

@ -3,48 +3,37 @@ from media.monitor.eventcontractor import EventContractor
#from media.monitor.exceptions import BadSongFile #from media.monitor.exceptions import BadSongFile
from media.monitor.events import FakePyinotify, NewFile, MoveFile, \ from media.monitor.events import FakePyinotify, NewFile, MoveFile, \
DeleteFile DeleteFile
from mock import patch
class TestMMP(unittest.TestCase): class TestMMP(unittest.TestCase):
def test_event_registered(self): def test_event_registered(self):
ev = EventContractor() ev = EventContractor()
e1 = NewFile( FakePyinotify('bullshit.mp3') ) e1 = NewFile( FakePyinotify('bullshit.mp3') ).proxify()
e2 = MoveFile( FakePyinotify('bullshit.mp3') ) e2 = MoveFile( FakePyinotify('bullshit.mp3') ).proxify()
ev.register(e1) ev.register(e1)
self.assertTrue( ev.event_registered(e2) ) self.assertTrue( ev.event_registered(e2) )
def test_get_old_event(self): def test_get_old_event(self):
ev = EventContractor() ev = EventContractor()
e1 = NewFile( FakePyinotify('bullshit.mp3') ) e1 = NewFile( FakePyinotify('bullshit.mp3') ).proxify()
e2 = MoveFile( FakePyinotify('bullshit.mp3') ) e2 = MoveFile( FakePyinotify('bullshit.mp3') ).proxify()
ev.register(e1) ev.register(e1)
self.assertEqual( ev.get_old_event(e2), e1 ) self.assertEqual( ev.get_old_event(e2), e1 )
def test_register(self): def test_register(self):
ev = EventContractor() ev = EventContractor()
e1 = NewFile( FakePyinotify('bullshit.mp3') ) e1 = NewFile( FakePyinotify('bullshit.mp3') ).proxify()
e2 = DeleteFile( FakePyinotify('bullshit.mp3') ) e2 = DeleteFile( FakePyinotify('bullshit.mp3') ).proxify()
self.assertTrue( ev.register(e1) ) self.assertTrue( ev.register(e1) )
# Check that morph_into is called when it should be
with patch.object(NewFile, 'morph_into', return_value='kimchi') \
as mock_method:
ret = ev.register(e2)
self.assertFalse(ret)
mock_method.assert_called_once_with(e2)
# This time we are not patching morph
self.assertFalse( ev.register(e2) ) self.assertFalse( ev.register(e2) )
# We did not an element
self.assertTrue( len(ev.store.keys()) == 1 ) self.assertEqual( len(ev.store.keys()), 1 )
morphed = ev.get_old_event(e2)
self.assertTrue( isinstance(morphed, DeleteFile) )
delete_ev = e1.safe_pack()[0] delete_ev = e1.safe_pack()[0]
self.assertEqual( delete_ev['mode'], u'delete') self.assertEqual( delete_ev['mode'], u'delete')
self.assertTrue( len(ev.store.keys()) == 0 ) self.assertEqual( len(ev.store.keys()), 0 )
e3 = DeleteFile( FakePyinotify('horseshit.mp3') ) e3 = DeleteFile( FakePyinotify('horseshit.mp3') ).proxify()
self.assertTrue( ev.register(e3) ) self.assertTrue( ev.register(e3) )
self.assertTrue( ev.register(e2) ) self.assertTrue( ev.register(e2) )
@ -58,11 +47,12 @@ class TestMMP(unittest.TestCase):
DeleteFile( FakePyinotify(p) ), DeleteFile( FakePyinotify(p) ),
NewFile( FakePyinotify(p) ), NewFile( FakePyinotify(p) ),
NewFile( FakePyinotify(p) ), ] NewFile( FakePyinotify(p) ), ]
events = map(lambda x: x.proxify(), events)
actual_events = [] actual_events = []
for e in events: for e in events:
if ev.register(e): if ev.register(e):
actual_events.append(e) actual_events.append(e)
self.assertEqual( len(ev.store.keys()), 1 ) self.assertEqual( len(ev.store.keys()), 1 )
packed = [ x.safe_pack() for x in actual_events ] #packed = [ x.safe_pack() for x in actual_events ]
if __name__ == '__main__': unittest.main() if __name__ == '__main__': unittest.main()