From 3599f32ee63f69c26269e36d5b0d8c9c4558df40 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Tue, 17 Jul 2012 11:14:56 -0400 Subject: [PATCH 1/3] cc-4105: fixed runtime error where recorder would initialize and api client instance from a method that would not exist\n\tRefactored getDateTimeObj a little bit --- python_apps/pypo/recorder.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/python_apps/pypo/recorder.py b/python_apps/pypo/recorder.py index 132a687ba..f63f47a5e 100644 --- a/python_apps/pypo/recorder.py +++ b/python_apps/pypo/recorder.py @@ -20,7 +20,14 @@ from threading import Thread import mutagen -from api_clients import api_client +from api_clients import api_client as apc + +def api_client(logger): + """ + api_client returns the correct instance of AirtimeApiClient. Although there is only one + instance to choose from at the moment. + """ + return apc.AirtimeApiClient(logger) # loading config file try: @@ -30,14 +37,17 @@ except Exception, e: sys.exit() def getDateTimeObj(time): + # TODO : clean up for this function later. + # - use tuples to parse result from split (instead of indices) + # - perhaps validate the input before doing dangerous casts? + # - rename this function to follow the standard convention + # - rename time to something else so that the module name does not get + # shadowed + # - add docstring to document all behaviour of this function timeinfo = time.split(" ") - date = timeinfo[0].split("-") - time = timeinfo[1].split(":") - - date = map(int, date) - time = map(int, time) - - return datetime.datetime(date[0], date[1], date[2], time[0], time[1], time[2], 0, None) + date = [ int(x) for x in timeinfo[0].split("-") ] + my_time = [ int(x) for x in timeinfo[1].split(":") ] + return datetime.datetime(date[0], date[1], date[2], my_time[0], my_time[1], my_time[2], 0, None) PUSH_INTERVAL = 2 From b64b84b9ce7caf7ea422d1ea6ae12b110b41371c Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Wed, 18 Jul 2012 14:55:29 -0400 Subject: [PATCH 2/3] re-add replay gain to old media-monitor --- .../airtimefilemonitor/replaygain.py | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 python_apps/media-monitor/airtimefilemonitor/replaygain.py diff --git a/python_apps/media-monitor/airtimefilemonitor/replaygain.py b/python_apps/media-monitor/airtimefilemonitor/replaygain.py new file mode 100644 index 000000000..bcb9cf6a7 --- /dev/null +++ b/python_apps/media-monitor/airtimefilemonitor/replaygain.py @@ -0,0 +1,107 @@ +from subprocess import Popen, PIPE +import re +import os +import sys +import shutil +import tempfile + +def get_process_output(command): + """ + Run subprocess and return stdout + """ + p = Popen(command, shell=True, stdout=PIPE) + return p.communicate()[0].strip() + +def run_process(command): + """ + Run subprocess and return "return code" + """ + p = Popen(command, shell=True) + return os.waitpid(p.pid, 0)[1] + +def get_mime_type(file_path): + """ + Attempts to get the mime type but will return prematurely if the process + takes longer than 5 seconds. Note that this function should only be called + for files which do not have a mp3/ogg/flac extension. + """ + + return get_process_output("timeout 5 file -b --mime-type %s" % file_path) + +def duplicate_file(file_path): + """ + Makes a duplicate of the file and returns the path of this duplicate file. + """ + fsrc = open(file_path, 'r') + fdst = tempfile.NamedTemporaryFile(delete=False) + + print "Copying %s to %s" % (file_path, fdst.name) + + shutil.copyfileobj(fsrc, fdst) + + fsrc.close() + fdst.close() + + return fdst.name + +def calculate_replay_gain(file_path): + """ + This function accepts files of type mp3/ogg/flac and returns a calculated ReplayGain value in dB. + If the value cannot be calculated for some reason, then we default to 0 (Unity Gain). + + http://wiki.hydrogenaudio.org/index.php?title=ReplayGain_1.0_specification + """ + + try: + """ + Making a duplicate is required because the ReplayGain extraction utilities we use + make unwanted modifications to the file. + """ + + search = None + temp_file_path = duplicate_file(file_path) + + if re.search(r'mp3$', temp_file_path, re.IGNORECASE) or get_mime_type(temp_file_path) == "audio/mpeg": + if run_process("which mp3gain > /dev/null") == 0: + out = get_process_output('mp3gain -q "%s" 2> /dev/null' % temp_file_path) + search = re.search(r'Recommended "Track" dB change: (.*)', out) + else: + print "mp3gain not found" + #Log warning + elif re.search(r'ogg$', temp_file_path, re.IGNORECASE) or get_mime_type(temp_file_path) == "application/ogg": + if run_process("which vorbisgain > /dev/null && which ogginfo > /dev/null") == 0: + run_process('vorbisgain -q -f "%s" 2>/dev/null >/dev/null' % temp_file_path) + out = get_process_output('ogginfo "%s"' % temp_file_path) + search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) + else: + print "vorbisgain/ogginfo not found" + #Log warning + elif re.search(r'flac$', temp_file_path, re.IGNORECASE) or get_mime_type(temp_file_path) == "audio/x-flac": + if run_process("which metaflac > /dev/null") == 0: + out = get_process_output('metaflac --show-tag=REPLAYGAIN_TRACK_GAIN "%s"' % temp_file_path) + search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) + else: + print "metaflac not found" + #Log warning + else: + pass + #Log unknown file type. + + #no longer need the temp, file simply remove it. + os.remove(temp_file_path) + except Exception, e: + print e + + replay_gain = 0 + if search: + matches = search.groups() + if len(matches) == 1: + replay_gain = matches[0] + + return replay_gain + + +# Example of running from command line: +# python replay_gain.py /path/to/filename.mp3 +if __name__ == "__main__": + print calculate_replay_gain(sys.argv[1]) From 2d8a9188ebd0d924ecee269d20f67fbfe38bb002 Mon Sep 17 00:00:00 2001 From: denise Date: Wed, 18 Jul 2012 15:48:15 -0400 Subject: [PATCH 3/3] CC-84: Smart Playlists - qtip wasn't showing up on 'new' and 'edit' events - changed 'Artist' to 'Creator' to be consistent with library --- .../forms/SmartPlaylistCriteria.php | 2 +- .../airtime/playlist/smart_playlistbuilder.js | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/airtime_mvc/application/forms/SmartPlaylistCriteria.php b/airtime_mvc/application/forms/SmartPlaylistCriteria.php index a12328de9..e5eed0c5d 100644 --- a/airtime_mvc/application/forms/SmartPlaylistCriteria.php +++ b/airtime_mvc/application/forms/SmartPlaylistCriteria.php @@ -9,12 +9,12 @@ class Application_Form_SmartPlaylistCriteria extends Zend_Form_SubForm $criteriaOptions = array( 0 => "Select criteria", "album_title" => "Album", - "artist_name" => "Artist", "bit_rate" => "Bit Rate", "bpm" => "Bpm", "comments" => "Comments", "composer" => "Composer", "conductor" => "Conductor", + "artist_name" => "Creator", "disc_number" => "Disc Number", "genre" => "Genre", "isrc_number" => "ISRC", diff --git a/airtime_mvc/public/js/airtime/playlist/smart_playlistbuilder.js b/airtime_mvc/public/js/airtime/playlist/smart_playlistbuilder.js index 760c2a96f..614f1d3b2 100644 --- a/airtime_mvc/public/js/airtime/playlist/smart_playlistbuilder.js +++ b/airtime_mvc/public/js/airtime/playlist/smart_playlistbuilder.js @@ -1,35 +1,11 @@ $(document).ready(function() { setSmartPlaylistEvents(); - - $(".playlist_type_help_icon").qtip({ - content: { - text: "A static playlist will save the criteria and generate the playlist content immediately." + - "This allows you to edit and view it in the Playlist Builder before adding it to a show.

" + - "A dynamic playlist will only save the criteria. The playlist content will get generated upon " + - "adding it to a show. You will not be able to view and edit it in the Playlist Builder." - }, - hide: { - delay: 500, - fixed: true - }, - style: { - border: { - width: 0, - radius: 4 - }, - classes: "ui-tooltip-dark ui-tooltip-rounded" - }, - position: { - my: "left bottom", - at: "right center" - }, - }) }); function setSmartPlaylistEvents() { var form = $('#smart-playlist-form'); - form.find('.criteria_add').live("click", function(){ + form.find('.criteria_add').live('click', function(){ var div = $('dd[id="sp_criteria-element"]').children('div:visible:last').next(); div.show(); @@ -42,7 +18,7 @@ function setSmartPlaylistEvents() { removeButtonCheck(); }); - form.find('a[id^="criteria_remove"]').live("click", function(){ + form.find('a[id^="criteria_remove"]').live('click', function(){ var curr = $(this).parent(); var curr_pos = curr.index(); var list = curr.parent(); @@ -207,6 +183,30 @@ function setupUI() { applyPlatformOpacityRules: false }); } + + $(".playlist_type_help_icon").qtip({ + content: { + text: "A static playlist will save the criteria and generate the playlist content immediately." + + "This allows you to edit and view it in the Playlist Builder before adding it to a show.

" + + "A dynamic playlist will only save the criteria. The playlist content will get generated upon " + + "adding it to a show. You will not be able to view and edit it in the Playlist Builder." + }, + hide: { + delay: 500, + fixed: true + }, + style: { + border: { + width: 0, + radius: 4 + }, + classes: "ui-tooltip-dark ui-tooltip-rounded" + }, + position: { + my: "left bottom", + at: "right center" + }, + }); } function enableAndShowExtraField(valEle, index) {