change variable names, to try to follow style conventions

This commit is contained in:
jb-alvarado 2021-03-25 14:39:03 +01:00
parent 6abadf9543
commit 0a1467b093
11 changed files with 295 additions and 287 deletions

View File

@ -21,7 +21,7 @@
import os import os
from pydoc import locate from pydoc import locate
from ffplayout.utils import PLAYOUT, STDIN_ARGS, validate_ffmpeg_libs from ffplayout.utils import playout, stdin_args, validate_ffmpeg_libs
try: try:
if os.name != 'posix': if os.name != 'posix':
@ -40,8 +40,8 @@ def main():
play out depending on output mode play out depending on output mode
""" """
if STDIN_ARGS.mode: if stdin_args.mode:
output = locate(f'ffplayout.output.{STDIN_ARGS.mode}.output') output = locate(f'ffplayout.output.{stdin_args.mode}.output')
output() output()
else: else:
@ -53,7 +53,7 @@ def main():
and output != '__init__.py': and output != '__init__.py':
mode = os.path.splitext(output)[0] mode = os.path.splitext(output)[0]
if mode == PLAYOUT.mode: if mode == playout.mode:
output = locate(f'ffplayout.output.{mode}.output') output = locate(f'ffplayout.output.{mode}.output')
output() output()

View File

@ -1,4 +1,4 @@
from ffplayout.utils import STDIN_ARGS, get_float from ffplayout.utils import get_float, stdin_args
def filter_link(node): def filter_link(node):
@ -6,5 +6,5 @@ def filter_link(node):
set audio volume set audio volume
""" """
if STDIN_ARGS.volume and get_float(STDIN_ARGS.volume, False): if stdin_args.volume and get_float(stdin_args.volume, False):
return f'volume={STDIN_ARGS.volume}' return f'volume={stdin_args.volume}'

View File

@ -23,7 +23,8 @@ import re
from glob import glob from glob import glob
from pydoc import locate from pydoc import locate
from ffplayout.utils import GENERAL, PRE, TEXT, is_advertisement, messenger from ffplayout.utils import (is_advertisement, lower_third, messenger, pre,
sync_op)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# building filters, # building filters,
@ -35,12 +36,12 @@ def text_filter():
filter_chain = [] filter_chain = []
font = '' font = ''
if TEXT.add_text and TEXT.over_pre: if lower_third.add_text and lower_third.over_pre:
if TEXT.fontfile and os.path.isfile(TEXT.fontfile): if lower_third.fontfile and os.path.isfile(lower_third.fontfile):
font = f":fontfile='{TEXT.fontfile}'" font = f":fontfile='{lower_third.fontfile}'"
filter_chain = [ filter_chain = [
"null,zmq=b=tcp\\\\://'{}',drawtext=text=''{}".format( "null,zmq=b=tcp\\\\://'{}',drawtext=text=''{}".format(
TEXT.address.replace(':', '\\:'), font)] lower_third.address.replace(':', '\\:'), font)]
return filter_chain return filter_chain
@ -67,13 +68,13 @@ def pad_filter(probe):
filter_chain = [] filter_chain = []
if not math.isclose(probe.video[0]['aspect'], if not math.isclose(probe.video[0]['aspect'],
PRE.aspect, abs_tol=0.03): pre.aspect, abs_tol=0.03):
if probe.video[0]['aspect'] < PRE.aspect: if probe.video[0]['aspect'] < pre.aspect:
filter_chain.append( filter_chain.append(
f'pad=ih*{PRE.w}/{PRE.h}/sar:ih:(ow-iw)/2:(oh-ih)/2') f'pad=ih*{pre.w}/{pre.h}/sar:ih:(ow-iw)/2:(oh-ih)/2')
elif probe.video[0]['aspect'] > PRE.aspect: elif probe.video[0]['aspect'] > pre.aspect:
filter_chain.append( filter_chain.append(
f'pad=iw:iw*{PRE.h}/{PRE.w}/sar:(ow-iw)/2:(oh-ih)/2') f'pad=iw:iw*{pre.h}/{pre.w}/sar:(ow-iw)/2:(oh-ih)/2')
return filter_chain return filter_chain
@ -84,8 +85,8 @@ def fps_filter(probe):
""" """
filter_chain = [] filter_chain = []
if probe.video[0]['fps'] != PRE.fps: if probe.video[0]['fps'] != pre.fps:
filter_chain.append(f'fps={PRE.fps}') filter_chain.append(f'fps={pre.fps}')
return filter_chain return filter_chain
@ -97,13 +98,13 @@ def scale_filter(probe):
""" """
filter_chain = [] filter_chain = []
if int(probe.video[0]['width']) != PRE.w or \ if int(probe.video[0]['width']) != pre.w or \
int(probe.video[0]['height']) != PRE.h: int(probe.video[0]['height']) != pre.h:
filter_chain.append(f'scale={PRE.w}:{PRE.h}') filter_chain.append(f'scale={pre.w}:{pre.h}')
if not math.isclose(probe.video[0]['aspect'], if not math.isclose(probe.video[0]['aspect'],
PRE.aspect, abs_tol=0.03): pre.aspect, abs_tol=0.03):
filter_chain.append(f'setdar=dar={PRE.aspect}') filter_chain.append(f'setdar=dar={pre.aspect}')
return filter_chain return filter_chain
@ -132,22 +133,22 @@ def overlay_filter(duration, ad, ad_last, ad_next):
logo_filter = '[v]null' logo_filter = '[v]null'
scale_filter = '' scale_filter = ''
if PRE.add_logo and os.path.isfile(PRE.logo) and not ad: if pre.add_logo and os.path.isfile(pre.logo) and not ad:
logo_chain = [] logo_chain = []
if PRE.logo_scale and \ if pre.logo_scale and \
re.match(r'\d+:-?\d+', PRE.logo_scale): re.match(r'\d+:-?\d+', pre.logo_scale):
scale_filter = f'scale={PRE.logo_scale},' scale_filter = f'scale={pre.logo_scale},'
logo_extras = (f'format=rgba,{scale_filter}' logo_extras = (f'format=rgba,{scale_filter}'
f'colorchannelmixer=aa={PRE.logo_opacity}') f'colorchannelmixer=aa={pre.logo_opacity}')
loop = 'loop=loop=-1:size=1:start=0' loop = 'loop=loop=-1:size=1:start=0'
logo_chain.append(f'movie={PRE.logo},{loop},{logo_extras}') logo_chain.append(f'movie={pre.logo},{loop},{logo_extras}')
if ad_last: if ad_last:
logo_chain.append('fade=in:st=0:d=1.0:alpha=1') logo_chain.append('fade=in:st=0:d=1.0:alpha=1')
if ad_next: if ad_next:
logo_chain.append(f'fade=out:st={duration - 1}:d=1.0:alpha=1') logo_chain.append(f'fade=out:st={duration - 1}:d=1.0:alpha=1')
logo_filter = (f'{",".join(logo_chain)}[l];[v][l]' logo_filter = (f'{",".join(logo_chain)}[l];[v][l]'
f'{PRE.logo_filter}:shortest=1') f'{pre.logo_filter}:shortest=1')
return logo_filter return logo_filter
@ -172,9 +173,9 @@ def add_loudnorm(probe):
""" """
loud_filter = [] loud_filter = []
if probe.audio and PRE.add_loudnorm: if probe.audio and pre.add_loudnorm:
loud_filter = [ loud_filter = [
f'loudnorm=I={PRE.loud_i}:TP={PRE.loud_tp}:LRA={PRE.loud_lra}'] f'loudnorm=I={pre.loud_i}:TP={pre.loud_tp}:LRA={pre.loud_lra}']
return loud_filter return loud_filter
@ -209,11 +210,11 @@ def extend_video(probe, duration, target_duration):
def realtime_filter(duration, track=''): def realtime_filter(duration, track=''):
speed_filter = '' speed_filter = ''
if PRE.realtime: if pre.realtime:
speed_filter = f',{track}realtime=speed=1' speed_filter = f',{track}realtime=speed=1'
if GENERAL.time_delta < 0: if sync_op.time_delta < 0:
speed = duration / (duration + GENERAL.time_delta) speed = duration / (duration + sync_op.time_delta)
if speed < 1.1: if speed < 1.1:
speed_filter = f',{track}realtime=speed={speed}' speed_filter = f',{track}realtime=speed={speed}'
@ -229,11 +230,11 @@ def split_filter(filter_type):
if filter_type == 'a': if filter_type == 'a':
prefix = 'a' prefix = 'a'
if PRE.output_count > 1: if pre.output_count > 1:
for num in range(PRE.output_count): for num in range(pre.output_count):
map_node.append(f'[{filter_type}out{num + 1}]') map_node.append(f'[{filter_type}out{num + 1}]')
_filter = f',{prefix}split={PRE.output_count}{"".join(map_node)}' _filter = f',{prefix}split={pre.output_count}{"".join(map_node)}'
else: else:
_filter = f'[{filter_type}out1]' _filter = f'[{filter_type}out1]'
@ -241,11 +242,11 @@ def split_filter(filter_type):
return _filter return _filter
def custom_filter(type, node): def custom_filter(filter_type, node):
filter_dir = os.path.dirname(os.path.abspath(__file__)) filter_dir = os.path.dirname(os.path.abspath(__file__))
filters = [] filters = []
for filter_file in glob(os.path.join(filter_dir, f'{type}_*')): for filter_file in glob(os.path.join(filter_dir, f'{filter_type}_*')):
filter_ = os.path.splitext(os.path.basename(filter_file))[0] filter_ = os.path.splitext(os.path.basename(filter_file))[0]
filter_function = locate(f'ffplayout.filters.{filter_}.filter_link') filter_function = locate(f'ffplayout.filters.{filter_}.filter_link')
link = filter_function(node) link = filter_function(node)

View File

@ -1,7 +1,7 @@
import os import os
import re import re
from ffplayout.utils import TEXT from ffplayout.utils import lower_third
def filter_link(node): def filter_link(node):
@ -10,12 +10,12 @@ def filter_link(node):
""" """
font = '' font = ''
source = os.path.basename(node.get('source')) source = os.path.basename(node.get('source'))
match = re.match(TEXT.regex, source) match = re.match(lower_third.regex, source)
title = match[1] if match else source title = match[1] if match else source
if TEXT.fontfile and os.path.isfile(TEXT.fontfile): if lower_third.fontfile and os.path.isfile(lower_third.fontfile):
font = f":fontfile='{TEXT.fontfile}'" font = f":fontfile='{lower_third.fontfile}'"
if TEXT.text_from_filename: if lower_third.text_from_filename:
escape = title.replace("'", "'\\\\\\''").replace("%", "\\\\\\%") escape = title.replace("'", "'\\\\\\''").replace("%", "\\\\\\%")
return f"drawtext=text='{escape}':{TEXT.style}{font}" return f"drawtext=text='{escape}':{lower_third.style}{font}"

View File

@ -27,7 +27,7 @@ from watchdog.events import PatternMatchingEventHandler
from watchdog.observers import Observer from watchdog.observers import Observer
from .filters.default import build_filtergraph from .filters.default import build_filtergraph
from .utils import FF, STDIN_ARGS, STORAGE, MediaProbe, messenger from .utils import MediaProbe, ff_proc, messenger, stdin_args, storage
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# folder watcher # folder watcher
@ -43,21 +43,21 @@ class MediaStore:
def __init__(self): def __init__(self):
self.store = [] self.store = []
if STDIN_ARGS.folder: if stdin_args.folder:
self.folder = STDIN_ARGS.folder self.folder = stdin_args.folder
else: else:
self.folder = STORAGE.path self.folder = storage.path
self.fill() self.fill()
def fill(self): def fill(self):
for ext in STORAGE.extensions: for ext in storage.extensions:
self.store.extend( self.store.extend(
glob.glob(os.path.join(self.folder, '**', f'*{ext}'), glob.glob(os.path.join(self.folder, '**', f'*{ext}'),
recursive=True)) recursive=True))
def sort_or_radomize(self): def sort_or_radomize(self):
if STORAGE.shuffle: if storage.shuffle:
self.rand() self.rand()
else: else:
self.sort() self.sort()
@ -86,7 +86,7 @@ class MediaWatcher:
def __init__(self, media): def __init__(self, media):
self._media = media self._media = media
self.extensions = [f'*{ext}' for ext in STORAGE.extensions] self.extensions = [f'*{ext}' for ext in storage.extensions]
self.current_clip = None self.current_clip = None
self.event_handler = PatternMatchingEventHandler( self.event_handler = PatternMatchingEventHandler(
@ -120,7 +120,7 @@ class MediaWatcher:
f'Move file from "{event.src_path}" to "{event.dest_path}"') f'Move file from "{event.src_path}" to "{event.dest_path}"')
if self.current_clip == event.src_path: if self.current_clip == event.src_path:
FF.decoder.terminate() ff_proc.decoder.terminate()
def on_deleted(self, event): def on_deleted(self, event):
self._media.remove(event.src_path) self._media.remove(event.src_path)
@ -128,7 +128,7 @@ class MediaWatcher:
messenger.info(f'Remove file from media list: "{event.src_path}"') messenger.info(f'Remove file from media list: "{event.src_path}"')
if self.current_clip == event.src_path: if self.current_clip == event.src_path:
FF.decoder.terminate() ff_proc.decoder.terminate()
def stop(self): def stop(self):
self.observer.stop() self.observer.stop()

View File

@ -21,9 +21,9 @@ from threading import Thread
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
from ffplayout.playlist import GetSourceFromPlaylist from ffplayout.playlist import GetSourceFromPlaylist
from ffplayout.utils import (FF, LOG, PLAYLIST, PRE, STDIN_ARGS, TEXT, from ffplayout.utils import (ff_proc, ffmpeg_stderr_reader, log, lower_third,
ffmpeg_stderr_reader, messenger, pre_audio_codec, messenger, playlist, pre, pre_audio_codec,
terminate_processes) stdin_args, terminate_processes)
_WINDOWS = os.name == 'nt' _WINDOWS = os.name == 'nt'
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424 COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424
@ -36,21 +36,22 @@ def output():
overlay = [] overlay = []
ff_pre_settings = [ ff_pre_settings = [
'-pix_fmt', 'yuv420p', '-r', str(PRE.fps), '-pix_fmt', 'yuv420p', '-r', str(pre.fps),
'-c:v', 'mpeg2video', '-intra', '-c:v', 'mpeg2video', '-intra',
'-b:v', f'{PRE.v_bitrate}k', '-b:v', f'{pre.v_bitrate}k',
'-minrate', f'{PRE.v_bitrate}k', '-minrate', f'{pre.v_bitrate}k',
'-maxrate', f'{PRE.v_bitrate}k', '-maxrate', f'{pre.v_bitrate}k',
'-bufsize', f'{PRE.v_bufsize}k' '-bufsize', f'{pre.v_bufsize}k'
] + pre_audio_codec() + ['-f', 'mpegts', '-'] ] + pre_audio_codec() + ['-f', 'mpegts', '-']
if TEXT.add_text and not TEXT.over_pre: if lower_third.add_text and not lower_third.over_pre:
messenger.info( messenger.info(
f'Using drawtext node, listening on address: {TEXT.address}') f'Using drawtext node, listening on address: {lower_third.address}'
)
overlay = [ overlay = [
'-vf', '-vf',
"null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format( "null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format(
TEXT.address.replace(':', '\\:'), TEXT.fontfile) lower_third.address.replace(':', '\\:'), lower_third.fontfile)
] ]
try: try:
@ -60,14 +61,14 @@ def output():
messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"') messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"')
FF.encoder = Popen(enc_cmd, stderr=PIPE, stdin=PIPE, stdout=None) ff_proc.encoder = Popen(enc_cmd, stderr=PIPE, stdin=PIPE, stdout=None)
enc_err_thread = Thread(target=ffmpeg_stderr_reader, enc_err_thread = Thread(target=ffmpeg_stderr_reader,
args=(FF.encoder.stderr, False)) args=(ff_proc.encoder.stderr, False))
enc_err_thread.daemon = True enc_err_thread.daemon = True
enc_err_thread.start() enc_err_thread.start()
if PLAYLIST.mode and not STDIN_ARGS.folder: if playlist.mode and not stdin_args.folder:
watcher = None watcher = None
get_source = GetSourceFromPlaylist() get_source = GetSourceFromPlaylist()
else: else:
@ -86,23 +87,25 @@ def output():
f'seconds: {node.get("source")}') f'seconds: {node.get("source")}')
dec_cmd = [ dec_cmd = [
'ffmpeg', '-v', LOG.ff_level.lower(), 'ffmpeg', '-v', log.ff_level.lower(),
'-hide_banner', '-nostats' '-hide_banner', '-nostats'
] + node['src_cmd'] + node['filter'] + ff_pre_settings ] + node['src_cmd'] + node['filter'] + ff_pre_settings
messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"') messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"')
with Popen(dec_cmd, stdout=PIPE, stderr=PIPE) as FF.decoder: with Popen(
dec_cmd, stdout=PIPE, stderr=PIPE) as ff_proc.decoder:
dec_err_thread = Thread(target=ffmpeg_stderr_reader, dec_err_thread = Thread(target=ffmpeg_stderr_reader,
args=(FF.decoder.stderr, True)) args=(ff_proc.decoder.stderr,
True))
dec_err_thread.daemon = True dec_err_thread.daemon = True
dec_err_thread.start() dec_err_thread.start()
while True: while True:
buf = FF.decoder.stdout.read(COPY_BUFSIZE) buf = ff_proc.decoder.stdout.read(COPY_BUFSIZE)
if not buf: if not buf:
break break
FF.encoder.stdin.write(buf) ff_proc.encoder.stdin.write(buf)
except BrokenPipeError: except BrokenPipeError:
messenger.error('Broken Pipe!') messenger.error('Broken Pipe!')
@ -117,10 +120,10 @@ def output():
terminate_processes(watcher) terminate_processes(watcher)
# close encoder when nothing is to do anymore # close encoder when nothing is to do anymore
if FF.encoder.poll() is None: if ff_proc.encoder.poll() is None:
FF.encoder.terminate() ff_proc.encoder.terminate()
finally: finally:
if FF.encoder.poll() is None: if ff_proc.encoder.poll() is None:
FF.encoder.terminate() ff_proc.encoder.terminate()
FF.encoder.wait() ff_proc.encoder.wait()

View File

@ -23,8 +23,8 @@ from threading import Thread
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
from ffplayout.playlist import GetSourceFromPlaylist from ffplayout.playlist import GetSourceFromPlaylist
from ffplayout.utils import (FF, LOG, PLAYLIST, PLAYOUT, STDIN_ARGS, from ffplayout.utils import (ff_proc, ffmpeg_stderr_reader, get_date, log,
ffmpeg_stderr_reader, get_date, messenger, messenger, playlist, playout, stdin_args,
terminate_processes) terminate_processes)
@ -35,15 +35,15 @@ def clean_ts():
then it checks if files on harddrive are older then this first *.ts then it checks if files on harddrive are older then this first *.ts
and if so delete them and if so delete them
""" """
playlists = [p for p in PLAYOUT.hls_output if 'm3u8' in p] m3u8_files = [p for p in playout.hls_output if 'm3u8' in p]
for playlist in playlists: for m3u8_file in m3u8_files:
messenger.debug(f'cleanup *.ts files from: "{playlist}"') messenger.debug(f'cleanup *.ts files from: "{m3u8_file}"')
test_num = 0 test_num = 0
hls_path = os.path.dirname(playlist) hls_path = os.path.dirname(m3u8_file)
if os.path.isfile(playlist): if os.path.isfile(m3u8_file):
with open(playlist, 'r') as m3u8: with open(m3u8_file, 'r') as m3u8:
for line in m3u8: for line in m3u8:
if '.ts' in line: if '.ts' in line:
test_num = int(re.findall(r'(\d+).ts', line)[0]) test_num = int(re.findall(r'(\d+).ts', line)[0])
@ -66,7 +66,7 @@ def output():
year = get_date(False).split('-')[0] year = get_date(False).split('-')[0]
try: try:
if PLAYLIST.mode and not STDIN_ARGS.folder: if playlist.mode and not stdin_args.folder:
watcher = None watcher = None
get_source = GetSourceFromPlaylist() get_source = GetSourceFromPlaylist()
else: else:
@ -83,20 +83,21 @@ def output():
messenger.info(f'Play: {node.get("source")}') messenger.info(f'Play: {node.get("source")}')
cmd = [ cmd = [
'ffmpeg', '-v', LOG.ff_level.lower(), '-hide_banner', 'ffmpeg', '-v', log.ff_level.lower(), '-hide_banner',
'-nostats' '-nostats'
] + node['src_cmd'] + node['filter'] + [ ] + node['src_cmd'] + node['filter'] + [
'-metadata', 'service_name=' + PLAYOUT.name, '-metadata', 'service_name=' + playout.name,
'-metadata', 'service_provider=' + PLAYOUT.provider, '-metadata', 'service_provider=' + playout.provider,
'-metadata', 'year={}'.format(year) '-metadata', 'year={}'.format(year)
] + PLAYOUT.ffmpeg_param + PLAYOUT.hls_output ] + playout.ffmpeg_param + playout.hls_output
messenger.debug(f'Encoder CMD: "{" ".join(cmd)}"') messenger.debug(f'Encoder CMD: "{" ".join(cmd)}"')
FF.encoder = Popen(cmd, stdin=PIPE, stderr=PIPE) ff_proc.encoder = Popen(cmd, stdin=PIPE, stderr=PIPE)
stderr_reader_thread = Thread(target=ffmpeg_stderr_reader, stderr_reader_thread = Thread(target=ffmpeg_stderr_reader,
args=(FF.encoder.stderr, False)) args=(ff_proc.encoder.stderr,
False))
stderr_reader_thread.daemon = True stderr_reader_thread.daemon = True
stderr_reader_thread.start() stderr_reader_thread.start()
stderr_reader_thread.join() stderr_reader_thread.join()
@ -118,10 +119,10 @@ def output():
terminate_processes(watcher) terminate_processes(watcher)
# close encoder when nothing is to do anymore # close encoder when nothing is to do anymore
if FF.encoder.poll() is None: if ff_proc.encoder.poll() is None:
FF.encoder.terminate() ff_proc.encoder.terminate()
finally: finally:
if FF.encoder.poll() is None: if ff_proc.encoder.poll() is None:
FF.encoder.terminate() ff_proc.encoder.terminate()
FF.encoder.wait() ff_proc.encoder.wait()

View File

@ -21,9 +21,9 @@ from threading import Thread
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
from ffplayout.playlist import GetSourceFromPlaylist from ffplayout.playlist import GetSourceFromPlaylist
from ffplayout.utils import (FF, LOG, PLAYLIST, PLAYOUT, PRE, STDIN_ARGS, TEXT, from ffplayout.utils import (ff_proc, ffmpeg_stderr_reader, get_date, log,
ffmpeg_stderr_reader, get_date, messenger, lower_third, messenger, playlist, playout, pre,
pre_audio_codec, terminate_processes) pre_audio_codec, stdin_args, terminate_processes)
_WINDOWS = os.name == 'nt' _WINDOWS = os.name == 'nt'
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424 COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424
@ -38,43 +38,44 @@ def output():
overlay = [] overlay = []
ff_pre_settings = [ ff_pre_settings = [
'-pix_fmt', 'yuv420p', '-r', str(PRE.fps), '-pix_fmt', 'yuv420p', '-r', str(pre.fps),
'-c:v', 'mpeg2video', '-intra', '-c:v', 'mpeg2video', '-intra',
'-b:v', f'{PRE.v_bitrate}k', '-b:v', f'{pre.v_bitrate}k',
'-minrate', f'{PRE.v_bitrate}k', '-minrate', f'{pre.v_bitrate}k',
'-maxrate', f'{PRE.v_bitrate}k', '-maxrate', f'{pre.v_bitrate}k',
'-bufsize', f'{PRE.v_bufsize}k' '-bufsize', f'{pre.v_bufsize}k'
] + pre_audio_codec() + ['-f', 'mpegts', '-'] ] + pre_audio_codec() + ['-f', 'mpegts', '-']
if TEXT.add_text and not TEXT.over_pre: if lower_third.add_text and not lower_third.over_pre:
messenger.info( messenger.info(
f'Using drawtext node, listening on address: {TEXT.address}') f'Using drawtext node, listening on address: {lower_third.address}'
)
overlay = [ overlay = [
'-vf', '-vf',
"null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format( "null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format(
TEXT.address.replace(':', '\\:'), TEXT.fontfile) lower_third.address.replace(':', '\\:'), lower_third.fontfile)
] ]
try: try:
enc_cmd = [ enc_cmd = [
'ffmpeg', '-v', LOG.ff_level.lower(), '-hide_banner', 'ffmpeg', '-v', log.ff_level.lower(), '-hide_banner',
'-nostats', '-re', '-thread_queue_size', '160', '-i', 'pipe:0' '-nostats', '-re', '-thread_queue_size', '160', '-i', 'pipe:0'
] + overlay + [ ] + overlay + [
'-metadata', 'service_name=' + PLAYOUT.name, '-metadata', 'service_name=' + playout.name,
'-metadata', 'service_provider=' + PLAYOUT.provider, '-metadata', 'service_provider=' + playout.provider,
'-metadata', f'year={year}' '-metadata', f'year={year}'
] + PLAYOUT.ffmpeg_param + PLAYOUT.stream_output ] + playout.ffmpeg_param + playout.stream_output
messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"') messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"')
FF.encoder = Popen(enc_cmd, stdin=PIPE, stderr=PIPE) ff_proc.encoder = Popen(enc_cmd, stdin=PIPE, stderr=PIPE)
enc_err_thread = Thread(target=ffmpeg_stderr_reader, enc_err_thread = Thread(target=ffmpeg_stderr_reader,
args=(FF.encoder.stderr, False)) args=(ff_proc.encoder.stderr, False))
enc_err_thread.daemon = True enc_err_thread.daemon = True
enc_err_thread.start() enc_err_thread.start()
if PLAYLIST.mode and not STDIN_ARGS.folder: if playlist.mode and not stdin_args.folder:
watcher = None watcher = None
get_source = GetSourceFromPlaylist() get_source = GetSourceFromPlaylist()
else: else:
@ -91,23 +92,25 @@ def output():
messenger.info(f'Play: {node.get("source")}') messenger.info(f'Play: {node.get("source")}')
dec_cmd = [ dec_cmd = [
'ffmpeg', '-v', LOG.ff_level.lower(), 'ffmpeg', '-v', log.ff_level.lower(),
'-hide_banner', '-nostats' '-hide_banner', '-nostats'
] + node['src_cmd'] + node['filter'] + ff_pre_settings ] + node['src_cmd'] + node['filter'] + ff_pre_settings
messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"') messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"')
with Popen(dec_cmd, stdout=PIPE, stderr=PIPE) as FF.decoder: with Popen(
dec_cmd, stdout=PIPE, stderr=PIPE) as ff_proc.decoder:
dec_err_thread = Thread(target=ffmpeg_stderr_reader, dec_err_thread = Thread(target=ffmpeg_stderr_reader,
args=(FF.decoder.stderr, True)) args=(ff_proc.decoder.stderr,
True))
dec_err_thread.daemon = True dec_err_thread.daemon = True
dec_err_thread.start() dec_err_thread.start()
while True: while True:
buf = FF.decoder.stdout.read(COPY_BUFSIZE) buf = ff_proc.decoder.stdout.read(COPY_BUFSIZE)
if not buf: if not buf:
break break
FF.encoder.stdin.write(buf) ff_proc.encoder.stdin.write(buf)
except BrokenPipeError: except BrokenPipeError:
messenger.error('Broken Pipe!') messenger.error('Broken Pipe!')
@ -122,10 +125,10 @@ def output():
terminate_processes(watcher) terminate_processes(watcher)
# close encoder when nothing is to do anymore # close encoder when nothing is to do anymore
if FF.encoder.poll() is None: if ff_proc.encoder.poll() is None:
FF.encoder.terminate() ff_proc.encoder.terminate()
finally: finally:
if FF.encoder.poll() is None: if ff_proc.encoder.poll() is None:
FF.encoder.terminate() ff_proc.encoder.terminate()
FF.encoder.wait() ff_proc.encoder.wait()

View File

@ -28,9 +28,9 @@ from threading import Thread
import requests import requests
from .filters.default import build_filtergraph from .filters.default import build_filtergraph
from .utils import (GENERAL, PLAYLIST, STDIN_ARGS, MediaProbe, check_sync, from .utils import (MediaProbe, check_sync, get_date, get_delta, get_float,
get_date, get_delta, get_float, get_time, messenger, get_time, messenger, playlist, src_or_dummy, stdin_args,
src_or_dummy, valid_json) sync_op, valid_json)
def handle_list_init(node): def handle_list_init(node):
@ -103,13 +103,13 @@ def timed_source(node, last):
delta, total_delta = get_delta(node['begin']) delta, total_delta = get_delta(node['begin'])
node_ = None node_ = None
if not STDIN_ARGS.loop and PLAYLIST.length: if not stdin_args.loop and playlist.length:
messenger.debug(f'delta: {delta:f}') messenger.debug(f'delta: {delta:f}')
messenger.debug(f'total_delta: {total_delta:f}') messenger.debug(f'total_delta: {total_delta:f}')
check_sync(delta) check_sync(delta)
if (total_delta > node['out'] - node['seek'] and not last) \ if (total_delta > node['out'] - node['seek'] and not last) \
or STDIN_ARGS.loop or not PLAYLIST.length: or stdin_args.loop or not playlist.length:
# when we are in the 24 houre range, get the clip # when we are in the 24 houre range, get the clip
node_ = src_or_dummy(node) node_ = src_or_dummy(node)
@ -126,12 +126,12 @@ def check_length(total_play_time, list_date):
""" """
check if playlist is long enough check if playlist is long enough
""" """
if PLAYLIST.length and total_play_time < PLAYLIST.length - 5 \ if playlist.length and total_play_time < playlist.length - 5 \
and not STDIN_ARGS.loop: and not stdin_args.loop:
messenger.error( messenger.error(
f'Playlist from {list_date} is not long enough!\n' f'Playlist from {list_date} is not long enough!\n'
f'Total play time is: {timedelta(seconds=total_play_time)}, ' f'Total play time is: {timedelta(seconds=total_play_time)}, '
f'target length is: {timedelta(seconds=PLAYLIST.length)}' f'target length is: {timedelta(seconds=playlist.length)}'
) )
@ -203,11 +203,11 @@ class PlaylistReader:
self.nodes = {'program': []} self.nodes = {'program': []}
self.error = False self.error = False
if STDIN_ARGS.playlist: if stdin_args.playlist:
json_file = STDIN_ARGS.playlist json_file = stdin_args.playlist
else: else:
year, month, day = self.list_date.split('-') year, month, day = self.list_date.split('-')
json_file = os.path.join(PLAYLIST.path, year, month, json_file = os.path.join(playlist.path, year, month,
f'{self.list_date}.json') f'{self.list_date}.json')
if '://' in json_file: if '://' in json_file:
@ -253,7 +253,7 @@ class GetSourceFromPlaylist:
def __init__(self): def __init__(self):
self.prev_date = get_date(True) self.prev_date = get_date(True)
self.list_start = PLAYLIST.start self.list_start = playlist.start
self.first = True self.first = True
self.last = False self.last = False
self.clip_nodes = [] self.clip_nodes = []
@ -261,7 +261,7 @@ class GetSourceFromPlaylist:
self.node = None self.node = None
self.prev_node = None self.prev_node = None
self.next_node = None self.next_node = None
self.playlist = PlaylistReader(get_date(True), 0.0) self.playlist_reader = PlaylistReader(get_date(True), 0.0)
self.last_error = False self.last_error = False
def get_playlist(self): def get_playlist(self):
@ -269,26 +269,26 @@ class GetSourceFromPlaylist:
read playlist from given date and fill clip_nodes read playlist from given date and fill clip_nodes
when playlist is not available, reset relevant values when playlist is not available, reset relevant values
""" """
self.playlist.read() self.playlist_reader.read()
if self.last_error and not self.playlist.error and \ if self.last_error and not self.playlist_reader.error and \
self.playlist.list_date == self.prev_date: self.playlist_reader.list_date == self.prev_date:
# when last playlist where not exists but now is there and # when last playlist where not exists but now is there and
# is still the same playlist date, # is still the same playlist date,
# set self.first to true to seek in clip # set self.first to true to seek in clip
# only in this situation seek in is correct!! # only in this situation seek in is correct!!
self.first = True self.first = True
self.last_error = self.playlist.error self.last_error = self.playlist_reader.error
if self.playlist.nodes.get('program'): if self.playlist_reader.nodes.get('program'):
self.clip_nodes = self.playlist.nodes.get('program') self.clip_nodes = self.playlist_reader.nodes.get('program')
self.node_count = len(self.clip_nodes) self.node_count = len(self.clip_nodes)
if self.playlist.error: if self.playlist_reader.error:
self.clip_nodes = [] self.clip_nodes = []
self.node_count = 0 self.node_count = 0
self.playlist.last_mod_time = 0.0 self.playlist_reader.last_mod_time = 0.0
self.last_error = self.playlist.error self.last_error = self.playlist_reader.error
def init_time(self): def init_time(self):
""" """
@ -296,12 +296,12 @@ class GetSourceFromPlaylist:
""" """
self.last_time = get_time('full_sec') self.last_time = get_time('full_sec')
if PLAYLIST.length: if playlist.length:
total_playtime = PLAYLIST.length total_playtime = playlist.length
else: else:
total_playtime = 86400.0 total_playtime = 86400.0
if self.last_time < PLAYLIST.start: if self.last_time < playlist.start:
self.last_time += total_playtime self.last_time += total_playtime
def check_for_next_playlist(self, begin): def check_for_next_playlist(self, begin):
@ -321,17 +321,17 @@ class GetSourceFromPlaylist:
delta, total_delta = get_delta(begin) delta, total_delta = get_delta(begin)
delta += seek + 1 delta += seek + 1
next_start = begin - PLAYLIST.start + out + delta next_start = begin - playlist.start + out + delta
else: else:
delta, total_delta = get_delta(begin) delta, total_delta = get_delta(begin)
next_start = begin - PLAYLIST.start + GENERAL.threshold + delta next_start = begin - playlist.start + sync_op.threshold + delta
if PLAYLIST.length and next_start >= PLAYLIST.length: if playlist.length and next_start >= playlist.length:
self.prev_date = get_date(False, next_start) self.prev_date = get_date(False, next_start)
self.playlist.list_date = self.prev_date self.playlist_reader.list_date = self.prev_date
self.playlist.last_mod_time = 0.0 self.playlist_reader.last_mod_time = 0.0
self.last_time = PLAYLIST.start - 1 self.last_time = playlist.start - 1
self.clip_nodes = [] self.clip_nodes = []
def previous_and_next_node(self, index): def previous_and_next_node(self, index):
@ -361,9 +361,9 @@ class GetSourceFromPlaylist:
""" """
current_time = get_time('full_sec') - 86400 current_time = get_time('full_sec') - 86400
# balance small difference to start time # balance small difference to start time
if PLAYLIST.start is not None and isclose(PLAYLIST.start, if playlist.start is not None and isclose(playlist.start,
current_time, abs_tol=2): current_time, abs_tol=2):
begin = PLAYLIST.start begin = playlist.start
else: else:
self.init_time() self.init_time()
begin = self.last_time begin = self.last_time
@ -385,14 +385,14 @@ class GetSourceFromPlaylist:
""" """
handle except playlist end handle except playlist end
""" """
if STDIN_ARGS.loop and self.node: if stdin_args.loop and self.node:
# when loop paramter is set and playlist node exists, # when loop paramter is set and playlist node exists,
# jump to playlist start and play again # jump to playlist start and play again
self.list_start = self.last_time + 1 self.list_start = self.last_time + 1
self.node = None self.node = None
messenger.info('Loop playlist') messenger.info('Loop playlist')
elif begin == PLAYLIST.start or not self.clip_nodes: elif begin == playlist.start or not self.clip_nodes:
# playlist not exist or is corrupt/empty # playlist not exist or is corrupt/empty
messenger.error('Clip nodes are empty!') messenger.error('Clip nodes are empty!')
self.first = False self.first = False
@ -454,7 +454,7 @@ class GetSourceFromPlaylist:
begin += self.node['out'] - self.node['seek'] begin += self.node['out'] - self.node['seek']
else: else:
if not PLAYLIST.length and not STDIN_ARGS.loop: if not playlist.length and not stdin_args.loop:
# when we reach playlist end, stop script # when we reach playlist end, stop script
messenger.info('Playlist reached end!') messenger.info('Playlist reached end!')
return None return None

View File

@ -98,7 +98,7 @@ for arg_file in glob(os.path.join(CONFIG_PATH, 'argparse_*')):
**config **config
) )
STDIN_ARGS = stdin_parser.parse_args() stdin_args = stdin_parser.parse_args()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -127,17 +127,17 @@ def get_time(time_format):
# default variables and values # default variables and values
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
GENERAL = SimpleNamespace(time_delta=0) sync_op = SimpleNamespace(time_delta=0)
MAIL = SimpleNamespace() mail = SimpleNamespace()
LOG = SimpleNamespace() log = SimpleNamespace()
PRE = SimpleNamespace() pre = SimpleNamespace()
PLAYLIST = SimpleNamespace() playlist = SimpleNamespace()
STORAGE = SimpleNamespace() storage = SimpleNamespace()
TEXT = SimpleNamespace() lower_third = SimpleNamespace()
PLAYOUT = SimpleNamespace() playout = SimpleNamespace()
INITIAL = SimpleNamespace(load=True) initial = SimpleNamespace(load=True)
FF = SimpleNamespace(decoder=None, encoder=None) ff_proc = SimpleNamespace(decoder=None, encoder=None)
def str_to_sec(s): def str_to_sec(s):
@ -164,90 +164,90 @@ def load_config():
some settings cannot be changed - like resolution, aspect, or output some settings cannot be changed - like resolution, aspect, or output
""" """
if STDIN_ARGS.config: if stdin_args.config:
cfg = read_config(STDIN_ARGS.config) cfg = read_config(stdin_args.config)
elif os.path.isfile('/etc/ffplayout/ffplayout.yml'): elif os.path.isfile('/etc/ffplayout/ffplayout.yml'):
cfg = read_config('/etc/ffplayout/ffplayout.yml') cfg = read_config('/etc/ffplayout/ffplayout.yml')
else: else:
cfg = read_config('ffplayout.yml') cfg = read_config('ffplayout.yml')
if STDIN_ARGS.start: if stdin_args.start:
p_start = str_to_sec(STDIN_ARGS.start) p_start = str_to_sec(stdin_args.start)
else: else:
p_start = str_to_sec(cfg['playlist']['day_start']) p_start = str_to_sec(cfg['playlist']['day_start'])
if p_start is None: if p_start is None:
p_start = get_time('full_sec') p_start = get_time('full_sec')
if STDIN_ARGS.length: if stdin_args.length:
p_length = str_to_sec(STDIN_ARGS.length) p_length = str_to_sec(stdin_args.length)
else: else:
p_length = str_to_sec(cfg['playlist']['length']) p_length = str_to_sec(cfg['playlist']['length'])
GENERAL.stop = cfg['general']['stop_on_error'] sync_op.stop = cfg['general']['stop_on_error']
GENERAL.threshold = cfg['general']['stop_threshold'] sync_op.threshold = cfg['general']['stop_threshold']
MAIL.subject = cfg['mail']['subject'] mail.subject = cfg['mail']['subject']
MAIL.server = cfg['mail']['smpt_server'] mail.server = cfg['mail']['smpt_server']
MAIL.port = cfg['mail']['smpt_port'] mail.port = cfg['mail']['smpt_port']
MAIL.s_addr = cfg['mail']['sender_addr'] mail.s_addr = cfg['mail']['sender_addr']
MAIL.s_pass = cfg['mail']['sender_pass'] mail.s_pass = cfg['mail']['sender_pass']
MAIL.recip = cfg['mail']['recipient'] mail.recip = cfg['mail']['recipient']
MAIL.level = cfg['mail']['mail_level'] mail.level = cfg['mail']['mail_level']
PRE.add_logo = cfg['processing']['add_logo'] pre.add_logo = cfg['processing']['add_logo']
PRE.logo = cfg['processing']['logo'] pre.logo = cfg['processing']['logo']
PRE.logo_scale = cfg['processing']['logo_scale'] pre.logo_scale = cfg['processing']['logo_scale']
PRE.logo_filter = cfg['processing']['logo_filter'] pre.logo_filter = cfg['processing']['logo_filter']
PRE.logo_opacity = cfg['processing']['logo_opacity'] pre.logo_opacity = cfg['processing']['logo_opacity']
PRE.add_loudnorm = cfg['processing']['add_loudnorm'] pre.add_loudnorm = cfg['processing']['add_loudnorm']
PRE.loud_i = cfg['processing']['loud_I'] pre.loud_i = cfg['processing']['loud_I']
PRE.loud_tp = cfg['processing']['loud_TP'] pre.loud_tp = cfg['processing']['loud_TP']
PRE.loud_lra = cfg['processing']['loud_LRA'] pre.loud_lra = cfg['processing']['loud_LRA']
PRE.output_count = cfg['processing']['output_count'] pre.output_count = cfg['processing']['output_count']
PLAYLIST.mode = cfg['playlist']['playlist_mode'] playlist.mode = cfg['playlist']['playlist_mode']
PLAYLIST.path = cfg['playlist']['path'] playlist.path = cfg['playlist']['path']
PLAYLIST.start = p_start playlist.start = p_start
PLAYLIST.length = p_length playlist.length = p_length
STORAGE.path = cfg['storage']['path'] storage.path = cfg['storage']['path']
STORAGE.filler = cfg['storage']['filler_clip'] storage.filler = cfg['storage']['filler_clip']
STORAGE.extensions = cfg['storage']['extensions'] storage.extensions = cfg['storage']['extensions']
STORAGE.shuffle = cfg['storage']['shuffle'] storage.shuffle = cfg['storage']['shuffle']
TEXT.add_text = cfg['text']['add_text'] lower_third.add_text = cfg['text']['add_text']
TEXT.over_pre = cfg['text']['over_pre'] lower_third.over_pre = cfg['text']['over_pre']
TEXT.address = cfg['text']['bind_address'] lower_third.address = cfg['text']['bind_address']
TEXT.fontfile = cfg['text']['fontfile'] lower_third.fontfile = cfg['text']['fontfile']
TEXT.text_from_filename = cfg['text']['text_from_filename'] lower_third.text_from_filename = cfg['text']['text_from_filename']
TEXT.style = cfg['text']['style'] lower_third.style = cfg['text']['style']
TEXT.regex = cfg['text']['regex'] lower_third.regex = cfg['text']['regex']
if INITIAL.load: if initial.load:
LOG.to_file = cfg['logging']['log_to_file'] log.to_file = cfg['logging']['log_to_file']
LOG.backup_count = cfg['logging']['backup_count'] log.backup_count = cfg['logging']['backup_count']
LOG.path = cfg['logging']['log_path'] log.path = cfg['logging']['log_path']
LOG.level = cfg['logging']['log_level'] log.level = cfg['logging']['log_level']
LOG.ff_level = cfg['logging']['ffmpeg_level'] log.ff_level = cfg['logging']['ffmpeg_level']
PRE.w = cfg['processing']['width'] pre.w = cfg['processing']['width']
PRE.h = cfg['processing']['height'] pre.h = cfg['processing']['height']
PRE.aspect = cfg['processing']['aspect'] pre.aspect = cfg['processing']['aspect']
PRE.fps = cfg['processing']['fps'] pre.fps = cfg['processing']['fps']
PRE.v_bitrate = cfg['processing']['width'] * \ pre.v_bitrate = cfg['processing']['width'] * \
cfg['processing']['height'] / 10 cfg['processing']['height'] / 10
PRE.v_bufsize = PRE.v_bitrate / 2 pre.v_bufsize = pre.v_bitrate / 2
PRE.realtime = cfg['processing']['use_realtime'] pre.realtime = cfg['processing']['use_realtime']
PLAYOUT.mode = cfg['out']['mode'] playout.mode = cfg['out']['mode']
PLAYOUT.name = cfg['out']['service_name'] playout.name = cfg['out']['service_name']
PLAYOUT.provider = cfg['out']['service_provider'] playout.provider = cfg['out']['service_provider']
PLAYOUT.ffmpeg_param = cfg['out']['ffmpeg_param'].split(' ') playout.ffmpeg_param = cfg['out']['ffmpeg_param'].split(' ')
PLAYOUT.stream_output = cfg['out']['stream_output'].split(' ') playout.stream_output = cfg['out']['stream_output'].split(' ')
PLAYOUT.hls_output = cfg['out']['hls_output'].split(' ') playout.hls_output = cfg['out']['hls_output'].split(' ')
INITIAL.load = False initial.load = False
load_config() load_config()
@ -308,21 +308,21 @@ class CustomFormatter(logging.Formatter):
# If the log file is specified on the command line then override the default # If the log file is specified on the command line then override the default
if STDIN_ARGS.log: if stdin_args.log:
LOG.path = STDIN_ARGS.log log.path = stdin_args.log
playout_logger = logging.getLogger('playout') playout_logger = logging.getLogger('playout')
playout_logger.setLevel(LOG.level) playout_logger.setLevel(log.level)
decoder_logger = logging.getLogger('decoder') decoder_logger = logging.getLogger('decoder')
decoder_logger.setLevel(LOG.ff_level) decoder_logger.setLevel(log.ff_level)
encoder_logger = logging.getLogger('encoder') encoder_logger = logging.getLogger('encoder')
encoder_logger.setLevel(LOG.ff_level) encoder_logger.setLevel(log.ff_level)
if LOG.to_file and LOG.path != 'none': if log.to_file and log.path != 'none':
if LOG.path and os.path.isdir(LOG.path): if log.path and os.path.isdir(log.path):
playout_log = os.path.join(LOG.path, 'ffplayout.log') playout_log = os.path.join(log.path, 'ffplayout.log')
decoder_log = os.path.join(LOG.path, 'decoder.log') decoder_log = os.path.join(log.path, 'decoder.log')
encoder_log = os.path.join(LOG.path, 'encoder.log') encoder_log = os.path.join(log.path, 'encoder.log')
else: else:
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
log_dir = os.path.join(base_dir, 'log') log_dir = os.path.join(base_dir, 'log')
@ -334,11 +334,11 @@ if LOG.to_file and LOG.path != 'none':
p_format = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s') p_format = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s')
f_format = logging.Formatter('[%(asctime)s] %(message)s') f_format = logging.Formatter('[%(asctime)s] %(message)s')
p_file_handler = TimedRotatingFileHandler(playout_log, when='midnight', p_file_handler = TimedRotatingFileHandler(playout_log, when='midnight',
backupCount=LOG.backup_count) backupCount=log.backup_count)
d_file_handler = TimedRotatingFileHandler(decoder_log, when='midnight', d_file_handler = TimedRotatingFileHandler(decoder_log, when='midnight',
backupCount=LOG.backup_count) backupCount=log.backup_count)
e_file_handler = TimedRotatingFileHandler(encoder_log, when='midnight', e_file_handler = TimedRotatingFileHandler(encoder_log, when='midnight',
backupCount=LOG.backup_count) backupCount=log.backup_count)
p_file_handler.setFormatter(p_format) p_file_handler.setFormatter(p_format)
d_file_handler.setFormatter(f_format) d_file_handler.setFormatter(f_format)
@ -370,7 +370,7 @@ class Mailer:
""" """
def __init__(self): def __init__(self):
self.level = MAIL.level self.level = mail.level
self.time = None self.time = None
self.timestamp = get_time('stamp') self.timestamp = get_time('stamp')
self.rate_limit = 600 self.rate_limit = 600
@ -380,7 +380,7 @@ class Mailer:
self.time = get_time(None) self.time = get_time(None)
def send_mail(self, msg): def send_mail(self, msg):
if MAIL.recip: if mail.recip:
# write message to temp file for rate limit # write message to temp file for rate limit
with open(self.temp_msg, 'w+') as f: with open(self.temp_msg, 'w+') as f:
f.write(msg) f.write(msg)
@ -388,15 +388,15 @@ class Mailer:
self.current_time() self.current_time()
message = MIMEMultipart() message = MIMEMultipart()
message['From'] = MAIL.s_addr message['From'] = mail.s_addr
message['To'] = MAIL.recip message['To'] = mail.recip
message['Subject'] = MAIL.subject message['Subject'] = mail.subject
message['Date'] = formatdate(localtime=True) message['Date'] = formatdate(localtime=True)
message.attach(MIMEText(f'{self.time} {msg}', 'plain')) message.attach(MIMEText(f'{self.time} {msg}', 'plain'))
text = message.as_string() text = message.as_string()
try: try:
server = smtplib.SMTP(MAIL.server, MAIL.port) server = smtplib.SMTP(mail.server, mail.port)
except socket.error as err: except socket.error as err:
playout_logger.error(err) playout_logger.error(err)
server = None server = None
@ -404,14 +404,14 @@ class Mailer:
if server is not None: if server is not None:
server.starttls() server.starttls()
try: try:
login = server.login(MAIL.s_addr, MAIL.s_pass) login = server.login(mail.s_addr, mail.s_pass)
except smtplib.SMTPAuthenticationError as serr: except smtplib.SMTPAuthenticationError as serr:
playout_logger.error(serr) playout_logger.error(serr)
login = None login = None
if login is not None: if login is not None:
server.sendmail(MAIL.s_addr, server.sendmail(mail.s_addr,
re.split(', |; |,|;', MAIL.recip), text) re.split(', |; |,|;', mail.recip), text)
server.quit() server.quit()
def check_if_new(self, msg): def check_if_new(self, msg):
@ -623,11 +623,11 @@ def terminate_processes(watcher=None):
""" """
kill orphaned processes kill orphaned processes
""" """
if FF.decoder and FF.decoder.poll() is None: if ff_proc.decoder and ff_proc.decoder.poll() is None:
FF.decoder.terminate() ff_proc.decoder.terminate()
if FF.encoder and FF.encoder.poll() is None: if ff_proc.encoder and ff_proc.encoder.poll() is None:
FF.encoder.terminate() ff_proc.encoder.terminate()
if watcher: if watcher:
watcher.stop() watcher.stop()
@ -647,9 +647,9 @@ def ffmpeg_stderr_reader(std_errors, decoder):
try: try:
for line in std_errors: for line in std_errors:
if LOG.ff_level == 'INFO': if log.ff_level == 'INFO':
logger.info(f'{prefix}{line.decode("utf-8").rstrip()}') logger.info(f'{prefix}{line.decode("utf-8").rstrip()}')
elif LOG.ff_level == 'WARNING': elif log.ff_level == 'WARNING':
logger.warning(f'{prefix}{line.decode("utf-8").rstrip()}') logger.warning(f'{prefix}{line.decode("utf-8").rstrip()}')
else: else:
logger.error(f'{prefix}{line.decode("utf-8").rstrip()}') logger.error(f'{prefix}{line.decode("utf-8").rstrip()}')
@ -663,17 +663,17 @@ def get_delta(begin):
""" """
current_time = get_time('full_sec') current_time = get_time('full_sec')
if STDIN_ARGS.length and str_to_sec(STDIN_ARGS.length): if stdin_args.length and str_to_sec(stdin_args.length):
target_playtime = str_to_sec(STDIN_ARGS.length) target_playtime = str_to_sec(stdin_args.length)
elif PLAYLIST.length: elif playlist.length:
target_playtime = PLAYLIST.length target_playtime = playlist.length
else: else:
target_playtime = 86400.0 target_playtime = 86400.0
if begin == PLAYLIST.start == 0 and 86400.0 - current_time < 4: if begin == playlist.start == 0 and 86400.0 - current_time < 4:
current_time -= target_playtime current_time -= target_playtime
elif PLAYLIST.start >= current_time and not begin == PLAYLIST.start: elif playlist.start >= current_time and not begin == playlist.start:
current_time += target_playtime current_time += target_playtime
current_delta = begin - current_time current_delta = begin - current_time
@ -681,7 +681,7 @@ def get_delta(begin):
if math.isclose(current_delta, 86400.0, abs_tol=6): if math.isclose(current_delta, 86400.0, abs_tol=6):
current_delta -= 86400.0 current_delta -= 86400.0
ref_time = target_playtime + PLAYLIST.start ref_time = target_playtime + playlist.start
total_delta = ref_time - begin + current_delta total_delta = ref_time - begin + current_delta
return current_delta, total_delta return current_delta, total_delta
@ -695,9 +695,9 @@ def get_date(seek_day, next_start=0):
""" """
d = date.today() d = date.today()
if seek_day and PLAYLIST.start > get_time('full_sec'): if seek_day and playlist.start > get_time('full_sec'):
return (d - timedelta(1)).strftime('%Y-%m-%d') return (d - timedelta(1)).strftime('%Y-%m-%d')
elif PLAYLIST.start == 0 and next_start >= 86400: elif playlist.start == 0 and next_start >= 86400:
return (d + timedelta(1)).strftime('%Y-%m-%d') return (d + timedelta(1)).strftime('%Y-%m-%d')
else: else:
return d.strftime('%Y-%m-%d') return d.strftime('%Y-%m-%d')
@ -738,12 +738,12 @@ def check_sync(delta):
check that we are in tolerance time check that we are in tolerance time
""" """
if PLAYLIST.mode and PLAYLIST.start and PLAYLIST.length: if playlist.mode and playlist.start and playlist.length:
# save time delta to global variable for syncing # save time delta to global variable for syncing
# this is needed for real time filter # this is needed for real time filter
GENERAL.time_delta = delta sync_op.time_delta = delta
if GENERAL.stop and abs(delta) > GENERAL.threshold: if sync_op.stop and abs(delta) > sync_op.threshold:
messenger.error( messenger.error(
f'Sync tolerance value exceeded with {delta:.2f} seconds,\n' f'Sync tolerance value exceeded with {delta:.2f} seconds,\n'
'program terminated!') 'program terminated!')
@ -785,7 +785,7 @@ def gen_dummy(duration):
# noise = 'noise=alls=50:allf=t+u,hue=s=0' # noise = 'noise=alls=50:allf=t+u,hue=s=0'
return [ return [
'-f', 'lavfi', '-i', '-f', 'lavfi', '-i',
f'color=c={color}:s={PRE.w}x{PRE.h}:d={duration}:r={PRE.fps},' f'color=c={color}:s={pre.w}x{pre.h}:d={duration}:r={pre.fps},'
'format=pix_fmts=yuv420p', 'format=pix_fmts=yuv420p',
'-f', 'lavfi', '-i', f'anoisesrc=d={duration}:c=pink:r=48000:a=0.05' '-f', 'lavfi', '-i', f'anoisesrc=d={duration}:c=pink:r=48000:a=0.05'
] ]
@ -796,7 +796,7 @@ def gen_filler(node):
generate filler clip to fill empty space in playlist generate filler clip to fill empty space in playlist
""" """
probe = MediaProbe() probe = MediaProbe()
probe.load(STORAGE.filler) probe.load(storage.filler)
duration = node['out'] - node['seek'] duration = node['out'] - node['seek']
node['probe'] = probe node['probe'] = probe
@ -808,13 +808,13 @@ def gen_filler(node):
# cut filler # cut filler
messenger.info( messenger.info(
f'Generate filler with {duration:.2f} seconds') f'Generate filler with {duration:.2f} seconds')
node['source'] = STORAGE.filler node['source'] = storage.filler
node['src_cmd'] = ['-i', STORAGE.filler] + set_length( node['src_cmd'] = ['-i', storage.filler] + set_length(
filler_duration, 0, duration) filler_duration, 0, duration)
return node return node
else: else:
# loop file n times # loop file n times
node['src_cmd'] = loop_input(STORAGE.filler, filler_duration, node['src_cmd'] = loop_input(storage.filler, filler_duration,
duration) duration)
return node return node
else: else:
@ -881,7 +881,7 @@ def pre_audio_codec():
s302m has higher quality, but is experimental s302m has higher quality, but is experimental
and works not well together with the loudnorm filter and works not well together with the loudnorm filter
""" """
if PRE.add_loudnorm: if pre.add_loudnorm:
return ['-c:a', 'mp2', '-b:a', '384k', '-ar', '48000', '-ac', '2'] return ['-c:a', 'mp2', '-b:a', '384k', '-ar', '48000', '-ac', '2']
else: else:
return ['-c:a', 's302m', '-strict', '-2', '-ar', '48000', '-ac', '2'] return ['-c:a', 's302m', '-strict', '-2', '-ar', '48000', '-ac', '2']

View File

@ -98,37 +98,37 @@ def run_with_no_elements(time_tuple):
if __name__ == '__main__': if __name__ == '__main__':
from ffplayout.output import desktop from ffplayout.output import desktop
from ffplayout.utils import PLAYLIST, terminate_processes from ffplayout.utils import playlist, terminate_processes
print('\ntest playlists, which are empty') print('\ntest playlists, which are empty')
PLAYLIST.start = 0 playlist.start = 0
run_time(140) run_time(140)
run_with_no_elements((2021, 2, 15, 23, 59, 53)) run_with_no_elements((2021, 2, 15, 23, 59, 53))
print_separater() print_separater()
print('\ntest playlists, which are to short') print('\ntest playlists, which are to short')
PLAYLIST.start = 0 playlist.start = 0
run_time(140) run_time(140)
run_with_less_elements((2021, 2, 15, 23, 58, 3)) run_with_less_elements((2021, 2, 15, 23, 58, 3))
print_separater() print_separater()
print('\ntest playlists, which are to long') print('\ntest playlists, which are to long')
PLAYLIST.start = 0 playlist.start = 0
run_time(140) run_time(140)
run_with_more_elements((2021, 2, 15, 23, 59, 33)) run_with_more_elements((2021, 2, 15, 23, 59, 33))
print_separater() print_separater()
print('\ntest transition from playlists, with day_start at: 05:59:25') print('\ntest transition from playlists, with day_start at: 05:59:25')
PLAYLIST.start = 21575 playlist.start = 21575
run_time(140) run_time(140)
run_at((2021, 2, 17, 5, 58, 3)) run_at((2021, 2, 17, 5, 58, 3))
print_separater() print_separater()
print('\ntest transition from playlists, with day_start at: 20:00:00') print('\ntest transition from playlists, with day_start at: 20:00:00')
PLAYLIST.start = 72000 playlist.start = 72000
run_time(140) run_time(140)
run_at((2021, 2, 17, 19, 58, 23)) run_at((2021, 2, 17, 19, 58, 23))