rename and uppercase constants

This commit is contained in:
jb-alvarado 2021-03-24 17:22:26 +01:00
parent 407e15452c
commit cb7034b3c8
8 changed files with 277 additions and 227 deletions

View File

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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -28,9 +28,9 @@ from threading import Thread
import requests import requests
from .filters.default import build_filtergraph from .filters.default import build_filtergraph
from .utils import (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

View File

@ -98,7 +98,7 @@ for arg_file in glob(os.path.join(CONFIG_PATH, 'argparse_*')):
**config **config
) )
stdin_args = stdin_parser.parse_args() STDIN_ARGS = stdin_parser.parse_args()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -127,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']

View File

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