Merge branch '2.0.x' into devel
Conflicts: VERSION airtime_mvc/application/models/Schedule.php airtime_mvc/application/models/Show.php airtime_mvc/public/js/airtime/dashboard/helperfunctions.js install_minimal/include/airtime-constants.php python_apps/api_clients/api_client.py python_apps/pypo/pypocli.py python_apps/pypo/pypofetch.py
This commit is contained in:
commit
6f270bfb3d
7
CREDITS
7
CREDITS
|
@ -1,3 +1,10 @@
|
||||||
|
=======
|
||||||
|
CREDITS
|
||||||
|
=======
|
||||||
|
Version 2.0.2
|
||||||
|
-------------
|
||||||
|
Same as previous version.
|
||||||
|
|
||||||
=======
|
=======
|
||||||
CREDITS
|
CREDITS
|
||||||
=======
|
=======
|
||||||
|
|
|
@ -3,24 +3,31 @@
|
||||||
class Application_Model_Nowplaying
|
class Application_Model_Nowplaying
|
||||||
{
|
{
|
||||||
|
|
||||||
private static function CreateHeaderRow($p_showName, $p_showStart, $p_showEnd){
|
private static function CreateHeaderRow($p_showName, $p_showStart, $p_showEnd){
|
||||||
return array("h", "", $p_showStart, $p_showEnd, $p_showName, "", "", "", "", "", "");
|
return array("h", "", $p_showStart, $p_showEnd, $p_showName, "", "", "", "", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function CreateDatatableRows($p_dbRows){
|
private static function CreateDatatableRows($p_dbRows){
|
||||||
$dataTablesRows = array();
|
$dataTablesRows = array();
|
||||||
|
|
||||||
$epochNow = time();
|
$epochNow = time();
|
||||||
|
|
||||||
foreach ($p_dbRows as $dbRow){
|
$lastRow = end($p_dbRows);
|
||||||
|
|
||||||
$showStartDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($dbRow['show_starts']);
|
//Information about show is true for all rows in parameter so only check the last row's show
|
||||||
$showEndDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($dbRow['show_ends']);
|
//start and end times.
|
||||||
$itemStartDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($dbRow['item_starts']);
|
if (isset($lastRow)){
|
||||||
$itemEndDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($dbRow['item_ends']);
|
$showStartDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($lastRow['show_starts']);
|
||||||
|
$showEndDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($lastRow['show_ends']);
|
||||||
$showStarts = $showStartDateTime->format("Y-m-d H:i:s");
|
$showStarts = $showStartDateTime->format("Y-m-d H:i:s");
|
||||||
$showEnds = $showEndDateTime->format("Y-m-d H:i:s");
|
$showEnds = $showEndDateTime->format("Y-m-d H:i:s");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($p_dbRows as $dbRow) {
|
||||||
|
|
||||||
|
$itemStartDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($dbRow['item_starts']);
|
||||||
|
$itemEndDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($dbRow['item_ends']);
|
||||||
|
|
||||||
$itemStarts = $itemStartDateTime->format("Y-m-d H:i:s");
|
$itemStarts = $itemStartDateTime->format("Y-m-d H:i:s");
|
||||||
$itemEnds = $itemEndDateTime->format("Y-m-d H:i:s");
|
$itemEnds = $itemEndDateTime->format("Y-m-d H:i:s");
|
||||||
|
|
||||||
|
@ -44,54 +51,82 @@ class Application_Model_Nowplaying
|
||||||
$dbRow['playlist_name'], $dbRow['show_name'], $status);
|
$dbRow['playlist_name'], $dbRow['show_name'], $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dataTablesRows;
|
//Modify the last entry in the data table to adjust its end time to be equal to the
|
||||||
}
|
//shows end time if it exceeds it.
|
||||||
|
$lastRow = end($dataTablesRows);
|
||||||
|
if (isset($lastRow) && strtotime($showEnds) < strtotime($lastRow[3])){
|
||||||
|
$dataTablesRows[sizeof($dataTablesRows)-1][3] = $showEnds;
|
||||||
|
}
|
||||||
|
return $dataTablesRows;
|
||||||
|
}
|
||||||
|
|
||||||
private static function CreateGapRow($p_gapTime){
|
private static function CreateGapRow($p_gapTime){
|
||||||
return array("g", "", "", "", $p_gapTime, "", "", "", "", "", "");
|
return array("g", "", "", "", $p_gapTime, "", "", "", "", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function CreateRecordingRow($p_showInstance){
|
private static function CreateRecordingRow($p_showInstance){
|
||||||
return array("r", "", "", "", $p_showInstance->getName(), "", "", "", "", "", "");
|
return array("r", "", "", "", $p_showInstance->getName(), "", "", "", "", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetDataGridData($viewType, $dateString){
|
|
||||||
|
|
||||||
if ($viewType == "now"){
|
/*
|
||||||
$dateTime = new DateTime("now", new DateTimeZone("UTC"));
|
* The purpose of this function is to return an array of scheduled
|
||||||
$timeNow = $dateTime->format("Y-m-d H:i:s");
|
* items. There are two parameters. $p_viewType can be either "now"
|
||||||
|
* or "day". If "now", we show all scheduled items in the near future.
|
||||||
|
*
|
||||||
|
* If "day" we need to find what day was requested by the user, and return
|
||||||
|
* scheduled items for that day.
|
||||||
|
*
|
||||||
|
* $p_dateString is only used when $p_viewType is "day" it is in the format
|
||||||
|
* "2012-12-31". In this case it tells us which day to use.
|
||||||
|
*/
|
||||||
|
public static function GetDataGridData($p_viewType, $p_dateString){
|
||||||
|
|
||||||
$startCutoff = 60;
|
if ($p_viewType == "now"){
|
||||||
$endCutoff = 86400; //60*60*24 - seconds in a day
|
$start_dt = new DateTime("now", new DateTimeZone("UTC"));
|
||||||
|
$end_dt = clone $start_dt;
|
||||||
|
|
||||||
|
$start_dt->sub(new DateInterval("PT60S"));
|
||||||
|
$end_dt->add(new DateInterval("PT24H"));
|
||||||
} else {
|
} else {
|
||||||
$date = new Application_Model_DateHelper;
|
//convert to UTC
|
||||||
$time = $date->getTime();
|
$utc_dt = Application_Model_DateHelper::ConvertToUtcDateTime($p_dateString);
|
||||||
$date->setDate($dateString." ".$time);
|
$start_dt = $utc_dt;
|
||||||
$timeNow = $date->getUtcTimestamp();
|
|
||||||
|
$end_dt = clone $utc_dt;
|
||||||
|
$end_dt->add(new DateInterval("PT24H"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$starts = $start_dt->format("Y-m-d H:i:s");
|
||||||
|
$ends = $end_dt->format("Y-m-d H:i:s");
|
||||||
|
|
||||||
$startCutoff = $date->getNowDayStartDiff();
|
$showIds = Application_Model_ShowInstance::GetShowsInstancesIdsInRange($starts, $ends);
|
||||||
$endCutoff = $date->getNowDayEndDiff();
|
|
||||||
|
//get all the pieces to be played between the start cut off and the end cut off.
|
||||||
|
$scheduledItems = Application_Model_Schedule::getScheduleItemsInRange($starts, $ends);
|
||||||
|
|
||||||
|
$orderedScheduledItems;
|
||||||
|
foreach ($scheduledItems as $scheduledItem){
|
||||||
|
$orderedScheduledItems[$scheduledItem['instance_id']][] = $scheduledItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
$showIds = Application_Model_ShowInstance::GetShowsInstancesIdsInRange($timeNow, $startCutoff, $endCutoff);
|
|
||||||
foreach ($showIds as $showId){
|
foreach ($showIds as $showId){
|
||||||
$instanceId = $showId['id'];
|
$instanceId = $showId['id'];
|
||||||
|
|
||||||
|
//gets the show information
|
||||||
$si = new Application_Model_ShowInstance($instanceId);
|
$si = new Application_Model_ShowInstance($instanceId);
|
||||||
|
|
||||||
$showId = $si->getShowId();
|
$showId = $si->getShowId();
|
||||||
$show = new Application_Model_Show($showId);
|
$show = new Application_Model_Show($showId);
|
||||||
|
|
||||||
$showStartDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($si->getShowInstanceStart());
|
$showStartDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($si->getShowInstanceStart());
|
||||||
$showEndDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($si->getShowInstanceEnd());
|
$showEndDateTime = Application_Model_DateHelper::ConvertToLocalDateTime($si->getShowInstanceEnd());
|
||||||
|
|
||||||
//append show header row
|
//append show header row
|
||||||
$data[] = self::CreateHeaderRow($show->getName(), $showStartDateTime->format("Y-m-d H:i:s"), $showEndDateTime->format("Y-m-d H:i:s"));
|
$data[] = self::CreateHeaderRow($show->getName(), $showStartDateTime->format("Y-m-d H:i:s"), $showEndDateTime->format("Y-m-d H:i:s"));
|
||||||
|
|
||||||
$scheduledItems = $si->getScheduleItemsInRange($timeNow, $startCutoff, $endCutoff);
|
$dataTablesRows = self::CreateDatatableRows($orderedScheduledItems[$instanceId]);
|
||||||
$dataTablesRows = self::CreateDatatableRows($scheduledItems);
|
|
||||||
|
|
||||||
//append show audio item rows
|
//append show audio item rows
|
||||||
$data = array_merge($data, $dataTablesRows);
|
$data = array_merge($data, $dataTablesRows);
|
||||||
|
@ -99,11 +134,12 @@ class Application_Model_Nowplaying
|
||||||
//append show gap time row
|
//append show gap time row
|
||||||
$gapTime = self::FormatDuration($si->getShowEndGapTime(), true);
|
$gapTime = self::FormatDuration($si->getShowEndGapTime(), true);
|
||||||
if ($si->isRecorded())
|
if ($si->isRecorded())
|
||||||
$data[] = self::CreateRecordingRow($si);
|
$data[] = self::CreateRecordingRow($si);
|
||||||
else if ($gapTime > 0)
|
else if ($gapTime > 0)
|
||||||
$data[] = self::CreateGapRow($gapTime);
|
$data[] = self::CreateGapRow($gapTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$timeNow = gmdate("Y-m-d H:i:s");
|
||||||
$rows = Application_Model_Show::GetCurrentShow($timeNow);
|
$rows = Application_Model_Show::GetCurrentShow($timeNow);
|
||||||
Application_Model_Show::ConvertToLocalTimeZone($rows, array("starts", "ends", "start_timestamp", "end_timestamp"));
|
Application_Model_Show::ConvertToLocalTimeZone($rows, array("starts", "ends", "start_timestamp", "end_timestamp"));
|
||||||
return array("currentShow"=>$rows, "rows"=>$data);
|
return array("currentShow"=>$rows, "rows"=>$data);
|
||||||
|
|
|
@ -65,18 +65,76 @@ class Application_Model_Schedule {
|
||||||
$date = new Application_Model_DateHelper;
|
$date = new Application_Model_DateHelper;
|
||||||
$timeNow = $date->getTimestamp();
|
$timeNow = $date->getTimestamp();
|
||||||
$utcTimeNow = $date->getUtcTimestamp();
|
$utcTimeNow = $date->getUtcTimestamp();
|
||||||
|
$currentShow = Application_Model_Show::GetCurrentShow($utcTimeNow);
|
||||||
|
$results = Application_Model_Schedule::GetPrevCurrentNext($utcTimeNow);
|
||||||
|
|
||||||
$range = array("env"=>APPLICATION_ENV,
|
$range = array("env"=>APPLICATION_ENV,
|
||||||
"schedulerTime"=>$timeNow,
|
"schedulerTime"=>$timeNow,
|
||||||
"previous"=>Application_Model_Dashboard::GetPreviousItem($utcTimeNow),
|
"previous"=>$results['previous'],
|
||||||
"current"=>Application_Model_Dashboard::GetCurrentItem($utcTimeNow),
|
"current"=>$results['current'],
|
||||||
"next"=>Application_Model_Dashboard::GetNextItem($utcTimeNow),
|
"next"=>$results['next'],
|
||||||
"currentShow"=>Application_Model_Show::GetCurrentShow($utcTimeNow),
|
"currentShow"=>$currentShow,
|
||||||
"nextShow"=>Application_Model_Show::GetNextShows($utcTimeNow, 1),
|
"nextShow"=>"",//Application_Model_Show::GetNextShows($utcTimeNow, 1),
|
||||||
"timezone"=> date("T"),
|
"timezone"=> date("T"),
|
||||||
"timezoneOffset"=> date("Z"));
|
"timezoneOffset"=> date("Z"));
|
||||||
|
|
||||||
return $range;
|
return $range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the database for the set of schedules one hour before and after the given time.
|
||||||
|
* If a show starts and ends within that time that is considered the current show. Then the
|
||||||
|
* scheduled item before it is the previous show, and the scheduled item after it 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.
|
||||||
|
**/
|
||||||
|
public static function GetPrevCurrentNext($p_timeNow)
|
||||||
|
{
|
||||||
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
|
||||||
|
|
||||||
|
$sql = "Select ft.artist_name, ft.track_title, st.starts as starts, st.ends as ends, st.media_item_played as media_item_played
|
||||||
|
FROM cc_schedule st LEFT JOIN cc_files ft ON st.file_id = ft.id LEFT JOIN cc_show_instances sit ON st.instance_id = sit.id
|
||||||
|
WHERE st.starts > (TIMESTAMP '$p_timeNow'-INTERVAL '24 hours') AND st.starts < (TIMESTAMP '$p_timeNow'+INTERVAL '24 hours') AND st.starts < sit.ends
|
||||||
|
ORDER BY st.starts";
|
||||||
|
$row = $CC_DBC->GetAll($sql);
|
||||||
|
|
||||||
|
$numberOfRows = count($row);
|
||||||
|
$results;
|
||||||
|
|
||||||
|
$timeNowAsMillis = strtotime($p_timeNow);
|
||||||
|
for( $i = 0; $i < $numberOfRows && !isset($results); ++$i ){
|
||||||
|
if ((strtotime($row[$i]['starts']) <= $timeNowAsMillis) && (strtotime($row[$i]['ends']) >= $timeNowAsMillis)){
|
||||||
|
if ( $i - 1 >= 0){
|
||||||
|
$results['previous'] = array("name"=>$row[$i-1]["artist_name"]." - ".$row[$i-1]["track_title"],
|
||||||
|
"starts"=>$row[$i-1]["starts"],
|
||||||
|
"ends"=>$row[$i-1]["ends"]);
|
||||||
|
}
|
||||||
|
$results['current'] = array("name"=>$row[$i]["artist_name"]." - ".$row[$i]["track_title"],
|
||||||
|
"starts"=>$row[$i]["starts"],
|
||||||
|
"ends"=>$row[$i]["ends"],
|
||||||
|
"media_item_played"=>$row[$i]["media_item_played"],
|
||||||
|
"record"=>0);
|
||||||
|
if ( isset($row[$i+1])){
|
||||||
|
$results['next'] = array("name"=>$row[$i+1]["artist_name"]." - ".$row[$i+1]["track_title"],
|
||||||
|
"starts"=>$row[$i+1]["starts"],
|
||||||
|
"ends"=>$row[$i+1]["ends"]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($results['previous'])){
|
||||||
|
$results['previous'] = Application_Model_Dashboard::GetPreviousItem($p_timeNow);
|
||||||
|
}
|
||||||
|
if (!isset($results['current'])){
|
||||||
|
$results['current'] = Application_Model_Dashboard::GetCurrentItem($p_timeNow);
|
||||||
|
}
|
||||||
|
if (!isset($results['next'])) {
|
||||||
|
$results['next'] = Application_Model_Dashboard::GetNextItem($p_timeNow);
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
public static function GetLastScheduleItem($p_timeNow){
|
public static function GetLastScheduleItem($p_timeNow){
|
||||||
global $CC_CONFIG, $CC_DBC;
|
global $CC_CONFIG, $CC_DBC;
|
||||||
|
@ -195,23 +253,59 @@ class Application_Model_Schedule {
|
||||||
." WHERE st.starts < si.ends";
|
." WHERE st.starts < si.ends";
|
||||||
|
|
||||||
if ($timePeriod < 0){
|
if ($timePeriod < 0){
|
||||||
$sql .= " AND st.ends < TIMESTAMP '$timeStamp'"
|
$sql .= " AND st.ends < TIMESTAMP '$timeStamp'"
|
||||||
." AND st.ends > (TIMESTAMP '$timeStamp' - INTERVAL '$interval')"
|
." AND st.ends > (TIMESTAMP '$timeStamp' - INTERVAL '$interval')"
|
||||||
." ORDER BY st.starts DESC"
|
." ORDER BY st.starts DESC"
|
||||||
." LIMIT $count";
|
." LIMIT $count";
|
||||||
} else if ($timePeriod == 0){
|
} else if ($timePeriod == 0){
|
||||||
$sql .= " AND st.starts <= TIMESTAMP '$timeStamp'"
|
$sql .= " AND st.starts <= TIMESTAMP '$timeStamp'"
|
||||||
." AND st.ends >= TIMESTAMP '$timeStamp'";
|
." AND st.ends >= TIMESTAMP '$timeStamp'";
|
||||||
} else if ($timePeriod > 0){
|
} else if ($timePeriod > 0){
|
||||||
$sql .= " AND st.starts > TIMESTAMP '$timeStamp'"
|
$sql .= " AND st.starts > TIMESTAMP '$timeStamp'"
|
||||||
." AND st.starts < (TIMESTAMP '$timeStamp' + INTERVAL '$interval')"
|
." AND st.starts < (TIMESTAMP '$timeStamp' + INTERVAL '$interval')"
|
||||||
." ORDER BY st.starts"
|
." ORDER BY st.starts"
|
||||||
." LIMIT $count";
|
." LIMIT $count";
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = $CC_DBC->GetAll($sql);
|
$rows = $CC_DBC->GetAll($sql);
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function getScheduleItemsInRange($starts, $ends)
|
||||||
|
{
|
||||||
|
global $CC_DBC, $CC_CONFIG;
|
||||||
|
|
||||||
|
$sql = "SELECT"
|
||||||
|
." si.starts as show_starts,"
|
||||||
|
." si.ends as show_ends,"
|
||||||
|
." si.rebroadcast as rebroadcast,"
|
||||||
|
." st.starts as item_starts,"
|
||||||
|
." st.ends as item_ends,"
|
||||||
|
." st.clip_length as clip_length,"
|
||||||
|
." ft.track_title as track_title,"
|
||||||
|
." ft.artist_name as artist_name,"
|
||||||
|
." ft.album_title as album_title,"
|
||||||
|
." s.name as show_name,"
|
||||||
|
." si.id as instance_id,"
|
||||||
|
." pt.name as playlist_name"
|
||||||
|
." FROM ".$CC_CONFIG["showInstances"]." si"
|
||||||
|
." LEFT JOIN ".$CC_CONFIG["scheduleTable"]." st"
|
||||||
|
." ON st.instance_id = si.id"
|
||||||
|
." LEFT JOIN ".$CC_CONFIG["playListTable"]." pt"
|
||||||
|
." ON st.playlist_id = pt.id"
|
||||||
|
." LEFT JOIN ".$CC_CONFIG["filesTable"]." ft"
|
||||||
|
." ON st.file_id = ft.id"
|
||||||
|
." LEFT JOIN ".$CC_CONFIG["showTable"]." s"
|
||||||
|
." ON si.show_id = s.id"
|
||||||
|
." WHERE ((si.starts < TIMESTAMP '$starts' AND si.ends > TIMESTAMP '$starts')"
|
||||||
|
." OR (si.starts > TIMESTAMP '$starts' AND si.ends < TIMESTAMP '$ends')"
|
||||||
|
." OR (si.starts < TIMESTAMP '$ends' AND si.ends > TIMESTAMP '$ends'))"
|
||||||
|
." AND (st.starts < si.ends)"
|
||||||
|
." ORDER BY si.id, si.starts, st.starts";
|
||||||
|
|
||||||
|
return $CC_DBC->GetAll($sql);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
@ -295,7 +389,7 @@ class Application_Model_Schedule {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSchduledPlaylistCount(){
|
public static function getSchduledPlaylistCount(){
|
||||||
global $CC_CONFIG, $CC_DBC;
|
global $CC_CONFIG, $CC_DBC;
|
||||||
$sql = "SELECT count(*) as cnt FROM ".$CC_CONFIG['scheduleTable'];
|
$sql = "SELECT count(*) as cnt FROM ".$CC_CONFIG['scheduleTable'];
|
||||||
return $CC_DBC->GetOne($sql);
|
return $CC_DBC->GetOne($sql);
|
||||||
}
|
}
|
||||||
|
@ -635,16 +729,16 @@ class Application_Model_Schedule {
|
||||||
$isSaas = Application_Model_Preference::GetPlanLevel() == 'disabled'?false:true;
|
$isSaas = Application_Model_Preference::GetPlanLevel() == 'disabled'?false:true;
|
||||||
|
|
||||||
$formWhat = new Application_Form_AddShowWhat();
|
$formWhat = new Application_Form_AddShowWhat();
|
||||||
$formWho = new Application_Form_AddShowWho();
|
$formWho = new Application_Form_AddShowWho();
|
||||||
$formWhen = new Application_Form_AddShowWhen();
|
$formWhen = new Application_Form_AddShowWhen();
|
||||||
$formRepeats = new Application_Form_AddShowRepeats();
|
$formRepeats = new Application_Form_AddShowRepeats();
|
||||||
$formStyle = new Application_Form_AddShowStyle();
|
$formStyle = new Application_Form_AddShowStyle();
|
||||||
|
|
||||||
$formWhat->removeDecorator('DtDdWrapper');
|
$formWhat->removeDecorator('DtDdWrapper');
|
||||||
$formWho->removeDecorator('DtDdWrapper');
|
$formWho->removeDecorator('DtDdWrapper');
|
||||||
$formWhen->removeDecorator('DtDdWrapper');
|
$formWhen->removeDecorator('DtDdWrapper');
|
||||||
$formRepeats->removeDecorator('DtDdWrapper');
|
$formRepeats->removeDecorator('DtDdWrapper');
|
||||||
$formStyle->removeDecorator('DtDdWrapper');
|
$formStyle->removeDecorator('DtDdWrapper');
|
||||||
|
|
||||||
$p_view->what = $formWhat;
|
$p_view->what = $formWhat;
|
||||||
$p_view->when = $formWhen;
|
$p_view->when = $formWhen;
|
||||||
|
@ -655,11 +749,11 @@ class Application_Model_Schedule {
|
||||||
$formWhat->populate(array('add_show_id' => '-1'));
|
$formWhat->populate(array('add_show_id' => '-1'));
|
||||||
$formWhen->populate(array('add_show_start_date' => date("Y-m-d"),
|
$formWhen->populate(array('add_show_start_date' => date("Y-m-d"),
|
||||||
'add_show_start_time' => '00:00',
|
'add_show_start_time' => '00:00',
|
||||||
'add_show_end_date_no_repeate' => date("Y-m-d"),
|
'add_show_end_date_no_repeate' => date("Y-m-d"),
|
||||||
'add_show_end_time' => '01:00',
|
'add_show_end_time' => '01:00',
|
||||||
'add_show_duration' => '1h'));
|
'add_show_duration' => '1h'));
|
||||||
|
|
||||||
$formRepeats->populate(array('add_show_end_date' => date("Y-m-d")));
|
$formRepeats->populate(array('add_show_end_date' => date("Y-m-d")));
|
||||||
|
|
||||||
if(!$isSaas){
|
if(!$isSaas){
|
||||||
$formRecord = new Application_Form_AddShowRR();
|
$formRecord = new Application_Form_AddShowRR();
|
||||||
|
@ -677,4 +771,3 @@ class Application_Model_Schedule {
|
||||||
$p_view->addNewShow = true;
|
$p_view->addNewShow = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -239,22 +239,22 @@ class Application_Model_Show {
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($showDays as $showDay) {
|
foreach($showDays as $showDay) {
|
||||||
Logging::log("Local show day is: {$showDay->getDbDay()}");
|
Logging::log("Local show day is: {$showDay->getDbDay()}");
|
||||||
Logging::log("First show day is: {$showDay->getDbFirstShow()}");
|
Logging::log("First show day is: {$showDay->getDbFirstShow()}");
|
||||||
Logging::log("Id show days is: {$showDay->getDbId()}");
|
Logging::log("Id show days is: {$showDay->getDbId()}");
|
||||||
|
|
||||||
if (in_array($showDay->getDbDay(), $p_uncheckedDays)) {
|
if (in_array($showDay->getDbDay(), $p_uncheckedDays)) {
|
||||||
$showDay->reload();
|
$showDay->reload();
|
||||||
//Logging::log("Local show day is: {$showDay->getDbDay()}");
|
//Logging::log("Local show day is: {$showDay->getDbDay()}");
|
||||||
//Logging::log("First show day is: {$showDay->getDbFirstShow()}");
|
//Logging::log("First show day is: {$showDay->getDbFirstShow()}");
|
||||||
//Logging::log("Id show days is: {$showDay->getDbId()}");
|
//Logging::log("Id show days is: {$showDay->getDbId()}");
|
||||||
$startDay = new DateTime("{$showDay->getDbFirstShow()} {$showDay->getDbStartTime()}", new DateTimeZone($showDay->getDbTimezone()));
|
$startDay = new DateTime("{$showDay->getDbFirstShow()} {$showDay->getDbStartTime()}", new DateTimeZone($showDay->getDbTimezone()));
|
||||||
Logging::log("Show start day: {$startDay->format('Y-m-d H:i:s')}");
|
Logging::log("Show start day: {$startDay->format('Y-m-d H:i:s')}");
|
||||||
$startDay->setTimezone(new DateTimeZone("UTC"));
|
$startDay->setTimezone(new DateTimeZone("UTC"));
|
||||||
Logging::log("Show start day UTC: {$startDay->format('Y-m-d H:i:s')}");
|
Logging::log("Show start day UTC: {$startDay->format('Y-m-d H:i:s')}");
|
||||||
$daysRemovedUTC[] = $startDay->format('w');
|
$daysRemovedUTC[] = $startDay->format('w');
|
||||||
Logging::log("UTC show day is: {$startDay->format('w')}");
|
Logging::log("UTC show day is: {$startDay->format('w')}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$uncheckedDaysImploded = implode(",", $daysRemovedUTC);
|
$uncheckedDaysImploded = implode(",", $daysRemovedUTC);
|
||||||
|
@ -867,18 +867,18 @@ class Application_Model_Show {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($p_data['add_show_start_date'] != $this->getStartDate()
|
if ($p_data['add_show_start_date'] != $this->getStartDate()
|
||||||
|| $p_data['add_show_start_time'] != $this->getStartTime()){
|
|| $p_data['add_show_start_time'] != $this->getStartTime()){
|
||||||
//start date/time has changed
|
//start date/time has changed
|
||||||
|
|
||||||
$newDate = strtotime($p_data['add_show_start_date']);
|
$newDate = strtotime($p_data['add_show_start_date']);
|
||||||
$oldDate = strtotime($this->getStartDate());
|
$oldDate = strtotime($this->getStartDate());
|
||||||
if ($newDate > $oldDate){
|
if ($newDate > $oldDate){
|
||||||
$this->removeAllInstancesBeforeDate($p_data['add_show_start_date']);
|
$this->removeAllInstancesBeforeDate($p_data['add_show_start_date']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->updateStartDateTime($p_data, $p_endDate);
|
$this->updateStartDateTime($p_data, $p_endDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if end date for the repeat option has changed. If so, need to take care
|
//Check if end date for the repeat option has changed. If so, need to take care
|
||||||
|
@ -1449,7 +1449,7 @@ class Application_Model_Show {
|
||||||
|
|
||||||
$sql = "SELECT starts, ends, record, rebroadcast, instance_id, show_id, name,
|
$sql = "SELECT starts, ends, record, rebroadcast, instance_id, show_id, name,
|
||||||
color, background_color, file_id, cc_show_instances.id AS instance_id,
|
color, background_color, file_id, cc_show_instances.id AS instance_id,
|
||||||
last_scheduled
|
last_scheduled, time_filled
|
||||||
FROM cc_show_instances
|
FROM cc_show_instances
|
||||||
LEFT JOIN cc_show ON cc_show.id = cc_show_instances.show_id
|
LEFT JOIN cc_show ON cc_show.id = cc_show_instances.show_id
|
||||||
WHERE cc_show_instances.modified_instance = FALSE";
|
WHERE cc_show_instances.modified_instance = FALSE";
|
||||||
|
@ -1541,14 +1541,15 @@ class Application_Model_Show {
|
||||||
* -in UTC time
|
* -in UTC time
|
||||||
* @param boolean $editable
|
* @param boolean $editable
|
||||||
*/
|
*/
|
||||||
public static function getFullCalendarEvents($start, $end, $editable=false)
|
public static function getFullCalendarEvents($p_start, $p_end, $p_editable=false)
|
||||||
{
|
{
|
||||||
|
|
||||||
$events = array();
|
$events = array();
|
||||||
|
|
||||||
$interval = $start->diff($end);
|
$interval = $p_start->diff($p_end);
|
||||||
$days = $interval->format('%a');
|
$days = $interval->format('%a');
|
||||||
|
|
||||||
$shows = Application_Model_Show::getShows($start, $end);
|
$shows = Application_Model_Show::getShows($p_start, $p_end);
|
||||||
|
|
||||||
$today_timestamp = gmdate("Y-m-d H:i:s");
|
$today_timestamp = gmdate("Y-m-d H:i:s");
|
||||||
|
|
||||||
|
@ -1556,20 +1557,32 @@ class Application_Model_Show {
|
||||||
$options = array();
|
$options = array();
|
||||||
|
|
||||||
//only bother calculating percent for week or day view.
|
//only bother calculating percent for week or day view.
|
||||||
if(intval($days) <= 7) {
|
|
||||||
$show_instance = new Application_Model_ShowInstance($show["instance_id"]);
|
if(intval($days) <= 7) {
|
||||||
$options["percent"] = $show_instance->getPercentScheduled();
|
$options["percent"] = Application_Model_Show::getPercentScheduled($show["starts"], $show["ends"], $show["time_filled"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($editable && (strtotime($today_timestamp) < strtotime($show["starts"]))) {
|
if ($p_editable && (strtotime($today_timestamp) < strtotime($show["starts"]))) {
|
||||||
$options["editable"] = true;
|
$options["editable"] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
|
$events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $events;
|
return $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the percentage of a show scheduled given the start and end times in date/time format
|
||||||
|
* and the time_filled as the total time the schow is scheduled for in time format.
|
||||||
|
**/
|
||||||
|
private static function getPercentScheduled($p_starts, $p_ends, $p_time_filled){
|
||||||
|
$durationSeconds = (strtotime($p_ends) - strtotime($p_starts));
|
||||||
|
$time_filled = Application_Model_Schedule::WallTimeToMillisecs($p_time_filled) / 1000;
|
||||||
|
$percent = ceil(( $time_filled / $durationSeconds) * 100);
|
||||||
|
|
||||||
|
return $percent;
|
||||||
|
}
|
||||||
|
|
||||||
private static function makeFullCalendarEvent($show, $options=array())
|
private static function makeFullCalendarEvent($show, $options=array())
|
||||||
{
|
{
|
||||||
|
|
|
@ -677,20 +677,22 @@ class Application_Model_ShowInstance {
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetShowsInstancesIdsInRange($p_timeNow, $p_start, $p_end)
|
public static function GetShowsInstancesIdsInRange($p_start, $p_end)
|
||||||
{
|
{
|
||||||
global $CC_DBC;
|
global $CC_DBC;
|
||||||
|
|
||||||
$sql = "SELECT id FROM cc_show_instances AS si "
|
$sql = "SELECT id FROM cc_show_instances AS si "
|
||||||
."WHERE modified_instance != TRUE AND ("
|
."WHERE modified_instance != TRUE AND ("
|
||||||
."(si.starts < TIMESTAMP '$p_timeNow' - INTERVAL '$p_start seconds' "
|
."(si.starts < TIMESTAMP '$p_start'"
|
||||||
."AND si.ends > TIMESTAMP '$p_timeNow' - INTERVAL '$p_start seconds') "
|
."AND si.ends > TIMESTAMP '$p_start') "
|
||||||
."OR (si.starts > TIMESTAMP '$p_timeNow' - INTERVAL '$p_start seconds' "
|
."OR (si.starts > TIMESTAMP '$p_start' "
|
||||||
."AND si.ends < TIMESTAMP '$p_timeNow' + INTERVAL '$p_end seconds') "
|
."AND si.ends < TIMESTAMP '$p_end') "
|
||||||
."OR (si.starts < TIMESTAMP '$p_timeNow' + INTERVAL '$p_end seconds' "
|
."OR (si.starts < TIMESTAMP '$p_end' "
|
||||||
."AND si.ends > TIMESTAMP '$p_timeNow' + INTERVAL '$p_end seconds') "
|
."AND si.ends > TIMESTAMP '$p_end') "
|
||||||
.") "
|
.") "
|
||||||
." ORDER BY si.starts";
|
." ORDER BY si.starts";
|
||||||
|
|
||||||
|
Logging::debug($sql);
|
||||||
|
|
||||||
$rows = $CC_DBC->GetAll($sql);
|
$rows = $CC_DBC->GetAll($sql);
|
||||||
return $rows;
|
return $rows;
|
||||||
|
@ -732,7 +734,7 @@ class Application_Model_ShowInstance {
|
||||||
|
|
||||||
return $CC_DBC->GetAll($sql);
|
return $CC_DBC->GetAll($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastAudioItemEnd(){
|
public function getLastAudioItemEnd(){
|
||||||
global $CC_DBC;
|
global $CC_DBC;
|
||||||
|
|
||||||
|
|
|
@ -20,27 +20,26 @@ class Airtime_View_Helper_VersionNotify extends Zend_View_Helper_Abstract{
|
||||||
$current = Application_Model_Preference::GetAirtimeVersion();
|
$current = Application_Model_Preference::GetAirtimeVersion();
|
||||||
$latest = Application_Model_Preference::GetLatestVersion();
|
$latest = Application_Model_Preference::GetLatestVersion();
|
||||||
$link = Application_Model_Preference::GetLatestLink();
|
$link = Application_Model_Preference::GetLatestLink();
|
||||||
$pattern = "/^([0-9]+)\.([0-9]+)\.[0-9]+/";
|
$currentExploded = explode('.', $current);
|
||||||
preg_match($pattern, $current, $curMatch);
|
$latestExploded = explode('.', $latest);
|
||||||
preg_match($pattern, $latest, $latestMatch);
|
if(count($currentExploded) != 3 || count($latestExploded) != 3) {
|
||||||
if(count($curMatch) == 0 || count($latestMatch) == 0) {
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate major version diff;
|
// Calculate the version difference;
|
||||||
// Example: if current = 1.9.5 and latest = 3.0.0, major diff = 11
|
// Example: if current = 1.9.5 and latest = 3.0.0, diff = 105
|
||||||
// Note: algorithm assumes the number after 1st dot never goes above 9
|
// Note: algorithm assumes the number after 1st dot never goes above 9
|
||||||
$diff = (intval($latestMatch[1]) * 10 + intval($latestMatch[2]))
|
$versionDifference = (intval($latestExploded[0]) * 100 + intval($latestExploded[1]) *10 + intval($latestExploded[2]))
|
||||||
- (intval($curMatch[1]) * 10 + intval($curMatch[2]));
|
- (intval($currentExploded[0]) * 100 + intval($currentExploded[1] *10 + intval($currentExploded[2])));
|
||||||
|
|
||||||
// Pick icon
|
// Pick icon based on distance this version is to the latest version available
|
||||||
if(($diff == 0 && $current == $latest) || $diff < 0) {
|
if($versionDifference <= 0) {
|
||||||
// current version is up to date
|
// current version is up to date or newer
|
||||||
$class = "uptodate";
|
$class = "uptodate";
|
||||||
} else if($diff <= 2) {
|
} else if($versionDifference < 20) {
|
||||||
// 2 or less major versions back
|
// 2 or less major versions back
|
||||||
$class = "update";
|
$class = "update";
|
||||||
} else if($diff == 3) {
|
} else if($versionDifference < 30) {
|
||||||
// 3 major versions back
|
// 3 major versions back
|
||||||
$class = "update2";
|
$class = "update2";
|
||||||
} else {
|
} else {
|
||||||
|
@ -48,7 +47,7 @@ class Airtime_View_Helper_VersionNotify extends Zend_View_Helper_Abstract{
|
||||||
$class = "outdated";
|
$class = "outdated";
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = "<div id='version-diff' style='display:none'>" . $diff . "</div>"
|
$result = "<div id='version-diff' style='display:none'>" . $versionDifference . "</div>"
|
||||||
. "<div id='version-current' style='display:none'>" . $current . "</div>"
|
. "<div id='version-current' style='display:none'>" . $current . "</div>"
|
||||||
. "<div id='version-latest' style='display:none'>" . $latest . "</div>"
|
. "<div id='version-latest' style='display:none'>" . $latest . "</div>"
|
||||||
. "<div id='version-link' style='display:none'>" . $link . "</div>"
|
. "<div id='version-link' style='display:none'>" . $link . "</div>"
|
||||||
|
|
|
@ -12,3 +12,14 @@ RewriteCond %{REQUEST_FILENAME} -d
|
||||||
RewriteRule ^.*$ - [NC,L]
|
RewriteRule ^.*$ - [NC,L]
|
||||||
RewriteRule ^.*$ index.php [NC,L]
|
RewriteRule ^.*$ index.php [NC,L]
|
||||||
RewriteBase /
|
RewriteBase /
|
||||||
|
|
||||||
|
AddOutputFilterByType DEFLATE text/plain
|
||||||
|
AddOutputFilterByType DEFLATE text/html
|
||||||
|
AddOutputFilterByType DEFLATE text/xml
|
||||||
|
AddOutputFilterByType DEFLATE text/css
|
||||||
|
AddOutputFilterByType DEFLATE application/xml
|
||||||
|
AddOutputFilterByType DEFLATE application/xhtml+xml
|
||||||
|
AddOutputFilterByType DEFLATE application/rss+xml
|
||||||
|
AddOutputFilterByType DEFLATE application/javascript
|
||||||
|
AddOutputFilterByType DEFLATE application/x-javascript
|
||||||
|
AddOutputFilterByType DEFLATE application/json
|
|
@ -0,0 +1,415 @@
|
||||||
|
/*
|
||||||
|
* jPlayer Plugin for jQuery JavaScript Library
|
||||||
|
* http://www.happyworm.com/jquery/jplayer
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 - 2011 Happyworm Ltd
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* - http://www.opensource.org/licenses/mit-license.php
|
||||||
|
* - http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* Author: Mark J Panaghiston
|
||||||
|
* Version: 2.1.0
|
||||||
|
* Date: 1st September 2011
|
||||||
|
*
|
||||||
|
* FlashVars expected: (AS3 property of: loaderInfo.parameters)
|
||||||
|
* id: (URL Encoded: String) Id of jPlayer instance
|
||||||
|
* vol: (Number) Sets the initial volume
|
||||||
|
* muted: (Boolean in a String) Sets the initial muted state
|
||||||
|
* jQuery: (URL Encoded: String) Sets the jQuery var name. Used with: someVar = jQuery.noConflict(true);
|
||||||
|
*
|
||||||
|
* Compiled using: Adobe Flex Compiler (mxmlc) Version 4.5.1 build 21328
|
||||||
|
*/
|
||||||
|
|
||||||
|
package {
|
||||||
|
import flash.system.Security;
|
||||||
|
import flash.external.ExternalInterface;
|
||||||
|
|
||||||
|
import flash.utils.Timer;
|
||||||
|
import flash.events.TimerEvent;
|
||||||
|
|
||||||
|
import flash.text.TextField;
|
||||||
|
import flash.text.TextFormat;
|
||||||
|
|
||||||
|
import flash.events.KeyboardEvent;
|
||||||
|
|
||||||
|
import flash.display.Sprite;
|
||||||
|
import happyworm.jPlayer.*;
|
||||||
|
|
||||||
|
import flash.display.StageAlign;
|
||||||
|
import flash.display.StageScaleMode;
|
||||||
|
import flash.events.Event;
|
||||||
|
import flash.events.MouseEvent;
|
||||||
|
|
||||||
|
import flash.ui.ContextMenu;
|
||||||
|
import flash.ui.ContextMenuItem;
|
||||||
|
import flash.events.ContextMenuEvent;
|
||||||
|
import flash.net.URLRequest;
|
||||||
|
import flash.net.navigateToURL;
|
||||||
|
|
||||||
|
public class Jplayer extends Sprite {
|
||||||
|
private var jQuery:String;
|
||||||
|
private var sentNumberFractionDigits:uint = 2;
|
||||||
|
|
||||||
|
public var commonStatus:JplayerStatus = new JplayerStatus(); // Used for inital ready event so volume is correct.
|
||||||
|
|
||||||
|
private var myInitTimer:Timer = new Timer(100, 0);
|
||||||
|
|
||||||
|
private var myMp3Player:JplayerMp3;
|
||||||
|
private var myMp4Player:JplayerMp4;
|
||||||
|
|
||||||
|
private var isMp3:Boolean = false;
|
||||||
|
private var isVideo:Boolean = false;
|
||||||
|
|
||||||
|
private var txLog:TextField;
|
||||||
|
private var debug:Boolean = false; // Set debug to false for release compile!
|
||||||
|
|
||||||
|
public function Jplayer() {
|
||||||
|
flash.system.Security.allowDomain("*");
|
||||||
|
|
||||||
|
jQuery = loaderInfo.parameters.jQuery + "('#" + loaderInfo.parameters.id + "').jPlayer";
|
||||||
|
commonStatus.volume = Number(loaderInfo.parameters.vol);
|
||||||
|
commonStatus.muted = loaderInfo.parameters.muted == "true";
|
||||||
|
|
||||||
|
stage.scaleMode = StageScaleMode.NO_SCALE;
|
||||||
|
stage.align = StageAlign.TOP_LEFT;
|
||||||
|
stage.addEventListener(Event.RESIZE, resizeHandler);
|
||||||
|
stage.addEventListener(MouseEvent.CLICK, clickHandler);
|
||||||
|
|
||||||
|
var initialVolume:Number = commonStatus.volume;
|
||||||
|
if(commonStatus.muted) {
|
||||||
|
initialVolume = 0;
|
||||||
|
}
|
||||||
|
myMp3Player = new JplayerMp3(initialVolume);
|
||||||
|
addChild(myMp3Player);
|
||||||
|
|
||||||
|
myMp4Player = new JplayerMp4(initialVolume);
|
||||||
|
addChild(myMp4Player);
|
||||||
|
|
||||||
|
setupListeners(!isMp3, isMp3); // Set up the listeners to the default isMp3 state.
|
||||||
|
|
||||||
|
// The ContextMenu only partially works. The menu select events never occur.
|
||||||
|
// Investigated and it is something to do with the way jPlayer inserts the Flash on the page.
|
||||||
|
// A simple test inserting the Jplayer.swf on a page using: 1) SWFObject 2.2 works. 2) AC_FL_RunContent() works.
|
||||||
|
// jPlayer Flash insertion is based on SWFObject 2.2 and the resaon behind this failure is not clear. The Flash insertion HTML on the page looks similar.
|
||||||
|
var myContextMenu:ContextMenu = new ContextMenu();
|
||||||
|
myContextMenu.hideBuiltInItems();
|
||||||
|
var menuItem_jPlayer:ContextMenuItem = new ContextMenuItem("jPlayer " + JplayerStatus.VERSION);
|
||||||
|
var menuItem_happyworm:ContextMenuItem = new ContextMenuItem("© 2009-2011 Happyworm Ltd", true);
|
||||||
|
menuItem_jPlayer.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuSelectHandler_jPlayer);
|
||||||
|
menuItem_happyworm.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuSelectHandler_happyworm);
|
||||||
|
myContextMenu.customItems.push(menuItem_jPlayer, menuItem_happyworm);
|
||||||
|
contextMenu = myContextMenu;
|
||||||
|
|
||||||
|
// Log console for dev compile option: debug
|
||||||
|
if(debug) {
|
||||||
|
txLog = new TextField();
|
||||||
|
txLog.x = 5;
|
||||||
|
txLog.y = 5;
|
||||||
|
txLog.width = 540;
|
||||||
|
txLog.height = 390;
|
||||||
|
txLog.border = true;
|
||||||
|
txLog.background = true;
|
||||||
|
txLog.backgroundColor = 0xEEEEFF;
|
||||||
|
txLog.multiline = true;
|
||||||
|
txLog.text = "jPlayer " + JplayerStatus.VERSION;
|
||||||
|
txLog.visible = false;
|
||||||
|
this.addChild(txLog);
|
||||||
|
this.stage.addEventListener(KeyboardEvent.KEY_UP, keyboardHandler);
|
||||||
|
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.DEBUG_MSG, debugMsgHandler);
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.DEBUG_MSG, debugMsgHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay init() because Firefox 3.5.7+ developed a bug with local testing in Firebug.
|
||||||
|
myInitTimer.addEventListener(TimerEvent.TIMER, init);
|
||||||
|
myInitTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function init(e:TimerEvent):void {
|
||||||
|
myInitTimer.stop();
|
||||||
|
if(ExternalInterface.available) {
|
||||||
|
ExternalInterface.addCallback("fl_setAudio_mp3", fl_setAudio_mp3);
|
||||||
|
ExternalInterface.addCallback("fl_setAudio_m4a", fl_setAudio_m4a);
|
||||||
|
ExternalInterface.addCallback("fl_setVideo_m4v", fl_setVideo_m4v);
|
||||||
|
ExternalInterface.addCallback("fl_clearMedia", fl_clearMedia);
|
||||||
|
ExternalInterface.addCallback("fl_load", fl_load);
|
||||||
|
ExternalInterface.addCallback("fl_play", fl_play);
|
||||||
|
ExternalInterface.addCallback("fl_pause", fl_pause);
|
||||||
|
ExternalInterface.addCallback("fl_play_head", fl_play_head);
|
||||||
|
ExternalInterface.addCallback("fl_volume", fl_volume);
|
||||||
|
ExternalInterface.addCallback("fl_mute", fl_mute);
|
||||||
|
|
||||||
|
ExternalInterface.call(jQuery, "jPlayerFlashEvent", JplayerEvent.JPLAYER_READY, extractStatusData(commonStatus)); // See JplayerStatus() class for version number.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function setupListeners(oldMP3:Boolean, newMP3:Boolean):void {
|
||||||
|
if(oldMP3 != newMP3) {
|
||||||
|
if(newMP3) {
|
||||||
|
listenToMp3(true);
|
||||||
|
listenToMp4(false);
|
||||||
|
} else {
|
||||||
|
listenToMp3(false);
|
||||||
|
listenToMp4(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function listenToMp3(active:Boolean):void {
|
||||||
|
if(active) {
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
|
||||||
|
myMp3Player.addEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
|
||||||
|
} else {
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
|
||||||
|
myMp3Player.removeEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function listenToMp4(active:Boolean):void {
|
||||||
|
if(active) {
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp4Player.addEventListener(JplayerEvent.JPLAYER_LOADEDMETADATA, jPlayerMetaDataHandler); // Note the unique handler
|
||||||
|
} else {
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_ERROR, jPlayerFlashEvent);
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_PROGRESS, jPlayerFlashEvent);
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_TIMEUPDATE, jPlayerFlashEvent);
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_ENDED, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_PLAY, jPlayerFlashEvent);
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_PAUSE, jPlayerFlashEvent);
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_LOADSTART, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_SEEKING, jPlayerFlashEvent);
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_SEEKED, jPlayerFlashEvent);
|
||||||
|
|
||||||
|
myMp4Player.removeEventListener(JplayerEvent.JPLAYER_LOADEDMETADATA, jPlayerMetaDataHandler); // Note the unique handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_setAudio_mp3(src:String):Boolean {
|
||||||
|
if (src != null) {
|
||||||
|
log("fl_setAudio_mp3: "+src);
|
||||||
|
setupListeners(isMp3, true);
|
||||||
|
isMp3 = true;
|
||||||
|
isVideo = false;
|
||||||
|
myMp4Player.clearFile();
|
||||||
|
myMp3Player.setFile(src);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log("fl_setAudio_mp3: null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_setAudio_m4a(src:String):Boolean {
|
||||||
|
if (src != null) {
|
||||||
|
log("fl_setAudio_m4a: "+src);
|
||||||
|
setupListeners(isMp3, false);
|
||||||
|
isMp3 = false;
|
||||||
|
isVideo = false;
|
||||||
|
myMp3Player.clearFile();
|
||||||
|
myMp4Player.setFile(src);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log("fl_setAudio_m4a: null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_setVideo_m4v(src:String):Boolean {
|
||||||
|
if (src != null) {
|
||||||
|
log("fl_setVideo_m4v: "+src);
|
||||||
|
setupListeners(isMp3, false);
|
||||||
|
isMp3 = false;
|
||||||
|
isVideo = true;
|
||||||
|
myMp3Player.clearFile();
|
||||||
|
myMp4Player.setFile(src);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log("fl_setVideo_m4v: null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_clearMedia():void {
|
||||||
|
log("clearMedia.");
|
||||||
|
myMp3Player.clearFile();
|
||||||
|
myMp4Player.clearFile();
|
||||||
|
}
|
||||||
|
private function fl_load():Boolean {
|
||||||
|
log("load.");
|
||||||
|
if(isMp3) {
|
||||||
|
return myMp3Player.load();
|
||||||
|
} else {
|
||||||
|
return myMp4Player.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_play(time:Number = NaN):Boolean {
|
||||||
|
log("play: time = " + time);
|
||||||
|
if(isMp3) {
|
||||||
|
return myMp3Player.play(time * 1000); // Flash uses milliseconds
|
||||||
|
} else {
|
||||||
|
return myMp4Player.play(time * 1000); // Flash uses milliseconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_pause(time:Number = NaN):Boolean {
|
||||||
|
log("pause: time = " + time);
|
||||||
|
if(isMp3) {
|
||||||
|
return myMp3Player.pause(time * 1000); // Flash uses milliseconds
|
||||||
|
} else {
|
||||||
|
return myMp4Player.pause(time * 1000); // Flash uses milliseconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_play_head(percent:Number):Boolean {
|
||||||
|
log("play_head: "+percent+"%");
|
||||||
|
if(isMp3) {
|
||||||
|
return myMp3Player.playHead(percent);
|
||||||
|
} else {
|
||||||
|
return myMp4Player.playHead(percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_volume(v:Number):void {
|
||||||
|
log("volume: "+v);
|
||||||
|
commonStatus.volume = v;
|
||||||
|
if(!commonStatus.muted) {
|
||||||
|
myMp3Player.setVolume(v);
|
||||||
|
myMp4Player.setVolume(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function fl_mute(mute:Boolean):void {
|
||||||
|
log("mute: "+mute);
|
||||||
|
commonStatus.muted = mute;
|
||||||
|
if(mute) {
|
||||||
|
myMp3Player.setVolume(0);
|
||||||
|
myMp4Player.setVolume(0);
|
||||||
|
} else {
|
||||||
|
myMp3Player.setVolume(commonStatus.volume);
|
||||||
|
myMp4Player.setVolume(commonStatus.volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function jPlayerFlashEvent(e:JplayerEvent):void {
|
||||||
|
log("jPlayer Flash Event: " + e.type + ": " + e.target);
|
||||||
|
if(ExternalInterface.available) {
|
||||||
|
ExternalInterface.call(jQuery, "jPlayerFlashEvent", e.type, extractStatusData(e.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function extractStatusData(data:JplayerStatus):Object {
|
||||||
|
var myStatus:Object = {
|
||||||
|
version: JplayerStatus.VERSION,
|
||||||
|
src: data.src,
|
||||||
|
paused: !data.isPlaying, // Changing this name requires inverting all assignments and conditional statements.
|
||||||
|
srcSet: data.srcSet,
|
||||||
|
seekPercent: data.seekPercent,
|
||||||
|
currentPercentRelative: data.currentPercentRelative,
|
||||||
|
currentPercentAbsolute: data.currentPercentAbsolute,
|
||||||
|
currentTime: data.currentTime / 1000, // JavaScript uses seconds
|
||||||
|
duration: data.duration / 1000, // JavaScript uses seconds
|
||||||
|
volume: commonStatus.volume,
|
||||||
|
muted: commonStatus.muted
|
||||||
|
};
|
||||||
|
log("extractStatusData: sp="+myStatus.seekPercent+" cpr="+myStatus.currentPercentRelative+" cpa="+myStatus.currentPercentAbsolute+" ct="+myStatus.currentTime+" d="+myStatus.duration);
|
||||||
|
return myStatus;
|
||||||
|
}
|
||||||
|
private function jPlayerMetaDataHandler(e:JplayerEvent):void {
|
||||||
|
log("jPlayerMetaDataHandler:" + e.target);
|
||||||
|
if(ExternalInterface.available) {
|
||||||
|
resizeHandler(new Event(Event.RESIZE));
|
||||||
|
ExternalInterface.call(jQuery, "jPlayerFlashEvent", e.type, extractStatusData(e.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function resizeHandler(e:Event):void {
|
||||||
|
log("resizeHandler: stageWidth = " + stage.stageWidth + " | stageHeight = " + stage.stageHeight);
|
||||||
|
|
||||||
|
var mediaX:Number = 0;
|
||||||
|
var mediaY:Number = 0;
|
||||||
|
var mediaWidth:Number = 0;
|
||||||
|
var mediaHeight:Number = 0;
|
||||||
|
|
||||||
|
if(stage.stageWidth > 0 && stage.stageHeight > 0 && myMp4Player.myVideo.width > 0 && myMp4Player.myVideo.height > 0) {
|
||||||
|
var aspectRatioStage:Number = stage.stageWidth / stage.stageHeight;
|
||||||
|
var aspectRatioVideo:Number = myMp4Player.myVideo.width / myMp4Player.myVideo.height;
|
||||||
|
if(aspectRatioStage < aspectRatioVideo) {
|
||||||
|
mediaWidth = stage.stageWidth;
|
||||||
|
mediaHeight = stage.stageWidth / aspectRatioVideo;
|
||||||
|
mediaX = 0;
|
||||||
|
mediaY = (stage.stageHeight - mediaHeight) / 2;
|
||||||
|
} else {
|
||||||
|
mediaWidth = stage.stageHeight * aspectRatioVideo;
|
||||||
|
mediaHeight = stage.stageHeight;
|
||||||
|
mediaX = (stage.stageWidth - mediaWidth) / 2;
|
||||||
|
mediaY = 0;
|
||||||
|
}
|
||||||
|
resizeEntity(myMp4Player, mediaX, mediaY, mediaWidth, mediaHeight);
|
||||||
|
}
|
||||||
|
if(debug && stage.stageWidth > 20 && stage.stageHeight > 20) {
|
||||||
|
txLog.width = stage.stageWidth - 10;
|
||||||
|
txLog.height = stage.stageHeight - 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function resizeEntity(entity:Sprite, mediaX:Number, mediaY:Number, mediaWidth:Number, mediaHeight:Number):void {
|
||||||
|
entity.x = mediaX;
|
||||||
|
entity.y = mediaY;
|
||||||
|
entity.width = mediaWidth;
|
||||||
|
entity.height = mediaHeight;
|
||||||
|
}
|
||||||
|
private function clickHandler(e:MouseEvent):void {
|
||||||
|
if(isMp3) {
|
||||||
|
jPlayerFlashEvent(new JplayerEvent(JplayerEvent.JPLAYER_CLICK, myMp3Player.myStatus, "click"))
|
||||||
|
} else {
|
||||||
|
jPlayerFlashEvent(new JplayerEvent(JplayerEvent.JPLAYER_CLICK, myMp4Player.myStatus, "click"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This event is never called. See comments in class constructor.
|
||||||
|
private function menuSelectHandler_jPlayer(e:ContextMenuEvent):void {
|
||||||
|
navigateToURL(new URLRequest("http://jplayer.org/"), "_blank");
|
||||||
|
}
|
||||||
|
// This event is never called. See comments in class constructor.
|
||||||
|
private function menuSelectHandler_happyworm(e:ContextMenuEvent):void {
|
||||||
|
navigateToURL(new URLRequest("http://happyworm.com/"), "_blank");
|
||||||
|
}
|
||||||
|
private function log(t:String):void {
|
||||||
|
if(debug) {
|
||||||
|
txLog.text = t + "\n" + txLog.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function debugMsgHandler(e:JplayerEvent):void {
|
||||||
|
log(e.msg);
|
||||||
|
}
|
||||||
|
private function keyboardHandler(e:KeyboardEvent):void {
|
||||||
|
log("keyboardHandler: e.keyCode = " + e.keyCode);
|
||||||
|
switch(e.keyCode) {
|
||||||
|
case 68 : // d
|
||||||
|
txLog.visible = !txLog.visible;
|
||||||
|
log("Toggled log display: " + txLog.visible);
|
||||||
|
break;
|
||||||
|
case 76 : // l
|
||||||
|
if(e.ctrlKey && e.shiftKey) {
|
||||||
|
txLog.text = "Cleared log.";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* jPlayer Plugin for jQuery JavaScript Library
|
||||||
|
* http://www.happyworm.com/jquery/jplayer
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 - 2011 Happyworm Ltd
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* - http://www.opensource.org/licenses/mit-license.php
|
||||||
|
* - http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* Author: Mark J Panaghiston
|
||||||
|
* Date: 8th August 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
package happyworm.jPlayer {
|
||||||
|
import flash.events.Event;
|
||||||
|
|
||||||
|
public class JplayerEvent extends Event {
|
||||||
|
|
||||||
|
// The event strings must match those in the JavaScript's $.jPlayer.event object
|
||||||
|
|
||||||
|
public static const JPLAYER_READY:String = "jPlayer_ready";
|
||||||
|
public static const JPLAYER_FLASHRESET:String = "jPlayer_flashreset"; // Handled in JavaScript
|
||||||
|
public static const JPLAYER_RESIZE:String = "jPlayer_resize"; // Handled in JavaScript
|
||||||
|
public static const JPLAYER_REPEAT:String = "jPlayer_repeat"; // Handled in JavaScript
|
||||||
|
public static const JPLAYER_CLICK:String = "jPlayer_click";
|
||||||
|
public static const JPLAYER_ERROR:String = "jPlayer_error";
|
||||||
|
public static const JPLAYER_WARNING:String = "jPlayer_warning"; // Currently not used by the flash solution
|
||||||
|
|
||||||
|
public static const JPLAYER_LOADSTART:String = "jPlayer_loadstart";
|
||||||
|
public static const JPLAYER_PROGRESS:String = "jPlayer_progress";
|
||||||
|
public static const JPLAYER_SUSPEND:String = "jPlayer_suspend"; // Not implemented
|
||||||
|
public static const JPLAYER_ABORT:String = "jPlayer_abort"; // Not implemented
|
||||||
|
public static const JPLAYER_EMPTIED:String = "jPlayer_emptied"; // Not implemented
|
||||||
|
public static const JPLAYER_STALLED:String = "jPlayer_stalled"; // Not implemented
|
||||||
|
public static const JPLAYER_PLAY:String = "jPlayer_play";
|
||||||
|
public static const JPLAYER_PAUSE:String = "jPlayer_pause";
|
||||||
|
public static const JPLAYER_LOADEDMETADATA:String = "jPlayer_loadedmetadata"; // MP3 has no equivilent
|
||||||
|
public static const JPLAYER_LOADEDDATA:String = "jPlayer_loadeddata"; // Not implemented
|
||||||
|
public static const JPLAYER_WAITING:String = "jPlayer_waiting"; // Not implemented
|
||||||
|
public static const JPLAYER_PLAYING:String = "jPlayer_playing"; // Not implemented
|
||||||
|
public static const JPLAYER_CANPLAY:String = "jPlayer_canplay"; // Not implemented
|
||||||
|
public static const JPLAYER_CANPLAYTHROUGH:String = "jPlayer_canplaythrough"; // Not implemented
|
||||||
|
public static const JPLAYER_SEEKING:String = "jPlayer_seeking";
|
||||||
|
public static const JPLAYER_SEEKED:String = "jPlayer_seeked";
|
||||||
|
public static const JPLAYER_TIMEUPDATE:String = "jPlayer_timeupdate";
|
||||||
|
public static const JPLAYER_ENDED:String = "jPlayer_ended";
|
||||||
|
public static const JPLAYER_RATECHANGE:String = "jPlayer_ratechange"; // Not implemented
|
||||||
|
public static const JPLAYER_DURATIONCHANGE:String = "jPlayer_durationchange"; // Not implemented
|
||||||
|
public static const JPLAYER_VOLUMECHANGE:String = "jPlayer_volumechange"; // See JavaScript
|
||||||
|
|
||||||
|
// Events used internal to jPlayer's Flash.
|
||||||
|
public static const DEBUG_MSG:String = "debug_msg";
|
||||||
|
|
||||||
|
public var data:JplayerStatus;
|
||||||
|
public var msg:String = ""
|
||||||
|
|
||||||
|
public function JplayerEvent(type:String, data:JplayerStatus, msg:String = "", bubbles:Boolean = false, cancelable:Boolean = false) {
|
||||||
|
super(type, bubbles, cancelable);
|
||||||
|
this.data = data;
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
public override function clone():Event {
|
||||||
|
return new JplayerEvent(type, data, msg, bubbles, cancelable);
|
||||||
|
}
|
||||||
|
public override function toString():String {
|
||||||
|
return formatToString("JplayerEvent", "type", "bubbles", "cancelable", "eventPhase", "data", "msg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,328 @@
|
||||||
|
/*
|
||||||
|
* jPlayer Plugin for jQuery JavaScript Library
|
||||||
|
* http://www.happyworm.com/jquery/jplayer
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 - 2011 Happyworm Ltd
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* - http://www.opensource.org/licenses/mit-license.php
|
||||||
|
* - http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* Author: Mark J Panaghiston
|
||||||
|
* Date: 1st September 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
package happyworm.jPlayer {
|
||||||
|
import flash.display.Sprite;
|
||||||
|
|
||||||
|
import flash.media.Sound;
|
||||||
|
import flash.media.SoundChannel;
|
||||||
|
import flash.media.SoundLoaderContext;
|
||||||
|
import flash.media.SoundTransform;
|
||||||
|
import flash.net.URLRequest;
|
||||||
|
import flash.utils.Timer;
|
||||||
|
import flash.errors.IOError;
|
||||||
|
import flash.events.*;
|
||||||
|
|
||||||
|
public class JplayerMp3 extends Sprite {
|
||||||
|
private var mySound:Sound = new Sound();
|
||||||
|
private var myChannel:SoundChannel = new SoundChannel();
|
||||||
|
private var myContext:SoundLoaderContext = new SoundLoaderContext(3000, false);
|
||||||
|
private var myTransform:SoundTransform = new SoundTransform();
|
||||||
|
private var myRequest:URLRequest = new URLRequest();
|
||||||
|
|
||||||
|
private var timeUpdateTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
|
||||||
|
private var progressTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
|
||||||
|
private var seekingTimer:Timer = new Timer(100, 0); // Internal: How often seeking is checked to see if it is over.
|
||||||
|
|
||||||
|
public var myStatus:JplayerStatus = new JplayerStatus();
|
||||||
|
|
||||||
|
public function JplayerMp3(volume:Number) {
|
||||||
|
timeUpdateTimer.addEventListener(TimerEvent.TIMER, timeUpdateHandler);
|
||||||
|
progressTimer.addEventListener(TimerEvent.TIMER, progressHandler);
|
||||||
|
seekingTimer.addEventListener(TimerEvent.TIMER, seekingHandler);
|
||||||
|
setVolume(volume);
|
||||||
|
}
|
||||||
|
public function setFile(src:String):void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "setFile: " + src));
|
||||||
|
if(myStatus.isPlaying) {
|
||||||
|
myChannel.stop();
|
||||||
|
progressUpdates(false);
|
||||||
|
timeUpdates(false);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mySound.close();
|
||||||
|
} catch (err:IOError) {
|
||||||
|
// Occurs if the file is either yet to be opened or has finished downloading.
|
||||||
|
}
|
||||||
|
mySound = null;
|
||||||
|
mySound = new Sound();
|
||||||
|
mySound.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
|
||||||
|
mySound.addEventListener(Event.OPEN, loadOpen);
|
||||||
|
mySound.addEventListener(Event.COMPLETE, loadComplete);
|
||||||
|
myRequest = new URLRequest(src);
|
||||||
|
myStatus.reset();
|
||||||
|
myStatus.src = src;
|
||||||
|
myStatus.srcSet = true;
|
||||||
|
timeUpdateEvent();
|
||||||
|
}
|
||||||
|
public function clearFile():void {
|
||||||
|
setFile("");
|
||||||
|
myStatus.srcSet = false;
|
||||||
|
}
|
||||||
|
private function errorHandler(err:IOErrorEvent):void {
|
||||||
|
// MP3 player needs to stop progress and timeupdate events as they are started before the error occurs.
|
||||||
|
// NB: The MP4 player works differently and the error occurs before they are started.
|
||||||
|
progressUpdates(false);
|
||||||
|
timeUpdates(false);
|
||||||
|
myStatus.error(); // Resets status except the src, and it sets srcError property.
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ERROR, myStatus));
|
||||||
|
}
|
||||||
|
private function loadOpen(e:Event):void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "loadOpen:"));
|
||||||
|
myStatus.loading();
|
||||||
|
if(myStatus.playOnLoad) {
|
||||||
|
myStatus.playOnLoad = false; // Capture the flag
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART, myStatus)); // So loadstart event happens before play event occurs.
|
||||||
|
play();
|
||||||
|
} else {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART, myStatus));
|
||||||
|
pause();
|
||||||
|
}
|
||||||
|
progressUpdates(true);
|
||||||
|
}
|
||||||
|
private function loadComplete(e:Event):void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "loadComplete:"));
|
||||||
|
myStatus.loaded();
|
||||||
|
progressUpdates(false);
|
||||||
|
progressEvent();
|
||||||
|
}
|
||||||
|
private function soundCompleteHandler(e:Event):void {
|
||||||
|
myStatus.pausePosition = 0;
|
||||||
|
myStatus.isPlaying = false;
|
||||||
|
timeUpdates(false);
|
||||||
|
timeUpdateEvent();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED, myStatus));
|
||||||
|
}
|
||||||
|
private function progressUpdates(active:Boolean):void {
|
||||||
|
// Using a timer rather than Flash's load progress event, because that event gave data at about 200Hz. The 10Hz timer is closer to HTML5 norm.
|
||||||
|
if(active) {
|
||||||
|
progressTimer.start();
|
||||||
|
} else {
|
||||||
|
progressTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function progressHandler(e:TimerEvent):void {
|
||||||
|
progressEvent();
|
||||||
|
}
|
||||||
|
private function progressEvent():void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "progressEvent:"));
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PROGRESS, myStatus));
|
||||||
|
}
|
||||||
|
private function timeUpdates(active:Boolean):void {
|
||||||
|
if(active) {
|
||||||
|
timeUpdateTimer.start();
|
||||||
|
} else {
|
||||||
|
timeUpdateTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function timeUpdateHandler(e:TimerEvent):void {
|
||||||
|
timeUpdateEvent();
|
||||||
|
}
|
||||||
|
private function timeUpdateEvent():void {
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_TIMEUPDATE, myStatus));
|
||||||
|
}
|
||||||
|
private function seeking(active:Boolean):void {
|
||||||
|
if(active) {
|
||||||
|
if(!myStatus.isSeeking) {
|
||||||
|
seekingEvent();
|
||||||
|
seekingTimer.start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seekingTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function seekingHandler(e:TimerEvent):void {
|
||||||
|
if(myStatus.pausePosition <= getDuration()) {
|
||||||
|
seekedEvent();
|
||||||
|
seeking(false);
|
||||||
|
if(myStatus.playOnSeek) {
|
||||||
|
myStatus.playOnSeek = false; // Capture the flag.
|
||||||
|
play();
|
||||||
|
}
|
||||||
|
} else if(myStatus.isLoaded && (myStatus.pausePosition > getDuration())) {
|
||||||
|
// Illegal seek time
|
||||||
|
seeking(false);
|
||||||
|
seekedEvent();
|
||||||
|
pause(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function seekingEvent():void {
|
||||||
|
myStatus.isSeeking = true;
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKING, myStatus));
|
||||||
|
}
|
||||||
|
private function seekedEvent():void {
|
||||||
|
myStatus.isSeeking = false;
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKED, myStatus));
|
||||||
|
}
|
||||||
|
public function load():Boolean {
|
||||||
|
if(myStatus.loadRequired()) {
|
||||||
|
myStatus.startingDownload();
|
||||||
|
mySound.load(myRequest, myContext);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function play(time:Number = NaN):Boolean {
|
||||||
|
var wasPlaying:Boolean = myStatus.isPlaying;
|
||||||
|
|
||||||
|
if(!isNaN(time) && myStatus.srcSet) {
|
||||||
|
if(myStatus.isPlaying) {
|
||||||
|
myChannel.stop();
|
||||||
|
myStatus.isPlaying = false;
|
||||||
|
}
|
||||||
|
myStatus.pausePosition = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(myStatus.isStartingDownload) {
|
||||||
|
myStatus.playOnLoad = true; // Raise flag, captured in loadOpen()
|
||||||
|
return true;
|
||||||
|
} else if(myStatus.loadRequired()) {
|
||||||
|
myStatus.playOnLoad = true; // Raise flag, captured in loadOpen()
|
||||||
|
return load();
|
||||||
|
} else if((myStatus.isLoading || myStatus.isLoaded) && !myStatus.isPlaying) {
|
||||||
|
if(myStatus.isLoaded && myStatus.pausePosition > getDuration()) { // The time is invalid, ie., past the end.
|
||||||
|
myStatus.pausePosition = 0;
|
||||||
|
timeUpdates(false);
|
||||||
|
timeUpdateEvent();
|
||||||
|
if(wasPlaying) { // For when playing and then get a play(huge)
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
|
||||||
|
}
|
||||||
|
} else if(myStatus.pausePosition > getDuration()) {
|
||||||
|
myStatus.playOnSeek = true;
|
||||||
|
seeking(true);
|
||||||
|
} else {
|
||||||
|
myStatus.isPlaying = true; // Set immediately before playing. Could affects events.
|
||||||
|
myChannel = mySound.play(myStatus.pausePosition);
|
||||||
|
myChannel.soundTransform = myTransform;
|
||||||
|
myChannel.addEventListener(Event.SOUND_COMPLETE, soundCompleteHandler);
|
||||||
|
timeUpdates(true);
|
||||||
|
if(!wasPlaying) {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function pause(time:Number = NaN):Boolean {
|
||||||
|
myStatus.playOnLoad = false; // Reset flag in case load/play issued immediately before this command, ie., before loadOpen() event.
|
||||||
|
myStatus.playOnSeek = false; // Reset flag in case play(time) issued before the command and is still seeking to time set.
|
||||||
|
|
||||||
|
var wasPlaying:Boolean = myStatus.isPlaying;
|
||||||
|
|
||||||
|
// To avoid possible loops with timeupdate and pause(time). A pause() does not have the problem.
|
||||||
|
var alreadyPausedAtTime:Boolean = false;
|
||||||
|
if(!isNaN(time) && myStatus.pausePosition == time) {
|
||||||
|
alreadyPausedAtTime = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(myStatus.isPlaying) {
|
||||||
|
myStatus.isPlaying = false;
|
||||||
|
myChannel.stop();
|
||||||
|
if(myChannel.position > 0) { // Required otherwise a fast play then pause causes myChannel.position to equal zero and not the correct value. ie., When it happens leave pausePosition alone.
|
||||||
|
myStatus.pausePosition = myChannel.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isNaN(time) && myStatus.srcSet) {
|
||||||
|
myStatus.pausePosition = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wasPlaying) {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(myStatus.isStartingDownload) {
|
||||||
|
return true;
|
||||||
|
} else if(myStatus.loadRequired()) {
|
||||||
|
if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation.
|
||||||
|
return load();
|
||||||
|
} else {
|
||||||
|
return true; // Technically the pause(0) succeeded. ie., It did nothing, since nothing was required.
|
||||||
|
}
|
||||||
|
} else if(myStatus.isLoading || myStatus.isLoaded) {
|
||||||
|
if(myStatus.isLoaded && myStatus.pausePosition > getDuration()) { // The time is invalid, ie., past the end.
|
||||||
|
myStatus.pausePosition = 0;
|
||||||
|
} else if(myStatus.pausePosition > getDuration()) {
|
||||||
|
seeking(true);
|
||||||
|
}
|
||||||
|
timeUpdates(false);
|
||||||
|
// Need to be careful with timeupdate event, otherwise a pause in a timeupdate event can cause a loop.
|
||||||
|
// Neither pause() nor pause(time) will cause a timeupdate loop.
|
||||||
|
if(wasPlaying || !isNaN(time) && !alreadyPausedAtTime) {
|
||||||
|
timeUpdateEvent();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function playHead(percent:Number):Boolean {
|
||||||
|
var time:Number = percent * getDuration() / 100;
|
||||||
|
if(myStatus.isPlaying || myStatus.playOnLoad || myStatus.playOnSeek) {
|
||||||
|
return play(time);
|
||||||
|
} else {
|
||||||
|
return pause(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function setVolume(v:Number):void {
|
||||||
|
myStatus.volume = v;
|
||||||
|
myTransform.volume = v;
|
||||||
|
myChannel.soundTransform = myTransform;
|
||||||
|
}
|
||||||
|
private function updateStatusValues():void {
|
||||||
|
myStatus.seekPercent = 100 * getLoadRatio();
|
||||||
|
myStatus.currentTime = getCurrentTime();
|
||||||
|
myStatus.currentPercentRelative = 100 * getCurrentRatioRel();
|
||||||
|
myStatus.currentPercentAbsolute = 100 * getCurrentRatioAbs();
|
||||||
|
myStatus.duration = getDuration();
|
||||||
|
}
|
||||||
|
public function getLoadRatio():Number {
|
||||||
|
if((myStatus.isLoading || myStatus.isLoaded) && mySound.bytesTotal > 0) {
|
||||||
|
return mySound.bytesLoaded / mySound.bytesTotal;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getDuration():Number {
|
||||||
|
if(mySound.length > 0) {
|
||||||
|
return mySound.length;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getCurrentTime():Number {
|
||||||
|
if(myStatus.isPlaying) {
|
||||||
|
return myChannel.position;
|
||||||
|
} else {
|
||||||
|
return myStatus.pausePosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getCurrentRatioRel():Number {
|
||||||
|
if((getDuration() > 0) && (getCurrentTime() <= getDuration())) {
|
||||||
|
return getCurrentTime() / getDuration();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getCurrentRatioAbs():Number {
|
||||||
|
return getCurrentRatioRel() * getLoadRatio();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,413 @@
|
||||||
|
/*
|
||||||
|
* jPlayer Plugin for jQuery JavaScript Library
|
||||||
|
* http://www.happyworm.com/jquery/jplayer
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 - 2011 Happyworm Ltd
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* - http://www.opensource.org/licenses/mit-license.php
|
||||||
|
* - http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* Author: Mark J Panaghiston
|
||||||
|
* Date: 7th August 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
package happyworm.jPlayer {
|
||||||
|
import flash.display.Sprite;
|
||||||
|
|
||||||
|
import flash.media.Video;
|
||||||
|
import flash.media.SoundTransform;
|
||||||
|
|
||||||
|
import flash.net.NetConnection;
|
||||||
|
import flash.net.NetStream;
|
||||||
|
|
||||||
|
import flash.utils.Timer;
|
||||||
|
|
||||||
|
import flash.events.NetStatusEvent;
|
||||||
|
import flash.events.SecurityErrorEvent;
|
||||||
|
import flash.events.TimerEvent;
|
||||||
|
|
||||||
|
public class JplayerMp4 extends Sprite {
|
||||||
|
|
||||||
|
public var myVideo:Video = new Video();
|
||||||
|
private var myConnection:NetConnection;
|
||||||
|
private var myStream:NetStream;
|
||||||
|
|
||||||
|
private var myTransform:SoundTransform = new SoundTransform();
|
||||||
|
|
||||||
|
public var myStatus:JplayerStatus = new JplayerStatus();
|
||||||
|
|
||||||
|
private var timeUpdateTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
|
||||||
|
private var progressTimer:Timer = new Timer(250, 0); // Matched to HTML event freq
|
||||||
|
private var seekingTimer:Timer = new Timer(100, 0); // Internal: How often seeking is checked to see if it is over.
|
||||||
|
|
||||||
|
public function JplayerMp4(volume:Number) {
|
||||||
|
myConnection = new NetConnection();
|
||||||
|
myConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
|
||||||
|
myConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
|
||||||
|
myVideo.smoothing = true;
|
||||||
|
this.addChild(myVideo);
|
||||||
|
|
||||||
|
timeUpdateTimer.addEventListener(TimerEvent.TIMER, timeUpdateHandler);
|
||||||
|
progressTimer.addEventListener(TimerEvent.TIMER, progressHandler);
|
||||||
|
seekingTimer.addEventListener(TimerEvent.TIMER, seekingHandler);
|
||||||
|
|
||||||
|
myStatus.volume = volume;
|
||||||
|
}
|
||||||
|
private function progressUpdates(active:Boolean):void {
|
||||||
|
if(active) {
|
||||||
|
progressTimer.start();
|
||||||
|
} else {
|
||||||
|
progressTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function progressHandler(e:TimerEvent):void {
|
||||||
|
if(myStatus.isLoading) {
|
||||||
|
if(getLoadRatio() == 1) { // Close as can get to a loadComplete event since client.onPlayStatus only works with FMS
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "progressHandler: loadComplete"));
|
||||||
|
myStatus.loaded();
|
||||||
|
progressUpdates(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
progressEvent();
|
||||||
|
}
|
||||||
|
private function progressEvent():void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "progressEvent:"));
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PROGRESS, myStatus));
|
||||||
|
}
|
||||||
|
private function timeUpdates(active:Boolean):void {
|
||||||
|
if(active) {
|
||||||
|
timeUpdateTimer.start();
|
||||||
|
} else {
|
||||||
|
timeUpdateTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function timeUpdateHandler(e:TimerEvent):void {
|
||||||
|
timeUpdateEvent();
|
||||||
|
}
|
||||||
|
private function timeUpdateEvent():void {
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_TIMEUPDATE, myStatus));
|
||||||
|
}
|
||||||
|
private function seeking(active:Boolean):void {
|
||||||
|
if(active) {
|
||||||
|
if(!myStatus.isSeeking) {
|
||||||
|
seekingEvent();
|
||||||
|
}
|
||||||
|
seekingTimer.start();
|
||||||
|
} else {
|
||||||
|
if(myStatus.isSeeking) {
|
||||||
|
seekedEvent();
|
||||||
|
}
|
||||||
|
seekingTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function seekingHandler(e:TimerEvent):void {
|
||||||
|
if(getSeekTimeRatio() <= getLoadRatio()) {
|
||||||
|
seeking(false);
|
||||||
|
if(myStatus.playOnSeek) {
|
||||||
|
myStatus.playOnSeek = false; // Capture the flag.
|
||||||
|
play(myStatus.pausePosition); // Must pass time or the seek time is never set.
|
||||||
|
} else {
|
||||||
|
pause(myStatus.pausePosition); // Must pass time or the stream.time is read.
|
||||||
|
}
|
||||||
|
} else if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration) {
|
||||||
|
// Illegal seek time
|
||||||
|
seeking(false);
|
||||||
|
pause(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function seekingEvent():void {
|
||||||
|
myStatus.isSeeking = true;
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKING, myStatus));
|
||||||
|
}
|
||||||
|
private function seekedEvent():void {
|
||||||
|
myStatus.isSeeking = false;
|
||||||
|
updateStatusValues();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_SEEKED, myStatus));
|
||||||
|
}
|
||||||
|
private function netStatusHandler(e:NetStatusEvent):void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "netStatusHandler: '" + e.info.code + "'"));
|
||||||
|
switch(e.info.code) {
|
||||||
|
case "NetConnection.Connect.Success":
|
||||||
|
connectStream();
|
||||||
|
break;
|
||||||
|
case "NetStream.Play.Start":
|
||||||
|
// This event code occurs once, when the media is opened. Equiv to loadOpen() in mp3 player.
|
||||||
|
myStatus.loading();
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADSTART, myStatus));
|
||||||
|
progressUpdates(true);
|
||||||
|
// See onMetaDataHandler() for other condition, since duration is vital.
|
||||||
|
break;
|
||||||
|
case "NetStream.Play.Stop":
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "NetStream.Play.Stop: getDuration() - getCurrentTime() = " + (getDuration() - getCurrentTime())));
|
||||||
|
|
||||||
|
// Check if media is at the end (or close) otherwise this was due to download bandwidth stopping playback. ie., Download is not fast enough.
|
||||||
|
if(Math.abs(getDuration() - getCurrentTime()) < 150) { // Testing found 150ms worked best for M4A files, where playHead(99.9) caused a stuck state due to firing with ~116ms left to play.
|
||||||
|
endedEvent();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "NetStream.Seek.InvalidTime":
|
||||||
|
// Used for capturing invalid set times and clicks on the end of the progress bar.
|
||||||
|
endedEvent();
|
||||||
|
break;
|
||||||
|
case "NetStream.Play.StreamNotFound":
|
||||||
|
myStatus.error(); // Resets status except the src, and it sets srcError property.
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ERROR, myStatus));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// "NetStream.Seek.Notify" event code is not very useful. It occurs after every seek(t) command issued and does not appear to wait for the media to be ready.
|
||||||
|
}
|
||||||
|
private function endedEvent():void {
|
||||||
|
var wasPlaying:Boolean = myStatus.isPlaying;
|
||||||
|
pause(0);
|
||||||
|
timeUpdates(false);
|
||||||
|
timeUpdateEvent();
|
||||||
|
if(wasPlaying) {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_ENDED, myStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function securityErrorHandler(event:SecurityErrorEvent):void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "securityErrorHandler."));
|
||||||
|
}
|
||||||
|
private function connectStream():void {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "connectStream."));
|
||||||
|
var customClient:Object = new Object();
|
||||||
|
customClient.onMetaData = onMetaDataHandler;
|
||||||
|
// customClient.onPlayStatus = onPlayStatusHandler; // According to the forums and my tests, onPlayStatus only works with FMS (Flash Media Server).
|
||||||
|
myStream = null;
|
||||||
|
myStream = new NetStream(myConnection);
|
||||||
|
myStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
|
||||||
|
myStream.client = customClient;
|
||||||
|
myVideo.attachNetStream(myStream);
|
||||||
|
setVolume(myStatus.volume);
|
||||||
|
myStream.play(myStatus.src);
|
||||||
|
}
|
||||||
|
public function setFile(src:String):void {
|
||||||
|
if(myStream != null) {
|
||||||
|
myStream.close();
|
||||||
|
}
|
||||||
|
myVideo.clear();
|
||||||
|
progressUpdates(false);
|
||||||
|
timeUpdates(false);
|
||||||
|
|
||||||
|
myStatus.reset();
|
||||||
|
myStatus.src = src;
|
||||||
|
myStatus.srcSet = true;
|
||||||
|
timeUpdateEvent();
|
||||||
|
}
|
||||||
|
public function clearFile():void {
|
||||||
|
setFile("");
|
||||||
|
myStatus.srcSet = false;
|
||||||
|
}
|
||||||
|
public function load():Boolean {
|
||||||
|
if(myStatus.loadRequired()) {
|
||||||
|
myStatus.startingDownload();
|
||||||
|
myConnection.connect(null);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function play(time:Number = NaN):Boolean {
|
||||||
|
var wasPlaying:Boolean = myStatus.isPlaying;
|
||||||
|
|
||||||
|
if(!isNaN(time) && myStatus.srcSet) {
|
||||||
|
if(myStatus.isPlaying) {
|
||||||
|
myStream.pause();
|
||||||
|
myStatus.isPlaying = false;
|
||||||
|
}
|
||||||
|
myStatus.pausePosition = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(myStatus.isStartingDownload) {
|
||||||
|
myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
|
||||||
|
return true;
|
||||||
|
} else if(myStatus.loadRequired()) {
|
||||||
|
myStatus.playOnLoad = true; // Raise flag, captured in onMetaDataHandler()
|
||||||
|
return load();
|
||||||
|
} else if((myStatus.isLoading || myStatus.isLoaded) && !myStatus.isPlaying) {
|
||||||
|
if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration) { // The time is invalid, ie., past the end.
|
||||||
|
myStream.pause(); // Since it is playing by default at this point.
|
||||||
|
myStatus.pausePosition = 0;
|
||||||
|
myStream.seek(0);
|
||||||
|
timeUpdates(false);
|
||||||
|
timeUpdateEvent();
|
||||||
|
if(wasPlaying) { // For when playing and then get a play(huge)
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
|
||||||
|
}
|
||||||
|
} else if(getSeekTimeRatio() > getLoadRatio()) { // Use an estimate based on the downloaded amount
|
||||||
|
myStatus.playOnSeek = true;
|
||||||
|
seeking(true);
|
||||||
|
myStream.pause(); // Since it is playing by default at this point.
|
||||||
|
} else {
|
||||||
|
if(!isNaN(time)) { // Avoid using seek() when it is already correct.
|
||||||
|
myStream.seek(myStatus.pausePosition/1000); // Since time is in ms and seek() takes seconds
|
||||||
|
}
|
||||||
|
myStatus.isPlaying = true; // Set immediately before playing. Could affects events.
|
||||||
|
myStream.resume();
|
||||||
|
timeUpdates(true);
|
||||||
|
if(!wasPlaying) {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PLAY, myStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function pause(time:Number = NaN):Boolean {
|
||||||
|
myStatus.playOnLoad = false; // Reset flag in case load/play issued immediately before this command, ie., before onMetadata() event.
|
||||||
|
myStatus.playOnSeek = false; // Reset flag in case play(time) issued before the command and is still seeking to time set.
|
||||||
|
|
||||||
|
var wasPlaying:Boolean = myStatus.isPlaying;
|
||||||
|
|
||||||
|
// To avoid possible loops with timeupdate and pause(time). A pause() does not have the problem.
|
||||||
|
var alreadyPausedAtTime:Boolean = false;
|
||||||
|
if(!isNaN(time) && myStatus.pausePosition == time) {
|
||||||
|
alreadyPausedAtTime = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to wait for metadata to load before ever issuing a pause. The metadata handler will call this function if needed, when ready.
|
||||||
|
if(myStream != null && myStatus.metaDataReady) { // myStream is a null until the 1st media is loaded. ie., The 1st ever setMedia being followed by a pause() or pause(t).
|
||||||
|
myStream.pause();
|
||||||
|
}
|
||||||
|
if(myStatus.isPlaying) {
|
||||||
|
myStatus.isPlaying = false;
|
||||||
|
myStatus.pausePosition = myStream.time * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isNaN(time) && myStatus.srcSet) {
|
||||||
|
myStatus.pausePosition = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wasPlaying) {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_PAUSE, myStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(myStatus.isStartingDownload) {
|
||||||
|
return true;
|
||||||
|
} else if(myStatus.loadRequired()) {
|
||||||
|
if(time > 0) { // We do not want the stop() command, which does pause(0), causing a load operation.
|
||||||
|
return load();
|
||||||
|
} else {
|
||||||
|
return true; // Technically the pause(0) succeeded. ie., It did nothing, since nothing was required.
|
||||||
|
}
|
||||||
|
} else if(myStatus.isLoading || myStatus.isLoaded) {
|
||||||
|
if(myStatus.metaDataReady && myStatus.pausePosition > myStatus.duration) { // The time is invalid, ie., past the end.
|
||||||
|
myStatus.pausePosition = 0;
|
||||||
|
myStream.seek(0);
|
||||||
|
seekedEvent(); // Deals with seeking effect when using setMedia() then pause(huge). NB: There is no preceeding seeking event.
|
||||||
|
} else if(!isNaN(time)) {
|
||||||
|
if(getSeekTimeRatio() > getLoadRatio()) { // Use an estimate based on the downloaded amount
|
||||||
|
seeking(true);
|
||||||
|
} else {
|
||||||
|
if(myStatus.metaDataReady) { // Otherwise seek(0) will stop the metadata loading.
|
||||||
|
myStream.seek(myStatus.pausePosition/1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeUpdates(false);
|
||||||
|
// Need to be careful with timeupdate event, otherwise a pause in a timeupdate event can cause a loop.
|
||||||
|
// Neither pause() nor pause(time) will cause a timeupdate loop.
|
||||||
|
if(wasPlaying || !isNaN(time) && !alreadyPausedAtTime) {
|
||||||
|
timeUpdateEvent();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function playHead(percent:Number):Boolean {
|
||||||
|
var time:Number = percent * getDuration() * getLoadRatio() / 100;
|
||||||
|
if(myStatus.isPlaying || myStatus.playOnLoad || myStatus.playOnSeek) {
|
||||||
|
return play(time);
|
||||||
|
} else {
|
||||||
|
return pause(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function setVolume(v:Number):void {
|
||||||
|
myStatus.volume = v;
|
||||||
|
myTransform.volume = v;
|
||||||
|
if(myStream != null) {
|
||||||
|
myStream.soundTransform = myTransform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private function updateStatusValues():void {
|
||||||
|
myStatus.seekPercent = 100 * getLoadRatio();
|
||||||
|
myStatus.currentTime = getCurrentTime();
|
||||||
|
myStatus.currentPercentRelative = 100 * getCurrentRatioRel();
|
||||||
|
myStatus.currentPercentAbsolute = 100 * getCurrentRatioAbs();
|
||||||
|
myStatus.duration = getDuration();
|
||||||
|
}
|
||||||
|
public function getLoadRatio():Number {
|
||||||
|
if((myStatus.isLoading || myStatus.isLoaded) && myStream.bytesTotal > 0) {
|
||||||
|
return myStream.bytesLoaded / myStream.bytesTotal;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getDuration():Number {
|
||||||
|
return myStatus.duration; // Set from meta data.
|
||||||
|
}
|
||||||
|
public function getCurrentTime():Number {
|
||||||
|
if(myStatus.isPlaying) {
|
||||||
|
return myStream.time * 1000;
|
||||||
|
} else {
|
||||||
|
return myStatus.pausePosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getCurrentRatioRel():Number {
|
||||||
|
if((getLoadRatio() > 0) && (getCurrentRatioAbs() <= getLoadRatio())) {
|
||||||
|
return getCurrentRatioAbs() / getLoadRatio();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getCurrentRatioAbs():Number {
|
||||||
|
if(getDuration() > 0) {
|
||||||
|
return getCurrentTime() / getDuration();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function getSeekTimeRatio():Number {
|
||||||
|
if(getDuration() > 0) {
|
||||||
|
return myStatus.pausePosition / getDuration();
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function onMetaDataHandler(info:Object):void { // Used in connectStream() in myStream.client object.
|
||||||
|
// This event occurs when jumping to the start of static files! ie., seek(0) will cause this event to occur.
|
||||||
|
if(!myStatus.metaDataReady) {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "onMetaDataHandler: " + info.duration + " | " + info.width + "x" + info.height));
|
||||||
|
|
||||||
|
myStatus.metaDataReady = true; // Set flag so that this event only effects jPlayer the 1st time.
|
||||||
|
myStatus.metaData = info;
|
||||||
|
myStatus.duration = info.duration * 1000; // Only available via Meta Data.
|
||||||
|
if(info.width != undefined) {
|
||||||
|
myVideo.width = info.width;
|
||||||
|
}
|
||||||
|
if(info.height != undefined) {
|
||||||
|
myVideo.height = info.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(myStatus.playOnLoad) {
|
||||||
|
myStatus.playOnLoad = false; // Capture the flag
|
||||||
|
if(myStatus.pausePosition > 0 ) { // Important for setMedia followed by play(time).
|
||||||
|
play(myStatus.pausePosition);
|
||||||
|
} else {
|
||||||
|
play(); // Not always sending pausePosition avoids the extra seek(0) for a normal play() command.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pause(myStatus.pausePosition); // Always send the pausePosition. Important for setMedia() followed by pause(time). Deals with not reading stream.time with setMedia() and play() immediately followed by stop() or pause(0)
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.JPLAYER_LOADEDMETADATA, myStatus));
|
||||||
|
} else {
|
||||||
|
this.dispatchEvent(new JplayerEvent(JplayerEvent.DEBUG_MSG, myStatus, "onMetaDataHandler: Already read (NO EFFECT)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* jPlayer Plugin for jQuery JavaScript Library
|
||||||
|
* http://www.happyworm.com/jquery/jplayer
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 - 2011 Happyworm Ltd
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* - http://www.opensource.org/licenses/mit-license.php
|
||||||
|
* - http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* Author: Mark J Panaghiston
|
||||||
|
* Date: 1st September 2011
|
||||||
|
*/
|
||||||
|
|
||||||
|
package happyworm.jPlayer {
|
||||||
|
public class JplayerStatus {
|
||||||
|
|
||||||
|
public static const VERSION:String = "2.1.0"; // The version of the Flash jPlayer entity.
|
||||||
|
|
||||||
|
public var volume:Number = 0.5; // Not affected by reset()
|
||||||
|
public var muted:Boolean = false; // Not affected by reset()
|
||||||
|
|
||||||
|
public var src:String;
|
||||||
|
public var srcError:Boolean;
|
||||||
|
|
||||||
|
public var srcSet:Boolean;
|
||||||
|
public var isPlaying:Boolean;
|
||||||
|
public var isSeeking:Boolean;
|
||||||
|
|
||||||
|
public var playOnLoad:Boolean;
|
||||||
|
public var playOnSeek:Boolean;
|
||||||
|
|
||||||
|
public var isStartingDownload:Boolean;
|
||||||
|
public var isLoading:Boolean;
|
||||||
|
public var isLoaded:Boolean;
|
||||||
|
|
||||||
|
public var pausePosition:Number;
|
||||||
|
|
||||||
|
public var seekPercent:Number;
|
||||||
|
public var currentTime:Number;
|
||||||
|
public var currentPercentRelative:Number;
|
||||||
|
public var currentPercentAbsolute:Number;
|
||||||
|
public var duration:Number;
|
||||||
|
|
||||||
|
public var metaDataReady:Boolean;
|
||||||
|
public var metaData:Object;
|
||||||
|
|
||||||
|
public function JplayerStatus() {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
public function reset():void {
|
||||||
|
src = "";
|
||||||
|
srcError = false;
|
||||||
|
|
||||||
|
srcSet = false;
|
||||||
|
isPlaying = false;
|
||||||
|
isSeeking = false;
|
||||||
|
|
||||||
|
playOnLoad = false;
|
||||||
|
playOnSeek = false;
|
||||||
|
|
||||||
|
isStartingDownload = false;
|
||||||
|
isLoading = false;
|
||||||
|
isLoaded = false;
|
||||||
|
|
||||||
|
pausePosition = 0;
|
||||||
|
|
||||||
|
seekPercent = 0;
|
||||||
|
currentTime = 0;
|
||||||
|
currentPercentRelative = 0;
|
||||||
|
currentPercentAbsolute = 0;
|
||||||
|
duration = 0;
|
||||||
|
|
||||||
|
metaDataReady = false;
|
||||||
|
metaData = {};
|
||||||
|
}
|
||||||
|
public function error():void {
|
||||||
|
var srcSaved:String = src;
|
||||||
|
reset();
|
||||||
|
src = srcSaved;
|
||||||
|
srcError = true;
|
||||||
|
}
|
||||||
|
public function loadRequired():Boolean {
|
||||||
|
return (srcSet && !isStartingDownload && !isLoading && !isLoaded);
|
||||||
|
}
|
||||||
|
public function startingDownload():void {
|
||||||
|
isStartingDownload = true;
|
||||||
|
isLoading = false;
|
||||||
|
isLoaded = false;
|
||||||
|
}
|
||||||
|
public function loading():void {
|
||||||
|
isStartingDownload = false;
|
||||||
|
isLoading = true;
|
||||||
|
isLoaded = false;
|
||||||
|
}
|
||||||
|
public function loaded():void {
|
||||||
|
isStartingDownload = false;
|
||||||
|
isLoading = false;
|
||||||
|
isLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
changelog
10
changelog
|
@ -1,3 +1,13 @@
|
||||||
|
2.0.2 - February 28, 2012
|
||||||
|
* Bug Fixes:
|
||||||
|
* Fixed Airtime could stop automatically playing after 24 hours if the web interface isn't used (regression in 2.0.1).
|
||||||
|
* Fixed Airtime could stop automatically recording after 2 hours if the web interface isn't used.
|
||||||
|
* Fixed upgrading from 1.8.2 when the stor directory was a symlink would cause filenames to not be preserved.
|
||||||
|
* Fixed Day View in the Now Playing tab showed some items on incorrect days.
|
||||||
|
* Fixed problems with having an equal '=' sign as an icecast password
|
||||||
|
* Other
|
||||||
|
* Various optimizations to make Airtime feel snappier in the browser. Various views should
|
||||||
|
load much quicker.
|
||||||
2.0.1 - February 14, 2012
|
2.0.1 - February 14, 2012
|
||||||
* Changes:
|
* Changes:
|
||||||
* Widgets should have a version string so users can make sure widgets are up to date
|
* Widgets should have a version string so users can make sure widgets are up to date
|
||||||
|
|
|
@ -617,7 +617,18 @@ class Airtime190Upgrade{
|
||||||
|
|
||||||
echo "Save DbMd to File".PHP_EOL;
|
echo "Save DbMd to File".PHP_EOL;
|
||||||
|
|
||||||
$stor_dir = realpath($values['general']['base_files_dir']."/stor");
|
|
||||||
|
/* Do not use realpath. It expands a symlinked path into its real path
|
||||||
|
* which then causes us problems for string comparisons later on. */
|
||||||
|
//$stor_dir = realpath($values['general']['base_files_dir']."/stor");
|
||||||
|
|
||||||
|
$baseDir = $values['general']['base_files_dir'];
|
||||||
|
if ($baseDir[strlen($baseDir)-1] != '/'){
|
||||||
|
$baseDir.='/';
|
||||||
|
}
|
||||||
|
|
||||||
|
$stor_dir = $baseDir.'stor';
|
||||||
|
|
||||||
|
|
||||||
$files = CcFilesQuery::create()
|
$files = CcFilesQuery::create()
|
||||||
->setFormatter(ModelCriteria::FORMAT_ON_DEMAND)
|
->setFormatter(ModelCriteria::FORMAT_ON_DEMAND)
|
||||||
|
|
|
@ -81,12 +81,25 @@ try:
|
||||||
print " Found %s (%s) on %s architecture" % (fullname, codename, arch)
|
print " Found %s (%s) on %s architecture" % (fullname, codename, arch)
|
||||||
|
|
||||||
print " * Installing Liquidsoap binary"
|
print " * Installing Liquidsoap binary"
|
||||||
if (os.path.exists("%s/liquidsoap_%s_%s"%(PATH_LIQUIDSOAP_BIN, codename, arch))):
|
|
||||||
shutil.copy("%s/liquidsoap_%s_%s"%(PATH_LIQUIDSOAP_BIN, codename, arch), "%s/liquidsoap"%PATH_LIQUIDSOAP_BIN)
|
binary_path = os.path.join(PATH_LIQUIDSOAP_BIN, "liquidsoap_%s_%s" % (codename, arch))
|
||||||
else:
|
|
||||||
print "Unsupported system architecture."
|
try:
|
||||||
sys.exit(1)
|
shutil.copy(binary_path, "%s/liquidsoap"%PATH_LIQUIDSOAP_BIN)
|
||||||
|
except IOError, e:
|
||||||
|
"""
|
||||||
|
shutil.copy can throw this exception for two reasons. First reason is that it cannot open the source file.
|
||||||
|
This is when the liquidsoap file we requested does not exist, and therefore tells the user we don't support
|
||||||
|
their OS/System architecture. The second reason for this exception is the shutil.copy cannot open the target file.
|
||||||
|
Since this script is being run as root (and we cannot install to a read-only device), this should never happen. So
|
||||||
|
it is safe to assume this exception is a result of the first case.
|
||||||
|
|
||||||
|
Note: We cannot simply use os.path.exists before this, since it sometimes gives us "false" incorrectly
|
||||||
|
"""
|
||||||
|
print "Unsupported OS/system architecture."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
#generate liquidsoap config file
|
#generate liquidsoap config file
|
||||||
#access the DB and generate liquidsoap.cfg under /etc/airtime/
|
#access the DB and generate liquidsoap.cfg under /etc/airtime/
|
||||||
ac = api_client.api_client_factory(config)
|
ac = api_client.api_client_factory(config)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from threading import Thread
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from Queue import Empty
|
||||||
import filecmp
|
import filecmp
|
||||||
|
|
||||||
from api_clients import api_client
|
from api_clients import api_client
|
||||||
|
@ -478,6 +479,19 @@ class PypoFetch(Thread):
|
||||||
while True:
|
while True:
|
||||||
self.logger.info("Loop #%s", loops)
|
self.logger.info("Loop #%s", loops)
|
||||||
try:
|
try:
|
||||||
|
"""
|
||||||
|
our simple_queue.get() requires a timeout, in which case we
|
||||||
|
fetch the Airtime schedule manually. It is important to fetch
|
||||||
|
the schedule periodically because if we didn't, we would only
|
||||||
|
get schedule updates via RabbitMq if the user was constantly
|
||||||
|
using the Airtime interface.
|
||||||
|
|
||||||
|
If the user is not using the interface, RabbitMq messages are not
|
||||||
|
sent, and we will have very stale (or non-existent!) data about the
|
||||||
|
schedule.
|
||||||
|
|
||||||
|
Currently we are checking every 3600 seconds (1 hour)
|
||||||
|
"""
|
||||||
message = self.fetch_queue.get(block=True, timeout=3600)
|
message = self.fetch_queue.get(block=True, timeout=3600)
|
||||||
self.handle_message(message)
|
self.handle_message(message)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
|
|
@ -1,26 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Copyright (c) 2010 Sourcefabric O.P.S.
|
|
||||||
#
|
|
||||||
# This file is part of the Airtime project.
|
|
||||||
# http://airtime.sourcefabric.org/
|
|
||||||
#
|
|
||||||
# Airtime is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Airtime is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Airtime; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# This script for a correct system environment for Airtime.
|
# This script for a correct system environment for Airtime.
|
||||||
#
|
#
|
||||||
# Absolute path to this script
|
# Absolute path to this script
|
||||||
|
|
|
@ -1,28 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2010 Sourcefabric O.P.S.
|
|
||||||
#
|
|
||||||
# This file is part of the Airtime project.
|
|
||||||
# http://airtime.sourcefabric.org/
|
|
||||||
#
|
|
||||||
# Airtime is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Airtime is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Airtime; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# This script sets stream setting(max bitrate and number of streams in Airtime.
|
|
||||||
#
|
|
||||||
# Absolute path to this script
|
# Absolute path to this script
|
||||||
SCRIPT=`readlink -f $0`
|
SCRIPT=`readlink -f $0`
|
||||||
# Absolute directory this script is in
|
# Absolute directory this script is in
|
||||||
|
|
|
@ -1,31 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2010 Sourcefabric O.P.S.
|
|
||||||
#
|
|
||||||
# This file is part of the Airtime project.
|
|
||||||
# http://airtime.sourcefabric.org/
|
|
||||||
#
|
|
||||||
# Airtime is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Airtime is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Airtime; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# This script imports audio files to the Airtime storageServer.
|
|
||||||
#
|
|
||||||
# To get usage help, try the -h option
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# Determine directories, files
|
# Determine directories, files
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2010 Sourcefabric O.P.S.
|
|
||||||
#
|
|
||||||
# This file is part of the Airtime project.
|
|
||||||
# http://airtime.sourcefabric.org/
|
|
||||||
# To report bugs, send an e-mail to contact@sourcefabric.org
|
|
||||||
#
|
|
||||||
# Airtime is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Airtime is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Airtime; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# This script converts an ICU localization file from Serbian Latin
|
|
||||||
# to Serbian Cyrillic.
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import sys, re, codecs
|
|
||||||
|
|
||||||
usageString = 'Usage: serbianLatinToCyrillicConverter.py' \
|
|
||||||
' inputfile outputfile'
|
|
||||||
|
|
||||||
if len(sys.argv) >= 3:
|
|
||||||
fileNameIn = sys.argv[1]
|
|
||||||
fileNameOut = sys.argv[2]
|
|
||||||
else:
|
|
||||||
print usageString
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
oldLines = codecs.open(fileNameIn, 'r', 'utf-8').readlines()
|
|
||||||
newLines = [ ]
|
|
||||||
|
|
||||||
def cyrillize(word):
|
|
||||||
if re.match(r'#.*#\Z', word):
|
|
||||||
return word
|
|
||||||
|
|
||||||
compound = { u'lj' : u'љ', u'Lj' : u'Љ',
|
|
||||||
u'nj' : u'њ', u'Nj' : u'Њ',
|
|
||||||
u'dž' : u'џ', u'Dž' : u'Џ' }
|
|
||||||
simple = dict(zip(u'abvgdđežzijklmnoprstćufhcčšw',
|
|
||||||
u'абвгдђежзијклмнопрстћуфхцчшв'))
|
|
||||||
simple.update(dict(zip(u'ABVGDĐEŽZIJKLMNOPRSTĆUFHCČŠW',
|
|
||||||
u'АБВГДЂЕЖЗИЈКЛМНОПРСТЋУФХЦЧШВ')))
|
|
||||||
exceptions = { ur'\н' : ur'\n',
|
|
||||||
u'Фаде ин' : u'Фејд ин',
|
|
||||||
u'Фаде оут' : u'Фејд аут',
|
|
||||||
u'фаде ин' : u'фејд ин',
|
|
||||||
u'фаде оут' : u'фејд аут',
|
|
||||||
u'есцапе' : u'ескејп',
|
|
||||||
u'Плаy' : u'Плеј',
|
|
||||||
u'Паусе' : u'Поуз',
|
|
||||||
u'трацк' : u'трак',
|
|
||||||
u'УРИ' : u'URI',
|
|
||||||
u'РДС' : u'RDS',
|
|
||||||
u'БПМ' : u'BPM',
|
|
||||||
u'ИСРЦ' : u'ISRC' }
|
|
||||||
|
|
||||||
for latin, cyrillic in compound.iteritems():
|
|
||||||
word = word.replace(latin, cyrillic)
|
|
||||||
for latin, cyrillic in simple.iteritems():
|
|
||||||
word = word.replace(latin, cyrillic)
|
|
||||||
for bad, good in exceptions.iteritems():
|
|
||||||
word = word.replace(bad, good)
|
|
||||||
|
|
||||||
return word
|
|
||||||
|
|
||||||
for line in oldLines:
|
|
||||||
m = re.match(r'(.*)"(.*)"(.*)\n', line)
|
|
||||||
if m:
|
|
||||||
line = m.groups()[0] + '"' \
|
|
||||||
+ cyrillize(m.groups()[1]) + '"' \
|
|
||||||
+ m.groups()[2] + '\n'
|
|
||||||
|
|
||||||
elif line == 'sr_CS:table\n':
|
|
||||||
line = 'sr_CS_CYRILLIC:table\n'
|
|
||||||
|
|
||||||
newLines += [line]
|
|
||||||
|
|
||||||
codecs.open(fileNameOut, 'w', 'utf-8').writelines(newLines)
|
|
Loading…
Reference in New Issue