change variable names, to try to follow style conventions
This commit is contained in:
parent
6abadf9543
commit
0a1467b093
@ -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()
|
||||
|
||||
|
@ -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}'
|
||||
|
@ -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)
|
||||
|
@ -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}"
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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']
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user