Merge branch 'devel' of dev.sourcefabric.org:airtime into devel
This commit is contained in:
commit
d2f0b10552
|
@ -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();
|
||||||
|
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
@ -118,8 +126,8 @@ SQL;
|
||||||
$numberOfRows = count($rows);
|
$numberOfRows = count($rows);
|
||||||
|
|
||||||
$results['previous'] = null;
|
$results['previous'] = null;
|
||||||
$results['current'] = null;
|
$results['current'] = null;
|
||||||
$results['next'] = null;
|
$results['next'] = null;
|
||||||
|
|
||||||
$timeNowAsMillis = strtotime($p_timeNow);
|
$timeNowAsMillis = strtotime($p_timeNow);
|
||||||
for ($i = 0; $i < $numberOfRows; ++$i) {
|
for ($i = 0; $i < $numberOfRows; ++$i) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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():
|
||||||
new_val = f(full_mutagen)
|
try:
|
||||||
if new_val is not None:
|
new_val = f(full_mutagen)
|
||||||
self.__metadata[special_key] = new_val
|
if new_val is not None:
|
||||||
|
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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue