From 39e32743e13180c839d829bce65fab1745105580 Mon Sep 17 00:00:00 2001 From: Daniel James Date: Tue, 19 Jun 2012 16:23:09 +0100 Subject: [PATCH 01/17] Fixed typos in changelog --- changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog b/changelog index 087af1103..ebe61ddda 100644 --- a/changelog +++ b/changelog @@ -19,7 +19,7 @@ * Removing a watched directory and adding it again preserves playlists & shows with those files. * An icon in the playlist shows whether a file is missing on disk, warning the user that the playlist will not go according to plan. * Media monitor detects add and removal of watched temporary local storage (USB disks for example) and network drives. - * Broadcast Log - export play count of tracks within a given time range.  Useful for royalty reporting purposes. + * Broadcast Log - export play count of tracks within a given time range. Useful for royalty reporting purposes. * Minor Improvements: * Ability to turn off the broadcast. * Editing metadata in the library will update the metadata on disk. @@ -30,7 +30,7 @@ * Repeating shows default to "No End" * Ability to "View on Soundcloud" for recorded shows in the calendar * "Listen" preview player no longer falls behind the broadcast (you can only mute the stream now, not stop it) - * Tracks that cannot be played will be rejected on upload and put in to the directory "/srv/airtime/store/problem_files" (but currently it will not tell you that it rejected them - sorry\!) + * Tracks that cannot be played will be rejected on upload and put in to the directory "/srv/airtime/stor/problem_files" (but currently it will not tell you that it rejected them - sorry\!) * Library is automatically refreshed when media import is finished * Show "Disk Full" message when trying to upload a file that wont fit on the disk * Reduced CPU utilization for OGG streams From 1e8c8f81572a28974f04a09f7fe1fa9805890a27 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Mon, 9 Jul 2012 11:21:12 -0400 Subject: [PATCH 02/17] add a few try catch blocks --- .../airtimefilemonitor/airtimemetadata.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/python_apps/media-monitor/airtimefilemonitor/airtimemetadata.py b/python_apps/media-monitor/airtimefilemonitor/airtimemetadata.py index e7b6ad01d..cea950cba 100644 --- a/python_apps/media-monitor/airtimefilemonitor/airtimemetadata.py +++ b/python_apps/media-monitor/airtimefilemonitor/airtimemetadata.py @@ -221,9 +221,20 @@ class AirtimeMetadata: md['MDATA_KEY_COPYRIGHT'] = self.truncate_to_length(md['MDATA_KEY_COPYRIGHT'], 512) #end of db truncation checks. - md['MDATA_KEY_BITRATE'] = getattr(file_info.info, "bitrate", None) - md['MDATA_KEY_SAMPLERATE'] = getattr(file_info.info, "sample_rate", None) - self.logger.info( "Bitrate: %s , Samplerate: %s", md['MDATA_KEY_BITRATE'], md['MDATA_KEY_SAMPLERATE'] ) + try: + md['MDATA_KEY_BITRATE'] = getattr(file_info.info, "bitrate", "0") + except Exception as e: + self.logger.warn("Could not get Bitrate") + md['MDATA_KEY_BITRATE'] = "0" + + try: + md['MDATA_KEY_SAMPLERATE'] = getattr(file_info.info, "sample_rate", "0") + except Exception as e: + self.logger.warn("Could not get Samplerate") + md['MDATA_KEY_SAMPLERATE'] = "0" + + self.logger.info("Bitrate: %s , Samplerate: %s", md['MDATA_KEY_BITRATE'], md['MDATA_KEY_SAMPLERATE']) + try: md['MDATA_KEY_DURATION'] = self.format_length(file_info.info.length) except Exception as e: self.logger.warn("File: '%s' raises: %s", filepath, str(e)) From 6ce52f081a0075fba226ba63cef981aaf5a26e9d Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Mon, 9 Jul 2012 12:20:41 -0400 Subject: [PATCH 03/17] CC-4091: More comprehensive error message for installs on systems with incorrect locale -done --- install_minimal/airtime-install | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/install_minimal/airtime-install b/install_minimal/airtime-install index 86691c427..8c4541685 100755 --- a/install_minimal/airtime-install +++ b/install_minimal/airtime-install @@ -107,11 +107,28 @@ echo "* Making sure /etc/default/locale is set properly" set +e update-locale cat /etc/default/locale | grep -i "LANG=.*UTF-\?8" -set -e if [ "$?" != "0" ]; then - echo "non UTF-8 default locale found in /etc/default/locale." + echo -e " * Fail\n" + echo "A non UTF-8 default locale found in /etc/default/locale. Airtime requires +a UTF-8 locale to run. To fix this please do the following: + +Ubuntu: +Put line 'en_US.UTF-8 UTF-8' (or similar) without quotes to '/var/lib/locales/supported.d/local', +replacing any existing lines. +A list of supported locales is available in '/usr/share/i18n/SUPPORTED' +Then run 'sudo dpkg-reconfigure locales' + +Debian: +Run 'sudo dpkg-reconfigure locales' and use the interface to select 'en_US.UTF-8 UTF-8' (or similar). +On the second page select this new locale as the default. + +After these changes have been made simply run install again. + +Now exiting install... +" exit 1 fi +set -e # Check if airtime exists already From e5bbed378213581cb1a0cdba5228e9638f7e8ba7 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Fri, 26 Oct 2012 00:35:44 -0400 Subject: [PATCH 04/17] remove CR line endings --- widgets/sample_page.html | 114 +++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/widgets/sample_page.html b/widgets/sample_page.html index 94cc51396..cba9c37ed 100644 --- a/widgets/sample_page.html +++ b/widgets/sample_page.html @@ -1,57 +1,57 @@ - - - - -Airtime widgets - - - - - - - -

"Now Playing" Widget

-This widget displays what is currently playing: -
-
-
-
-
-

"Today's Program" Widget

-This widget displays what is being played today: -
-
-
-
-
-

"Weekly Program" Widget

-This widget displays all the shows for the entire week: -
-
-
- - - + + + + +Airtime widgets + + + + + + + +

"Now Playing" Widget

+This widget displays what is currently playing: +
+
+
+
+
+

"Today's Program" Widget

+This widget displays what is being played today: +
+
+
+
+
+

"Weekly Program" Widget

+This widget displays all the shows for the entire week: +
+
+
+ + + From e285bcfba1e64266c3f53577cbcab2a85e29142f Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Fri, 26 Oct 2012 12:54:10 -0400 Subject: [PATCH 05/17] update liquidsoap library scripts -git commit hash f776e7f4c5d3729cbcf863381f3cadd96c578a5b --- .../library/external-todo.liq | 314 ------------------ .../liquidsoap_scripts/library/externals.liq | 1 + .../pypo/liquidsoap_scripts/library/http.liq | 16 +- .../liquidsoap_scripts/library/pervasives.liq | 3 +- .../pypo/liquidsoap_scripts/library/utils.liq | 59 ++-- .../liquidsoap_scripts/library/video_text.liq | 2 + 6 files changed, 46 insertions(+), 349 deletions(-) delete mode 100644 python_apps/pypo/liquidsoap_scripts/library/external-todo.liq diff --git a/python_apps/pypo/liquidsoap_scripts/library/external-todo.liq b/python_apps/pypo/liquidsoap_scripts/library/external-todo.liq deleted file mode 100644 index 24ae18cdc..000000000 --- a/python_apps/pypo/liquidsoap_scripts/library/external-todo.liq +++ /dev/null @@ -1,314 +0,0 @@ -# These operators need to be updated.. - - -# Stream data from mplayer -# @category Source / Input -# @param s data URI. -# @param ~restart restart on exit. -# @param ~restart_on_error restart on exit with error. -# @param ~buffer Duration of the pre-buffered data. -# @param ~max Maximum duration of the buffered data. -def input.mplayer(~id="input.mplayer", - ~restart=true,~restart_on_error=false, - ~buffer=0.2,~max=10.,s) = - input.external(id=id,restart=restart, - restart_on_error=restart_on_error, - buffer=buffer,max=max, - "mplayer -really-quiet -ao pcm:file=/dev/stdout \ - -vc null -vo null #{quote(s)} 2>/dev/null") -end - - -# Output the stream using aplay. -# Using this turns "root.sync" to false -# since aplay will do the synchronisation -# @category Source / Output -# @param ~id Output's ID -# @param ~device Alsa pcm device name -# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop. -# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped. -# @param ~on_start Callback executed when outputting starts. -# @param ~on_stop Callback executed when outputting stops. -# @param s Source to play -def output.aplay(~id="output.aplay",~device="default", - ~fallible=false,~on_start={()},~on_stop={()}, - ~restart_on_crash=false,s) - def aplay_p(m) = - "aplay -D #{device}" - end - log(label=id,level=3,"Setting root.sync to false") - set("root.sync",false) - output.pipe.external(id=id, - fallible=fallible,on_start=on_start,on_stop=on_stop, - restart_on_crash=restart_on_crash, - restart_on_new_track=false, - process=aplay_p,s) -end - -%ifdef output.icecast.external -# Output to icecast using the lame command line encoder. -# @category Source / Output -# @param ~id Output's ID -# @param ~start Start output threads on operator initialization. -# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed. -# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled. -# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop. -# @param ~restart_on_new_track Restart encoder upon new track. -# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds. -# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users. -# @param ~lame The lame binary -# @param ~bitrate Encoder bitrate -# @param ~swap Swap audio samples. Depends on local machine's endianess and lame's version. Test this parameter if you experience garbaged mp3 audio data. On intel 32 and 64 architectures, the parameter should be "true" for lame version >= 3.98. -# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty. -# @param ~protocol Protocol of the streaming server: 'http' for Icecast, 'icy' for Shoutcast. -# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped. -# @param ~on_start Callback executed when outputting starts. -# @param ~on_stop Callback executed when outputting stops. -# @param s The source to output -def output.icecast.lame( - ~id="output.icecast.lame",~start=true, - ~restart=false,~restart_delay=3, - ~host="localhost",~port=8000, - ~user="source",~password="hackme", - ~genre="Misc",~url="http://savonet.sf.net/", - ~description="Liquidsoap Radio!",~public=true, - ~dumpfile="",~mount="Use [name]", - ~name="Use [mount]",~protocol="http", - ~lame="lame",~bitrate=128,~swap=false, - ~fallible=false,~on_start={()},~on_stop={()}, - ~restart_on_crash=false,~restart_on_new_track=false, - ~restart_encoder_delay=3600,~headers=[],s) - samplerate = get(default=44100,"frame.samplerate") - samplerate = float_of_int(samplerate) / 1000. - channels = get(default=2,"frame.channels") - swap = if swap then "-x" else "" end - mode = - if channels == 2 then - "j" # Encoding in joint stereo.. - else - "m" - end - # Metadata update is set by ICY with icecast - def lame_p(m) - "#{lame} -b #{bitrate} -r --bitwidth 16 -s #{samplerate} \ - --signed -m #{mode} --nores #{swap} -t - -" - end - output.icecast.external(id=id, - process=lame_p,bitrate=bitrate,start=start, - restart=restart,restart_delay=restart_delay, - host=host,port=port,user=user,password=password, - genre=genre,url=url,description=description, - public=public,dumpfile=dumpfile,restart_encoder_delay=restart_encoder_delay, - name=name,mount=mount,protocol=protocol, - header=false,restart_on_crash=restart_on_crash, - restart_on_new_track=restart_on_new_track,headers=headers, - fallible=fallible,on_start=on_start,on_stop=on_stop, - s) -end - -# Output to shoutcast using the lame encoder. -# @category Source / Output -# @param ~id Output's ID -# @param ~start Start output threads on operator initialization. -# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed. -# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled. -# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop. -# @param ~restart_on_new_track Restart encoder upon new track. -# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds. -# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users. -# @param ~lame The lame binary -# @param ~bitrate Encoder bitrate -# @param ~icy_reset Reset shoutcast source buffer upon connecting (necessary for NSV). -# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty. -# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped. -# @param ~on_start Callback executed when outputting starts. -# @param ~on_stop Callback executed when outputting stops. -# @param s The source to output -def output.shoutcast.lame( - ~id="output.shoutcast.mp3",~start=true, - ~restart=false,~restart_delay=3, - ~host="localhost",~port=8000, - ~user="source",~password="hackme", - ~genre="Misc",~url="http://savonet.sf.net/", - ~description="Liquidsoap Radio!",~public=true, - ~dumpfile="",~name="Use [mount]",~icy_reset=true, - ~lame="lame",~aim="",~icq="",~irc="", - ~fallible=false,~on_start={()},~on_stop={()}, - ~restart_on_crash=false,~restart_on_new_track=false, - ~restart_encoder_delay=3600,~bitrate=128,s) = - icy_reset = if icy_reset then "1" else "0" end - headers = [("icy-aim",aim),("icy-irc",irc), - ("icy-icq",icq),("icy-reset",icy_reset)] - output.icecast.lame( - id=id, headers=headers, lame=lame, - bitrate=bitrate, start=start, - restart=restart, restart_encoder_delay=restart_encoder_delay, - host=host, port=port, user=user, password=password, - genre=genre, url=url, description=description, - public=public, dumpfile=dumpfile, - restart_on_crash=restart_on_crash, - restart_on_new_track=restart_on_new_track, - name=name, mount="/", protocol="icy", - fallible=fallible,on_start=on_start,on_stop=on_stop, - s) -end - -# Output to icecast using the flac command line encoder. -# @category Source / Output -# @param ~id Output's ID -# @param ~start Start output threads on operator initialization. -# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed. -# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled. -# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop. -# @param ~restart_on_new_track Restart encoder upon new track. If false, the resulting stream will have a single track. -# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds. -# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users. -# @param ~flac The flac binary -# @param ~quality Encoder quality (0..8) -# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty. -# @param ~protocol Protocol of the streaming server: 'http' for Icecast, 'icy' for Shoutcast. -# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped. -# @param ~on_start Callback executed when outputting starts. -# @param ~on_stop Callback executed when outputting stops. -# @param s The source to output -def output.icecast.flac( - ~id="output.icecast.flac",~start=true, - ~restart=false,~restart_delay=3, - ~host="localhost",~port=8000, - ~user="source",~password="hackme", - ~genre="Misc",~url="http://savonet.sf.net/", - ~description="Liquidsoap Radio!",~public=true, - ~dumpfile="",~mount="Use [name]", - ~name="Use [mount]",~protocol="http", - ~flac="flac",~quality=6, - ~restart_on_crash=false, - ~restart_on_new_track=true, - ~restart_encoder_delay=(-1), - ~fallible=false,~on_start={()},~on_stop={()}, - s) - # We will use raw format, to - # bypass input length value in WAV - # header (input length is not known) - channels = get(default=2,"frame.channels") - samplerate = get(default=44100,"frame.samplerate") - def flac_p(m)= - def option(x) = - "-T #{quote(fst(x))}=#{quote(snd(x))}" - end - m = list.map(option,m) - m = string.concat(separator=" ",m) - "#{flac} --force-raw-format --endian=little --channels=#{channels} \ - --bps=16 --sample-rate=#{samplerate} --sign=signed #{m} \ - -#{quality} --ogg -c -" - end - output.icecast.external(id=id, - process=flac_p,bitrate=(-1),start=start, - restart=restart,restart_delay=restart_delay, - host=host,port=port,user=user,password=password, - genre=genre,url=url,description=description, - public=public,dumpfile=dumpfile, - name=name,mount=mount,protocol=protocol, - fallible=fallible,on_start=on_start,on_stop=on_stop, - restart_on_new_track=restart_on_new_track, - format="ogg",header=false,icy_metadata=false, - restart_on_crash=restart_on_crash, - restart_encoder_delay=restart_encoder_delay, - s) -end - -# Output to icecast using the aacplusenc command line encoder. -# @category Source / Output -# @param ~id Output's ID -# @param ~start Start output threads on operator initialization. -# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed. -# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled. -# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop. -# @param ~restart_on_new_track Restart encoder upon new track. -# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds. -# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users. -# @param ~aacplusenc The aacplusenc binary -# @param ~bitrate Encoder bitrate -# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty. -# @param ~protocol Protocol of the streaming server: 'http' for Icecast, 'icy' for Shoutcast. -# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped. -# @param ~on_start Callback executed when outputting starts. -# @param ~on_stop Callback executed when outputting stops. -# @param s The source to output -def output.icecast.aacplusenc( - ~id="output.icecast.aacplusenc",~start=true, - ~restart=false,~restart_delay=3, - ~host="localhost",~port=8000, - ~user="source",~password="hackme", - ~genre="Misc",~url="http://savonet.sf.net/", - ~description="Liquidsoap Radio!",~public=true, - ~dumpfile="",~mount="Use [name]", - ~name="Use [mount]",~protocol="http", - ~aacplusenc="aacplusenc",~bitrate=64, - ~fallible=false,~on_start={()},~on_stop={()}, - ~restart_on_crash=false,~restart_on_new_track=false, - ~restart_encoder_delay=3600,~headers=[],s) - # Metadata update is set by ICY with icecast - def aacplusenc_p(m) - "#{aacplusenc} - - #{bitrate}" - end - output.icecast.external(id=id, - process=aacplusenc_p,bitrate=bitrate,start=start, - restart=restart,restart_delay=restart_delay, - host=host,port=port,user=user,password=password, - genre=genre,url=url,description=description, - public=public,dumpfile=dumpfile, - name=name,mount=mount,protocol=protocol, - fallible=fallible,on_start=on_start,on_stop=on_stop, - header=true,restart_on_crash=restart_on_crash, - restart_on_new_track=restart_on_new_track,headers=headers, - restart_encoder_delay=restart_encoder_delay,format="audio/aacp",s) -end - -# Output to shoutcast using the aacplusenc encoder. -# @category Source / Output -# @param ~id Output's ID -# @param ~start Start output threads on operator initialization. -# @param ~restart Restart output after a failure. By default, liquidsoap will stop if the output failed. -# @param ~restart_delay Delay, in seconds, before attempting new connection, if restart is enabled. -# @param ~restart_on_crash Restart external process on crash. If false, liquidsoap will stop. -# @param ~restart_on_new_track Restart encoder upon new track. -# @param ~restart_encoder_delay Restart the encoder after this delay, in seconds. -# @param ~user User for shout source connection. Useful only in special cases, like with per-mountpoint users. -# @param ~aacplusenc The aacplusenc binary -# @param ~bitrate Encoder bitrate -# @param ~icy_reset Reset shoutcast source buffer upon connecting (necessary for NSV). -# @param ~dumpfile Dump stream to file, for debugging purpose. Disabled if empty. -# @param ~fallible Allow the child source to fail, in which case the output will be (temporarily) stopped. -# @param ~on_start Callback executed when outputting starts. -# @param ~on_stop Callback executed when outputting stops. -# @param s The source to output -def output.shoutcast.aacplusenc( - ~id="output.shoutcast.aacplusenc",~start=true, - ~restart=false,~restart_delay=3, - ~host="localhost",~port=8000, - ~user="source",~password="hackme", - ~genre="Misc",~url="http://savonet.sf.net/", - ~description="Liquidsoap Radio!",~public=true, - ~fallible=false,~on_start={()},~on_stop={()}, - ~dumpfile="",~name="Use [mount]",~icy_reset=true, - ~aim="",~icq="",~irc="",~aacplusenc="aacplusenc", - ~restart_on_crash=false,~restart_on_new_track=false, - ~restart_encoder_delay=3600,~bitrate=64,s) = - icy_reset = if icy_reset then "1" else "0" end - headers = [("icy-aim",aim),("icy-irc",irc), - ("icy-icq",icq),("icy-reset",icy_reset)] - output.icecast.aacplusenc( - id=id, headers=headers, aacplusenc=aacplusenc, - bitrate=bitrate, start=start, - restart=restart, restart_delay=restart_delay, - host=host, port=port, user=user, password=password, - genre=genre, url=url, description=description, - public=public, dumpfile=dumpfile, - fallible=fallible,on_start=on_start,on_stop=on_stop, - restart_on_crash=restart_on_crash, restart_encoder_delay=restart_encoder_delay, - restart_on_new_track=restart_on_new_track, - name=name, mount="/", protocol="icy", - s) -end -%endif - diff --git a/python_apps/pypo/liquidsoap_scripts/library/externals.liq b/python_apps/pypo/liquidsoap_scripts/library/externals.liq index 6e1b98a60..b56f13d0c 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/externals.liq +++ b/python_apps/pypo/liquidsoap_scripts/library/externals.liq @@ -4,6 +4,7 @@ # Enable external Musepack decoder. Requires the # mpcdec binary in the path. Does not work on # Win32. +# @category Liquidsoap def enable_external_mpc_decoder() = # A list of know extensions and content-type for Musepack. # Values from http://en.wikipedia.org/wiki/Musepack diff --git a/python_apps/pypo/liquidsoap_scripts/library/http.liq b/python_apps/pypo/liquidsoap_scripts/library/http.liq index 109baf41e..53644ce38 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/http.liq +++ b/python_apps/pypo/liquidsoap_scripts/library/http.liq @@ -13,18 +13,12 @@ def http_response(~protocol="HTTP/1.1", ~headers=[], ~data="") = status = http_codes[string_of(code)] - # Set content-length if needed and not set by the - # user. + # Set content-length and connection: close headers = - if data != "" and - not list.mem_assoc("Content-Length",headers) - then - list.append([("Content-Length", - "#{string.length(data)}")], - headers) - else - headers - end + list.append(headers, + [("Content-Length", "#{string.length(data)}"), + ("Connection", "close")]) + headers = list.map(fun (x) -> "#{fst(x)}: #{snd(x)}",headers) headers = string.concat(separator="\r\n",headers) # If no headers are provided, we should avoid diff --git a/python_apps/pypo/liquidsoap_scripts/library/pervasives.liq b/python_apps/pypo/liquidsoap_scripts/library/pervasives.liq index bd894766b..722138018 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/pervasives.liq +++ b/python_apps/pypo/liquidsoap_scripts/library/pervasives.liq @@ -4,4 +4,5 @@ %include "lastfm.liq" %include "flows.liq" %include "http.liq" -%include "video_text.liq" \ No newline at end of file +%include "video_text.liq" +%include "gstreamer.liq" diff --git a/python_apps/pypo/liquidsoap_scripts/library/utils.liq b/python_apps/pypo/liquidsoap_scripts/library/utils.liq index 3d82d8997..a35af94f3 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/utils.liq +++ b/python_apps/pypo/liquidsoap_scripts/library/utils.liq @@ -21,10 +21,14 @@ end # @param a Key to look for # @param l List of pairs (key,value) def list.mem_assoc(a,l) - v = list.assoc(a,l) - # We check for existence, since "" may indicate - # either a binding (a,"") or no binding.. - list.mem((a,v),l) + def f(cur, el) = + if not cur then + fst(el) == a + else + cur + end + end + list.fold(f, false, l) end # Remove a pair from an associative list @@ -164,8 +168,7 @@ def out(s) output.prefered(mksafe(s)) end -# Special track insensitive fallback that -# always skip current song before switching. +# Special track insensitive fallback that always skips current song before switching. # @category Source / Track Processing # @param ~input The input source # @param f The fallback source @@ -212,14 +215,17 @@ end # Simple crossfade. # @category Source / Track Processing # @param ~start_next Duration in seconds of the crossed end of track. -# @param ~fade_in Duration of the fade in for next track -# @param ~fade_out Duration of the fade out for previous track -# @param s The source to use -def crossfade(~id="",~start_next,~fade_in,~fade_out,s) +# @param ~fade_in Duration of the fade in for next track. +# @param ~fade_out Duration of the fade out for previous track. +# @param ~conservative Always prepare for a premature end-of-track. +# @param s The source to use. +def crossfade(~id="",~conservative=true, + ~start_next=5.,~fade_in=3.,~fade_out=3., + s) s = fade.in(duration=fade_in,s) s = fade.out(duration=fade_out,s) fader = fun (a,b) -> add(normalize=false,[b,a]) - cross(id=id,conservative=true,duration=start_next,fader,s) + cross(id=id,conservative=conservative,duration=start_next,fader,s) end # Append speech-synthesized tracks reading the metadata. @@ -242,8 +248,7 @@ def helium(s) end %endif -# Return true if process exited with 0 code. -# Command should return quickly. +# Return true if process exited with 0 code. Command should return quickly. # @category System # @param command Command to test def test_process(command) @@ -277,12 +282,9 @@ def url.split(uri) = end end -# Register a server/telnet command to -# update a source's metadata. Returns -# a new source, which will receive the -# updated metadata. It behaves just like -# the pre-1.0 insert_metadata() operator, -# i.e. insert key1="val1",key2="val2",... +# Register a server/telnet command to update a source's metadata. Returns +# a new source, which will receive the updated metadata. The command has +# the following format: insert key1="val1",key2="val2",... # @category Source / Track Processing # @param ~id Force the value of the source ID. def server.insert_metadata(~id="",s) = @@ -424,15 +426,15 @@ end # @param ~conservative Always prepare for a premature end-of-track. # @param ~default Transition used when no rule applies \ # (default: sequence). -# @param ~high Value, in dB, for loud sound level -# @param ~medium Value, in dB, for medium sound level +# @param ~high Value, in dB, for loud sound level. +# @param ~medium Value, in dB, for medium sound level. # @param ~margin Margin to detect sources that have too different \ # sound level for crossing. # @param s The input source. def smart_crossfade (~start_next=5.,~fade_in=3.,~fade_out=3., ~default=(fun (a,b) -> sequence([a, b])), ~high=-15., ~medium=-32., ~margin=4., - ~width=2.,~conservative=false,s) + ~width=2.,~conservative=true,s) fade.out = fade.out(type="sin",duration=fade_out) fade.in = fade.in(type="sin",duration=fade_in) add = fun (a,b) -> add(normalize=false,[b, a]) @@ -549,7 +551,18 @@ def playlist.reloadable(~id="",~random=false,~on_done={()},uri) if request.resolve(playlist) then playlist = request.filename(playlist) files = playlist.parse(playlist) - list.map(snd,files) + def file_request(el) = + meta = fst(el) + file = snd(el) + s = list.fold(fun (cur, el) -> + "#{cur},#{fst(el)}=#{string.escape(snd(el))}", "", meta) + if s == "" then + file + else + "annotate:#{s}:#{file}" + end + end + list.map(file_request,files) else log(label=id,"Couldn't read playlist: request resolution failed.") [] diff --git a/python_apps/pypo/liquidsoap_scripts/library/video_text.liq b/python_apps/pypo/liquidsoap_scripts/library/video_text.liq index b372a1f7b..8c017a93d 100644 --- a/python_apps/pypo/liquidsoap_scripts/library/video_text.liq +++ b/python_apps/pypo/liquidsoap_scripts/library/video_text.liq @@ -1,5 +1,6 @@ %ifdef video.add_text.gd # Add a scrolling line of text on video frames. +# @category Source / Video Processing # @param ~id Force the value of the source ID. # @param ~color Text color (in 0xRRGGBB format). # @param ~cycle Cycle text. @@ -22,6 +23,7 @@ end %ifdef video.add_text.sdl # Add a scrolling line of text on video frames. +# @category Source / Video Processing # @param ~id Force the value of the source ID. # @param ~color Text color (in 0xRRGGBB format). # @param ~cycle Cycle text. From 0a396378e52397c6c9d5ecf8a0c744760d808f27 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Fri, 26 Oct 2012 17:15:28 -0400 Subject: [PATCH 06/17] cc-4635: Fix --- python_apps/media-monitor2/media/metadata/definitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/media-monitor2/media/metadata/definitions.py b/python_apps/media-monitor2/media/metadata/definitions.py index 9919eb041..ec3bed48f 100644 --- a/python_apps/media-monitor2/media/metadata/definitions.py +++ b/python_apps/media-monitor2/media/metadata/definitions.py @@ -124,7 +124,7 @@ def load_definitions(): default_title = re.sub(r'__\d+\.',u'.', default_title) # format is: track_number-title-123kbps.mp3 - m = re.match(".+-(?P.+)-(\d+kbps|unknown)$", default_title) + m = re.match(".+?-(?P<title>.+)-(\d+kbps|unknown)$", default_title) if m: new_title = m.group('title') else: new_title = re.sub(r'-\d+kbps$', u'', default_title) From 3cb2346af95a1b11436e774bf9bd8b595e6a97d8 Mon Sep 17 00:00:00 2001 From: Martin Konecny <martin.konecny@gmail.com> Date: Fri, 26 Oct 2012 18:23:28 -0400 Subject: [PATCH 07/17] add pcre debian package for compiling liquidsoap --- dev_tools/fabric/fab_liquidsoap_compile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev_tools/fabric/fab_liquidsoap_compile.py b/dev_tools/fabric/fab_liquidsoap_compile.py index 221aa41c6..98f816e47 100644 --- a/dev_tools/fabric/fab_liquidsoap_compile.py +++ b/dev_tools/fabric/fab_liquidsoap_compile.py @@ -181,7 +181,8 @@ libmad-ocaml-dev libtaglib-ocaml-dev libalsa-ocaml-dev libtaglib-ocaml-dev libvo libspeex-dev libspeexdsp-dev speex libladspa-ocaml-dev festival festival-dev \ libsamplerate-dev libxmlplaylist-ocaml-dev libxmlrpc-light-ocaml-dev libflac-dev \ libxml-dom-perl libxml-dom-xpath-perl patch autoconf libmp3lame-dev \ -libcamomile-ocaml-dev libcamlimages-ocaml-dev libtool libpulse-dev libjack-dev camlidl libfaad-dev''') +libcamomile-ocaml-dev libcamlimages-ocaml-dev libtool libpulse-dev libjack-dev +camlidl libfaad-dev libpcre-ocaml-dev''') root = '/home/martin/src' do_run('mkdir -p %s' % root) From d6a4f2f7136fa6c1f165fe7ef7eedfb67f79b7ba Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Fri, 26 Oct 2012 18:45:27 -0400 Subject: [PATCH 08/17] Cleaned up soundcloud --- airtime_mvc/application/models/Soundcloud.php | 115 +++++++++--------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/airtime_mvc/application/models/Soundcloud.php b/airtime_mvc/application/models/Soundcloud.php index 2b1068e57..70148c05c 100644 --- a/airtime_mvc/application/models/Soundcloud.php +++ b/airtime_mvc/application/models/Soundcloud.php @@ -25,66 +25,67 @@ class Application_Model_Soundcloud public function uploadTrack($filepath, $filename, $description, $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'; - - $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 { + if (!$this->getToken()) { 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) From 3b51c93766383f73b7193368e21ba1c99708014a Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Mon, 29 Oct 2012 11:25:05 -0400 Subject: [PATCH 09/17] Refactored pyponotify. Removed code duplication and made runner reusable by other scripts. --- python_apps/pypo/pyponotify.py | 61 +++++++++++++--------------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/python_apps/pypo/pyponotify.py b/python_apps/pypo/pyponotify.py index 446da8d49..9c2f1688c 100644 --- a/python_apps/pypo/pyponotify.py +++ b/python_apps/pypo/pyponotify.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import traceback """ Python part of radio playout (pypo) @@ -102,6 +103,24 @@ class Notify: logger.debug('# Calling server to update webstream data #') logger.debug('#################################################') 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__': @@ -112,41 +131,9 @@ if __name__ == '__main__': print '#########################################' # initialize - if options.error and options.stream_id: - try: - n = Notify() - n.notify_liquidsoap_status(options.error, options.stream_id, options.time) - except Exception, e: - 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 + try: + n = Notify() + n.run_with_options(options) + except Exception as e: + print( traceback.format_exc() ) From 35a9e77062c5400039ce224d7d01c928162d5163 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Mon, 29 Oct 2012 11:40:23 -0400 Subject: [PATCH 10/17] Formatted spaces. --- utils/airtime-import/airtime-import.py | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/utils/airtime-import/airtime-import.py b/utils/airtime-import/airtime-import.py index 5f1d305ca..408bd91ac 100644 --- a/utils/airtime-import/airtime-import.py +++ b/utils/airtime-import/airtime-import.py @@ -21,7 +21,7 @@ logger.addHandler(ch) if (os.geteuid() != 0): print 'Must be a root user.' sys.exit() - + # loading config file try: config = ConfigObj('/etc/airtime/media-monitor.cfg') @@ -66,12 +66,12 @@ def copy_or_move_files_to(paths, dest, flag): print "Cannot find file or path: %s" % path except Exception as e: print "Error: ", e - + def format_dir_string(path): if(path[-1] != '/'): path = path+'/' return path - + def helper_get_stor_dir(): res = api_client.list_all_watched_dirs() if(res is None): @@ -87,18 +87,18 @@ def checkOtherOption(args): for i in args: if(i[0] == '-'): return True - + def errorIfMultipleOption(args, msg=''): if(checkOtherOption(args)): if(msg != ''): raise OptionValueError(msg) else: raise OptionValueError("This option cannot be combined with other options") - + def printHelp(): storage_dir = helper_get_stor_dir() if(storage_dir is None): - storage_dir = "Unknown" + storage_dir = "Unknown" else: storage_dir += "imported/" print """ @@ -111,15 +111,15 @@ There are two ways to import audio files into Airtime: Copied or moved files will be placed into the folder: %s - + Files will be automatically organized into the structure "Artist/Album/TrackNumber-TrackName-Bitrate.file_extension". 2) Use airtime-import to add a folder to the Airtime library ("watch" a folder). - + All the files in the watched folder will be imported to Airtime and the folder will be monitored to automatically detect any changes. Hence any - changes done in the folder(add, delete, edit a file) will trigger + changes done in the folder(add, delete, edit a file) will trigger updates in Airtime library. """ % storage_dir parser.print_help() @@ -209,7 +209,7 @@ def WatchRemoveAction(option, opt, value, parser): print "Removing the watch folder failed: %s" % res['msg']['error'] else: print "The given path is not a directory: %s" % path - + def StorageSetAction(option, opt, value, parser): bypass = False isF = '-f' in parser.rargs @@ -231,12 +231,12 @@ def StorageSetAction(option, opt, value, parser): confirm = confirm or 'N' if(confirm == 'n' or confirm =='N'): sys.exit(1) - + if(len(parser.rargs) > 1): raise OptionValueError("Too many arguments. This option requires exactly one argument.") elif(len(parser.rargs) == 0 ): raise OptionValueError("No argument found. This option requires exactly one argument.") - + path = parser.rargs[0] if (path[0] == "/" or path[0] == "~"): path = os.path.realpath(path) @@ -254,17 +254,17 @@ def StorageSetAction(option, opt, value, parser): print "Setting storage folder failed: %s" % res['msg']['error'] else: print "The given path is not a directory: %s" % path - + def StorageGetAction(option, opt, value, parser): errorIfMultipleOption(parser.rargs) if(len(parser.rargs) > 0): raise OptionValueError("This option does not take any arguments.") print helper_get_stor_dir() - + class OptionValueError(RuntimeError): def __init__(self, msg): self.msg = msg - + usage = """[-c|--copy FILE/DIR [FILE/DIR...]] [-m|--move FILE/DIR [FILE/DIR...]] [--watch-add DIR] [--watch-list] [--watch-remove DIR] [--storage-dir-set DIR] [--storage-dir-get]""" @@ -293,7 +293,7 @@ if('-h' in sys.argv): if(len(sys.argv) == 1 or '-' not in sys.argv[1]): printHelp() sys.exit() - + try: (option, args) = parser.parse_args() except Exception, e: @@ -306,7 +306,7 @@ except Exception, e: except SystemExit: printHelp() sys.exit() - + if option.help: printHelp() sys.exit() From 31a7b8f94353f4d683276094b3c005eb0003f006 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Mon, 29 Oct 2012 11:40:41 -0400 Subject: [PATCH 11/17] Removed usesless try catch from send_media_monitor_requests. --- python_apps/api_clients/api_client.py | 85 ++++++++++++--------------- 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index 130724f66..5b855686a 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -422,53 +422,46 @@ class AirtimeApiClient(): 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 - of dictionaries where every dictionary is representing an action. Every - action dict must contain a 'mode' key that says what kind of action it - is and an optional 'is_record' key that says whether the show was - recorded or not. The value of this key does not matter, only if it's - present or not. + Send a gang of media monitor events at a time. actions_list is a + list of dictionaries where every dictionary is representing an + action. Every action dict must contain a 'mode' key that says + what kind of action it is and an optional 'is_record' key that + says whether the show was recorded or not. The value of this key + does not matter, only if it's present or not. """ - logger = self.logger - try: - url = self.construct_url('reload_metadata_group') - # We are assuming that action_list is a list of dictionaries such - # that every dictionary represents the metadata of a file along - # with a special mode key that is the action to be executed by the - # controller. - valid_actions = [] - # We could get a list of valid_actions in a much shorter way using - # filter but here we prefer a little more verbosity to help - # debugging - for action in action_list: - if not 'mode' in action: - self.logger.debug("Warning: Trying to send a request element without a 'mode'") - self.logger.debug("Here is the the request: '%s'" % str(action) ) - else: - # We alias the value of is_record to true or false no - # matter what it is based on if it's absent in the action - if 'is_record' not in action: - action['is_record'] = 0 - valid_actions.append(action) - # Note that we must prefix every key with: mdX where x is a number - # Is there a way to format the next line a little better? The - # parenthesis make the code almost unreadable - md_list = dict((("md%d" % i), json.dumps(convert_dict_value_to_utf8(md))) \ - for i,md in enumerate(valid_actions)) - # For testing we add the following "dry" parameter to tell the - # controller not to actually do any changes - if dry: md_list['dry'] = 1 - self.logger.info("Pumping out %d requests..." % len(valid_actions)) - data = urllib.urlencode(md_list) - req = urllib2.Request(url, data) - response = self.get_response_from_server(req) - 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 + url = self.construct_url('reload_metadata_group') + # We are assuming that action_list is a list of dictionaries such + # that every dictionary represents the metadata of a file along + # with a special mode key that is the action to be executed by the + # controller. + valid_actions = [] + # We could get a list of valid_actions in a much shorter way using + # filter but here we prefer a little more verbosity to help + # debugging + for action in action_list: + if not 'mode' in action: + self.logger.debug("Warning: Trying to send a request element without a 'mode'") + self.logger.debug("Here is the the request: '%s'" % str(action) ) + else: + # We alias the value of is_record to true or false no + # matter what it is based on if it's absent in the action + if 'is_record' not in action: + action['is_record'] = 0 + valid_actions.append(action) + # Note that we must prefix every key with: mdX where x is a number + # Is there a way to format the next line a little better? The + # parenthesis make the code almost unreadable + md_list = dict((("md%d" % i), json.dumps(convert_dict_value_to_utf8(md))) \ + for i,md in enumerate(valid_actions)) + # For testing we add the following "dry" parameter to tell the + # controller not to actually do any changes + if dry: md_list['dry'] = 1 + self.logger.info("Pumping out %d requests..." % len(valid_actions)) + data = urllib.urlencode(md_list) + req = urllib2.Request(url, data) + response = self.get_response_from_server(req) + response = json.loads(response) + return response #returns a list of all db files for a given directory in JSON format: #{"files":["path/to/file1", "path/to/file2"]} From 3cec987d100b14403ff8eb31dfee9946eaa7011d Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Mon, 29 Oct 2012 11:42:24 -0400 Subject: [PATCH 12/17] Added todo --- python_apps/api_clients/api_client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index 5b855686a..9ef49618d 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -20,6 +20,11 @@ import traceback 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'): if isinstance(obj, basestring): if not isinstance(obj, unicode): From bf5bc3a1166ca19dcc60d35cfe5026851f8209e8 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg <rudi.grinberg@sourcefabric.org> Date: Mon, 29 Oct 2012 11:43:39 -0400 Subject: [PATCH 13/17] Changed APC to new-style classes --- python_apps/api_clients/api_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index 9ef49618d..5ca176ec2 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -44,7 +44,7 @@ def convert_dict_value_to_utf8(md): # 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 # everywhere where AirtimeApiClient needs to be initialized From 8ac0f419873355c35b7ea27e01d38c48b1c65ce2 Mon Sep 17 00:00:00 2001 From: denise <denise@denise-DX4860sourcefabric.org> Date: Mon, 29 Oct 2012 11:57:20 -0400 Subject: [PATCH 14/17] CC-4590: New smart block says 'Empty playlist' -fixed --- .../application/views/scripts/playlist/update.phtml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/playlist/update.phtml b/airtime_mvc/application/views/scripts/playlist/update.phtml index 70176ff9e..87cd0662f 100644 --- a/airtime_mvc/application/views/scripts/playlist/update.phtml +++ b/airtime_mvc/application/views/scripts/playlist/update.phtml @@ -92,5 +92,13 @@ if ($item['type'] == 2) { <?php endforeach; ?> <?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; ?> From fdd889d7a8e2f661945cd047896f33a36be42719 Mon Sep 17 00:00:00 2001 From: denise <denise@denise-DX4860sourcefabric.org> Date: Mon, 29 Oct 2012 12:09:11 -0400 Subject: [PATCH 15/17] - fixed formatting --- .../js/airtime/showbuilder/main_builder.js | 336 +++++++++--------- 1 file changed, 171 insertions(+), 165 deletions(-) diff --git a/airtime_mvc/public/js/airtime/showbuilder/main_builder.js b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js index b640a2882..48e503ac6 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/main_builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js @@ -113,174 +113,180 @@ AIRTIME = (function(AIRTIME) { } mod.onReady = function() { - //define module vars. - $lib = $("#library_content"); - $builder = $("#show_builder"); - $fs = $builder.find('fieldset'); - - /* - * Icon hover states for search. - */ - $builder.on("mouseenter", ".sb-timerange .ui-button", function(ev) { - $(this).addClass("ui-state-hover"); - }); - $builder.on("mouseleave", ".sb-timerange .ui-button", function(ev) { - $(this).removeClass("ui-state-hover"); - }); - - $builder.find(dateStartId).datepicker(oBaseDatePickerSettings); - $builder.find(timeStartId).timepicker(oBaseTimePickerSettings); - $builder.find(dateEndId).datepicker(oBaseDatePickerSettings); - $builder.find(timeEndId).timepicker(oBaseTimePickerSettings); - - oRange = AIRTIME.utilities.fnGetScheduleRange(dateStartId, timeStartId, dateEndId, timeEndId); - AIRTIME.showbuilder.fnServerData.start = oRange.start; - AIRTIME.showbuilder.fnServerData.end = oRange.end; + // define module vars. + $lib = $("#library_content"); + $builder = $("#show_builder"); + $fs = $builder.find('fieldset'); - AIRTIME.library.libraryInit(); - AIRTIME.showbuilder.builderDataTable(); - setWidgetSize(); - - $libWrapper = $lib.find("#library_display_wrapper"); - $libWrapper.prepend($libClose); - - $builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 95); - - $builder.on("click", "#sb_submit", showSearchSubmit); + /* + * Icon hover states for search. + */ + $builder.on("mouseenter", ".sb-timerange .ui-button", function(ev) { + $(this).addClass("ui-state-hover"); + }); + $builder.on("mouseleave", ".sb-timerange .ui-button", function(ev) { + $(this).removeClass("ui-state-hover"); + }); - $builder.on("click","#sb_edit", function (ev){ - var schedTable = $("#show_builder_table").dataTable(); - - //reset timestamp to redraw the cursors. - AIRTIME.showbuilder.resetTimestamp(); - - $lib.show() - .width(Math.floor(screenWidth * 0.48)); - - $builder.width(Math.floor(screenWidth * 0.48)) - .find("#sb_edit") - .remove() - .end() - .find("#sb_date_start") - .css("margin-left", 0) - .end(); - - schedTable.fnDraw(); - - $.ajax({ - url: "/usersettings/set-now-playing-screen-settings", - type: "POST", - data: {settings : {library : true}, format: "json"}, - dataType: "json", - success: function(){} - }); - }); - - $lib.on("click", "#sb_lib_close", function() { - var schedTable = $("#show_builder_table").dataTable(); + $builder.find(dateStartId).datepicker(oBaseDatePickerSettings); + $builder.find(timeStartId).timepicker(oBaseTimePickerSettings); + $builder.find(dateEndId).datepicker(oBaseDatePickerSettings); + $builder.find(timeEndId).timepicker(oBaseTimePickerSettings); + + oRange = AIRTIME.utilities.fnGetScheduleRange(dateStartId, timeStartId, + dateEndId, timeEndId); + AIRTIME.showbuilder.fnServerData.start = oRange.start; + AIRTIME.showbuilder.fnServerData.end = oRange.end; + + AIRTIME.library.libraryInit(); + AIRTIME.showbuilder.builderDataTable(); + setWidgetSize(); + + $libWrapper = $lib.find("#library_display_wrapper"); + $libWrapper.prepend($libClose); + + $builder.find('.dataTables_scrolling').css("max-height", + widgetHeight - 95); + + $builder.on("click", "#sb_submit", showSearchSubmit); + + $builder.on("click", "#sb_edit", function(ev) { + var schedTable = $("#show_builder_table").dataTable(); + + // reset timestamp to redraw the cursors. + AIRTIME.showbuilder.resetTimestamp(); + + $lib.show().width(Math.floor(screenWidth * 0.48)); + + $builder.width(Math.floor(screenWidth * 0.48)).find("#sb_edit") + .remove().end().find("#sb_date_start") + .css("margin-left", 0).end(); + + schedTable.fnDraw(); + + $.ajax( { + url : "/usersettings/set-now-playing-screen-settings", + type : "POST", + data : { + settings : { + library : true + }, + format : "json" + }, + dataType : "json", + success : function() { + } + }); + }); + + $lib.on("click", "#sb_lib_close", function() { + var schedTable = $("#show_builder_table").dataTable(); + + $lib.hide(); + $builder.width(screenWidth).find(".sb-timerange").prepend( + $toggleLib).find("#sb_date_start").css("margin-left", 30) + .end().end(); + + $toggleLib.removeClass("ui-state-hover"); + schedTable.fnDraw(); + + $.ajax( { + url : "/usersettings/set-now-playing-screen-settings", + type : "POST", + data : { + settings : { + library : false + }, + format : "json" + }, + dataType : "json", + success : function() { + } + }); + }); + + $builder.find('legend').click( + function(ev, item) { + + if ($fs.hasClass("closed")) { + + $fs.removeClass("closed"); + $builder.find('.dataTables_scrolling').css( + "max-height", widgetHeight - 150); + } else { + $fs.addClass("closed"); + + // set defaults for the options. + $fs.find('select').val(0); + $fs.find('input[type="checkbox"]').attr("checked", + false); + $builder.find('.dataTables_scrolling').css( + "max-height", widgetHeight - 110); + } + }); + + // set click event for all my shows checkbox. + $builder.on("click", "#sb_my_shows", function(ev) { + + if ($(this).is(':checked')) { + $(ev.delegateTarget).find('#sb_show_filter').val(0); + } + + showSearchSubmit(); + }); + + //set select event for choosing a show. + $builder.on("change", '#sb_show_filter', function(ev) { + + if ($(this).val() !== 0) { + $(ev.delegateTarget).find('#sb_my_shows') + .attr("checked", false); + } + + showSearchSubmit(); + + }); + + function checkScheduleUpdates() { + var data = {}, oTable = $('#show_builder_table').dataTable(), fn = oTable + .fnSettings().fnServerData, start = fn.start, end = fn.end; + + data["format"] = "json"; + data["start"] = start; + data["end"] = end; + data["timestamp"] = AIRTIME.showbuilder.getTimestamp(); + data["instances"] = AIRTIME.showbuilder.getShowInstances(); + + if (fn.hasOwnProperty("ops")) { + data["myShows"] = fn.ops.myShows; + data["showFilter"] = fn.ops.showFilter; + } + + $.ajax( { + "dataType" : "json", + "type" : "GET", + "url" : "/showbuilder/check-builder-feed", + "data" : data, + "success" : function(json) { + if (json.update === true) { + oTable.fnDraw(); + } + } + }); + } + + //check if the timeline view needs updating. + setInterval(checkScheduleUpdates, 5 * 1000); //need refresh in milliseconds + }; + + mod.onResize = function() { + + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(setWidgetSize, 100); + }; + + return AIRTIME; - $lib.hide(); - $builder.width(screenWidth) - .find(".sb-timerange") - .prepend($toggleLib) - .find("#sb_date_start") - .css("margin-left", 30) - .end() - .end(); - - $toggleLib.removeClass("ui-state-hover"); - schedTable.fnDraw(); - - $.ajax({ - url: "/usersettings/set-now-playing-screen-settings", - type: "POST", - data: {settings : {library : false}, format: "json"}, - dataType: "json", - success: function(){} - }); - }); - - $builder.find('legend').click(function(ev, item){ - - if ($fs.hasClass("closed")) { - - $fs.removeClass("closed"); - $builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 150); - } - else { - $fs.addClass("closed"); - - //set defaults for the options. - $fs.find('select').val(0); - $fs.find('input[type="checkbox"]').attr("checked", false); - $builder.find('.dataTables_scrolling').css("max-height", widgetHeight - 110); - } - }); - - //set click event for all my shows checkbox. - $builder.on("click", "#sb_my_shows", function(ev) { - - if ($(this).is(':checked')) { - $(ev.delegateTarget).find('#sb_show_filter').val(0); - } - - showSearchSubmit(); - }); - - //set select event for choosing a show. - $builder.on("change", '#sb_show_filter', function(ev) { - - if ($(this).val() !== 0) { - $(ev.delegateTarget).find('#sb_my_shows').attr("checked", false); - } - - showSearchSubmit(); - - }); - - function checkScheduleUpdates(){ - var data = {}, - oTable = $('#show_builder_table').dataTable(), - fn = oTable.fnSettings().fnServerData, - start = fn.start, - end = fn.end; - - data["format"] = "json"; - data["start"] = start; - data["end"] = end; - data["timestamp"] = AIRTIME.showbuilder.getTimestamp(); - data["instances"] = AIRTIME.showbuilder.getShowInstances(); - - if (fn.hasOwnProperty("ops")) { - data["myShows"] = fn.ops.myShows; - data["showFilter"] = fn.ops.showFilter; - } - - $.ajax( { - "dataType": "json", - "type": "GET", - "url": "/showbuilder/check-builder-feed", - "data": data, - "success": function(json) { - if (json.update === true) { - oTable.fnDraw(); - } - } - } ); - } - - //check if the timeline view needs updating. - setInterval(checkScheduleUpdates, 5 * 1000); //need refresh in milliseconds - }; - - mod.onResize = function() { - - clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(setWidgetSize, 100); - }; - - return AIRTIME; - } (AIRTIME || {})); $(document).ready(AIRTIME.builderMain.onReady); From 332f9993c0890a52101bf9cecb8c0283b2325021 Mon Sep 17 00:00:00 2001 From: denise <denise@denise-DX4860sourcefabric.org> Date: Mon, 29 Oct 2012 15:20:16 -0400 Subject: [PATCH 16/17] CC-4640: Automatically jump to current song when loading the Now Playing page. -done --- .../public/js/airtime/showbuilder/builder.js | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/airtime_mvc/public/js/airtime/showbuilder/builder.js b/airtime_mvc/public/js/airtime/showbuilder/builder.js index fc8463481..daf44a510 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/builder.js @@ -339,11 +339,23 @@ 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() { $sbContent = $('#show_builder'); $lib = $("#library_content"), $sbTable = $sbContent.find('table'); - + var isInitialized = false; + oSchedTable = $sbTable.dataTable( { "aoColumns": [ /* checkbox */ {"mDataProp": "allowed", "sTitle": "", "sWidth": "15px", "sClass": "sb-checkbox"}, @@ -636,6 +648,11 @@ var AIRTIME = (function(AIRTIME){ $("#draggingContainer").remove(); }, "fnDrawCallback": function fnBuilderDrawCallback(oSettings, json) { + if (!isInitialized) { + mod.jumpToCurrentTrack(); + } + + isInitialized = true; var wrapperDiv, markerDiv, $td, @@ -1021,7 +1038,7 @@ var AIRTIME = (function(AIRTIME){ if (AIRTIME.button.isDisabled('icon-step-forward', true) === true) { return; } - + /* var $scroll = $sbContent.find(".dataTables_scrolling"), scrolled = $scroll.scrollTop(), scrollingTop = $scroll.offset().top, @@ -1029,6 +1046,8 @@ var AIRTIME = (function(AIRTIME){ currentTop = current.offset().top; $scroll.scrollTop(currentTop - scrollingTop + scrolled); + */ + mod.jumpToCurrentTrack(); }); //delete overbooked tracks. @@ -1196,7 +1215,7 @@ var AIRTIME = (function(AIRTIME){ }; } - }); + }); }; return AIRTIME; From d97afabaea958660053db8ff7f2e3a474d4424d3 Mon Sep 17 00:00:00 2001 From: denise <denise@denise-DX4860sourcefabric.org> Date: Mon, 29 Oct 2012 15:40:58 -0400 Subject: [PATCH 17/17] CC-4640: Automatically jump to current song when loading the Now Playing page. -added check to see if a show is currently playing --- airtime_mvc/public/js/airtime/showbuilder/builder.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/public/js/airtime/showbuilder/builder.js b/airtime_mvc/public/js/airtime/showbuilder/builder.js index daf44a510..21d0b5c43 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/builder.js @@ -649,7 +649,9 @@ var AIRTIME = (function(AIRTIME){ }, "fnDrawCallback": function fnBuilderDrawCallback(oSettings, json) { if (!isInitialized) { - mod.jumpToCurrentTrack(); + if ($(this).find("."+NOW_PLAYING_CLASS).length > 0) { + mod.jumpToCurrentTrack(); + } } isInitialized = true;