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
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:
if os.name != 'posix':
@ -40,8 +40,8 @@ def main():
play out depending on output mode
"""
if STDIN_ARGS.mode:
output = locate(f'ffplayout.output.{STDIN_ARGS.mode}.output')
if stdin_args.mode:
output = locate(f'ffplayout.output.{stdin_args.mode}.output')
output()
else:
@ -53,7 +53,7 @@ def main():
and output != '__init__.py':
mode = os.path.splitext(output)[0]
if mode == PLAYOUT.mode:
if mode == playout.mode:
output = locate(f'ffplayout.output.{mode}.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):
@ -6,5 +6,5 @@ def filter_link(node):
set audio volume
"""
if STDIN_ARGS.volume and get_float(STDIN_ARGS.volume, False):
return f'volume={STDIN_ARGS.volume}'
if stdin_args.volume and get_float(stdin_args.volume, False):
return f'volume={stdin_args.volume}'

View File

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

View File

@ -1,7 +1,7 @@
import os
import re
from ffplayout.utils import TEXT
from ffplayout.utils import lower_third
def filter_link(node):
@ -10,12 +10,12 @@ def filter_link(node):
"""
font = ''
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
if TEXT.fontfile and os.path.isfile(TEXT.fontfile):
font = f":fontfile='{TEXT.fontfile}'"
if lower_third.fontfile and os.path.isfile(lower_third.fontfile):
font = f":fontfile='{lower_third.fontfile}'"
if TEXT.text_from_filename:
if lower_third.text_from_filename:
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 .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
@ -43,21 +43,21 @@ class MediaStore:
def __init__(self):
self.store = []
if STDIN_ARGS.folder:
self.folder = STDIN_ARGS.folder
if stdin_args.folder:
self.folder = stdin_args.folder
else:
self.folder = STORAGE.path
self.folder = storage.path
self.fill()
def fill(self):
for ext in STORAGE.extensions:
for ext in storage.extensions:
self.store.extend(
glob.glob(os.path.join(self.folder, '**', f'*{ext}'),
recursive=True))
def sort_or_radomize(self):
if STORAGE.shuffle:
if storage.shuffle:
self.rand()
else:
self.sort()
@ -86,7 +86,7 @@ class MediaWatcher:
def __init__(self, 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.event_handler = PatternMatchingEventHandler(
@ -120,7 +120,7 @@ class MediaWatcher:
f'Move file from "{event.src_path}" to "{event.dest_path}"')
if self.current_clip == event.src_path:
FF.decoder.terminate()
ff_proc.decoder.terminate()
def on_deleted(self, event):
self._media.remove(event.src_path)
@ -128,7 +128,7 @@ class MediaWatcher:
messenger.info(f'Remove file from media list: "{event.src_path}"')
if self.current_clip == event.src_path:
FF.decoder.terminate()
ff_proc.decoder.terminate()
def stop(self):
self.observer.stop()

View File

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

View File

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

View File

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

View File

@ -28,9 +28,9 @@ from threading import Thread
import requests
from .filters.default import build_filtergraph
from .utils import (GENERAL, PLAYLIST, STDIN_ARGS, MediaProbe, check_sync,
get_date, get_delta, get_float, get_time, messenger,
src_or_dummy, valid_json)
from .utils import (MediaProbe, check_sync, get_date, get_delta, get_float,
get_time, messenger, playlist, src_or_dummy, stdin_args,
sync_op, valid_json)
def handle_list_init(node):
@ -103,13 +103,13 @@ def timed_source(node, last):
delta, total_delta = get_delta(node['begin'])
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'total_delta: {total_delta:f}')
check_sync(delta)
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
node_ = src_or_dummy(node)
@ -126,12 +126,12 @@ def check_length(total_play_time, list_date):
"""
check if playlist is long enough
"""
if PLAYLIST.length and total_play_time < PLAYLIST.length - 5 \
and not STDIN_ARGS.loop:
if playlist.length and total_play_time < playlist.length - 5 \
and not stdin_args.loop:
messenger.error(
f'Playlist from {list_date} is not long enough!\n'
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.error = False
if STDIN_ARGS.playlist:
json_file = STDIN_ARGS.playlist
if stdin_args.playlist:
json_file = stdin_args.playlist
else:
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')
if '://' in json_file:
@ -253,7 +253,7 @@ class GetSourceFromPlaylist:
def __init__(self):
self.prev_date = get_date(True)
self.list_start = PLAYLIST.start
self.list_start = playlist.start
self.first = True
self.last = False
self.clip_nodes = []
@ -261,7 +261,7 @@ class GetSourceFromPlaylist:
self.node = None
self.prev_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
def get_playlist(self):
@ -269,26 +269,26 @@ class GetSourceFromPlaylist:
read playlist from given date and fill clip_nodes
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 \
self.playlist.list_date == self.prev_date:
if self.last_error and not self.playlist_reader.error and \
self.playlist_reader.list_date == self.prev_date:
# when last playlist where not exists but now is there and
# is still the same playlist date,
# set self.first to true to seek in clip
# only in this situation seek in is correct!!
self.first = True
self.last_error = self.playlist.error
self.last_error = self.playlist_reader.error
if self.playlist.nodes.get('program'):
self.clip_nodes = self.playlist.nodes.get('program')
if self.playlist_reader.nodes.get('program'):
self.clip_nodes = self.playlist_reader.nodes.get('program')
self.node_count = len(self.clip_nodes)
if self.playlist.error:
if self.playlist_reader.error:
self.clip_nodes = []
self.node_count = 0
self.playlist.last_mod_time = 0.0
self.last_error = self.playlist.error
self.playlist_reader.last_mod_time = 0.0
self.last_error = self.playlist_reader.error
def init_time(self):
"""
@ -296,12 +296,12 @@ class GetSourceFromPlaylist:
"""
self.last_time = get_time('full_sec')
if PLAYLIST.length:
total_playtime = PLAYLIST.length
if playlist.length:
total_playtime = playlist.length
else:
total_playtime = 86400.0
if self.last_time < PLAYLIST.start:
if self.last_time < playlist.start:
self.last_time += total_playtime
def check_for_next_playlist(self, begin):
@ -321,17 +321,17 @@ class GetSourceFromPlaylist:
delta, total_delta = get_delta(begin)
delta += seek + 1
next_start = begin - PLAYLIST.start + out + delta
next_start = begin - playlist.start + out + delta
else:
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.playlist.list_date = self.prev_date
self.playlist.last_mod_time = 0.0
self.last_time = PLAYLIST.start - 1
self.playlist_reader.list_date = self.prev_date
self.playlist_reader.last_mod_time = 0.0
self.last_time = playlist.start - 1
self.clip_nodes = []
def previous_and_next_node(self, index):
@ -361,9 +361,9 @@ class GetSourceFromPlaylist:
"""
current_time = get_time('full_sec') - 86400
# 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):
begin = PLAYLIST.start
begin = playlist.start
else:
self.init_time()
begin = self.last_time
@ -385,14 +385,14 @@ class GetSourceFromPlaylist:
"""
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,
# jump to playlist start and play again
self.list_start = self.last_time + 1
self.node = None
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
messenger.error('Clip nodes are empty!')
self.first = False
@ -454,7 +454,7 @@ class GetSourceFromPlaylist:
begin += self.node['out'] - self.node['seek']
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
messenger.info('Playlist reached end!')
return None

View File

@ -98,7 +98,7 @@ for arg_file in glob(os.path.join(CONFIG_PATH, 'argparse_*')):
**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
# ------------------------------------------------------------------------------
GENERAL = SimpleNamespace(time_delta=0)
MAIL = SimpleNamespace()
LOG = SimpleNamespace()
PRE = SimpleNamespace()
PLAYLIST = SimpleNamespace()
STORAGE = SimpleNamespace()
TEXT = SimpleNamespace()
PLAYOUT = SimpleNamespace()
sync_op = SimpleNamespace(time_delta=0)
mail = SimpleNamespace()
log = SimpleNamespace()
pre = SimpleNamespace()
playlist = SimpleNamespace()
storage = SimpleNamespace()
lower_third = SimpleNamespace()
playout = SimpleNamespace()
INITIAL = SimpleNamespace(load=True)
FF = SimpleNamespace(decoder=None, encoder=None)
initial = SimpleNamespace(load=True)
ff_proc = SimpleNamespace(decoder=None, encoder=None)
def str_to_sec(s):
@ -164,90 +164,90 @@ def load_config():
some settings cannot be changed - like resolution, aspect, or output
"""
if STDIN_ARGS.config:
cfg = read_config(STDIN_ARGS.config)
if stdin_args.config:
cfg = read_config(stdin_args.config)
elif os.path.isfile('/etc/ffplayout/ffplayout.yml'):
cfg = read_config('/etc/ffplayout/ffplayout.yml')
else:
cfg = read_config('ffplayout.yml')
if STDIN_ARGS.start:
p_start = str_to_sec(STDIN_ARGS.start)
if stdin_args.start:
p_start = str_to_sec(stdin_args.start)
else:
p_start = str_to_sec(cfg['playlist']['day_start'])
if p_start is None:
p_start = get_time('full_sec')
if STDIN_ARGS.length:
p_length = str_to_sec(STDIN_ARGS.length)
if stdin_args.length:
p_length = str_to_sec(stdin_args.length)
else:
p_length = str_to_sec(cfg['playlist']['length'])
GENERAL.stop = cfg['general']['stop_on_error']
GENERAL.threshold = cfg['general']['stop_threshold']
sync_op.stop = cfg['general']['stop_on_error']
sync_op.threshold = cfg['general']['stop_threshold']
MAIL.subject = cfg['mail']['subject']
MAIL.server = cfg['mail']['smpt_server']
MAIL.port = cfg['mail']['smpt_port']
MAIL.s_addr = cfg['mail']['sender_addr']
MAIL.s_pass = cfg['mail']['sender_pass']
MAIL.recip = cfg['mail']['recipient']
MAIL.level = cfg['mail']['mail_level']
mail.subject = cfg['mail']['subject']
mail.server = cfg['mail']['smpt_server']
mail.port = cfg['mail']['smpt_port']
mail.s_addr = cfg['mail']['sender_addr']
mail.s_pass = cfg['mail']['sender_pass']
mail.recip = cfg['mail']['recipient']
mail.level = cfg['mail']['mail_level']
PRE.add_logo = cfg['processing']['add_logo']
PRE.logo = cfg['processing']['logo']
PRE.logo_scale = cfg['processing']['logo_scale']
PRE.logo_filter = cfg['processing']['logo_filter']
PRE.logo_opacity = cfg['processing']['logo_opacity']
PRE.add_loudnorm = cfg['processing']['add_loudnorm']
PRE.loud_i = cfg['processing']['loud_I']
PRE.loud_tp = cfg['processing']['loud_TP']
PRE.loud_lra = cfg['processing']['loud_LRA']
PRE.output_count = cfg['processing']['output_count']
pre.add_logo = cfg['processing']['add_logo']
pre.logo = cfg['processing']['logo']
pre.logo_scale = cfg['processing']['logo_scale']
pre.logo_filter = cfg['processing']['logo_filter']
pre.logo_opacity = cfg['processing']['logo_opacity']
pre.add_loudnorm = cfg['processing']['add_loudnorm']
pre.loud_i = cfg['processing']['loud_I']
pre.loud_tp = cfg['processing']['loud_TP']
pre.loud_lra = cfg['processing']['loud_LRA']
pre.output_count = cfg['processing']['output_count']
PLAYLIST.mode = cfg['playlist']['playlist_mode']
PLAYLIST.path = cfg['playlist']['path']
PLAYLIST.start = p_start
PLAYLIST.length = p_length
playlist.mode = cfg['playlist']['playlist_mode']
playlist.path = cfg['playlist']['path']
playlist.start = p_start
playlist.length = p_length
STORAGE.path = cfg['storage']['path']
STORAGE.filler = cfg['storage']['filler_clip']
STORAGE.extensions = cfg['storage']['extensions']
STORAGE.shuffle = cfg['storage']['shuffle']
storage.path = cfg['storage']['path']
storage.filler = cfg['storage']['filler_clip']
storage.extensions = cfg['storage']['extensions']
storage.shuffle = cfg['storage']['shuffle']
TEXT.add_text = cfg['text']['add_text']
TEXT.over_pre = cfg['text']['over_pre']
TEXT.address = cfg['text']['bind_address']
TEXT.fontfile = cfg['text']['fontfile']
TEXT.text_from_filename = cfg['text']['text_from_filename']
TEXT.style = cfg['text']['style']
TEXT.regex = cfg['text']['regex']
lower_third.add_text = cfg['text']['add_text']
lower_third.over_pre = cfg['text']['over_pre']
lower_third.address = cfg['text']['bind_address']
lower_third.fontfile = cfg['text']['fontfile']
lower_third.text_from_filename = cfg['text']['text_from_filename']
lower_third.style = cfg['text']['style']
lower_third.regex = cfg['text']['regex']
if INITIAL.load:
LOG.to_file = cfg['logging']['log_to_file']
LOG.backup_count = cfg['logging']['backup_count']
LOG.path = cfg['logging']['log_path']
LOG.level = cfg['logging']['log_level']
LOG.ff_level = cfg['logging']['ffmpeg_level']
if initial.load:
log.to_file = cfg['logging']['log_to_file']
log.backup_count = cfg['logging']['backup_count']
log.path = cfg['logging']['log_path']
log.level = cfg['logging']['log_level']
log.ff_level = cfg['logging']['ffmpeg_level']
PRE.w = cfg['processing']['width']
PRE.h = cfg['processing']['height']
PRE.aspect = cfg['processing']['aspect']
PRE.fps = cfg['processing']['fps']
PRE.v_bitrate = cfg['processing']['width'] * \
pre.w = cfg['processing']['width']
pre.h = cfg['processing']['height']
pre.aspect = cfg['processing']['aspect']
pre.fps = cfg['processing']['fps']
pre.v_bitrate = cfg['processing']['width'] * \
cfg['processing']['height'] / 10
PRE.v_bufsize = PRE.v_bitrate / 2
PRE.realtime = cfg['processing']['use_realtime']
pre.v_bufsize = pre.v_bitrate / 2
pre.realtime = cfg['processing']['use_realtime']
PLAYOUT.mode = cfg['out']['mode']
PLAYOUT.name = cfg['out']['service_name']
PLAYOUT.provider = cfg['out']['service_provider']
PLAYOUT.ffmpeg_param = cfg['out']['ffmpeg_param'].split(' ')
PLAYOUT.stream_output = cfg['out']['stream_output'].split(' ')
PLAYOUT.hls_output = cfg['out']['hls_output'].split(' ')
playout.mode = cfg['out']['mode']
playout.name = cfg['out']['service_name']
playout.provider = cfg['out']['service_provider']
playout.ffmpeg_param = cfg['out']['ffmpeg_param'].split(' ')
playout.stream_output = cfg['out']['stream_output'].split(' ')
playout.hls_output = cfg['out']['hls_output'].split(' ')
INITIAL.load = False
initial.load = False
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 STDIN_ARGS.log:
LOG.path = STDIN_ARGS.log
if stdin_args.log:
log.path = stdin_args.log
playout_logger = logging.getLogger('playout')
playout_logger.setLevel(LOG.level)
playout_logger.setLevel(log.level)
decoder_logger = logging.getLogger('decoder')
decoder_logger.setLevel(LOG.ff_level)
decoder_logger.setLevel(log.ff_level)
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.path and os.path.isdir(LOG.path):
playout_log = os.path.join(LOG.path, 'ffplayout.log')
decoder_log = os.path.join(LOG.path, 'decoder.log')
encoder_log = os.path.join(LOG.path, 'encoder.log')
if log.to_file and log.path != 'none':
if log.path and os.path.isdir(log.path):
playout_log = os.path.join(log.path, 'ffplayout.log')
decoder_log = os.path.join(log.path, 'decoder.log')
encoder_log = os.path.join(log.path, 'encoder.log')
else:
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
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')
f_format = logging.Formatter('[%(asctime)s] %(message)s')
p_file_handler = TimedRotatingFileHandler(playout_log, when='midnight',
backupCount=LOG.backup_count)
backupCount=log.backup_count)
d_file_handler = TimedRotatingFileHandler(decoder_log, when='midnight',
backupCount=LOG.backup_count)
backupCount=log.backup_count)
e_file_handler = TimedRotatingFileHandler(encoder_log, when='midnight',
backupCount=LOG.backup_count)
backupCount=log.backup_count)
p_file_handler.setFormatter(p_format)
d_file_handler.setFormatter(f_format)
@ -370,7 +370,7 @@ class Mailer:
"""
def __init__(self):
self.level = MAIL.level
self.level = mail.level
self.time = None
self.timestamp = get_time('stamp')
self.rate_limit = 600
@ -380,7 +380,7 @@ class Mailer:
self.time = get_time(None)
def send_mail(self, msg):
if MAIL.recip:
if mail.recip:
# write message to temp file for rate limit
with open(self.temp_msg, 'w+') as f:
f.write(msg)
@ -388,15 +388,15 @@ class Mailer:
self.current_time()
message = MIMEMultipart()
message['From'] = MAIL.s_addr
message['To'] = MAIL.recip
message['Subject'] = MAIL.subject
message['From'] = mail.s_addr
message['To'] = mail.recip
message['Subject'] = mail.subject
message['Date'] = formatdate(localtime=True)
message.attach(MIMEText(f'{self.time} {msg}', 'plain'))
text = message.as_string()
try:
server = smtplib.SMTP(MAIL.server, MAIL.port)
server = smtplib.SMTP(mail.server, mail.port)
except socket.error as err:
playout_logger.error(err)
server = None
@ -404,14 +404,14 @@ class Mailer:
if server is not None:
server.starttls()
try:
login = server.login(MAIL.s_addr, MAIL.s_pass)
login = server.login(mail.s_addr, mail.s_pass)
except smtplib.SMTPAuthenticationError as serr:
playout_logger.error(serr)
login = None
if login is not None:
server.sendmail(MAIL.s_addr,
re.split(', |; |,|;', MAIL.recip), text)
server.sendmail(mail.s_addr,
re.split(', |; |,|;', mail.recip), text)
server.quit()
def check_if_new(self, msg):
@ -623,11 +623,11 @@ def terminate_processes(watcher=None):
"""
kill orphaned processes
"""
if FF.decoder and FF.decoder.poll() is None:
FF.decoder.terminate()
if ff_proc.decoder and ff_proc.decoder.poll() is None:
ff_proc.decoder.terminate()
if FF.encoder and FF.encoder.poll() is None:
FF.encoder.terminate()
if ff_proc.encoder and ff_proc.encoder.poll() is None:
ff_proc.encoder.terminate()
if watcher:
watcher.stop()
@ -647,9 +647,9 @@ def ffmpeg_stderr_reader(std_errors, decoder):
try:
for line in std_errors:
if LOG.ff_level == 'INFO':
if log.ff_level == 'INFO':
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()}')
else:
logger.error(f'{prefix}{line.decode("utf-8").rstrip()}')
@ -663,17 +663,17 @@ def get_delta(begin):
"""
current_time = get_time('full_sec')
if STDIN_ARGS.length and str_to_sec(STDIN_ARGS.length):
target_playtime = str_to_sec(STDIN_ARGS.length)
elif PLAYLIST.length:
target_playtime = PLAYLIST.length
if stdin_args.length and str_to_sec(stdin_args.length):
target_playtime = str_to_sec(stdin_args.length)
elif playlist.length:
target_playtime = playlist.length
else:
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
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_delta = begin - current_time
@ -681,7 +681,7 @@ def get_delta(begin):
if math.isclose(current_delta, 86400.0, abs_tol=6):
current_delta -= 86400.0
ref_time = target_playtime + PLAYLIST.start
ref_time = target_playtime + playlist.start
total_delta = ref_time - begin + current_delta
return current_delta, total_delta
@ -695,9 +695,9 @@ def get_date(seek_day, next_start=0):
"""
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')
elif PLAYLIST.start == 0 and next_start >= 86400:
elif playlist.start == 0 and next_start >= 86400:
return (d + timedelta(1)).strftime('%Y-%m-%d')
else:
return d.strftime('%Y-%m-%d')
@ -738,12 +738,12 @@ def check_sync(delta):
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
# 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(
f'Sync tolerance value exceeded with {delta:.2f} seconds,\n'
'program terminated!')
@ -785,7 +785,7 @@ def gen_dummy(duration):
# noise = 'noise=alls=50:allf=t+u,hue=s=0'
return [
'-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',
'-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
"""
probe = MediaProbe()
probe.load(STORAGE.filler)
probe.load(storage.filler)
duration = node['out'] - node['seek']
node['probe'] = probe
@ -808,13 +808,13 @@ def gen_filler(node):
# cut filler
messenger.info(
f'Generate filler with {duration:.2f} seconds')
node['source'] = STORAGE.filler
node['src_cmd'] = ['-i', STORAGE.filler] + set_length(
node['source'] = storage.filler
node['src_cmd'] = ['-i', storage.filler] + set_length(
filler_duration, 0, duration)
return node
else:
# loop file n times
node['src_cmd'] = loop_input(STORAGE.filler, filler_duration,
node['src_cmd'] = loop_input(storage.filler, filler_duration,
duration)
return node
else:
@ -881,7 +881,7 @@ def pre_audio_codec():
s302m has higher quality, but is experimental
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']
else:
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__':
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')
PLAYLIST.start = 0
playlist.start = 0
run_time(140)
run_with_no_elements((2021, 2, 15, 23, 59, 53))
print_separater()
print('\ntest playlists, which are to short')
PLAYLIST.start = 0
playlist.start = 0
run_time(140)
run_with_less_elements((2021, 2, 15, 23, 58, 3))
print_separater()
print('\ntest playlists, which are to long')
PLAYLIST.start = 0
playlist.start = 0
run_time(140)
run_with_more_elements((2021, 2, 15, 23, 59, 33))
print_separater()
print('\ntest transition from playlists, with day_start at: 05:59:25')
PLAYLIST.start = 21575
playlist.start = 21575
run_time(140)
run_at((2021, 2, 17, 5, 58, 3))
print_separater()
print('\ntest transition from playlists, with day_start at: 20:00:00')
PLAYLIST.start = 72000
playlist.start = 72000
run_time(140)
run_at((2021, 2, 17, 19, 58, 23))