remove classes, splitt functions, optimize code

This commit is contained in:
jb-alvarado 2018-01-09 17:54:50 +01:00
parent 67a41b9dce
commit 5691192b7a

301
ffplayout.py Normal file → Executable file
View File

@ -30,8 +30,8 @@ from shutil import copyfileobj
from subprocess import Popen, PIPE
from threading import Thread
from time import sleep
from xml.dom import minidom
from xml.parsers.expat import ExpatError
from types import SimpleNamespace
import xml.etree.ElementTree as ET
# ------------------------------------------------------------------------------
@ -43,78 +43,80 @@ cfg = configparser.ConfigParser()
cfg.read("/etc/ffplayout/ffplayout.conf")
class _mail:
server = cfg.get('MAIL', 'smpt_server')
port = cfg.get('MAIL', 'smpt_port')
s_addr = cfg.get('MAIL', 'sender_addr')
s_pass = cfg.get('MAIL', 'sender_pass')
_mail = SimpleNamespace(
server=cfg.get('MAIL', 'smpt_server'),
port=cfg.getint('MAIL', 'smpt_port'),
s_addr=cfg.get('MAIL', 'sender_addr'),
s_pass=cfg.get('MAIL', 'sender_pass'),
recip=cfg.get('MAIL', 'recipient')
)
_pre_comp = SimpleNamespace(
w=cfg.getint('PRE_COMPRESS', 'width'),
h=cfg.getint('PRE_COMPRESS', 'height'),
aspect=cfg.getfloat('PRE_COMPRESS', 'width') /
cfg.getfloat('PRE_COMPRESS', 'height'),
fps=cfg.getint('PRE_COMPRESS', 'fps'),
v_bitrate=cfg.getint('PRE_COMPRESS', 'v_bitrate'),
v_bufsize=cfg.getint('PRE_COMPRESS', 'v_bitrate') / 2,
a_bitrate=cfg.getint('PRE_COMPRESS', 'a_bitrate'),
a_sample=cfg.getint('PRE_COMPRESS', 'a_sample'),
)
class _pre_comp:
w = cfg.get('PRE_COMPRESS', 'width')
h = cfg.get('PRE_COMPRESS', 'height')
aspect = float(w) / float(h)
fps = cfg.get('PRE_COMPRESS', 'fps')
v_bitrate = cfg.get('PRE_COMPRESS', 'v_bitrate')
v_bufsize = int(v_bitrate) / 2
a_bitrate = cfg.get('PRE_COMPRESS', 'a_bitrate')
a_sample = cfg.get('PRE_COMPRESS', 'a_sample')
_playlist = SimpleNamespace(
path=cfg.get('PLAYLIST', 'playlist_path'),
start=cfg.getint('PLAYLIST', 'day_start')
)
class _playlist:
path = cfg.get('PLAYLIST', 'playlist_path')
start = int(cfg.get('PLAYLIST', 'day_start'))
class _buffer:
length = cfg.get('BUFFER', 'buffer_length')
cli = cfg.get('BUFFER', 'buffer_cli')
_buffer = SimpleNamespace(
length=cfg.getint('BUFFER', 'buffer_length'),
cli=cfg.get('BUFFER', 'buffer_cli'),
cmd=literal_eval(cfg.get('BUFFER', 'buffer_cmd'))
)
class _playout:
name = cfg.get('OUT', 'service_name')
provider = cfg.get('OUT', 'service_provider')
out_addr = cfg.get('OUT', 'out_addr')
_playout = SimpleNamespace(
name=cfg.get('OUT', 'service_name'),
provider=cfg.get('OUT', 'service_provider'),
out_addr=cfg.get('OUT', 'out_addr'),
post_comp_video=literal_eval(cfg.get('OUT', 'post_comp_video')),
post_comp_audio=literal_eval(cfg.get('OUT', 'post_comp_audio')),
post_comp_extra=literal_eval(cfg.get('OUT', 'post_comp_extra'))
)
# set logo filtergraph
if Path(cfg.get('OUT', 'logo')).is_file():
logo_path = ['-thread_queue_size', '512', '-i', cfg.get('OUT', 'logo')]
logo_graph = [
_playout.logo = ['-thread_queue_size', '512', '-i', cfg.get('OUT', 'logo')]
_playout.filter = [
'-filter_complex', '[0:v][1:v]' + cfg.get('OUT', 'logo_o') + '[o]',
'-map', '[o]', '-map', '0:a'
]
else:
logo_path = []
logo_graph = []
post_comp_video = literal_eval(cfg.get('OUT', 'post_comp_video'))
post_comp_audio = literal_eval(cfg.get('OUT', 'post_comp_audio'))
post_comp_extra = literal_eval(cfg.get('OUT', 'post_comp_extra'))
_playout.logo = []
_playout.filter = []
# ------------------------------------------------------------------------------
# global functions
# global helper functions
# ------------------------------------------------------------------------------
# get different time informations
def cur_ts(time_value, day):
start_clock = datetime.now().strftime('%H:%M:%S')
start_h, start_m, start_s = re.split(':', start_clock)
time_in_sec = int(start_h) * 3600 + int(start_m) * 60 + int(start_s)
if time_value == 't_hour':
return start_h
elif time_value == 't_full':
return time_in_sec
elif time_value == 't_date':
if int(start_h) < int(_playlist.start) and day != 'today':
yesterday = date.today() - timedelta(1)
list_date = yesterday.strftime('%Y-%m-%d')
# get time
def get_time(time_format):
t = datetime.today()
if time_format == 'hour':
return t.hour
elif time_format == 'full_sec':
return t.hour * 3600 + t.minute * 60 + t.second
else:
list_date = datetime.now().strftime('%Y-%m-%d')
return t.strftime("%H:%M:%S")
return list_date
# get date
def get_date(seek_day):
if get_time('hour') < _playlist.start and seek_day:
yesterday = date.today() - timedelta(1)
return yesterday.strftime('%Y-%m-%d')
else:
return datetime.now().strftime('%Y-%m-%d')
# send error messages to email addresses
@ -138,10 +140,7 @@ def send_mail(message, path):
# calculating the size for the buffer in bytes
def calc_buffer_size():
total_size = (int(_pre_comp.v_bitrate) + int(_pre_comp.a_bitrate)) * \
int(_buffer.length)
return int(total_size)
return (_pre_comp.v_bitrate + _pre_comp.a_bitrate) * _buffer.length
# check if processes a well
@ -155,39 +154,58 @@ def check_process(watch_proc, terminate_proc):
# check if path exist,
# when not send email and generate blackclip
def check_path(f_o_l, in_file, duration, seek_t):
def check_file_exist(in_file):
in_path = Path(in_file)
if f_o_l == 'list':
error_message = 'Plylist does not exist:'
elif f_o_l == 'file':
error_message = 'File does not exist:'
elif f_o_l == 'dummy_l':
error_message = 'XML Playlist is not valid!'
if in_path.is_file():
return True
else:
send_mail('File does not exist{}:'.format(get_time('str')), in_path)
return False
if not in_path.is_file() or f_o_l == 'dummy_l' or f_o_l == 'dummy_c':
if f_o_l != 'dummy_c':
send_mail(error_message, in_path)
out_path = [
def seek_in_clip(in_file, seek_t):
return [
'-ss', str(seek_t), '-i', in_file,
'-vf', 'fade=in:st=0:d=0.5', '-af', 'afade=in:st=0:d=0.5'
]
def gen_dummy(duration):
return [
'-f', 'lavfi', '-i',
'color=s={}x{}:d={}'.format(
_pre_comp.w, _pre_comp.h, duration
),
'-f', 'lavfi', '-i', 'anullsrc=r=' + _pre_comp.a_sample,
'-shortest'
'-f', 'lavfi', '-i', 'anullsrc=r=' + _pre_comp.a_sample, '-shortest'
]
else:
if float(seek_t) > 0.00:
out_path = [
'-ss', str(seek_t), '-i', in_file,
'-vf', 'fade=in:st=0:d=0.5',
'-af', 'afade=in:st=0:d=0.5'
]
else:
out_path = ['-i', in_file]
return out_path
# last clip can be a filler
# so we get the IN point and calculate the new duration
# if the new duration is smaller then 6 sec put a blank clip
def prepare_last_clip(in_node, start):
clip_path = in_node.get('src')
clip_len = in_node.get('dur').rstrip('s')
clip_in = in_node.get('in').rstrip('s')
tmp_dur = float(clip_len) - float(clip_in)
current_time = get_time('full_sec')
# check if we are in time
if get_time('full_sec') > start + 10:
send_mail('we are out of time...:', current_time)
if tmp_dur > 6.00:
if check_file_exist():
src_cmd = seek_in_clip(clip_path, clip_in)
else:
src_cmd = gen_dummy(tmp_dur)
elif tmp_dur > 1.00:
src_cmd = gen_dummy(tmp_dur)
else:
src_cmd = 'next'
return src_cmd
# ------------------------------------------------------------------------------
@ -195,98 +213,79 @@ def check_path(f_o_l, in_file, duration, seek_t):
# ------------------------------------------------------------------------------
# read values from xml playlist
def get_from_playlist(last_time, list_date, seek_clip):
def get_from_playlist(last_time, list_date, seek):
# path to current playlist
l_y, l_m, l_d = re.split('-', list_date)
c_p = '{}/{}/{}/{}.xml'.format(_playlist.path, l_y, l_m, list_date)
src_cmd = check_path('list', c_p, 300, 0.00)
if '-shortest' in src_cmd:
clip_start = last_time
else:
try:
xmldoc = minidom.parse(c_p)
except ExpatError:
src_cmd = check_path('dummy_l', c_p, 300, 0.00)
return src_cmd, last_time, list_date, seek_clip
clip_ls = xmldoc.getElementsByTagName('video')
for i in range(len(clip_ls)):
clip_start = re.sub(
'[a-z=]', '', clip_ls[i].attributes['clipBegin'].value
year, month, day = re.split('-', list_date)
xml_path = '{}/{}/{}/{}.xml'.format(
_playlist.path, year, month, list_date
)
clip_dur = re.sub('s', '', clip_ls[i].attributes['dur'].value)
clip_path = clip_ls[i].attributes['src'].value
# last clip in playlist
if i == len(clip_ls) - 1:
# last clip can be a filler
# so we get the IN point and calculate the new duration
# if the new duration is smaller then 6 sec put a blank clip
clip_in = re.sub('s', '', clip_ls[i].attributes['in'].value)
tmp_dur = float(clip_dur) - float(clip_in)
if check_file_exist(xml_path):
xml_root = ET.parse(xml_path).getroot()
clip_nodes = xml_root.findall('body/video')
clip_start = _playlist.start * 3600 - 5
list_date = cur_ts('t_date', 'today')
get_time = cur_ts('t_full', '0')
# all clips in playlist except last one
for clip_node in clip_nodes[:-1]:
clip_path = clip_node.get('src')
clip_start = re.sub('[a-z=]', '', clip_node.get('clipBegin'))
clip_len = clip_node.get('dur').rstrip('s')
if tmp_dur > 6.00:
src_cmd = check_path('file', clip_path, clip_dur, clip_in)
elif tmp_dur > 1.00:
src_cmd = check_path('dummy_c', clip_path, tmp_dur, 0.00)
else:
src_cmd = 'next'
# check if we are in time
if int(get_time) > int(clip_start) + 10:
send_mail('we are out of time...:', get_time)
# all other clips in playlist
elif seek_clip is True:
if seek:
# first time we end up here
if float(last_time) < float(clip_start) + float(clip_dur):
if float(last_time) < float(clip_start) + float(clip_len):
# calculate seek time
seek_t = float(last_time) - float(clip_start)
clip_len = float(clip_dur) - seek_t
src_cmd = check_path('file', clip_path, clip_len, seek_t)
if check_file_exist(clip_path):
src_cmd = seek_in_clip(clip_path, seek_t)
else:
src_cmd = gen_dummy(float(clip_len) - seek_t)
seek_clip = False
seek = False
break
else:
if float(last_time) < float(clip_start):
src_cmd = check_path('file', clip_path, clip_dur, 0.00)
break
if check_file_exist(clip_path):
src_cmd = ['-i', clip_path]
else:
src_cmd = gen_dummy(clip_len)
return src_cmd, clip_start, list_date, seek_clip
break
# last clip in playlist
else:
clip_start = _playlist.start * 3600 - 5
src_cmd = prepare_last_clip(clip_nodes[-1], clip_start)
list_date = get_date(False)
else:
src_cmd = gen_dummy(300)
return src_cmd, last_time + 300, list_date, seek
return src_cmd, clip_start, get_date(True), seek
# independent thread for clip preparation
def play_clips(out_file):
if cur_ts('t_full', '0') > 0 and \
cur_ts('t_full', '0') < int(_playlist.start) * 3600:
last_time = int(cur_ts('t_full', '0')) + 86400
if get_time('full_sec') > 0 and \
get_time('full_sec') < _playlist.start * 3600:
last_time = get_time('full_sec') + 86400
else:
last_time = cur_ts('t_full', '0')
last_time = get_time('full_sec')
list_date = cur_ts('t_date', '0')
seek_clip = True
list_date = get_date(True)
seek = True
# infinit loop
# send current file from xml playlist to stdin from buffer
while True:
try:
src_cmd, last_time, list_date, seek_clip = get_from_playlist(
last_time, list_date, seek_clip
src_cmd, last_time, list_date, seek = get_from_playlist(
last_time, list_date, seek
)
if src_cmd == 'next':
src_cmd, last_time, list_date, seek_clip = get_from_playlist(
last_time, list_date, seek_clip
src_cmd, last_time, list_date, seek = get_from_playlist(
last_time, list_date, seek
)
# tm_str = str(timedelta(seconds=int(float(last_time))))
@ -309,8 +308,7 @@ def play_clips(out_file):
'-ar', str(_pre_comp.a_sample), '-ac', '2', '-f', 'mpegts',
'-threads', '2', '-'
],
stdout=PIPE,
stderr=PIPE
stdout=PIPE
)
copyfileobj(filePiper.stdout, out_file)
@ -336,21 +334,20 @@ def main():
'ffmpeg', '-v', 'error', '-hide_banner', '-re',
'-fflags', '+igndts', '-i', 'pipe:0', '-fflags', '+genpts'
] +
list(_playout.logo_path) +
list(_playout.logo_graph) +
list(_playout.logo) +
list(_playout.filter) +
list(_playout.post_comp_video) +
list(_playout.post_comp_audio) +
[
'-metadata', 'service_name=' + _playout.name,
'-metadata', 'service_provider=' + _playout.provider,
'-metadata', 'year=' + cur_ts('t_date', 'today')
'-metadata', 'year=' + get_date(False)
] +
list(_playout.post_comp_extra) +
[
_playout.out_addr
],
stdin=mbuffer.stdout,
stdout=PIPE
stdin=mbuffer.stdout
)
play_thread = Thread(