rename and uppercase constants
This commit is contained in:
parent
407e15452c
commit
cb7034b3c8
@ -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()
|
||||||
|
|
||||||
|
@ -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 MediaProbe, _ff, _storage, messenger, stdin_args
|
from .utils import FF, STDIN_ARGS, STORAGE, MediaProbe, messenger
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# 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.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.decoder.terminate()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.observer.stop()
|
self.observer.stop()
|
||||||
|
@ -1,12 +1,29 @@
|
|||||||
|
# This file is part of ffplayout.
|
||||||
|
#
|
||||||
|
# ffplayout is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# ffplayout is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with ffplayout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from subprocess import PIPE, Popen
|
from subprocess import PIPE, Popen
|
||||||
from threading import Thread
|
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, _text,
|
from ffplayout.utils import (FF, LOG, PLAYLIST, PRE, STDIN_ARGS, TEXT,
|
||||||
ffmpeg_stderr_reader, messenger, pre_audio_codec,
|
ffmpeg_stderr_reader, messenger, pre_audio_codec,
|
||||||
stdin_args, terminate_processes)
|
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
|
||||||
@ -19,21 +36,21 @@ 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 TEXT.add_text and not TEXT.over_pre:
|
||||||
messenger.info(
|
messenger.info(
|
||||||
f'Using drawtext node, listening on address: {_text.address}')
|
f'Using drawtext node, listening on address: {TEXT.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)
|
TEXT.address.replace(':', '\\:'), TEXT.fontfile)
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -43,14 +60,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.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.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:
|
||||||
@ -69,23 +86,23 @@ 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.decoder:
|
||||||
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
||||||
args=(_ff.decoder.stderr, True))
|
args=(FF.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.decoder.stdout.read(COPY_BUFSIZE)
|
||||||
if not buf:
|
if not buf:
|
||||||
break
|
break
|
||||||
_ff.encoder.stdin.write(buf)
|
FF.encoder.stdin.write(buf)
|
||||||
|
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
messenger.error('Broken Pipe!')
|
messenger.error('Broken Pipe!')
|
||||||
@ -100,10 +117,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.encoder.poll() is None:
|
||||||
_ff.encoder.terminate()
|
FF.encoder.terminate()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if _ff.encoder.poll() is None:
|
if FF.encoder.poll() is None:
|
||||||
_ff.encoder.terminate()
|
FF.encoder.terminate()
|
||||||
_ff.encoder.wait()
|
FF.encoder.wait()
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
# This file is part of ffplayout.
|
||||||
|
#
|
||||||
|
# ffplayout is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# ffplayout is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with ffplayout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from glob import iglob
|
from glob import iglob
|
||||||
@ -6,9 +23,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,
|
from ffplayout.utils import (FF, LOG, PLAYLIST, PLAYOUT, STDIN_ARGS,
|
||||||
ffmpeg_stderr_reader, get_date, messenger,
|
ffmpeg_stderr_reader, get_date, messenger,
|
||||||
stdin_args, terminate_processes)
|
terminate_processes)
|
||||||
|
|
||||||
|
|
||||||
def clean_ts():
|
def clean_ts():
|
||||||
@ -18,7 +35,7 @@ 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]
|
playlists = [p for p in PLAYOUT.hls_output if 'm3u8' in p]
|
||||||
|
|
||||||
for playlist in playlists:
|
for playlist in playlists:
|
||||||
messenger.debug(f'cleanup *.ts files from: "{playlist}"')
|
messenger.debug(f'cleanup *.ts files from: "{playlist}"')
|
||||||
@ -49,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:
|
||||||
@ -66,20 +83,20 @@ 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.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.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()
|
||||||
@ -101,10 +118,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.encoder.poll() is None:
|
||||||
_ff.encoder.terminate()
|
FF.encoder.terminate()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if _ff.encoder.poll() is None:
|
if FF.encoder.poll() is None:
|
||||||
_ff.encoder.terminate()
|
FF.encoder.terminate()
|
||||||
_ff.encoder.wait()
|
FF.encoder.wait()
|
||||||
|
@ -1,12 +1,29 @@
|
|||||||
|
# This file is part of ffplayout.
|
||||||
|
#
|
||||||
|
# ffplayout is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# ffplayout is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with ffplayout. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from subprocess import PIPE, Popen
|
from subprocess import PIPE, Popen
|
||||||
from threading import Thread
|
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, _text,
|
from ffplayout.utils import (FF, LOG, PLAYLIST, PLAYOUT, PRE, STDIN_ARGS, TEXT,
|
||||||
ffmpeg_stderr_reader, get_date, messenger,
|
ffmpeg_stderr_reader, get_date, messenger,
|
||||||
pre_audio_codec, stdin_args, terminate_processes)
|
pre_audio_codec, 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
|
||||||
@ -21,43 +38,43 @@ 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 TEXT.add_text and not TEXT.over_pre:
|
||||||
messenger.info(
|
messenger.info(
|
||||||
f'Using drawtext node, listening on address: {_text.address}')
|
f'Using drawtext node, listening on address: {TEXT.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)
|
TEXT.address.replace(':', '\\:'), TEXT.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.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.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:
|
||||||
@ -74,23 +91,23 @@ 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.decoder:
|
||||||
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
||||||
args=(_ff.decoder.stderr, True))
|
args=(FF.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.decoder.stdout.read(COPY_BUFSIZE)
|
||||||
if not buf:
|
if not buf:
|
||||||
break
|
break
|
||||||
_ff.encoder.stdin.write(buf)
|
FF.encoder.stdin.write(buf)
|
||||||
|
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
messenger.error('Broken Pipe!')
|
messenger.error('Broken Pipe!')
|
||||||
@ -105,10 +122,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.encoder.poll() is None:
|
||||||
_ff.encoder.terminate()
|
FF.encoder.terminate()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if _ff.encoder.poll() is None:
|
if FF.encoder.poll() is None:
|
||||||
_ff.encoder.terminate()
|
FF.encoder.terminate()
|
||||||
_ff.encoder.wait()
|
FF.encoder.wait()
|
||||||
|
@ -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 (MediaProbe, _general, _playlist, check_sync, get_date,
|
from .utils import (GENERAL, PLAYLIST, STDIN_ARGS, MediaProbe, check_sync,
|
||||||
get_delta, get_float, get_time, messenger, src_or_dummy,
|
get_date, get_delta, get_float, get_time, messenger,
|
||||||
stdin_args, valid_json)
|
src_or_dummy, 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 = []
|
||||||
@ -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 + GENERAL.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.list_date = self.prev_date
|
||||||
self.playlist.last_mod_time = 0.0
|
self.playlist.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
|
||||||
|
@ -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,18 +127,17 @@ def get_time(time_format):
|
|||||||
# default variables and values
|
# default variables and values
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
_general = SimpleNamespace()
|
GENERAL = 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()
|
TEXT = SimpleNamespace()
|
||||||
_playout = SimpleNamespace()
|
PLAYOUT = SimpleNamespace()
|
||||||
|
|
||||||
_init = SimpleNamespace(load=True)
|
INITIAL = SimpleNamespace(load=True)
|
||||||
_ff = SimpleNamespace(decoder=None, encoder=None)
|
FF = SimpleNamespace(decoder=None, encoder=None)
|
||||||
_global = SimpleNamespace(time_delta=0)
|
|
||||||
|
|
||||||
|
|
||||||
def str_to_sec(s):
|
def str_to_sec(s):
|
||||||
@ -165,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']
|
GENERAL.stop = cfg['general']['stop_on_error']
|
||||||
_general.threshold = cfg['general']['stop_threshold']
|
GENERAL.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']
|
TEXT.add_text = cfg['text']['add_text']
|
||||||
_text.over_pre = cfg['text']['over_pre']
|
TEXT.over_pre = cfg['text']['over_pre']
|
||||||
_text.address = cfg['text']['bind_address']
|
TEXT.address = cfg['text']['bind_address']
|
||||||
_text.fontfile = cfg['text']['fontfile']
|
TEXT.fontfile = cfg['text']['fontfile']
|
||||||
_text.text_from_filename = cfg['text']['text_from_filename']
|
TEXT.text_from_filename = cfg['text']['text_from_filename']
|
||||||
_text.style = cfg['text']['style']
|
TEXT.style = cfg['text']['style']
|
||||||
_text.regex = cfg['text']['regex']
|
TEXT.regex = cfg['text']['regex']
|
||||||
|
|
||||||
if _init.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(' ')
|
||||||
|
|
||||||
_init.load = False
|
INITIAL.load = False
|
||||||
|
|
||||||
|
|
||||||
load_config()
|
load_config()
|
||||||
@ -309,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')
|
||||||
@ -335,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)
|
||||||
@ -371,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
|
||||||
@ -381,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)
|
||||||
@ -389,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
|
||||||
@ -405,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):
|
||||||
@ -624,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.decoder and FF.decoder.poll() is None:
|
||||||
_ff.decoder.terminate()
|
FF.decoder.terminate()
|
||||||
|
|
||||||
if _ff.encoder and _ff.encoder.poll() is None:
|
if FF.encoder and FF.encoder.poll() is None:
|
||||||
_ff.encoder.terminate()
|
FF.encoder.terminate()
|
||||||
|
|
||||||
if watcher:
|
if watcher:
|
||||||
watcher.stop()
|
watcher.stop()
|
||||||
@ -648,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()}')
|
||||||
@ -664,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
|
||||||
@ -682,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
|
||||||
@ -696,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')
|
||||||
@ -736,12 +735,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
|
||||||
_global.time_delta = delta
|
GENERAL.time_delta = delta
|
||||||
|
|
||||||
if _general.stop and abs(delta) > _general.threshold:
|
if GENERAL.stop and abs(delta) > GENERAL.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!')
|
||||||
@ -787,7 +786,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'
|
||||||
]
|
]
|
||||||
@ -798,7 +797,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
|
||||||
@ -810,13 +809,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:
|
||||||
@ -883,7 +882,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']
|
||||||
|
@ -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))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user