Merge branch '2.3.x' into 2.3.x-saas

This commit is contained in:
Martin Konecny 2013-03-12 16:38:16 -04:00
commit 13b3eec0ad
8 changed files with 126 additions and 29 deletions

2
debian/control vendored
View file

@ -41,7 +41,7 @@ Depends: apache2,
pwgen, pwgen,
python, python,
rabbitmq-server, rabbitmq-server,
silan, silan (>= 0.3.0~),
sudo, sudo,
sysv-rc, sysv-rc,
tar (>= 1.22), tar (>= 1.22),

View file

@ -415,6 +415,7 @@ def file_playable(pathname):
""" Returns True if 'pathname' is playable by liquidsoap. False """ Returns True if 'pathname' is playable by liquidsoap. False
otherwise. """ otherwise. """
#currently disabled because this confuses inotify....
return True return True
#remove all write permissions. This is due to stupid taglib library bug #remove all write permissions. This is due to stupid taglib library bug
#where all files are opened in write mode. The only way around this is to #where all files are opened in write mode. The only way around this is to

View file

@ -42,13 +42,13 @@ queue = amplify(1., override="replay_gain", queue)
# the crossfade function controls fade in/out # the crossfade function controls fade in/out
queue = crossfade_airtime(queue) queue = crossfade_airtime(queue)
queue = on_metadata(notify, queue) queue = on_metadata(notify, queue)
queue = map_metadata(update=false, append_title, queue)
output.dummy(fallible=true, queue) output.dummy(fallible=true, queue)
http = input.http_restart(id="http") http = input.http_restart(id="http")
http = cross_http(http_input_id="http",http) http = cross_http(http_input_id="http",http)
output.dummy(fallible=true, http) output.dummy(fallible=true, http)
stream_queue = http_fallback(http_input_id="http", http=http, default=queue) stream_queue = http_fallback(http_input_id="http", http=http, default=queue)
stream_queue = map_metadata(update=false, append_title, stream_queue)
ignore(output.dummy(stream_queue, fallible=true)) ignore(output.dummy(stream_queue, fallible=true))

View file

@ -14,14 +14,14 @@ def get_process_output(command):
Run subprocess and return stdout Run subprocess and return stdout
""" """
logger.debug(command) logger.debug(command)
p = Popen(command, shell=True, stdout=PIPE) p = Popen(command, stdout=PIPE, stderr=PIPE)
return p.communicate()[0].strip() return p.communicate()[0].strip()
def run_process(command): def run_process(command):
""" """
Run subprocess and return "return code" Run subprocess and return "return code"
""" """
p = Popen(command, shell=True) p = Popen(command, stdout=PIPE, stderr=PIPE)
return os.waitpid(p.pid, 0)[1] return os.waitpid(p.pid, 0)[1]
def get_mime_type(file_path): def get_mime_type(file_path):
@ -31,7 +31,8 @@ def get_mime_type(file_path):
for files which do not have a mp3/ogg/flac extension. for files which do not have a mp3/ogg/flac extension.
""" """
return get_process_output("timeout 5 file -b --mime-type %s" % file_path) command = ['timeout', '5', 'file', '-b', '--mime-type', file_path]
return get_process_output(command)
def duplicate_file(file_path): def duplicate_file(file_path):
""" """
@ -89,41 +90,37 @@ def calculate_replay_gain(file_path):
temp_file_path = duplicate_file(file_path) temp_file_path = duplicate_file(file_path)
file_type = get_file_type(file_path) file_type = get_file_type(file_path)
nice_level = '15' nice_level = '19'
if file_type: if file_type:
if file_type == 'mp3': if file_type == 'mp3':
if run_process("which mp3gain > /dev/null") == 0: if run_process(['which', 'mp3gain']) == 0:
command = 'nice -n %s mp3gain -q "%s" 2> /dev/null' \ command = ['nice', '-n', nice_level, 'mp3gain', '-q', temp_file_path]
% (nice_level, temp_file_path)
out = get_process_output(command) out = get_process_output(command)
search = re.search(r'Recommended "Track" dB change: (.*)', \ search = re.search(r'Recommended "Track" dB change: (.*)', \
out) out)
else: else:
logger.warn("mp3gain not found") logger.warn("mp3gain not found")
elif file_type == 'vorbis': elif file_type == 'vorbis':
command = "which vorbisgain > /dev/null && which ogginfo > \ if run_process(['which', 'ogginfo']) == 0 and \
/dev/null" run_process(['which', 'vorbisgain']) == 0:
if run_process(command) == 0: command = ['nice', '-n', nice_level, 'vorbisgain', '-q', '-f', temp_file_path]
command = 'nice -n %s vorbisgain -q -f "%s" 2>/dev/null \
>/dev/null' % (nice_level,temp_file_path)
run_process(command) run_process(command)
out = get_process_output('ogginfo "%s"' % temp_file_path) out = get_process_output(['ogginfo', temp_file_path])
search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out)
else: else:
logger.warn("vorbisgain/ogginfo not found") logger.warn("vorbisgain/ogginfo not found")
elif file_type == 'flac': elif file_type == 'flac':
if run_process("which metaflac > /dev/null") == 0: if run_process(['which', 'metaflac']) == 0:
command = 'nice -n %s metaflac --add-replay-gain "%s"' \ command = ['nice', '-n', nice_level, 'metaflac', \
% (nice_level, temp_file_path) '--add-replay-gain', temp_file_path]
run_process(command) run_process(command)
command = 'nice -n %s metaflac \ command = ['nice', '-n', nice_level, 'metaflac', \
--show-tag=REPLAYGAIN_TRACK_GAIN "%s"' \ '--show-tag=REPLAYGAIN_TRACK_GAIN', \
% (nice_level, temp_file_path) temp_file_path]
out = get_process_output(command) out = get_process_output(command)
search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out) search = re.search(r'REPLAYGAIN_TRACK_GAIN=(.*) dB', out)
else: logger.warn("metaflac not found") else: logger.warn("metaflac not found")

View file

@ -0,0 +1,84 @@
from threading import Thread
import traceback
import time
import subprocess
import json
class SilanAnalyzer(Thread):
"""
The purpose of the class is to query the server for a list of files which
do not have a Silan value calculated. This class will iterate over the
list calculate the values, update the server and repeat the process until
the server reports there are no files left.
"""
@staticmethod
def start_silan(apc, logger):
me = SilanAnalyzer(apc, logger)
me.start()
def __init__(self, apc, logger):
Thread.__init__(self)
self.api_client = apc
self.logger = logger
def main(self):
while True:
# keep getting few rows at a time for current music_dir (stor
# or watched folder).
total = 0
# return a list of pairs where the first value is the
# file's database row id and the second value is the
# filepath
files = self.api_client.get_files_without_silan_value()
total_files = len(files)
if total_files == 0: return
processed_data = []
for f in files:
full_path = f['fp']
# silence detect(set default queue in and out)
try:
command = ['nice', '-n', '19', 'silan', '-b', '-f', 'JSON', full_path]
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
out = proc.communicate()[0].strip('\r\n')
info = json.loads(out)
data = {}
data['cuein'] = str('{0:f}'.format(info['sound'][0][0]))
data['cueout'] = str('{0:f}'.format(info['sound'][-1][1]))
processed_data.append((f['id'], data))
total += 1
if total % 5 == 0:
self.logger.info("Total %s / %s files has been processed.." % (total, total_files))
except Exception, e:
self.logger.error(e)
self.logger.error(traceback.format_exc())
try:
self.api_client.update_cue_values_by_silan(processed_data)
except Exception ,e:
self.logger.error(e)
self.logger.error(traceback.format_exc())
self.logger.info("Processed: %d songs" % total)
def run(self):
while True:
try:
self.logger.info("Running Silan analyzer")
self.main()
except Exception, e:
self.logger.error('Silan Analyzer Exception: %s', traceback.format_exc())
self.logger.error(e)
self.logger.info("Sleeping for 5...")
time.sleep(60 * 5)
if __name__ == "__main__":
from api_clients import api_client
import logging
logging.basicConfig(level=logging.DEBUG)
api_client = api_client.AirtimeApiClient()
SilanAnalyzer.start_silan(api_client, logging)

View file

@ -25,6 +25,7 @@ from listenerstat import ListenerStat
from pypomessagehandler import PypoMessageHandler from pypomessagehandler import PypoMessageHandler
from media.update.replaygainupdater import ReplayGainUpdater from media.update.replaygainupdater import ReplayGainUpdater
from media.update.silananalyzer import SilanAnalyzer
from configobj import ConfigObj from configobj import ConfigObj
@ -126,21 +127,21 @@ def keyboardInterruptHandler(signum, frame):
def liquidsoap_running_test(telnet_lock, host, port, logger): def liquidsoap_running_test(telnet_lock, host, port, logger):
logger.debug("Checking to see if Liquidsoap is running") logger.debug("Checking to see if Liquidsoap is running")
success = True
try: try:
telnet_lock.acquire() telnet_lock.acquire()
tn = telnetlib.Telnet(host, port) tn = telnetlib.Telnet(host, port)
msg = "version\n" msg = "version\n"
tn.write(msg) tn.write(msg)
tn.write("exit\n") tn.write("exit\n")
logger.info("Found: %s", tn.read_all()) response = tn.read_all()
logger.info("Found: %s", response)
except Exception, e: except Exception, e:
logger.error(str(e)) logger.error(str(e))
success = False return False
finally: finally:
telnet_lock.release() telnet_lock.release()
return success return "Liquidsoap" in response
if __name__ == '__main__': if __name__ == '__main__':
@ -178,6 +179,7 @@ if __name__ == '__main__':
api_client = api_client.AirtimeApiClient() api_client = api_client.AirtimeApiClient()
ReplayGainUpdater.start_reply_gain(api_client) ReplayGainUpdater.start_reply_gain(api_client)
SilanAnalyzer.start_silan(api_client, logger)
success = False success = False
while not success: while not success:

View file

@ -372,7 +372,17 @@ class PypoPush(Thread):
first_link = chain[0] first_link = chain[0]
self.modify_cue_point(first_link) self.modify_cue_point(first_link)
if float(first_link['cue_in']) >= float(first_link['cue_out']):
#ATM, we should never have an exception here. However in the future, it
#would be nice to allow cue_out to be None which would then allow us to
#fallback to the length of the track as the end point.
try:
end = first_link['cue_out']
except TypeError:
#cue_out is type None
end = first_link['length']
if float(first_link['cue_in']) >= float(end):
chain = chain [1:] chain = chain [1:]
return chain return chain
@ -498,6 +508,11 @@ class PypoPush(Thread):
self.logger.debug(msg) self.logger.debug(msg)
tn.write(msg) tn.write(msg)
show_name = media_item['show_name']
msg = 'vars.show_name %s\n' % show_name.encode('utf-8')
tn.write(msg)
self.logger.debug(msg)
tn.write("exit\n") tn.write("exit\n")
self.logger.debug(tn.read_all()) self.logger.debug(tn.read_all())

View file

@ -3,8 +3,6 @@ from api_clients import api_client as apc
import logging import logging
import json import json
import shutil
import commands
import os import os
import sys import sys
import subprocess import subprocess