add docstrings and clean code with pylint
This commit is contained in:
parent
22f971ddc8
commit
67be3afb3d
0
__init__.py
Normal file
0
__init__.py
Normal file
@ -17,6 +17,10 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module is the starting program for running ffplayout engine.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pydoc import locate
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
"""
|
||||
cunstom audio filter, which get loaded automatically
|
||||
"""
|
||||
|
||||
from ffplayout.utils import get_float, stdin_args
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def filter_link(node):
|
||||
"""
|
||||
set audio volume
|
||||
@ -8,3 +13,5 @@ def filter_link(node):
|
||||
|
||||
if stdin_args.volume and get_float(stdin_args.volume, False):
|
||||
return f'volume={stdin_args.volume}'
|
||||
|
||||
return None
|
||||
|
@ -15,6 +15,11 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module prepare all ffmpeg filters.
|
||||
This is mainly for unify clips to have a unique output.
|
||||
"""
|
||||
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
@ -31,6 +36,9 @@ from ffplayout.utils import (is_advertisement, lower_third, messenger, pre,
|
||||
|
||||
|
||||
def text_filter():
|
||||
"""
|
||||
add drawtext filter for lower thirds messages
|
||||
"""
|
||||
filter_chain = []
|
||||
font = ''
|
||||
|
||||
@ -122,21 +130,21 @@ def fade_filter(duration, seek, out, track=''):
|
||||
return filter_chain
|
||||
|
||||
|
||||
def overlay_filter(duration, ad, ad_last, ad_next):
|
||||
def overlay_filter(duration, advertisement, ad_last, ad_next):
|
||||
"""
|
||||
overlay logo: when is an ad don't overlay,
|
||||
when ad is comming next fade logo out,
|
||||
when clip before was an ad fade logo in
|
||||
"""
|
||||
logo_filter = '[v]null'
|
||||
scale_filter = ''
|
||||
scale = ''
|
||||
|
||||
if pre.add_logo and os.path.isfile(pre.logo) and not ad:
|
||||
if pre.add_logo and os.path.isfile(pre.logo) and not advertisement:
|
||||
logo_chain = []
|
||||
if pre.logo_scale and \
|
||||
re.match(r'\d+:-?\d+', pre.logo_scale):
|
||||
scale_filter = f'scale={pre.logo_scale},'
|
||||
logo_extras = (f'format=rgba,{scale_filter}'
|
||||
scale = f'scale={pre.logo_scale},'
|
||||
logo_extras = (f'format=rgba,{scale}'
|
||||
f'colorchannelmixer=aa={pre.logo_opacity}')
|
||||
loop = 'loop=loop=-1:size=1:start=0'
|
||||
logo_chain.append(f'movie={pre.logo},{loop},{logo_extras}')
|
||||
@ -182,30 +190,33 @@ def extend_audio(probe, duration):
|
||||
"""
|
||||
check audio duration, is it shorter then clip duration - pad it
|
||||
"""
|
||||
pad_filter = []
|
||||
pad = []
|
||||
|
||||
if probe.audio and 'duration' in probe.audio[0] and \
|
||||
duration > float(probe.audio[0]['duration']) + 0.1:
|
||||
pad_filter.append(f'apad=whole_dur={duration}')
|
||||
pad.append(f'apad=whole_dur={duration}')
|
||||
|
||||
return pad_filter
|
||||
return pad
|
||||
|
||||
|
||||
def extend_video(probe, duration, target_duration):
|
||||
"""
|
||||
check video duration, is it shorter then clip duration - pad it
|
||||
"""
|
||||
pad_filter = []
|
||||
pad = []
|
||||
vid_dur = probe.video[0].get('duration')
|
||||
|
||||
if vid_dur and target_duration < duration > float(vid_dur) + 0.1:
|
||||
pad_filter.append(
|
||||
pad.append(
|
||||
f'tpad=stop_mode=add:stop_duration={duration - float(vid_dur)}')
|
||||
|
||||
return pad_filter
|
||||
return pad
|
||||
|
||||
|
||||
def realtime_filter(duration, track=''):
|
||||
"""
|
||||
this realtime filter is important for HLS output to stay in sync
|
||||
"""
|
||||
speed_filter = ''
|
||||
|
||||
if pre.realtime:
|
||||
@ -221,6 +232,10 @@ def realtime_filter(duration, track=''):
|
||||
|
||||
|
||||
def split_filter(filter_type):
|
||||
"""
|
||||
this filter splits the media input in multiple outputs,
|
||||
to be able to have differnt streaming/HLS outputs
|
||||
"""
|
||||
map_node = []
|
||||
prefix = ''
|
||||
_filter = ''
|
||||
@ -241,6 +256,9 @@ def split_filter(filter_type):
|
||||
|
||||
|
||||
def custom_filter(filter_type, node):
|
||||
"""
|
||||
read custom filters from filters folder
|
||||
"""
|
||||
filter_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
filters = []
|
||||
|
||||
@ -260,7 +278,7 @@ def build_filtergraph(node, node_last, node_next):
|
||||
build final filter graph, with video and audio chain
|
||||
"""
|
||||
|
||||
ad = is_advertisement(node)
|
||||
advertisement = is_advertisement(node)
|
||||
ad_last = is_advertisement(node_last)
|
||||
ad_next = is_advertisement(node_next)
|
||||
|
||||
@ -277,12 +295,12 @@ def build_filtergraph(node, node_last, node_next):
|
||||
|
||||
if probe and probe.video[0]:
|
||||
custom_v_filter = custom_filter('v', node)
|
||||
video_chain += text_filter()
|
||||
video_chain += deinterlace_filter(probe)
|
||||
video_chain += pad_filter(probe)
|
||||
video_chain += fps_filter(probe)
|
||||
video_chain += scale_filter(probe)
|
||||
video_chain += extend_video(probe, duration, out - seek)
|
||||
video_chain += text_filter() \
|
||||
+ deinterlace_filter(probe) \
|
||||
+ pad_filter(probe) \
|
||||
+ fps_filter(probe) \
|
||||
+ scale_filter(probe) \
|
||||
+ extend_video(probe, duration, out - seek)
|
||||
if custom_v_filter:
|
||||
video_chain += custom_v_filter
|
||||
video_chain += fade_filter(duration, seek, out)
|
||||
@ -292,9 +310,9 @@ def build_filtergraph(node, node_last, node_next):
|
||||
if not audio_chain:
|
||||
custom_a_filter = custom_filter('a', node)
|
||||
|
||||
audio_chain.append('[0:a]anull')
|
||||
audio_chain += add_loudnorm(probe)
|
||||
audio_chain += extend_audio(probe, out - seek)
|
||||
audio_chain += ['[0:a]anull'] \
|
||||
+ add_loudnorm(probe) \
|
||||
+ extend_audio(probe, out - seek)
|
||||
if custom_a_filter:
|
||||
audio_chain += custom_a_filter
|
||||
audio_chain += fade_filter(duration, seek, out, 'a')
|
||||
@ -304,7 +322,7 @@ def build_filtergraph(node, node_last, node_next):
|
||||
else:
|
||||
video_filter = 'null[v]'
|
||||
|
||||
logo_filter = overlay_filter(out - seek, ad, ad_last, ad_next)
|
||||
logo_filter = overlay_filter(out - seek, advertisement, ad_last, ad_next)
|
||||
v_speed = realtime_filter(out - seek)
|
||||
v_split = split_filter('v')
|
||||
video_map = ['-map', '[vout1]']
|
||||
@ -320,5 +338,5 @@ def build_filtergraph(node, node_last, node_next):
|
||||
|
||||
if probe and probe.video[0]:
|
||||
return video_filter + audio_filter + video_map + audio_map
|
||||
else:
|
||||
|
||||
return video_filter + video_map + ['-map', '1:a']
|
||||
|
@ -1,3 +1,7 @@
|
||||
"""
|
||||
cunstom video filter, which get loaded automatically
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
@ -19,3 +23,5 @@ def filter_link(node):
|
||||
if lower_third.text_from_filename:
|
||||
escape = title.replace("'", "'\\\\\\''").replace("%", "\\\\\\%")
|
||||
return f"drawtext=text='{escape}':{lower_third.style}{font}"
|
||||
|
||||
return None
|
||||
|
@ -15,6 +15,10 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module handles folder reading. It monitor file adding, deleting or moving
|
||||
"""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import random
|
||||
@ -49,31 +53,47 @@ class MediaStore:
|
||||
self.fill()
|
||||
|
||||
def fill(self):
|
||||
"""
|
||||
fill media list
|
||||
"""
|
||||
for ext in storage.extensions:
|
||||
self.store.extend(
|
||||
glob.glob(os.path.join(self.folder, '**', f'*{ext}'),
|
||||
recursive=True))
|
||||
|
||||
def sort_or_radomize(self):
|
||||
"""
|
||||
sort or randomize file list
|
||||
"""
|
||||
if storage.shuffle:
|
||||
self.rand()
|
||||
else:
|
||||
self.sort()
|
||||
|
||||
def add(self, file):
|
||||
"""
|
||||
add new file to media list
|
||||
"""
|
||||
self.store.append(file)
|
||||
self.sort_or_radomize()
|
||||
|
||||
def remove(self, file):
|
||||
"""
|
||||
remove file from media list
|
||||
"""
|
||||
self.store.remove(file)
|
||||
self.sort_or_radomize()
|
||||
|
||||
def sort(self):
|
||||
# sort list for sorted playing
|
||||
"""
|
||||
sort list for sorted playing
|
||||
"""
|
||||
self.store = sorted(self.store)
|
||||
|
||||
def rand(self):
|
||||
# randomize list for playing
|
||||
"""
|
||||
randomize list for playing
|
||||
"""
|
||||
random.shuffle(self.store)
|
||||
|
||||
|
||||
@ -100,7 +120,9 @@ class MediaWatcher:
|
||||
self.observer.start()
|
||||
|
||||
def on_created(self, event):
|
||||
# add file to media list only if it is completely copied
|
||||
"""
|
||||
add file to media list only if it is completely copied
|
||||
"""
|
||||
file_size = -1
|
||||
while file_size != os.path.getsize(event.src_path):
|
||||
file_size = os.path.getsize(event.src_path)
|
||||
@ -111,6 +133,9 @@ class MediaWatcher:
|
||||
messenger.info(f'Add file to media list: "{event.src_path}"')
|
||||
|
||||
def on_moved(self, event):
|
||||
"""
|
||||
operation when file on storage are moved
|
||||
"""
|
||||
self._media.remove(event.src_path)
|
||||
self._media.add(event.dest_path)
|
||||
|
||||
@ -121,6 +146,9 @@ class MediaWatcher:
|
||||
ff_proc.decoder.terminate()
|
||||
|
||||
def on_deleted(self, event):
|
||||
"""
|
||||
operation when file on storage are deleted
|
||||
"""
|
||||
self._media.remove(event.src_path)
|
||||
|
||||
messenger.info(f'Remove file from media list: "{event.src_path}"')
|
||||
@ -129,6 +157,9 @@ class MediaWatcher:
|
||||
ff_proc.decoder.terminate()
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
stop monitoring storage
|
||||
"""
|
||||
self.observer.stop()
|
||||
self.observer.join()
|
||||
|
||||
@ -150,6 +181,9 @@ class GetSourceFromFolder:
|
||||
self.node_next = None
|
||||
|
||||
def next(self):
|
||||
"""
|
||||
generator for getting always a new file
|
||||
"""
|
||||
while True:
|
||||
while self.index < len(self._media.store):
|
||||
if self.node_next:
|
||||
@ -188,5 +222,5 @@ class GetSourceFromFolder:
|
||||
yield self.node
|
||||
self.index += 1
|
||||
self.node_last = deepcopy(self.node)
|
||||
else:
|
||||
|
||||
self.index = 0
|
||||
|
@ -15,6 +15,10 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module plays the compressed output directly on the desktop.
|
||||
"""
|
||||
|
||||
import os
|
||||
from subprocess import PIPE, Popen
|
||||
from threading import Thread
|
||||
|
@ -15,6 +15,10 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module write the files compression directly to a hls (m3u8) playlist.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from glob import iglob
|
||||
|
@ -15,6 +15,10 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module streams the files out to a remote target.
|
||||
"""
|
||||
|
||||
import os
|
||||
from subprocess import PIPE, Popen
|
||||
from threading import Thread
|
||||
|
@ -15,6 +15,12 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module handles playlists, it can be aware of time syncing.
|
||||
Empty, missing or any other playlist related failure should be compensate.
|
||||
Missing clips will be replaced by a dummy clip.
|
||||
"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
@ -50,9 +56,8 @@ def handle_list_init(node):
|
||||
node['out'] = out
|
||||
node['seek'] = seek
|
||||
return src_or_dummy(node)
|
||||
else:
|
||||
messenger.warning(
|
||||
f'Clip less then a second, skip:\n{node["source"]}')
|
||||
|
||||
messenger.warning(f'Clip less then a second, skip:\n{node["source"]}')
|
||||
|
||||
return None
|
||||
|
||||
@ -191,6 +196,11 @@ def validate_thread(clip_nodes, list_date):
|
||||
|
||||
|
||||
class PlaylistReader:
|
||||
"""
|
||||
Class which read playlists, it checks if playlist got modified,
|
||||
when yes it reads the file new, when not it used the cached one
|
||||
"""
|
||||
|
||||
def __init__(self, list_date, last_mod_time):
|
||||
self.list_date = list_date
|
||||
self.last_mod_time = last_mod_time
|
||||
@ -198,13 +208,16 @@ class PlaylistReader:
|
||||
self.error = False
|
||||
|
||||
def read(self):
|
||||
"""
|
||||
read and process playlist
|
||||
"""
|
||||
self.nodes = {'program': []}
|
||||
self.error = False
|
||||
|
||||
if stdin_args.playlist:
|
||||
json_file = stdin_args.playlist
|
||||
else:
|
||||
year, month, day = self.list_date.split('-')
|
||||
year, month, _ = self.list_date.split('-')
|
||||
json_file = os.path.join(playlist.path, year, month,
|
||||
f'{self.list_date}.json')
|
||||
|
||||
@ -231,8 +244,8 @@ class PlaylistReader:
|
||||
# check last modification time from playlist
|
||||
mod_time = os.path.getmtime(json_file)
|
||||
if mod_time > self.last_mod_time:
|
||||
with open(json_file, 'r', encoding='utf-8') as f:
|
||||
self.nodes = valid_json(f)
|
||||
with open(json_file, 'r', encoding='utf-8') as playlist_file:
|
||||
self.nodes = valid_json(playlist_file)
|
||||
|
||||
self.last_mod_time = mod_time
|
||||
messenger.info('Open: ' + json_file)
|
||||
@ -252,6 +265,7 @@ class GetSourceFromPlaylist:
|
||||
def __init__(self):
|
||||
self.prev_date = get_date(True)
|
||||
self.list_start = playlist.start
|
||||
self.last_time = 0
|
||||
self.first = True
|
||||
self.last = False
|
||||
self.clip_nodes = []
|
||||
@ -316,13 +330,13 @@ class GetSourceFromPlaylist:
|
||||
|
||||
if self.last:
|
||||
seek = self.node['seek'] if self.node['seek'] > 0 else 0
|
||||
delta, total_delta = get_delta(begin)
|
||||
delta, _ = get_delta(begin)
|
||||
delta += seek + 1
|
||||
|
||||
next_start = begin - playlist.start + out + delta
|
||||
|
||||
else:
|
||||
delta, total_delta = get_delta(begin)
|
||||
delta, _ = get_delta(begin)
|
||||
next_start = begin - playlist.start + sync_op.threshold + delta
|
||||
|
||||
if playlist.length and next_start >= playlist.length:
|
||||
@ -456,7 +470,7 @@ class GetSourceFromPlaylist:
|
||||
# when we reach playlist end, stop script
|
||||
messenger.info('Playlist reached end!')
|
||||
return None
|
||||
else:
|
||||
|
||||
self.eof_handling(begin)
|
||||
|
||||
if self.node:
|
||||
|
@ -15,6 +15,10 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
This module contains default variables and helper functions
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
@ -285,6 +289,9 @@ class CustomFormatter(logging.Formatter):
|
||||
}
|
||||
|
||||
def format_message(self, msg):
|
||||
"""
|
||||
match strings with regex and add different color tags to it
|
||||
"""
|
||||
if '"' in msg:
|
||||
msg = re.sub('(".*?")', self.cyan + r'\1' + self.reset, msg)
|
||||
elif '[decoder]' in msg:
|
||||
@ -302,6 +309,9 @@ class CustomFormatter(logging.Formatter):
|
||||
return msg
|
||||
|
||||
def format(self, record):
|
||||
"""
|
||||
override logging format
|
||||
"""
|
||||
record.msg = self.format_message(record.getMessage())
|
||||
log_fmt = self.FORMATS.get(record.levelno)
|
||||
formatter = logging.Formatter(log_fmt)
|
||||
@ -378,13 +388,19 @@ class Mailer:
|
||||
self.temp_msg = os.path.join(tempfile.gettempdir(), 'ffplayout.txt')
|
||||
|
||||
def current_time(self):
|
||||
"""
|
||||
set sending time
|
||||
"""
|
||||
self.time = get_time(None)
|
||||
|
||||
def send_mail(self, msg):
|
||||
"""
|
||||
send emails to specified recipients
|
||||
"""
|
||||
if mail.recip:
|
||||
# write message to temp file for rate limit
|
||||
with open(self.temp_msg, 'w+') as f:
|
||||
f.write(msg)
|
||||
with open(self.temp_msg, 'w+') as msg_file:
|
||||
msg_file.write(msg)
|
||||
|
||||
self.current_time()
|
||||
|
||||
@ -416,12 +432,14 @@ class Mailer:
|
||||
server.quit()
|
||||
|
||||
def check_if_new(self, msg):
|
||||
# send messege only when is new or the rate_limit is pass
|
||||
"""
|
||||
send messege only when is new or the rate_limit is pass
|
||||
"""
|
||||
if os.path.isfile(self.temp_msg):
|
||||
mod_time = os.path.getmtime(self.temp_msg)
|
||||
|
||||
with open(self.temp_msg, 'r', encoding='utf-8') as f:
|
||||
last_msg = f.read()
|
||||
with open(self.temp_msg, 'r', encoding='utf-8') as msg_file:
|
||||
last_msg = msg_file.read()
|
||||
|
||||
if msg != last_msg \
|
||||
or get_time('stamp') - mod_time > self.rate_limit:
|
||||
@ -430,14 +448,23 @@ class Mailer:
|
||||
self.send_mail(msg)
|
||||
|
||||
def info(self, msg):
|
||||
"""
|
||||
send emails with level INFO, WARNING and ERROR
|
||||
"""
|
||||
if self.level in ['INFO']:
|
||||
self.check_if_new(msg)
|
||||
|
||||
def warning(self, msg):
|
||||
"""
|
||||
send emails with level WARNING and ERROR
|
||||
"""
|
||||
if self.level in ['INFO', 'WARNING']:
|
||||
self.check_if_new(msg)
|
||||
|
||||
def error(self, msg):
|
||||
"""
|
||||
send emails with level ERROR
|
||||
"""
|
||||
if self.level in ['INFO', 'WARNING', 'ERROR']:
|
||||
self.check_if_new(msg)
|
||||
|
||||
@ -451,18 +478,31 @@ class Messenger:
|
||||
def __init__(self):
|
||||
self._mailer = Mailer()
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def debug(self, msg):
|
||||
"""
|
||||
log debugging messages
|
||||
"""
|
||||
playout_logger.debug(msg.replace('\n', ' '))
|
||||
|
||||
def info(self, msg):
|
||||
"""
|
||||
log and mail info messages
|
||||
"""
|
||||
playout_logger.info(msg.replace('\n', ' '))
|
||||
self._mailer.info(msg)
|
||||
|
||||
def warning(self, msg):
|
||||
"""
|
||||
log and mail warning messages
|
||||
"""
|
||||
playout_logger.warning(msg.replace('\n', ' '))
|
||||
self._mailer.warning(msg)
|
||||
|
||||
def error(self, msg):
|
||||
"""
|
||||
log and mail error messages
|
||||
"""
|
||||
playout_logger.error(msg.replace('\n', ' '))
|
||||
self._mailer.error(msg)
|
||||
|
||||
@ -521,6 +561,9 @@ FF_LIBS = ffmpeg_libs()
|
||||
|
||||
|
||||
def validate_ffmpeg_libs():
|
||||
"""
|
||||
check if ffmpeg contains some basic libs
|
||||
"""
|
||||
if 'libx264' not in FF_LIBS['libs']:
|
||||
playout_logger.error('ffmpeg contains no libx264!')
|
||||
if 'libfdk-aac' not in FF_LIBS['libs']:
|
||||
@ -542,8 +585,18 @@ class MediaProbe:
|
||||
get infos about media file, similare to mediainfo
|
||||
"""
|
||||
|
||||
def load(self, file):
|
||||
def __init__(self):
|
||||
self.remote_source = ['http', 'https', 'ftp', 'smb', 'sftp']
|
||||
self.src = None
|
||||
self.format = None
|
||||
self.audio = []
|
||||
self.video = []
|
||||
self.is_remote = False
|
||||
|
||||
def load(self, file):
|
||||
"""
|
||||
load media file with ffprobe and get infos out of it
|
||||
"""
|
||||
self.src = file
|
||||
self.format = None
|
||||
self.audio = []
|
||||
@ -582,14 +635,14 @@ class MediaProbe:
|
||||
|
||||
if stream['codec_type'] == 'video':
|
||||
if stream.get('display_aspect_ratio'):
|
||||
w, h = stream['display_aspect_ratio'].split(':')
|
||||
stream['aspect'] = float(w) / float(h)
|
||||
width, heigth = stream['display_aspect_ratio'].split(':')
|
||||
stream['aspect'] = float(width) / float(heigth)
|
||||
else:
|
||||
stream['aspect'] = float(
|
||||
stream['width']) / float(stream['height'])
|
||||
|
||||
a, b = stream['r_frame_rate'].split('/')
|
||||
stream['fps'] = float(a) / float(b)
|
||||
rate, factor = stream['r_frame_rate'].split('/')
|
||||
stream['fps'] = float(rate) / float(factor)
|
||||
|
||||
self.video.append(stream)
|
||||
|
||||
@ -602,9 +655,10 @@ def handle_sigterm(sig, frame):
|
||||
"""
|
||||
handler for ctrl+c signal
|
||||
"""
|
||||
raise(SystemExit)
|
||||
raise SystemExit
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def handle_sighub(sig, frame):
|
||||
"""
|
||||
handling SIGHUB signal for reload configuration
|
||||
@ -694,14 +748,15 @@ def get_date(seek_day, next_start=0):
|
||||
when seek_day is set:
|
||||
check if playlist date must be from yesterday
|
||||
"""
|
||||
d = date.today()
|
||||
date_ = date.today()
|
||||
|
||||
if seek_day and playlist.start > get_time('full_sec'):
|
||||
return (d - timedelta(1)).strftime('%Y-%m-%d')
|
||||
elif playlist.start == 0 and next_start >= 86400:
|
||||
return (d + timedelta(1)).strftime('%Y-%m-%d')
|
||||
else:
|
||||
return d.strftime('%Y-%m-%d')
|
||||
return (date_ - timedelta(1)).strftime('%Y-%m-%d')
|
||||
|
||||
if playlist.start == 0 and next_start >= 86400:
|
||||
return (date_ + timedelta(1)).strftime('%Y-%m-%d')
|
||||
|
||||
return date_.strftime('%Y-%m-%d')
|
||||
|
||||
|
||||
def get_float(value, default=False):
|
||||
@ -721,6 +776,8 @@ def is_advertisement(node):
|
||||
if node and node.get('category') == 'advertisement':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def valid_json(file):
|
||||
"""
|
||||
@ -804,28 +861,26 @@ def gen_filler(node):
|
||||
|
||||
if probe.format:
|
||||
if probe.format.get('duration'):
|
||||
filler_duration = float(probe.format['duration'])
|
||||
if filler_duration > duration:
|
||||
filler_dur = float(probe.format['duration'])
|
||||
if filler_dur > duration:
|
||||
# cut filler
|
||||
messenger.info(
|
||||
f'Generate filler with {duration:.2f} seconds')
|
||||
node['source'] = storage.filler
|
||||
node['src_cmd'] = ['-i', storage.filler] + set_length(
|
||||
filler_duration, 0, duration)
|
||||
filler_dur, 0, duration)
|
||||
return node
|
||||
else:
|
||||
|
||||
# loop file n times
|
||||
node['src_cmd'] = loop_input(storage.filler, filler_duration,
|
||||
duration)
|
||||
node['src_cmd'] = loop_input(storage.filler, filler_dur, duration)
|
||||
return node
|
||||
else:
|
||||
|
||||
messenger.error("Can't get filler length, generate dummy!")
|
||||
dummy = gen_dummy(duration)
|
||||
node['source'] = dummy[3]
|
||||
node['src_cmd'] = dummy
|
||||
return node
|
||||
|
||||
else:
|
||||
# when no filler is set, generate a dummy
|
||||
messenger.warning('No filler is set!')
|
||||
dummy = gen_dummy(duration)
|
||||
@ -862,7 +917,7 @@ def src_or_dummy(node):
|
||||
] + set_length(node['duration'], node['seek'],
|
||||
node['out'] - node['seek'])
|
||||
else:
|
||||
# FIXME: when list starts with looped clip,
|
||||
# when list starts with looped clip,
|
||||
# the logo length will be wrong
|
||||
node['src_cmd'] = loop_input(node['source'], node['duration'],
|
||||
node['out'])
|
||||
@ -884,5 +939,5 @@ def pre_audio_codec():
|
||||
"""
|
||||
if pre.add_loudnorm:
|
||||
return ['-c:a', 'mp2', '-b:a', '384k', '-ar', '48000', '-ac', '2']
|
||||
else:
|
||||
|
||||
return ['-c:a', 's302m', '-strict', '-2', '-ar', '48000', '-ac', '2']
|
||||
|
Loading…
Reference in New Issue
Block a user