migrate to yaml config file

This commit is contained in:
Jonathan Baecker 2020-02-03 12:07:21 +01:00
parent 6c4bb61bf7
commit 472b11965e
2 changed files with 223 additions and 68 deletions

View File

@ -18,7 +18,6 @@
# ------------------------------------------------------------------------------
import configparser
import glob
import json
import logging
@ -33,6 +32,7 @@ import ssl
import sys
import tempfile
import time
import yaml
from argparse import ArgumentParser
from datetime import date, datetime, timedelta
from email.mime.multipart import MIMEMultipart
@ -142,15 +142,7 @@ _WINDOWS = os.name == 'nt'
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024
def load_config():
"""
this function can reload most settings from configuration file,
the change does not take effect immediately, but with the after next file,
some settings cannot be changed - like resolution, aspect, or output
"""
cfg = configparser.ConfigParser()
def str_to_sec(s):
def str_to_sec(s):
if s in ['now', '', None, 'none']:
return None
else:
@ -161,17 +153,41 @@ def load_config():
print('Wrong time format!')
sys.exit(1)
if stdin_args.config:
cfg.read(stdin_args.config)
elif os.path.isfile('/etc/ffplayout/ffplayout.conf'):
cfg.read('/etc/ffplayout/ffplayout.conf')
def read_config(path):
with open(path, 'r') as config_file:
return yaml.safe_load(config_file)
def dict_to_list(d):
li = []
for key, value in d.items():
if value:
li += ['-{}'.format(key), str(value)]
else:
cfg.read('ffplayout.conf')
li += ['-{}'.format(key)]
return li
def load_config():
"""
this function can reload most settings from configuration file,
the change does not take effect immediately, but with the after next file,
some settings cannot be changed - like resolution, aspect, or output
"""
if stdin_args.config:
cfg = read_config(stdin_args.config)
elif os.path.isfile('/etc/ffplayout/ffplayout.yml'):
cfg = read_config('/etc/ffplayout/ffplayout.yml')
else:
cfg = read_config('ffplayout.yml')
if stdin_args.start:
p_start = str_to_sec(stdin_args.start)
else:
p_start = str_to_sec(cfg.get('PLAYLIST', 'day_start'))
p_start = str_to_sec(cfg['playlist']['day_start'])
if not p_start:
p_start = get_time('full_sec')
@ -179,65 +195,61 @@ def load_config():
if stdin_args.length:
p_length = str_to_sec(stdin_args.length)
else:
p_length = str_to_sec(cfg.get('PLAYLIST', 'length'))
p_length = str_to_sec(cfg['playlist']['length'])
_general.stop = cfg.getboolean('GENERAL', 'stop_on_error')
_general.threshold = cfg.getfloat('GENERAL', 'stop_threshold')
_general.stop = cfg['general']['stop_on_error']
_general.threshold = cfg['general']['stop_threshold']
_mail.subject = cfg.get('MAIL', 'subject')
_mail.server = cfg.get('MAIL', 'smpt_server')
_mail.port = cfg.getint('MAIL', 'smpt_port')
_mail.s_addr = cfg.get('MAIL', 'sender_addr')
_mail.s_pass = cfg.get('MAIL', 'sender_pass')
_mail.recip = cfg.get('MAIL', 'recipient')
_mail.level = cfg.get('MAIL', 'mail_level')
_mail.subject = cfg['mail']['subject']
_mail.server = cfg['mail']['smpt_server']
_mail.port = cfg['mail']['smpt_port']
_mail.s_addr = cfg['mail']['sender_addr']
_mail.s_pass = cfg['mail']['sender_pass']
_mail.recip = cfg['mail']['recipient']
_mail.level = cfg['mail']['mail_level']
_pre_comp.add_logo = cfg.getboolean('PRE_COMPRESS', 'add_logo')
_pre_comp.logo = cfg.get('PRE_COMPRESS', 'logo')
_pre_comp.opacity = cfg.get('PRE_COMPRESS', 'logo_opacity')
_pre_comp.logo_filter = cfg.get('PRE_COMPRESS', 'logo_filter')
_pre_comp.add_loudnorm = cfg.getboolean('PRE_COMPRESS', 'add_loudnorm')
_pre_comp.loud_i = cfg.getfloat('PRE_COMPRESS', 'loud_I')
_pre_comp.loud_tp = cfg.getfloat('PRE_COMPRESS', 'loud_TP')
_pre_comp.loud_lra = cfg.getfloat('PRE_COMPRESS', 'loud_LRA')
_pre_comp.add_logo = cfg['pre_compress']['add_logo']
_pre_comp.logo = cfg['pre_compress']['logo']
_pre_comp.opacity = cfg['pre_compress']['logo_opacity']
_pre_comp.logo_filter = cfg['pre_compress']['logo_filter']
_pre_comp.add_loudnorm = cfg['pre_compress']['add_loudnorm']
_pre_comp.loud_i = cfg['pre_compress']['loud_I']
_pre_comp.loud_tp = cfg['pre_compress']['loud_TP']
_pre_comp.loud_lra = cfg['pre_compress']['loud_LRA']
_playlist.mode = cfg.getboolean('PLAYLIST', 'playlist_mode')
_playlist.path = cfg.get('PLAYLIST', 'path')
_playlist.mode = cfg['playlist']['playlist_mode']
_playlist.path = cfg['playlist']['path']
_playlist.start = p_start
_playlist.length = p_length
_storage.path = cfg.get('STORAGE', 'path')
_storage.filler = cfg.get('STORAGE', 'filler_clip')
_storage.extensions = json.loads(cfg.get('STORAGE', 'extensions'))
_storage.shuffle = cfg.getboolean('STORAGE', 'shuffle')
_storage.path = cfg['storage']['path']
_storage.filler = cfg['storage']['filler_clip']
_storage.extensions = cfg['storage']['extensions']
_storage.shuffle = cfg['storage']['shuffle']
_text.add_text = cfg.getboolean('TEXT', 'add_text')
_text.address = cfg.get('TEXT', 'bind_address')
_text.fontfile = cfg.get('TEXT', 'fontfile')
_text.add_text = cfg['text']['add_text']
_text.address = cfg['text']['bind_address']
_text.fontfile = cfg['text']['fontfile']
if _init.load:
_log.to_file = cfg.getboolean('LOGGING', 'log_to_file')
_log.path = cfg.get('LOGGING', 'log_path')
_log.level = cfg.get('LOGGING', 'log_level')
_log.ff_level = cfg.get('LOGGING', 'ffmpeg_level')
_log.to_file = cfg['logging']['log_to_file']
_log.path = cfg['logging']['log_path']
_log.level = cfg['logging']['log_level']
_log.ff_level = cfg['logging']['ffmpeg_level']
_pre_comp.w = cfg.getint('PRE_COMPRESS', 'width')
_pre_comp.h = cfg.getint('PRE_COMPRESS', 'height')
_pre_comp.aspect = cfg.getfloat('PRE_COMPRESS', 'aspect')
_pre_comp.fps = cfg.getint('PRE_COMPRESS', 'fps')
_pre_comp.v_bitrate = cfg.getint('PRE_COMPRESS', 'width') * 50
_pre_comp.v_bufsize = cfg.getint('PRE_COMPRESS', 'width') * 50 / 2
_pre_comp.w = cfg['pre_compress']['width']
_pre_comp.h = cfg['pre_compress']['height']
_pre_comp.aspect = cfg['pre_compress']['aspect']
_pre_comp.fps = cfg['pre_compress']['fps']
_pre_comp.v_bitrate = cfg['pre_compress']['width'] * 50
_pre_comp.v_bufsize = cfg['pre_compress']['width'] * 50 / 2
_playout.preview = cfg.getboolean('OUT', 'preview')
_playout.name = cfg.get('OUT', 'service_name')
_playout.provider = cfg.get('OUT', 'service_provider')
_playout.out_addr = cfg.get('OUT', 'out_addr')
_playout.post_comp_video = json.loads(
cfg.get('OUT', 'post_comp_video'))
_playout.post_comp_audio = json.loads(
cfg.get('OUT', 'post_comp_audio'))
_playout.post_comp_extra = json.loads(
cfg.get('OUT', 'post_comp_extra'))
_playout.preview = cfg['out']['preview']
_playout.name = cfg['out']['service_name']
_playout.provider = cfg['out']['service_provider']
_playout.post_comp_param = dict_to_list(
cfg['out']['post_ffmpeg_param'])
_playout.out_addr = cfg['out']['out_addr']
_init.load = False
@ -1606,12 +1618,11 @@ def main():
_ff.encoder = Popen([
'ffmpeg', '-v', _log.ff_level.lower(), '-hide_banner',
'-nostats', '-re', '-thread_queue_size', '256',
'-i', 'pipe:0'] + overlay + _playout.post_comp_video
+ _playout.post_comp_audio + [
'-i', 'pipe:0'] + overlay + [
'-metadata', 'service_name=' + _playout.name,
'-metadata', 'service_provider=' + _playout.provider,
'-metadata', 'year={}'.format(year)
] + _playout.post_comp_extra + [_playout.out_addr],
] + _playout.post_comp_param + [_playout.out_addr],
stdin=PIPE, stderr=PIPE)
enc_err_thread = Thread(target=ffmpeg_stderr_reader,

144
ffplayout.yml Normal file
View File

@ -0,0 +1,144 @@
# 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/>.
# ------------------------------------------------------------------------------
# sometimes it can happen, that a file is corrupt but still playable,
# this can produce an streaming error over all following files
# the only way in this case is, to stop ffplayout and start it again
# here we only say it can stop, the starting process is in your hand
# best way is a systemd serivce on linux
# stop_threshold: stop ffplayout, if it is async in time above this value
general:
stop_on_error: True
stop_threshold: 11
# send error messages to email address, like:
# missing playlist
# unvalid json format
# missing clip path
# leave recipient blank, if you don't need this
# mail_level can be: WARNING, ERROR
mail:
subject: "Playout Error"
smpt_server: "mail.example.org"
smpt_port: 587
sender_addr: "ffplayout@example.org"
sender_pass: "12345"
recipient:
mail_level: "ERROR"
# Logging to file
# if log_to_file = False > log to console
# path to /var/log/ only if you run this program as deamon
# log_level can be: DEBUG, INFO, WARNING, ERROR
# ffmpeg_level can be: INFO, WARNING, ERROR
logging:
log_to_file: True
log_path: "/var/log/ffplayout/"
log_level: "DEBUG"
ffmpeg_level: "ERROR"
# output settings for the pre-compression
# all clips get prepared in that way,
# so the input for the final compression is unique
# aspect mus be a float number
# logo is only used if the path exist
# with logo_opacity logo can make transparent
# with logo_filter = overlay=W-w-12:12 you can modify the logo position
# with use_loudnorm you can activate single pass EBU R128 loudness normalization
# loud_* can adjust the loudnorm filter
# INFO: output is progressive!
pre_compress:
width: 1024
height: 576
aspect: 1.778
fps: 25
add_logo: True
logo: "docs/logo.png"
logo_opacity: 0.7
logo_filter: "overlay=W-w-12:12"
add_loudnorm: False
loud_I: -18
loud_TP: -1.5
loud_LRA: 11
# playlist settings
# set playlist_mode to False if you want to play clips from the [STORAGE] section
# put only the root path here, for example: "/playlists"
# subfolders are readed by the script
# subfolders needs this structur:
# "/playlists/2018/01" (/playlists/year/month)
# day_start means at which time the playlist should start
# leave day_start blank when playlist should always start at the begin
# length represent the target length from playlist, when is blank real length will not consider
playlist:
playlist_mode: True
path: "/playlists"
day_start: "5:59:25"
length: "24:00:00"
# play ordered or ramdomly files from path
# filler_path are for the GUI only at the moment
# filler_clip is for fill the end to reach 24 hours, it will loop when is necessary
# extensions: search only files with this extension, can be a list
# set shuffle to True to pick files randomly
storage:
path: "/mediaStorage"
filler_path: "/mediaStorage/filler/filler-clips"
filler_clip: "/mediaStorage/filler/filler.mp4"
extensions:
- "*.mp4"
- "*.mkv"
shuffle: True
# overlay text in combination with messenger: https://github.com/ffplayout/messenger
# on windows fontfile path need to be like this: C\:/WINDOWS/fonts/DejaVuSans.ttf
# in a standard environment the filter drawtext node is: Parsed_drawtext_2
text:
add_text: True
bind_address: "tcp://127.0.0.1:5555"
fontfile: "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
# the final playout post compression
# set the settings to your needs
# preview works only on a desktop system with ffplay!! Set it to True, if you need it
out:
preview: False
service_name: "Live Stream"
service_provider: "example.org"
post_ffmpeg_param:
c:v: "libx264"
crf: "23"
x264-params: "keyint=50:min-keyint=25:scenecut=-1"
maxrate: "1300k"
bufsize: "2600k"
preset: "medium"
profile:v: "Main"
level: "3.1"
c:a: "aac"
ar: "44100"
b:a: "128k"
flags: +global_header
f: "flv"
out_addr: "rtmp://localhost/live/stream"