cc-3476: dynamic timeout
This commit is contained in:
parent
c301cd5256
commit
1a4e6c563b
2 changed files with 75 additions and 123 deletions
|
@ -676,102 +676,6 @@ class Application_Model_Schedule {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the schedule in json formatted for pypo (the liquidsoap scheduler)
|
|
||||||
*
|
|
||||||
* @param string $p_fromDateTime
|
|
||||||
* In the format "YYYY-MM-DD-HH-mm-SS"
|
|
||||||
* @param string $p_toDateTime
|
|
||||||
* In the format "YYYY-MM-DD-HH-mm-SS"
|
|
||||||
*/
|
|
||||||
public static function GetScheduledPlaylistsOld($p_fromDateTime = null, $p_toDateTime = null)
|
|
||||||
{
|
|
||||||
global $CC_CONFIG, $CC_DBC;
|
|
||||||
|
|
||||||
if (is_null($p_fromDateTime)) {
|
|
||||||
$t1 = new DateTime("@".time());
|
|
||||||
$range_start = $t1->format("Y-m-d H:i:s");
|
|
||||||
} else {
|
|
||||||
$range_start = Application_Model_Schedule::PypoTimeToAirtimeTime($p_fromDateTime);
|
|
||||||
}
|
|
||||||
if (is_null($p_fromDateTime)) {
|
|
||||||
$t2 = new DateTime("@".time());
|
|
||||||
$t2->add(new DateInterval("PT24H"));
|
|
||||||
$range_end = $t2->format("Y-m-d H:i:s");
|
|
||||||
} else {
|
|
||||||
$range_end = Application_Model_Schedule::PypoTimeToAirtimeTime($p_toDateTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scheduler wants everything in a playlist
|
|
||||||
$data = Application_Model_Schedule::GetItems($range_start, $range_end, true);
|
|
||||||
$playlists = array();
|
|
||||||
|
|
||||||
if (is_array($data)){
|
|
||||||
foreach ($data as $dx){
|
|
||||||
$start = $dx['start'];
|
|
||||||
|
|
||||||
//chop off subseconds
|
|
||||||
$start = substr($start, 0, 19);
|
|
||||||
|
|
||||||
//Start time is the array key, needs to be in the format "YYYY-MM-DD-HH-mm-ss"
|
|
||||||
$pkey = Application_Model_Schedule::AirtimeTimeToPypoTime($start);
|
|
||||||
$timestamp = strtotime($start);
|
|
||||||
$playlists[$pkey]['source'] = "PLAYLIST";
|
|
||||||
$playlists[$pkey]['x_ident'] = $dx['group_id'];
|
|
||||||
$playlists[$pkey]['timestamp'] = $timestamp;
|
|
||||||
$playlists[$pkey]['duration'] = $dx['clip_length'];
|
|
||||||
$playlists[$pkey]['played'] = '0';
|
|
||||||
$playlists[$pkey]['schedule_id'] = $dx['group_id'];
|
|
||||||
$playlists[$pkey]['show_name'] = $dx['show_name'];
|
|
||||||
$playlists[$pkey]['show_start'] = Application_Model_Schedule::AirtimeTimeToPypoTime($dx['show_start']);
|
|
||||||
$playlists[$pkey]['show_end'] = Application_Model_Schedule::AirtimeTimeToPypoTime($dx['show_end']);
|
|
||||||
$playlists[$pkey]['user_id'] = 0;
|
|
||||||
$playlists[$pkey]['id'] = $dx['group_id'];
|
|
||||||
$playlists[$pkey]['start'] = Application_Model_Schedule::AirtimeTimeToPypoTime($dx["start"]);
|
|
||||||
$playlists[$pkey]['end'] = Application_Model_Schedule::AirtimeTimeToPypoTime($dx["end"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ksort($playlists);
|
|
||||||
|
|
||||||
foreach ($playlists as &$playlist)
|
|
||||||
{
|
|
||||||
$scheduleGroup = new Application_Model_ScheduleGroup($playlist["schedule_id"]);
|
|
||||||
$items = $scheduleGroup->getItems();
|
|
||||||
$medias = array();
|
|
||||||
foreach ($items as $item)
|
|
||||||
{
|
|
||||||
$storedFile = Application_Model_StoredFile::Recall($item["file_id"]);
|
|
||||||
$uri = $storedFile->getFileUrlUsingConfigAddress();
|
|
||||||
|
|
||||||
$starts = Application_Model_Schedule::AirtimeTimeToPypoTime($item["starts"]);
|
|
||||||
$medias[$starts] = array(
|
|
||||||
'id' => $storedFile->getGunid(),
|
|
||||||
'uri' => $uri,
|
|
||||||
'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]),
|
|
||||||
'fade_out' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_out"]),
|
|
||||||
'fade_cross' => 0,
|
|
||||||
'cue_in' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_in"]),
|
|
||||||
'cue_out' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_out"]),
|
|
||||||
'start' => $starts,
|
|
||||||
'end' => Application_Model_Schedule::AirtimeTimeToPypoTime($item["ends"])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ksort($medias);
|
|
||||||
$playlist['medias'] = $medias;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = array();
|
|
||||||
$result['status'] = array('range' => array('start' => $range_start, 'end' => $range_end),
|
|
||||||
'version' => AIRTIME_REST_VERSION);
|
|
||||||
$result['playlists'] = $playlists;
|
|
||||||
$result['check'] = 1;
|
|
||||||
$result['stream_metadata'] = array();
|
|
||||||
$result['stream_metadata']['format'] = Application_Model_Preference::GetStreamLabelFormat();
|
|
||||||
$result['stream_metadata']['station_name'] = Application_Model_Preference::GetStationName();
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function deleteAll()
|
public static function deleteAll()
|
||||||
{
|
{
|
||||||
global $CC_CONFIG, $CC_DBC;
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
|
@ -68,18 +68,28 @@ class PypoPush(Thread):
|
||||||
|
|
||||||
#We get to the following lines only if a schedule was received.
|
#We get to the following lines only if a schedule was received.
|
||||||
liquidsoap_queue_approx = self.get_queue_items_from_liquidsoap()
|
liquidsoap_queue_approx = self.get_queue_items_from_liquidsoap()
|
||||||
self.handle_new_media_schedule(media_schedule, liquidsoap_queue_approx)
|
|
||||||
next_media_item_chain = self.get_next_schedule_chain(media_schedule)
|
|
||||||
self.logger.debug("Next schedule chain: %s", next_media_item_chain)
|
|
||||||
|
|
||||||
if next_media_item_chain is not None:
|
|
||||||
tnow = datetime.utcnow()
|
chains = self.get_all_chains(media_schedule)
|
||||||
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
current_chain = self.get_current_chain(chains)
|
||||||
time_until_next_play = self.date_interval_to_seconds(chain_start - tnow)
|
if len(current_chain) > 0 and len(liquidsoap_queue_approx) == 0:
|
||||||
self.logger.debug("Blocking %s seconds until show start", time_until_next_play)
|
#Something is scheduled but Liquidsoap is not playing anything!
|
||||||
|
#Need to schedule it immediately
|
||||||
|
modify_cue_point_of_first_link(current_chain)
|
||||||
|
next_media_item_chain = current_chain
|
||||||
|
time_until_next_play = 0
|
||||||
else:
|
else:
|
||||||
self.logger.debug("Blocking indefinitely since no show scheduled next")
|
self.handle_new_media_schedule(media_schedule, liquidsoap_queue_approx)
|
||||||
time_until_next_play = None
|
next_media_item_chain = self.get_next_schedule_chain(media_schedule)
|
||||||
|
self.logger.debug("Next schedule chain: %s", next_media_item_chain)
|
||||||
|
if next_media_item_chain is not None:
|
||||||
|
tnow = datetime.utcnow()
|
||||||
|
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
|
time_until_next_play = self.date_interval_to_seconds(chain_start - tnow)
|
||||||
|
self.logger.debug("Blocking %s seconds until show start", time_until_next_play)
|
||||||
|
else:
|
||||||
|
self.logger.debug("Blocking indefinitely since no show scheduled")
|
||||||
|
time_until_next_play = None
|
||||||
except Empty, e:
|
except Empty, e:
|
||||||
#We only get here when a new chain of tracks are ready to be played.
|
#We only get here when a new chain of tracks are ready to be played.
|
||||||
self.push_to_liquidsoap(next_media_item_chain)
|
self.push_to_liquidsoap(next_media_item_chain)
|
||||||
|
@ -87,7 +97,8 @@ class PypoPush(Thread):
|
||||||
#TODO
|
#TODO
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
next_media_item_chain = self.get_next_schedule_chain(media_schedule)
|
chains = self.get_all_chains(media_schedule)
|
||||||
|
next_media_item_chain = self.get_next_schedule_chain(chains)
|
||||||
if next_media_item_chain is not None:
|
if next_media_item_chain is not None:
|
||||||
tnow = datetime.utcnow()
|
tnow = datetime.utcnow()
|
||||||
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
chain_start = datetime.strptime(next_media_item_chain[0]['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
|
@ -142,7 +153,7 @@ class PypoPush(Thread):
|
||||||
|
|
||||||
return liquidsoap_queue_approx
|
return liquidsoap_queue_approx
|
||||||
|
|
||||||
def handle_new_media_schedule(self, media, liquidsoap_queue_approx):
|
def handle_new_media_schedule(self, media_schedule, liquidsoap_queue_approx):
|
||||||
"""
|
"""
|
||||||
This function's purpose is to gracefully handle situations where
|
This function's purpose is to gracefully handle situations where
|
||||||
Liquidsoap already has a track in its queue, but the schedule
|
Liquidsoap already has a track in its queue, but the schedule
|
||||||
|
@ -156,8 +167,9 @@ class PypoPush(Thread):
|
||||||
iteration = 0
|
iteration = 0
|
||||||
problem_at_iteration = None
|
problem_at_iteration = None
|
||||||
for queue_item in liquidsoap_queue_approx:
|
for queue_item in liquidsoap_queue_approx:
|
||||||
if queue_item['start'] in media.keys():
|
|
||||||
if queue_item['id'] == media['start']['id']:
|
if queue_item['start'] in media_schedule.keys():
|
||||||
|
if queue_item['id'] == media_schedule[queue_item['start']]['id']:
|
||||||
#Everything OK for this iteration.
|
#Everything OK for this iteration.
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -178,21 +190,18 @@ class PypoPush(Thread):
|
||||||
#The first item in the Liquidsoap queue (the one that is currently playing)
|
#The first item in the Liquidsoap queue (the one that is currently playing)
|
||||||
#has changed or been removed from the schedule. We need to clear the entire
|
#has changed or been removed from the schedule. We need to clear the entire
|
||||||
#queue, and push the new schedule
|
#queue, and push the new schedule
|
||||||
|
self.logger.debug("Problem at iteration %s", problem_at_iteration)
|
||||||
self.remove_from_liquidsoap_queue(problem_at_iteration, liquidsoap_queue_approx)
|
self.remove_from_liquidsoap_queue(problem_at_iteration, liquidsoap_queue_approx)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_chains(self, media_schedule):
|
||||||
"""
|
|
||||||
The purpose of this function is to take a look at the last received schedule from
|
|
||||||
pypo-fetch and return the next chain of media_items. A chain is defined as a sequence
|
|
||||||
of media_items where the end time of media_item 'n' is the start time of media_item
|
|
||||||
'n+1'
|
|
||||||
"""
|
|
||||||
def get_next_schedule_chain(self, media_schedule):
|
|
||||||
chains = []
|
chains = []
|
||||||
|
|
||||||
current_chain = []
|
current_chain = []
|
||||||
for mkey in media_schedule:
|
|
||||||
|
sorted_keys = sorted(media_schedule.keys())
|
||||||
|
|
||||||
|
for mkey in sorted_keys:
|
||||||
media_item = media_schedule[mkey]
|
media_item = media_schedule[mkey]
|
||||||
if len(current_chain) == 0:
|
if len(current_chain) == 0:
|
||||||
current_chain.append(media_item)
|
current_chain.append(media_item)
|
||||||
|
@ -207,9 +216,48 @@ class PypoPush(Thread):
|
||||||
if len(current_chain) > 0:
|
if len(current_chain) > 0:
|
||||||
chains.append(current_chain)
|
chains.append(current_chain)
|
||||||
|
|
||||||
self.logger.debug('media_schedule %s', media_schedule)
|
return chains
|
||||||
self.logger.debug("chains %s", chains)
|
|
||||||
|
|
||||||
|
def modify_cue_point_of_first_link(self, chain):
|
||||||
|
tnow = datetime.utcnow()
|
||||||
|
link = chain[0]
|
||||||
|
|
||||||
|
link_start = datetime.strptime(link['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
|
diff = tnow - link_start
|
||||||
|
|
||||||
|
self.logger.debug("media item was supposed to start %s ago. Preparing to start..", diff)
|
||||||
|
link['cue_in'] = link['cue_in'] + diff
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_chain(self, chains):
|
||||||
|
tnow = datetime.utcnow()
|
||||||
|
current_chain = []
|
||||||
|
|
||||||
|
|
||||||
|
for chain in chains:
|
||||||
|
iteration = 0
|
||||||
|
for link in chain:
|
||||||
|
link_start = datetime.strptime(link['start'], "%Y-%m-%d-%H-%M-%S")
|
||||||
|
link_end = datetime.strptime(link['end'], "%Y-%m-%d-%H-%M-%S")
|
||||||
|
|
||||||
|
self.logger.debug("tnow %s, chain_start %s", tnow, link_start)
|
||||||
|
if link_start <= tnow and tnow < link_end:
|
||||||
|
current_chain = chain[iteration:]
|
||||||
|
break
|
||||||
|
iteration += 1
|
||||||
|
|
||||||
|
return current_chain
|
||||||
|
|
||||||
|
"""
|
||||||
|
The purpose of this function is to take a look at the last received schedule from
|
||||||
|
pypo-fetch and return the next chain of media_items. A chain is defined as a sequence
|
||||||
|
of media_items where the end time of media_item 'n' is the start time of media_item
|
||||||
|
'n+1'
|
||||||
|
"""
|
||||||
|
def get_next_schedule_chain(self, chains):
|
||||||
#all media_items are now divided into chains. Let's find the one that
|
#all media_items are now divided into chains. Let's find the one that
|
||||||
#starts closest in the future.
|
#starts closest in the future.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue