Merge branch '2.2.x' of dev.sourcefabric.org:airtime into 2.2.x

This commit is contained in:
Martin Konecny 2012-10-29 16:29:20 -04:00
commit 5f0fd0ef09
8 changed files with 350 additions and 328 deletions

View File

@ -661,6 +661,7 @@ SQL;
$data["media"][$switch_start]['start'] = $switch_start; $data["media"][$switch_start]['start'] = $switch_start;
$data["media"][$switch_start]['end'] = $switch_start; $data["media"][$switch_start]['end'] = $switch_start;
$data["media"][$switch_start]['event_type'] = "switch_off"; $data["media"][$switch_start]['event_type'] = "switch_off";
$data["media"][$kick_start]['type'] = "event";
$data["media"][$switch_start]['independent_event'] = true; $data["media"][$switch_start]['independent_event'] = true;
} }
} }

View File

@ -26,65 +26,66 @@ class Application_Model_Soundcloud
public function uploadTrack($filepath, $filename, $description, public function uploadTrack($filepath, $filename, $description,
$tags=array(), $release=null, $genre=null) $tags=array(), $release=null, $genre=null)
{ {
if ($this->getToken()) {
if (count($tags)) {
$tags = join(" ", $tags);
$tags = $tags." ".Application_Model_Preference::GetSoundCloudTags();
} else {
$tags = Application_Model_Preference::GetSoundCloudTags();
}
$downloadable = Application_Model_Preference::GetSoundCloudDownloadbleOption() == '1'; if (!$this->getToken()) {
$track_data = array(
'track[sharing]' => 'private',
'track[title]' => $filename,
'track[asset_data]' => '@' . $filepath,
'track[tag_list]' => $tags,
'track[description]' => $description,
'track[downloadable]' => $downloadable,
);
if (isset($release)) {
$release = str_replace(" ", "-", $release);
$release = str_replace(":", "-", $release);
//YYYY-MM-DD-HH-mm-SS
$release = explode("-", $release);
$track_data['track[release_year]'] = $release[0];
$track_data['track[release_month]'] = $release[1];
$track_data['track[release_day]'] = $release[2];
}
if (isset($genre) && $genre != "") {
$track_data['track[genre]'] = $genre;
} else {
$default_genre = Application_Model_Preference::GetSoundCloudGenre();
if ($default_genre != "") {
$track_data['track[genre]'] = $default_genre;
}
}
$track_type = Application_Model_Preference::GetSoundCloudTrackType();
if ($track_type != "") {
$track_data['track[track_type]'] = $track_type;
}
$license = Application_Model_Preference::GetSoundCloudLicense();
if ($license != "") {
$track_data['track[license]'] = $license;
}
$response = json_decode(
$this->_soundcloud->post('tracks', $track_data),
true
);
return $response;
} else {
throw new NoSoundCloundToken(); throw new NoSoundCloundToken();
} }
if (count($tags)) {
$tags = join(" ", $tags);
$tags = $tags." ".Application_Model_Preference::GetSoundCloudTags();
} else {
$tags = Application_Model_Preference::GetSoundCloudTags();
}
$downloadable = Application_Model_Preference::GetSoundCloudDownloadbleOption() == '1';
$track_data = array(
'track[sharing]' => 'private',
'track[title]' => $filename,
'track[asset_data]' => '@' . $filepath,
'track[tag_list]' => $tags,
'track[description]' => $description,
'track[downloadable]' => $downloadable,
);
if (isset($release)) {
$release = str_replace(" ", "-", $release);
$release = str_replace(":", "-", $release);
//YYYY-MM-DD-HH-mm-SS
$release = explode("-", $release);
$track_data['track[release_year]'] = $release[0];
$track_data['track[release_month]'] = $release[1];
$track_data['track[release_day]'] = $release[2];
}
if (isset($genre) && $genre != "") {
$track_data['track[genre]'] = $genre;
} else {
$default_genre = Application_Model_Preference::GetSoundCloudGenre();
if ($default_genre != "") {
$track_data['track[genre]'] = $default_genre;
}
}
$track_type = Application_Model_Preference::GetSoundCloudTrackType();
if ($track_type != "") {
$track_data['track[track_type]'] = $track_type;
}
$license = Application_Model_Preference::GetSoundCloudLicense();
if ($license != "") {
$track_data['track[license]'] = $license;
}
$response = json_decode(
$this->_soundcloud->post('tracks', $track_data),
true
);
return $response;
} }
public static function uploadSoundcloud($id) public static function uploadSoundcloud($id)

View File

@ -92,5 +92,13 @@ if ($item['type'] == 2) {
<?php endforeach; ?> <?php endforeach; ?>
<?php else : ?> <?php else : ?>
<li class="spl_empty">Empty playlist</li> <li class="spl_empty">
<?php
if ($this->obj instanceof Application_Model_Block) {
echo 'Empty smart block';
} else {
echo 'Empty playlist';
}
?>
</li>
<?php endif; ?> <?php endif; ?>

View File

@ -339,10 +339,22 @@ var AIRTIME = (function(AIRTIME){
}); });
}; };
mod.jumpToCurrentTrack = function() {
var $scroll = $sbContent.find(".dataTables_scrolling");
var scrolled = $scroll.scrollTop();
var scrollingTop = $scroll.offset().top;
var oTable = $('#show_builder_table').dataTable();
var current = $sbTable.find("."+NOW_PLAYING_CLASS);
var currentTop = current.offset().top;
$scroll.scrollTop(currentTop - scrollingTop + scrolled);
}
mod.builderDataTable = function() { mod.builderDataTable = function() {
$sbContent = $('#show_builder'); $sbContent = $('#show_builder');
$lib = $("#library_content"), $lib = $("#library_content"),
$sbTable = $sbContent.find('table'); $sbTable = $sbContent.find('table');
var isInitialized = false;
oSchedTable = $sbTable.dataTable( { oSchedTable = $sbTable.dataTable( {
"aoColumns": [ "aoColumns": [
@ -636,6 +648,13 @@ var AIRTIME = (function(AIRTIME){
$("#draggingContainer").remove(); $("#draggingContainer").remove();
}, },
"fnDrawCallback": function fnBuilderDrawCallback(oSettings, json) { "fnDrawCallback": function fnBuilderDrawCallback(oSettings, json) {
if (!isInitialized) {
if ($(this).find("."+NOW_PLAYING_CLASS).length > 0) {
mod.jumpToCurrentTrack();
}
}
isInitialized = true;
var wrapperDiv, var wrapperDiv,
markerDiv, markerDiv,
$td, $td,
@ -1021,7 +1040,7 @@ var AIRTIME = (function(AIRTIME){
if (AIRTIME.button.isDisabled('icon-step-forward', true) === true) { if (AIRTIME.button.isDisabled('icon-step-forward', true) === true) {
return; return;
} }
/*
var $scroll = $sbContent.find(".dataTables_scrolling"), var $scroll = $sbContent.find(".dataTables_scrolling"),
scrolled = $scroll.scrollTop(), scrolled = $scroll.scrollTop(),
scrollingTop = $scroll.offset().top, scrollingTop = $scroll.offset().top,
@ -1029,6 +1048,8 @@ var AIRTIME = (function(AIRTIME){
currentTop = current.offset().top; currentTop = current.offset().top;
$scroll.scrollTop(currentTop - scrollingTop + scrolled); $scroll.scrollTop(currentTop - scrollingTop + scrolled);
*/
mod.jumpToCurrentTrack();
}); });
//delete overbooked tracks. //delete overbooked tracks.

View File

@ -113,173 +113,179 @@ AIRTIME = (function(AIRTIME) {
} }
mod.onReady = function() { mod.onReady = function() {
//define module vars. // define module vars.
$lib = $("#library_content"); $lib = $("#library_content");
$builder = $("#show_builder"); $builder = $("#show_builder");
$fs = $builder.find('fieldset'); $fs = $builder.find('fieldset');
/* /*
* Icon hover states for search. * Icon hover states for search.
*/ */
$builder.on("mouseenter", ".sb-timerange .ui-button", function(ev) { $builder.on("mouseenter", ".sb-timerange .ui-button", function(ev) {
$(this).addClass("ui-state-hover"); $(this).addClass("ui-state-hover");
}); });
$builder.on("mouseleave", ".sb-timerange .ui-button", function(ev) { $builder.on("mouseleave", ".sb-timerange .ui-button", function(ev) {
$(this).removeClass("ui-state-hover"); $(this).removeClass("ui-state-hover");
}); });
$builder.find(dateStartId).datepicker(oBaseDatePickerSettings); $builder.find(dateStartId).datepicker(oBaseDatePickerSettings);
$builder.find(timeStartId).timepicker(oBaseTimePickerSettings); $builder.find(timeStartId).timepicker(oBaseTimePickerSettings);
$builder.find(dateEndId).datepicker(oBaseDatePickerSettings); $builder.find(dateEndId).datepicker(oBaseDatePickerSettings);
$builder.find(timeEndId).timepicker(oBaseTimePickerSettings); $builder.find(timeEndId).timepicker(oBaseTimePickerSettings);
oRange = AIRTIME.utilities.fnGetScheduleRange(dateStartId, timeStartId, dateEndId, timeEndId); oRange = AIRTIME.utilities.fnGetScheduleRange(dateStartId, timeStartId,
AIRTIME.showbuilder.fnServerData.start = oRange.start; dateEndId, timeEndId);
AIRTIME.showbuilder.fnServerData.end = oRange.end; AIRTIME.showbuilder.fnServerData.start = oRange.start;
AIRTIME.showbuilder.fnServerData.end = oRange.end;
AIRTIME.library.libraryInit(); AIRTIME.library.libraryInit();
AIRTIME.showbuilder.builderDataTable(); AIRTIME.showbuilder.builderDataTable();
setWidgetSize(); setWidgetSize();
$libWrapper = $lib.find("#library_display_wrapper"); $libWrapper = $lib.find("#library_display_wrapper");
$libWrapper.prepend($libClose); $libWrapper.prepend($libClose);
$builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 95); $builder.find('.dataTables_scrolling').css("max-height",
widgetHeight - 95);
$builder.on("click", "#sb_submit", showSearchSubmit); $builder.on("click", "#sb_submit", showSearchSubmit);
$builder.on("click","#sb_edit", function (ev){ $builder.on("click", "#sb_edit", function(ev) {
var schedTable = $("#show_builder_table").dataTable(); var schedTable = $("#show_builder_table").dataTable();
//reset timestamp to redraw the cursors. // reset timestamp to redraw the cursors.
AIRTIME.showbuilder.resetTimestamp(); AIRTIME.showbuilder.resetTimestamp();
$lib.show() $lib.show().width(Math.floor(screenWidth * 0.48));
.width(Math.floor(screenWidth * 0.48));
$builder.width(Math.floor(screenWidth * 0.48)) $builder.width(Math.floor(screenWidth * 0.48)).find("#sb_edit")
.find("#sb_edit") .remove().end().find("#sb_date_start")
.remove() .css("margin-left", 0).end();
.end()
.find("#sb_date_start")
.css("margin-left", 0)
.end();
schedTable.fnDraw(); schedTable.fnDraw();
$.ajax({ $.ajax( {
url: "/usersettings/set-now-playing-screen-settings", url : "/usersettings/set-now-playing-screen-settings",
type: "POST", type : "POST",
data: {settings : {library : true}, format: "json"}, data : {
dataType: "json", settings : {
success: function(){} library : true
}); },
}); format : "json"
},
dataType : "json",
success : function() {
}
});
});
$lib.on("click", "#sb_lib_close", function() { $lib.on("click", "#sb_lib_close", function() {
var schedTable = $("#show_builder_table").dataTable(); var schedTable = $("#show_builder_table").dataTable();
$lib.hide(); $lib.hide();
$builder.width(screenWidth) $builder.width(screenWidth).find(".sb-timerange").prepend(
.find(".sb-timerange") $toggleLib).find("#sb_date_start").css("margin-left", 30)
.prepend($toggleLib) .end().end();
.find("#sb_date_start")
.css("margin-left", 30)
.end()
.end();
$toggleLib.removeClass("ui-state-hover"); $toggleLib.removeClass("ui-state-hover");
schedTable.fnDraw(); schedTable.fnDraw();
$.ajax({ $.ajax( {
url: "/usersettings/set-now-playing-screen-settings", url : "/usersettings/set-now-playing-screen-settings",
type: "POST", type : "POST",
data: {settings : {library : false}, format: "json"}, data : {
dataType: "json", settings : {
success: function(){} library : false
}); },
}); format : "json"
},
dataType : "json",
success : function() {
}
});
});
$builder.find('legend').click(function(ev, item){ $builder.find('legend').click(
function(ev, item) {
if ($fs.hasClass("closed")) { if ($fs.hasClass("closed")) {
$fs.removeClass("closed"); $fs.removeClass("closed");
$builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 150); $builder.find('.dataTables_scrolling').css(
} "max-height", widgetHeight - 150);
else { } else {
$fs.addClass("closed"); $fs.addClass("closed");
//set defaults for the options. // set defaults for the options.
$fs.find('select').val(0); $fs.find('select').val(0);
$fs.find('input[type="checkbox"]').attr("checked", false); $fs.find('input[type="checkbox"]').attr("checked",
$builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 110); false);
} $builder.find('.dataTables_scrolling').css(
}); "max-height", widgetHeight - 110);
}
});
//set click event for all my shows checkbox. // set click event for all my shows checkbox.
$builder.on("click", "#sb_my_shows", function(ev) { $builder.on("click", "#sb_my_shows", function(ev) {
if ($(this).is(':checked')) { if ($(this).is(':checked')) {
$(ev.delegateTarget).find('#sb_show_filter').val(0); $(ev.delegateTarget).find('#sb_show_filter').val(0);
} }
showSearchSubmit(); showSearchSubmit();
}); });
//set select event for choosing a show. //set select event for choosing a show.
$builder.on("change", '#sb_show_filter', function(ev) { $builder.on("change", '#sb_show_filter', function(ev) {
if ($(this).val() !== 0) { if ($(this).val() !== 0) {
$(ev.delegateTarget).find('#sb_my_shows').attr("checked", false); $(ev.delegateTarget).find('#sb_my_shows')
} .attr("checked", false);
}
showSearchSubmit(); showSearchSubmit();
}); });
function checkScheduleUpdates(){ function checkScheduleUpdates() {
var data = {}, var data = {}, oTable = $('#show_builder_table').dataTable(), fn = oTable
oTable = $('#show_builder_table').dataTable(), .fnSettings().fnServerData, start = fn.start, end = fn.end;
fn = oTable.fnSettings().fnServerData,
start = fn.start,
end = fn.end;
data["format"] = "json"; data["format"] = "json";
data["start"] = start; data["start"] = start;
data["end"] = end; data["end"] = end;
data["timestamp"] = AIRTIME.showbuilder.getTimestamp(); data["timestamp"] = AIRTIME.showbuilder.getTimestamp();
data["instances"] = AIRTIME.showbuilder.getShowInstances(); data["instances"] = AIRTIME.showbuilder.getShowInstances();
if (fn.hasOwnProperty("ops")) { if (fn.hasOwnProperty("ops")) {
data["myShows"] = fn.ops.myShows; data["myShows"] = fn.ops.myShows;
data["showFilter"] = fn.ops.showFilter; data["showFilter"] = fn.ops.showFilter;
} }
$.ajax( { $.ajax( {
"dataType": "json", "dataType" : "json",
"type": "GET", "type" : "GET",
"url": "/showbuilder/check-builder-feed", "url" : "/showbuilder/check-builder-feed",
"data": data, "data" : data,
"success": function(json) { "success" : function(json) {
if (json.update === true) { if (json.update === true) {
oTable.fnDraw(); oTable.fnDraw();
} }
} }
} ); });
} }
//check if the timeline view needs updating. //check if the timeline view needs updating.
setInterval(checkScheduleUpdates, 5 * 1000); //need refresh in milliseconds setInterval(checkScheduleUpdates, 5 * 1000); //need refresh in milliseconds
}; };
mod.onResize = function() { mod.onResize = function() {
clearTimeout(resizeTimeout); clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(setWidgetSize, 100); resizeTimeout = setTimeout(setWidgetSize, 100);
}; };
return AIRTIME; return AIRTIME;
} (AIRTIME || {})); } (AIRTIME || {}));

View File

@ -20,6 +20,11 @@ import traceback
AIRTIME_VERSION = "2.2.0" AIRTIME_VERSION = "2.2.0"
# TODO : Place these functions in some common module. Right now, media
# monitor uses the same functions and it would be better to reuse them
# instead of copy pasting them around
def to_unicode(obj, encoding='utf-8'): def to_unicode(obj, encoding='utf-8'):
if isinstance(obj, basestring): if isinstance(obj, basestring):
if not isinstance(obj, unicode): if not isinstance(obj, unicode):
@ -39,7 +44,7 @@ def convert_dict_value_to_utf8(md):
# Airtime API Client # Airtime API Client
################################################################################ ################################################################################
class AirtimeApiClient(): class AirtimeApiClient(object):
# This is a little hacky fix so that I don't have to pass the config object # This is a little hacky fix so that I don't have to pass the config object
# everywhere where AirtimeApiClient needs to be initialized # everywhere where AirtimeApiClient needs to be initialized
@ -422,53 +427,46 @@ class AirtimeApiClient():
def send_media_monitor_requests(self, action_list, dry=False): def send_media_monitor_requests(self, action_list, dry=False):
""" """
Send a gang of media monitor events at a time. actions_list is a list Send a gang of media monitor events at a time. actions_list is a
of dictionaries where every dictionary is representing an action. Every list of dictionaries where every dictionary is representing an
action dict must contain a 'mode' key that says what kind of action it action. Every action dict must contain a 'mode' key that says
is and an optional 'is_record' key that says whether the show was what kind of action it is and an optional 'is_record' key that
recorded or not. The value of this key does not matter, only if it's says whether the show was recorded or not. The value of this key
present or not. does not matter, only if it's present or not.
""" """
logger = self.logger url = self.construct_url('reload_metadata_group')
try: # We are assuming that action_list is a list of dictionaries such
url = self.construct_url('reload_metadata_group') # that every dictionary represents the metadata of a file along
# We are assuming that action_list is a list of dictionaries such # with a special mode key that is the action to be executed by the
# that every dictionary represents the metadata of a file along # controller.
# with a special mode key that is the action to be executed by the valid_actions = []
# controller. # We could get a list of valid_actions in a much shorter way using
valid_actions = [] # filter but here we prefer a little more verbosity to help
# We could get a list of valid_actions in a much shorter way using # debugging
# filter but here we prefer a little more verbosity to help for action in action_list:
# debugging if not 'mode' in action:
for action in action_list: self.logger.debug("Warning: Trying to send a request element without a 'mode'")
if not 'mode' in action: self.logger.debug("Here is the the request: '%s'" % str(action) )
self.logger.debug("Warning: Trying to send a request element without a 'mode'") else:
self.logger.debug("Here is the the request: '%s'" % str(action) ) # We alias the value of is_record to true or false no
else: # matter what it is based on if it's absent in the action
# We alias the value of is_record to true or false no if 'is_record' not in action:
# matter what it is based on if it's absent in the action action['is_record'] = 0
if 'is_record' not in action: valid_actions.append(action)
action['is_record'] = 0 # Note that we must prefix every key with: mdX where x is a number
valid_actions.append(action) # Is there a way to format the next line a little better? The
# Note that we must prefix every key with: mdX where x is a number # parenthesis make the code almost unreadable
# Is there a way to format the next line a little better? The md_list = dict((("md%d" % i), json.dumps(convert_dict_value_to_utf8(md))) \
# parenthesis make the code almost unreadable for i,md in enumerate(valid_actions))
md_list = dict((("md%d" % i), json.dumps(convert_dict_value_to_utf8(md))) \ # For testing we add the following "dry" parameter to tell the
for i,md in enumerate(valid_actions)) # controller not to actually do any changes
# For testing we add the following "dry" parameter to tell the if dry: md_list['dry'] = 1
# controller not to actually do any changes self.logger.info("Pumping out %d requests..." % len(valid_actions))
if dry: md_list['dry'] = 1 data = urllib.urlencode(md_list)
self.logger.info("Pumping out %d requests..." % len(valid_actions)) req = urllib2.Request(url, data)
data = urllib.urlencode(md_list) response = self.get_response_from_server(req)
req = urllib2.Request(url, data) response = json.loads(response)
response = self.get_response_from_server(req) return response
response = json.loads(response)
return response
except ValueError: raise
except Exception, e:
logger.error('Exception: %s', e)
logger.error("traceback: %s", traceback.format_exc())
raise
#returns a list of all db files for a given directory in JSON format: #returns a list of all db files for a given directory in JSON format:
#{"files":["path/to/file1", "path/to/file2"]} #{"files":["path/to/file1", "path/to/file2"]}

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import traceback
""" """
Python part of radio playout (pypo) Python part of radio playout (pypo)
@ -102,6 +103,24 @@ class Notify:
logger.debug('# Calling server to update webstream data #') logger.debug('# Calling server to update webstream data #')
logger.debug('#################################################') logger.debug('#################################################')
response = self.api_client.notify_webstream_data(data, media_id) response = self.api_client.notify_webstream_data(data, media_id)
logger.debug("Response: " + json.dumps(response))
def run_with_options(self, options):
if options.error and options.stream_id:
self.notify_liquidsoap_status(options.error, options.stream_id, options.time)
elif options.connect and options.stream_id:
self.notify_liquidsoap_status("OK", options.stream_id, options.time)
elif options.source_name and options.source_status:
self.notify_source_status(options.source_name, options.source_status)
elif options.webstream:
self.notify_webstream_data(options.webstream, options.media_id)
elif options.media_id:
self.notify_media_start_playing(options.media_id)
elif options.liquidsoap_started:
self.notify_liquidsoap_started()
else:
logger.debug("Unrecognized option in options(%s). Doing nothing" \
% str(options))
if __name__ == '__main__': if __name__ == '__main__':
@ -112,41 +131,9 @@ if __name__ == '__main__':
print '#########################################' print '#########################################'
# initialize # initialize
if options.error and options.stream_id: try:
try: n = Notify()
n = Notify() n.run_with_options(options)
n.notify_liquidsoap_status(options.error, options.stream_id, options.time) except Exception as e:
except Exception, e: print( traceback.format_exc() )
print e
elif options.connect and options.stream_id:
try:
n = Notify()
n.notify_liquidsoap_status("OK", options.stream_id, options.time)
except Exception, e:
print e
elif options.source_name and options.source_status:
try:
n = Notify()
n.notify_source_status(options.source_name, options.source_status)
except Exception, e:
print e
elif options.webstream:
try:
n = Notify()
n.notify_webstream_data(options.webstream, options.media_id)
except Exception, e:
print e
elif options.media_id:
try:
n = Notify()
n.notify_media_start_playing(options.media_id)
except Exception, e:
print e
elif options.liquidsoap_started:
try:
n = Notify()
n.notify_liquidsoap_started()
except Exception, e:
print e