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

This commit is contained in:
James 2012-09-13 17:05:56 -04:00
commit d2f0b10552
6 changed files with 112 additions and 24 deletions

View File

@ -901,7 +901,7 @@ class ApiController extends Zend_Controller_Action
foreach ($data as $pair) { foreach ($data as $pair) {
list($id, $gain) = $pair; list($id, $gain) = $pair;
// TODO : move this code into model -- RG
$file = Application_Model_StoredFile::Recall($p_id = $id)->getPropelOrm(); $file = Application_Model_StoredFile::Recall($p_id = $id)->getPropelOrm();
$file->setDbReplayGain($gain); $file->setDbReplayGain($gain);
$file->save(); $file->save();

View File

@ -24,7 +24,7 @@ class WebstreamController extends Zend_Controller_Action
$webstream = new CcWebstream(); $webstream = new CcWebstream();
//we're not saving this primary key in the DB so it's OK //we're not saving this primary key in the DB so it's OK to be -1
$webstream->setDbId(-1); $webstream->setDbId(-1);
$webstream->setDbName("Untitled Webstream"); $webstream->setDbName("Untitled Webstream");
$webstream->setDbDescription(""); $webstream->setDbDescription("");
@ -34,7 +34,6 @@ class WebstreamController extends Zend_Controller_Action
$webstream->setDbCreatorId($userInfo->id); $webstream->setDbCreatorId($userInfo->id);
$webstream->setDbUtime(new DateTime("now", new DateTimeZone('UTC'))); $webstream->setDbUtime(new DateTime("now", new DateTimeZone('UTC')));
$webstream->setDbMtime(new DateTime("now", new DateTimeZone('UTC'))); $webstream->setDbMtime(new DateTime("now", new DateTimeZone('UTC')));
//$webstream->save();
/* /*
$type = "stream"; $type = "stream";
@ -50,7 +49,7 @@ class WebstreamController extends Zend_Controller_Action
//clear the session in case an old playlist was open: CC-4196 //clear the session in case an old playlist was open: CC-4196
Application_Model_Library::changePlaylist(null, null); Application_Model_Library::changePlaylist(null, null);
$this->view->obj = new Application_Model_Webstream($webstream->getDbId()); $this->view->obj = new Application_Model_Webstream($webstream);
$this->view->action = "new"; $this->view->action = "new";
$this->view->html = $this->view->render('webstream/webstream.phtml'); $this->view->html = $this->view->render('webstream/webstream.phtml');
} }
@ -68,7 +67,7 @@ class WebstreamController extends Zend_Controller_Action
if ($webstream) { if ($webstream) {
Application_Model_Library::changePlaylist($id, "stream"); Application_Model_Library::changePlaylist($id, "stream");
} }
$this->view->obj = new Application_Model_Webstream($webstream->getDbId()); $this->view->obj = new Application_Model_Webstream($webstream);
$this->view->action = "edit"; $this->view->action = "edit";
$this->view->html = $this->view->render('webstream/webstream.phtml'); $this->view->html = $this->view->render('webstream/webstream.phtml');
} }

View File

@ -61,11 +61,13 @@ SQL;
} }
/** /**
* Queries the database for the set of schedules one hour before and after the given time. * Queries the database for the set of schedules one hour before
* If a show starts and ends within that time that is considered the current show. Then the * and after the given time. If a show starts and ends within that
* scheduled item before it is the previous show, and the scheduled item after it is the next * time that is considered the current show. Then the scheduled item
* show. This way the dashboard getCurrentPlaylist is very fast. But if any one of the three * before it is the previous show, and the scheduled item after it
* show types are not found through this mechanism a call is made to the old way of querying * is the next show. This way the dashboard getCurrentPlaylist is
* very fast. But if any one of the three show types are not found
* through this mechanism a call is made to the old way of querying
* the database to find the track info. * the database to find the track info.
**/ **/
public static function GetPrevCurrentNext($p_previousShowID, $p_currentShowID, $p_nextShowID, $p_timeNow) public static function GetPrevCurrentNext($p_previousShowID, $p_currentShowID, $p_nextShowID, $p_timeNow)
@ -83,10 +85,16 @@ SQL;
LEFT JOIN cc_show_instances si ON st.instance_id = si.id"; LEFT JOIN cc_show_instances si ON st.instance_id = si.id";
$streamColumns = "ws.name AS artist_name, wm.liquidsoap_data AS track_title, "; $streamColumns = "ws.name AS artist_name, wm.liquidsoap_data AS track_title, ";
$streamJoin = "FROM cc_schedule AS st JOIN cc_webstream ws ON st.stream_id = ws.id $streamJoin = <<<SQL
LEFT JOIN cc_show_instances AS si ON st.instance_id = si.id FROM cc_schedule AS st
LEFT JOIN cc_subjs AS sub on sub.id = ws.creator_id JOIN cc_webstream ws ON st.stream_id = ws.id
LEFT JOIN (SELECT * FROM cc_webstream_metadata ORDER BY start_time DESC LIMIT 1) AS wm on st.id = wm.instance_id"; LEFT JOIN cc_show_instances AS si ON st.instance_id = si.id
LEFT JOIN cc_subjs AS sub ON sub.id = ws.creator_id
LEFT JOIN
(SELECT *
FROM cc_webstream_metadata
ORDER BY start_time DESC LIMIT 1) AS wm ON st.id = wm.instance_id
SQL;
$predicateArr = array(); $predicateArr = array();
$paramMap = array(); $paramMap = array();
@ -478,8 +486,9 @@ SQL;
} }
/** /**
* Compute the difference between two times in the format "HH:MM:SS.mmmmmm". * Compute the difference between two times in the format .
* Note: currently only supports calculating millisec differences. * "HH:MM:SS.mmmmmm" Note: currently only supports calculating .
* millisec differences .
* *
* @param string $p_time1 * @param string $p_time1
* @param string $p_time2 * @param string $p_time2
@ -789,6 +798,30 @@ SQL;
} }
} }
/**
* Purpose of this function is to iterate through the entire
* schedule array that was just built and fix the data up a bit. For
* example, if we have two consecutive webstreams, we don't need the
* first webstream to shutdown the output, when the second one will
* just switch it back on. Preventing this behaviour stops hiccups
* in output sound.
*/
private static function filterData(&$data)
{
$previous_key = null;
$previous_val = null;
foreach ($data as $k => $v) {
if ($v["type"] == "stream_buffer_start"
&& !is_null($previous_val)
&& $previous_val["type"] == "stream_output_end") {
unset($data[$previous_key]);
}
$previous_key = $k;
$previous_val = $v;
}
}
public static function getSchedule($p_fromDateTime = null, $p_toDateTime = null) public static function getSchedule($p_fromDateTime = null, $p_toDateTime = null)
{ {
list($range_start, $range_end) = self::getRangeStartAndEnd($p_fromDateTime, $p_toDateTime); list($range_start, $range_end) = self::getRangeStartAndEnd($p_fromDateTime, $p_toDateTime);
@ -799,6 +832,8 @@ SQL;
self::createInputHarborKickTimes($data, $range_start, $range_end); self::createInputHarborKickTimes($data, $range_start, $range_end);
self::createScheduledEvents($data, $range_start, $range_end); self::createScheduledEvents($data, $range_start, $range_end);
self::filterData($data["media"]);
return $data; return $data;
} }

View File

@ -1,6 +1,9 @@
DELETE FROM cc_pref WHERE keystr = 'system_version'; DELETE FROM cc_pref WHERE keystr = 'system_version';
INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '2.2.0'); INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '2.2.0');
--DELETE user column order prefs, since the number of columns has increased in 2.2
DELETE FROM cc_pref where keystr = 'library_datatable';
DELETE FROM cc_pref where keystr = 'timeline_datatable';
INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s1_name', 'Airtime!', 'string'); INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s1_name', 'Airtime!', 'string');
INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s2_name', '', 'string'); INSERT INTO cc_stream_setting (keyname, value, type) VALUES ('s2_name', '', 'string');

View File

@ -3,6 +3,9 @@ import mutagen
import math import math
import os import os
import copy import copy
import wave
import contextlib
from collections import namedtuple
from mutagen.easymp4 import EasyMP4KeyError from mutagen.easymp4 import EasyMP4KeyError
from media.monitor.exceptions import BadSongFile from media.monitor.exceptions import BadSongFile
@ -41,6 +44,22 @@ airtime2mutagen = {
"MDATA_KEY_COPYRIGHT" : "copyright", "MDATA_KEY_COPYRIGHT" : "copyright",
} }
class FakeMutagen(dict):
"""
Need this fake mutagen object so that airtime_special functions
return a proper default value instead of throwing an exceptions for
files that mutagen doesn't recognize
"""
FakeInfo = namedtuple('FakeInfo','length bitrate')
def __init__(self,path):
self.path = path
self.mime = []
self.info = FakeMutagen.FakeInfo(0.0, '')
dict.__init__(self)
def set_length(self,l):
old_bitrate = self.info.bitrate
self.info = FakeMutagen.FakeInfo(l, old_bitrate)
# Some airtime attributes are special because they must use the mutagen object # Some airtime attributes are special because they must use the mutagen object
# itself to calculate the value that they need. The lambda associated with each # itself to calculate the value that they need. The lambda associated with each
# key should attempt to extract the corresponding value from the mutagen object # key should attempt to extract the corresponding value from the mutagen object
@ -178,14 +197,29 @@ class Metadata(Loggable):
return return
# 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.
if full_mutagen is None: raise BadSongFile(fpath) #if full_mutagen is None: raise BadSongFile(fpath)
if full_mutagen is None: full_mutagen = FakeMutagen(fpath)
self.__metadata = Metadata.airtime_dict(full_mutagen) self.__metadata = Metadata.airtime_dict(full_mutagen)
# 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():
try:
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] = new_val self.__metadata[special_key] = new_val
except Exception as e:
self.logger.info("Could not get special key %s for %s" %
(special_key, fpath))
self.logger.info(str(e))
# Hickity Hackity for .wav files. Properly do this later
if mmp.extension(fpath) == 'wav':
with contextlib.closing(wave.open(fpath,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = frames/float(rate)
full_mutagen.set_length(duration)
# 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

@ -434,7 +434,9 @@ class PypoPush(Thread):
self.start_web_stream_buffer(media_item) self.start_web_stream_buffer(media_item)
self.start_web_stream(media_item) self.start_web_stream(media_item)
elif media_item['type'] == "stream_buffer_end": elif media_item['type'] == "stream_buffer_end":
self.stop_web_stream(media_item) self.stop_web_stream_buffer(media_item)
elif media_item['type'] == "stream_output_end":
self.stop_web_stream_output(media_item)
except Exception, e: except Exception, e:
self.logger.error('Pypo Push Exception: %s', e) self.logger.error('Pypo Push Exception: %s', e)
@ -509,7 +511,7 @@ class PypoPush(Thread):
finally: finally:
self.telnet_lock.release() self.telnet_lock.release()
def stop_web_stream(self, media_item): def stop_web_stream_buffer(self, media_item):
try: try:
self.telnet_lock.acquire() self.telnet_lock.acquire()
tn = telnetlib.Telnet(LS_HOST, LS_PORT) tn = telnetlib.Telnet(LS_HOST, LS_PORT)
@ -519,6 +521,21 @@ class PypoPush(Thread):
self.logger.debug(msg) self.logger.debug(msg)
tn.write(msg) tn.write(msg)
tn.write("exit\n")
self.logger.debug(tn.read_all())
self.current_stream_info = None
except Exception, e:
self.logger.error(str(e))
finally:
self.telnet_lock.release()
def stop_web_stream_output(self, media_item):
try:
self.telnet_lock.acquire()
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
#dynamic_source.stop http://87.230.101.24:80/top100station.mp3
msg = 'dynamic_source.output_stop\n' msg = 'dynamic_source.output_stop\n'
self.logger.debug(msg) self.logger.debug(msg)
tn.write(msg) tn.write(msg)