add messenger class for unifying logging and mail sending
This commit is contained in:
parent
1c105f0d49
commit
73bf0f71c0
103
ffplayout.py
103
ffplayout.py
@ -271,7 +271,29 @@ class Mailer:
|
|||||||
self.send_mail(msg)
|
self.send_mail(msg)
|
||||||
|
|
||||||
|
|
||||||
mailer = Mailer()
|
class Messenger:
|
||||||
|
"""
|
||||||
|
all logging and mail messages end up here,
|
||||||
|
from here they go to logger and mailer
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._mailer = Mailer()
|
||||||
|
|
||||||
|
def debug(self, msg):
|
||||||
|
logger.debug(msg.replace('\\n', ' '))
|
||||||
|
|
||||||
|
def info(self, msg):
|
||||||
|
logger.info(msg.replace('\\n', ' '))
|
||||||
|
self._mailer.info(msg)
|
||||||
|
|
||||||
|
def warning(self, msg):
|
||||||
|
logger.warning(msg.replace('\\n', ' '))
|
||||||
|
self._mailer.warning(msg)
|
||||||
|
|
||||||
|
def error(self, msg):
|
||||||
|
logger.error(msg.replace('\\n', ' '))
|
||||||
|
self._mailer.error(msg)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@ -284,12 +306,13 @@ class MediaProbe:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def load(self, file):
|
def load(self, file):
|
||||||
|
self.src = file
|
||||||
self.format = None
|
self.format = None
|
||||||
self.audio = []
|
self.audio = []
|
||||||
self.video = []
|
self.video = []
|
||||||
|
|
||||||
cmd = ['ffprobe', '-v', 'quiet', '-print_format',
|
cmd = ['ffprobe', '-v', 'quiet', '-print_format',
|
||||||
'json', '-show_format', '-show_streams', file]
|
'json', '-show_format', '-show_streams', self.src]
|
||||||
|
|
||||||
info = json.loads(check_output(cmd).decode(encoding='UTF-8'))
|
info = json.loads(check_output(cmd).decode(encoding='UTF-8'))
|
||||||
|
|
||||||
@ -403,7 +426,7 @@ def valid_json(file):
|
|||||||
json_object = json.load(file)
|
json_object = json.load(file)
|
||||||
return json_object
|
return json_object
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.error("Playlist {} is not JSON conform".format(file))
|
Messenger.error("Playlist {} is not JSON conform".format(file))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -421,13 +444,9 @@ def check_sync(begin, encoder):
|
|||||||
|
|
||||||
# check that we are in tolerance time
|
# check that we are in tolerance time
|
||||||
if _general.stop and abs(time_distance) > _general.threshold:
|
if _general.stop and abs(time_distance) > _general.threshold:
|
||||||
mailer.error(
|
Messenger.error(
|
||||||
'Sync tolerance value exceeded with {0:.2f} seconds,\n'
|
'Sync tolerance value exceeded with {0:.2f} seconds,\n'
|
||||||
'program terminated!'.format(time_distance))
|
'program terminated!'.format(time_distance))
|
||||||
logger.error(
|
|
||||||
('Sync tolerance value exceeded with '
|
|
||||||
'{0:.2f} seconds, program terminated!').format(time_distance)
|
|
||||||
)
|
|
||||||
encoder.terminate()
|
encoder.terminate()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@ -447,14 +466,12 @@ def check_length(json_nodes, total_play_time):
|
|||||||
date = get_date(True)
|
date = get_date(True)
|
||||||
|
|
||||||
if total_play_time < length - 5:
|
if total_play_time < length - 5:
|
||||||
mailer.error(
|
Messenger.error(
|
||||||
'Playlist ({}) is not long enough!\n'
|
'Playlist ({}) is not long enough!\n'
|
||||||
'total play time is: {}'.format(
|
'Total play time is: {}'.format(
|
||||||
date,
|
date,
|
||||||
timedelta(seconds=total_play_time))
|
timedelta(seconds=total_play_time))
|
||||||
)
|
)
|
||||||
logger.error('Playlist is only {} hours long!'.format(
|
|
||||||
timedelta(seconds=total_play_time)))
|
|
||||||
|
|
||||||
|
|
||||||
def validate_thread(clip_nodes):
|
def validate_thread(clip_nodes):
|
||||||
@ -498,11 +515,10 @@ def validate_thread(clip_nodes):
|
|||||||
|
|
||||||
line = '\n'.join(missing)
|
line = '\n'.join(missing)
|
||||||
if line:
|
if line:
|
||||||
logger.error('Validation error :: {}'.format(line))
|
|
||||||
error += line + '\nIn line: {}\n\n'.format(node)
|
error += line + '\nIn line: {}\n\n'.format(node)
|
||||||
|
|
||||||
if error:
|
if error:
|
||||||
mailer.error(
|
Messenger.error(
|
||||||
'Validation error, check JSON playlist, '
|
'Validation error, check JSON playlist, '
|
||||||
'values are missing:\n{}'.format(error)
|
'values are missing:\n{}'.format(error)
|
||||||
)
|
)
|
||||||
@ -537,7 +553,7 @@ def set_length(duration, seek, out):
|
|||||||
def loop_input(source, src_duration, target_duration):
|
def loop_input(source, src_duration, target_duration):
|
||||||
# loop filles n times
|
# loop filles n times
|
||||||
loop_count = math.ceil(target_duration / src_duration)
|
loop_count = math.ceil(target_duration / src_duration)
|
||||||
logger.info(
|
Messenger.info(
|
||||||
'Loop "{0}" {1} times, total duration: {2:.2f}'.format(
|
'Loop "{0}" {1} times, total duration: {2:.2f}'.format(
|
||||||
source, loop_count, target_duration))
|
source, loop_count, target_duration))
|
||||||
return ['-stream_loop', str(loop_count),
|
return ['-stream_loop', str(loop_count),
|
||||||
@ -567,7 +583,7 @@ def gen_filler(duration):
|
|||||||
"""
|
"""
|
||||||
if not _storage.filler:
|
if not _storage.filler:
|
||||||
# when no filler is set, generate a dummy
|
# when no filler is set, generate a dummy
|
||||||
logger.warning('No filler is set!')
|
Messenger.warning('No filler is set!')
|
||||||
return gen_dummy(duration)
|
return gen_dummy(duration)
|
||||||
else:
|
else:
|
||||||
# get duration from filler
|
# get duration from filler
|
||||||
@ -583,7 +599,7 @@ def gen_filler(duration):
|
|||||||
if file_duration:
|
if file_duration:
|
||||||
if file_duration > duration:
|
if file_duration > duration:
|
||||||
# cut filler
|
# cut filler
|
||||||
logger.info(
|
Messenger.info(
|
||||||
'Generate filler with {0:.2f} seconds'.format(duration))
|
'Generate filler with {0:.2f} seconds'.format(duration))
|
||||||
return ['-i', _storage.filler] + set_length(
|
return ['-i', _storage.filler] + set_length(
|
||||||
file_duration, 0, duration)
|
file_duration, 0, duration)
|
||||||
@ -591,7 +607,7 @@ def gen_filler(duration):
|
|||||||
# loop file n times
|
# loop file n times
|
||||||
return loop_input(_storage.filler, file_duration, duration)
|
return loop_input(_storage.filler, file_duration, duration)
|
||||||
else:
|
else:
|
||||||
logger.error("Can't get filler length, generate dummy!")
|
Messenger.error("Can't get filler length, generate dummy!")
|
||||||
return gen_dummy(duration)
|
return gen_dummy(duration)
|
||||||
|
|
||||||
|
|
||||||
@ -609,8 +625,7 @@ def src_or_dummy(src, dur, seek, out):
|
|||||||
else:
|
else:
|
||||||
return seek_in(seek) + ['-i', src] + set_length(dur, seek, out)
|
return seek_in(seek) + ['-i', src] + set_length(dur, seek, out)
|
||||||
else:
|
else:
|
||||||
mailer.error('Clip not exist:\n{}'.format(src))
|
Messenger.error('Clip not exist:\n{}'.format(src))
|
||||||
logger.error('Clip not exist: {}'.format(src))
|
|
||||||
return gen_dummy(out - seek)
|
return gen_dummy(out - seek)
|
||||||
|
|
||||||
|
|
||||||
@ -644,23 +659,22 @@ def gen_input(src, begin, dur, seek, out, last):
|
|||||||
new_len = dur - (time_diff - ref_time)
|
new_len = dur - (time_diff - ref_time)
|
||||||
|
|
||||||
if time_diff >= ref_time:
|
if time_diff >= ref_time:
|
||||||
logger.info('we are under time, new_len is: {0:.2f}'.format(
|
Messenger.info('we are under time, new_len is: {0:.2f}'.format(
|
||||||
new_len))
|
new_len))
|
||||||
src_cmd = src_or_dummy(src, dur, 0, new_len)
|
src_cmd = src_or_dummy(src, dur, 0, new_len)
|
||||||
else:
|
else:
|
||||||
src_cmd = src_or_dummy(src, dur, 0, dur)
|
src_cmd = src_or_dummy(src, dur, 0, dur)
|
||||||
|
|
||||||
mailer.error(
|
Messenger.error(
|
||||||
'Playlist is not long enough:\n{0:.2f} seconds needed.'.format(
|
'Playlist is not long enough:\n{0:.2f} seconds needed.'.format(
|
||||||
new_len))
|
new_len))
|
||||||
logger.error('Playlist is {} seconds to short'.format(new_len))
|
|
||||||
|
|
||||||
return src_cmd, new_len - dur
|
return src_cmd, new_len - dur
|
||||||
|
|
||||||
elif time_diff > ref_time:
|
elif time_diff > ref_time:
|
||||||
new_len = out - seek - (time_diff - ref_time)
|
new_len = out - seek - (time_diff - ref_time)
|
||||||
# when we over the 24 hours range, trim clip
|
# when we over the 24 hours range, trim clip
|
||||||
logger.info('we are over time, new_len is: {0:.2f}'.format(new_len))
|
Messenger.info('we are over time, new_len is: {0:.2f}'.format(new_len))
|
||||||
|
|
||||||
# When calculated length from last clip is longer then 5 seconds,
|
# When calculated length from last clip is longer then 5 seconds,
|
||||||
# we use the clip. When the length is less then 5 and bigger then 1
|
# we use the clip. When the length is less then 5 and bigger then 1
|
||||||
@ -806,7 +820,7 @@ def add_audio(probe, duration):
|
|||||||
line = []
|
line = []
|
||||||
|
|
||||||
if not probe.audio:
|
if not probe.audio:
|
||||||
logger.warning('Clip has no audio!')
|
Messenger.warning('Clip "{}" has no audio!'.format(probe.src))
|
||||||
line = [
|
line = [
|
||||||
'aevalsrc=0:channel_layout=2:duration={}:sample_rate={}'.format(
|
'aevalsrc=0:channel_layout=2:duration={}:sample_rate={}'.format(
|
||||||
duration, 48000)]
|
duration, 48000)]
|
||||||
@ -980,19 +994,20 @@ class MediaWatcher:
|
|||||||
|
|
||||||
self._media.add(event.src_path)
|
self._media.add(event.src_path)
|
||||||
|
|
||||||
logger.info('Add file to media list: "{}"'.format(event.src_path))
|
Messenger.info('Add file to media list: "{}"'.format(event.src_path))
|
||||||
|
|
||||||
def on_moved(self, event):
|
def on_moved(self, event):
|
||||||
self._media.remove(event.src_path)
|
self._media.remove(event.src_path)
|
||||||
self._media.add(event.dest_path)
|
self._media.add(event.dest_path)
|
||||||
|
|
||||||
logger.info('Move file from "{}" to "{}"'.format(event.src_path,
|
Messenger.info('Move file from "{}" to "{}"'.format(event.src_path,
|
||||||
event.dest_path))
|
event.dest_path))
|
||||||
|
|
||||||
def on_deleted(self, event):
|
def on_deleted(self, event):
|
||||||
self._media.remove(event.src_path)
|
self._media.remove(event.src_path)
|
||||||
|
|
||||||
logger.info('Remove file from media list: "{}"'.format(event.src_path))
|
Messenger.info(
|
||||||
|
'Remove file from media list: "{}"'.format(event.src_path))
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.observer.stop()
|
self.observer.stop()
|
||||||
@ -1112,7 +1127,7 @@ class GetSourceIter(object):
|
|||||||
if mod_time > self.last_mod_time:
|
if mod_time > self.last_mod_time:
|
||||||
self.clip_nodes = valid_json(req)
|
self.clip_nodes = valid_json(req)
|
||||||
self.last_mod_time = mod_time
|
self.last_mod_time = mod_time
|
||||||
logger.info('open: ' + self.json_file)
|
Messenger.info('open: ' + self.json_file)
|
||||||
validate_thread(self.clip_nodes)
|
validate_thread(self.clip_nodes)
|
||||||
except (request.URLError, socket.timeout):
|
except (request.URLError, socket.timeout):
|
||||||
self.eof_handling('Get playlist from url failed!', False)
|
self.eof_handling('Get playlist from url failed!', False)
|
||||||
@ -1125,7 +1140,7 @@ class GetSourceIter(object):
|
|||||||
self.clip_nodes = valid_json(f)
|
self.clip_nodes = valid_json(f)
|
||||||
|
|
||||||
self.last_mod_time = mod_time
|
self.last_mod_time = mod_time
|
||||||
logger.info('open: ' + self.json_file)
|
Messenger.info('open: ' + self.json_file)
|
||||||
validate_thread(self.clip_nodes)
|
validate_thread(self.clip_nodes)
|
||||||
else:
|
else:
|
||||||
# when we have no playlist for the current day,
|
# when we have no playlist for the current day,
|
||||||
@ -1161,12 +1176,11 @@ class GetSourceIter(object):
|
|||||||
try:
|
try:
|
||||||
output = check_output(cmd).decode('utf-8')
|
output = check_output(cmd).decode('utf-8')
|
||||||
except CalledProcessError as err:
|
except CalledProcessError as err:
|
||||||
logger.error("ffprobe error: {}".format(err))
|
Messenger.error("ffprobe error: {}".format(err))
|
||||||
output = None
|
output = None
|
||||||
|
|
||||||
if not output:
|
if not output:
|
||||||
mailer.error('Clip not exist:\n{}'.format(self.src))
|
Messenger.error('Clip not exist:\n{}'.format(self.src))
|
||||||
logger.error('Clip not exist: {}'.format(self.src))
|
|
||||||
self.src = None
|
self.src = None
|
||||||
elif is_float(output):
|
elif is_float(output):
|
||||||
self.duration = float(output)
|
self.duration = float(output)
|
||||||
@ -1241,7 +1255,6 @@ class GetSourceIter(object):
|
|||||||
self.duration = abs(new_len)
|
self.duration = abs(new_len)
|
||||||
self.list_date = get_date(False)
|
self.list_date = get_date(False)
|
||||||
self.last_mod_time = 0.0
|
self.last_mod_time = 0.0
|
||||||
# TODO: remove -> self.first?
|
|
||||||
self.first = False
|
self.first = False
|
||||||
self.last_time = 0.0
|
self.last_time = 0.0
|
||||||
|
|
||||||
@ -1262,10 +1275,10 @@ class GetSourceIter(object):
|
|||||||
if get_time('stamp') - self.timestamp > 3600 \
|
if get_time('stamp') - self.timestamp > 3600 \
|
||||||
and message != self.last_error:
|
and message != self.last_error:
|
||||||
self.last_error = message
|
self.last_error = message
|
||||||
mailer.error('{}\n{}'.format(message, self.json_file))
|
Messenger.error('{}\n{}'.format(message, self.json_file))
|
||||||
self.timestamp = get_time('stamp')
|
self.timestamp = get_time('stamp')
|
||||||
|
|
||||||
logger.error('{} {}'.format(message, self.json_file))
|
Messenger.error('{} {}'.format(message, self.json_file))
|
||||||
|
|
||||||
self.last = False
|
self.last = False
|
||||||
|
|
||||||
@ -1347,7 +1360,7 @@ class GetSourceIter(object):
|
|||||||
else:
|
else:
|
||||||
if not _playlist.start or 'length' not in self.clip_nodes:
|
if not _playlist.start or 'length' not in self.clip_nodes:
|
||||||
# when we reach currect end, stop script
|
# when we reach currect end, stop script
|
||||||
logger.info('Playlist reach End!')
|
Messenger.info('Playlist reach End!')
|
||||||
return
|
return
|
||||||
|
|
||||||
elif self.begin == self.init_time:
|
elif self.begin == self.init_time:
|
||||||
@ -1380,7 +1393,7 @@ def main():
|
|||||||
'-f', 'mpegts', '-']
|
'-f', 'mpegts', '-']
|
||||||
|
|
||||||
if os.path.isfile(_text.textfile):
|
if os.path.isfile(_text.textfile):
|
||||||
logger.info('Overlay text file: "{}"'.format(_text.textfile))
|
Messenger.info('Overlay text file: "{}"'.format(_text.textfile))
|
||||||
overlay = [
|
overlay = [
|
||||||
'-vf', ("drawtext=box={}:boxcolor='{}':boxborderw={}"
|
'-vf', ("drawtext=box={}:boxcolor='{}':boxborderw={}"
|
||||||
":fontsize={}:fontcolor={}:fontfile='{}':textfile={}"
|
":fontsize={}:fontcolor={}:fontfile='{}':textfile={}"
|
||||||
@ -1411,20 +1424,20 @@ def main():
|
|||||||
watcher = None
|
watcher = None
|
||||||
get_source = GetSourceIter(encoder)
|
get_source = GetSourceIter(encoder)
|
||||||
else:
|
else:
|
||||||
logger.info("start folder mode")
|
Messenger.info("start folder mode")
|
||||||
media = MediaStore()
|
media = MediaStore()
|
||||||
watcher = MediaWatcher(media)
|
watcher = MediaWatcher(media)
|
||||||
get_source = GetSource(media)
|
get_source = GetSource(media)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for src_cmd in get_source.next():
|
for src_cmd in get_source.next():
|
||||||
logger.debug('src_cmd: "{}"'.format(src_cmd))
|
Messenger.debug('src_cmd: "{}"'.format(src_cmd))
|
||||||
if src_cmd[0] == '-i':
|
if src_cmd[0] == '-i':
|
||||||
current_file = src_cmd[1]
|
current_file = src_cmd[1]
|
||||||
else:
|
else:
|
||||||
current_file = src_cmd[3]
|
current_file = src_cmd[3]
|
||||||
|
|
||||||
logger.info('play: "{}"'.format(current_file))
|
Messenger.info('play: "{}"'.format(current_file))
|
||||||
|
|
||||||
with Popen([
|
with Popen([
|
||||||
'ffmpeg', '-v', 'error', '-hide_banner', '-nostats'
|
'ffmpeg', '-v', 'error', '-hide_banner', '-nostats'
|
||||||
@ -1438,15 +1451,15 @@ def main():
|
|||||||
encoder.stdin.write(buf)
|
encoder.stdin.write(buf)
|
||||||
|
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
logger.error('Broken Pipe!')
|
Messenger.error('Broken Pipe!')
|
||||||
terminate_processes(decoder, encoder, watcher)
|
terminate_processes(decoder, encoder, watcher)
|
||||||
|
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
logger.info("got close command")
|
Messenger.info("got close command")
|
||||||
terminate_processes(decoder, encoder, watcher)
|
terminate_processes(decoder, encoder, watcher)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.warning('program terminated')
|
Messenger.warning('program terminated')
|
||||||
terminate_processes(decoder, encoder, watcher)
|
terminate_processes(decoder, encoder, watcher)
|
||||||
|
|
||||||
# close encoder when nothing is to do anymore
|
# close encoder when nothing is to do anymore
|
||||||
|
Loading…
x
Reference in New Issue
Block a user