addedd rtsp streamer inferastructure, applied fstrings, updated method names to align with standards in project
This commit is contained in:
parent
b30a8f4b44
commit
22c437e8ed
|
@ -29,7 +29,7 @@ class Config(object):
|
|||
LOGIN_PATH = "OIDC" # Value can be OIDC or FLASK_LOGIN
|
||||
OIDC_TOKEN_TYPE_HINT = 'access_token'
|
||||
APP_REDIRECT_URI = "https%3A%2F%2Fwww.webfm.com%2F" # This path is submitted as the redirect URI in certain code flows
|
||||
OIDC_CLIENT_SECRETS = ROOT_FILE_PTH + '/client_secrets.json'
|
||||
OIDC_CLIENT_SECRETS = f'{ROOT_FILE_PTH}/client_secrets.json'
|
||||
OIDC_ID_TOKEN_COOKIE_SECURE = True
|
||||
OIDC_REQUIRE_VERIFIED_EMAIL = False
|
||||
OIDC_USER_INFO_ENABLED = True
|
||||
|
@ -38,14 +38,14 @@ class Config(object):
|
|||
'https://www.ssoapps.com/auth/realms/apps'
|
||||
]
|
||||
|
||||
STATIC_FPTH = ROOT_FILE_PTH + "/static"
|
||||
STATIC_FPTH = f"{ROOT_FILE_PTH}/static"
|
||||
REL_THUMBS_PTH = "static/imgs/thumbnails" # Used for flask thumbnail return
|
||||
|
||||
# We are overiding some of the the shellmen view settings with these to make it all work with flask.
|
||||
# These are passed along to the shellmen view from the Routes file upon the window controller creation.
|
||||
ABS_THUMBS_PTH = STATIC_FPTH + "/imgs/thumbnails" # Used for thumbnail generation
|
||||
REMUX_FOLDER = STATIC_FPTH + "/remuxs" # Remuxed files folder
|
||||
FFMPG_THUMBNLR = STATIC_FPTH + "/ffmpegthumbnailer" # Thumbnail generator binary
|
||||
ABS_THUMBS_PTH = f"{STATIC_FPTH}/imgs/thumbnails" # Used for thumbnail generation
|
||||
REMUX_FOLDER = f"{STATIC_FPTH}/remuxs" # Remuxed files folder
|
||||
FFMPG_THUMBNLR = f"{STATIC_FPTH}/ffmpegthumbnailer" # Thumbnail generator binary
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -67,11 +67,11 @@ def listFiles(_hash = None):
|
|||
msg = "Log in with an Admin privlidged user to view the requested path!"
|
||||
is_locked = view.is_folder_locked(_hash)
|
||||
if is_locked and not oidc.user_loggedin:
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
elif is_locked and oidc.user_loggedin:
|
||||
isAdmin = oidc.user_getfield("isAdmin")
|
||||
if isAdmin != "yes" :
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
if dot_dots[0][1] != _hash and dot_dots[1][1] != _hash:
|
||||
path = view.get_path_part_from_hash(_hash)
|
||||
|
@ -80,7 +80,7 @@ def listFiles(_hash = None):
|
|||
error_msg = view.get_error_message()
|
||||
if error_msg != None:
|
||||
view.unset_error_message()
|
||||
return msgHandler.createMessageJSON("danger", error_msg)
|
||||
return msgHandler.create_JSON_message("danger", error_msg)
|
||||
|
||||
|
||||
sub_path = view.get_current_sub_path()
|
||||
|
@ -92,7 +92,7 @@ def listFiles(_hash = None):
|
|||
date = current_directory[startIndex:endIndex]
|
||||
video_data = tmdb.search(title, date)
|
||||
background_url = video_data[0]["backdrop_path"]
|
||||
background_pth = view.get_current_directory() + "/" + "000.jpg"
|
||||
background_pth = f"{view.get_current_directory()}/000.jpg"
|
||||
|
||||
if not os.path.isfile(background_pth):
|
||||
r = requests.get(background_url, stream = True)
|
||||
|
@ -116,20 +116,17 @@ def listFiles(_hash = None):
|
|||
return files
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
@app.route('/api/get-posters', methods=['GET', 'POST'])
|
||||
def getPosters():
|
||||
if request.method == 'POST':
|
||||
view = get_window_controller().get_window(1).get_view(0)
|
||||
videos = view.get_videos()
|
||||
|
||||
print(videos)
|
||||
# tmdb.search("Matrix")
|
||||
return videos
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
@app.route('/api/file-manager-action/<_type>/<_hash>', methods=['GET', 'POST'])
|
||||
def fileManagerAction(_type, _hash = None):
|
||||
|
@ -138,7 +135,7 @@ def fileManagerAction(_type, _hash = None):
|
|||
if _type == "reset-path" and _hash == "None":
|
||||
view.set_to_home()
|
||||
msg = "Returning to home directory..."
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
return msgHandler.create_JSON_message("success", msg)
|
||||
|
||||
folder = view.get_current_directory()
|
||||
file = view.get_path_part_from_hash(_hash)
|
||||
|
@ -146,34 +143,39 @@ def fileManagerAction(_type, _hash = None):
|
|||
logger.debug(fpath)
|
||||
|
||||
if _type == "files":
|
||||
return send_from_directory(folder, file)
|
||||
logger.debug(f"Downloading:\n\tDirectory: {folder}\n\tFile: {file}")
|
||||
return send_from_directory(directory=folder, filename=file)
|
||||
if _type == "remux":
|
||||
# NOTE: Need to actually implimint a websocket to communicate back to client that remux has completed.
|
||||
# As is, the remux thread hangs until completion and client tries waiting until server reaches connection timeout.
|
||||
# I.E....this is stupid but for now works better than nothing
|
||||
good_result = view.remuxVideo(_hash, fpath)
|
||||
good_result = view.remux_video(_hash, fpath)
|
||||
if good_result:
|
||||
return '{"path":"static/remuxs/' + _hash + '.mp4"}'
|
||||
else:
|
||||
msg = "Remuxing: Remux failed or took too long; please, refresh the page and try again..."
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
if _type == "run-locally":
|
||||
msg = "Opened media..."
|
||||
view.openFilelocally(fpath)
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
return msgHandler.create_JSON_message("success", msg)
|
||||
|
||||
if _type == "remux":
|
||||
stream_target = view.remux_video(_hash, fpath)
|
||||
|
||||
|
||||
# NOTE: Positionally protecting actions further down that are privlidged
|
||||
# Be aware of ordering!
|
||||
msg = "Log in with an Admin privlidged user to do this action!"
|
||||
if not oidc.user_loggedin:
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
elif oidc.user_loggedin:
|
||||
isAdmin = oidc.user_getfield("isAdmin")
|
||||
if isAdmin != "yes" :
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
|
||||
if _type == "run-locally":
|
||||
msg = "Opened media..."
|
||||
view.open_file_locally(fpath)
|
||||
return msgHandler.create_JSON_message("success", msg)
|
||||
|
||||
if _type == "delete":
|
||||
try:
|
||||
msg = f"[Success] Deleted the file/folder -->: {file} !"
|
||||
|
@ -181,10 +183,10 @@ def fileManagerAction(_type, _hash = None):
|
|||
os.unlink(fpath)
|
||||
else:
|
||||
shutil.rmtree(fpath)
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
return msgHandler.create_JSON_message("success", msg)
|
||||
except Exception as e:
|
||||
msg = "[Error] Unable to delete the file/folder...."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
|
||||
@app.route('/api/list-favorites', methods=['GET', 'POST'])
|
||||
|
@ -198,7 +200,7 @@ def listFavorites():
|
|||
return '{"faves_list":' + json.dumps(faves) + '}'
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
@app.route('/api/load-favorite/<_id>', methods=['GET', 'POST'])
|
||||
def loadFavorite(_id):
|
||||
|
@ -212,10 +214,10 @@ def loadFavorite(_id):
|
|||
except Exception as e:
|
||||
print(repr(e))
|
||||
msg = "Incorrect Favorites ID..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
|
||||
@app.route('/api/manage-favorites/<_action>', methods=['GET', 'POST'])
|
||||
|
@ -235,13 +237,13 @@ def manageFavorites(_action):
|
|||
msg = "Deleted from Favorites successfully..."
|
||||
else:
|
||||
msg = "Couldn't handle action for favorites item..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
db.session.commit()
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
return msgHandler.create_JSON_message("success", msg)
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
|
||||
@app.route('/api/create/<_type>', methods=['GET', 'POST'])
|
||||
|
@ -249,42 +251,42 @@ def create_item(_type = None):
|
|||
if request.method == 'POST':
|
||||
msg = "Log in with an Admin privlidged user to upload files!"
|
||||
if not oidc.user_loggedin:
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
elif oidc.user_loggedin:
|
||||
isAdmin = oidc.user_getfield("isAdmin")
|
||||
if isAdmin != "yes" :
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
TYPE = _type.strip()
|
||||
FNAME = str(request.values['fname']).strip()
|
||||
|
||||
if not re.fullmatch(valid_fname_pat, FNAME):
|
||||
msg = "A new item name can only contain alphanumeric, -, _, |, [], (), or spaces and must be minimum of 4 and max of 20 characters..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
view = get_window_controller().get_window(1).get_view(0)
|
||||
folder = view.get_current_directory()
|
||||
new_item = folder + '/' + FNAME
|
||||
new_item = f"{folder}/{FNAME}"
|
||||
|
||||
try:
|
||||
if TYPE == "dir":
|
||||
os.mkdir(new_item)
|
||||
elif TYPE == "file":
|
||||
open(new_item + ".txt", 'a').close()
|
||||
open(f"{new_item}.txt", 'a').close()
|
||||
else:
|
||||
msg = "Couldn't handle action type for api create..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
msg = "Couldn't create file/folder. An unexpected error occured..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
|
||||
msg = "[Success] created the file/dir..."
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
return msgHandler.create_JSON_message("success", msg)
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
|
||||
@app.route('/upload', methods=['GET', 'POST'])
|
||||
|
@ -292,15 +294,15 @@ def upload():
|
|||
if request.method == 'POST' and len(request.files) > 0:
|
||||
msg = "Log in with an Admin privlidged user to upload files!"
|
||||
if not oidc.user_loggedin:
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
elif oidc.user_loggedin:
|
||||
isAdmin = oidc.user_getfield("isAdmin")
|
||||
if isAdmin != "yes" :
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
view = get_window_controller().get_window(1).get_view(0)
|
||||
folder = view.get_current_directory()
|
||||
UPLOADS_PTH = folder + '/'
|
||||
UPLOADS_PTH = f'{folder}/'
|
||||
files = UploadSet('files', ALL, default_dest=lambda x: UPLOADS_PTH)
|
||||
configure_uploads(app, files)
|
||||
|
||||
|
@ -310,10 +312,10 @@ def upload():
|
|||
except Exception as e:
|
||||
print(repr(e))
|
||||
msg = "[Error] Failed to upload some or all of the file(s)..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
||||
msg = "[Success] Uploaded file(s)..."
|
||||
return msgHandler.createMessageJSON("success", msg)
|
||||
return msgHandler.create_JSON_message("success", msg)
|
||||
else:
|
||||
msg = "Can't manage the request type..."
|
||||
return msgHandler.createMessageJSON("danger", msg)
|
||||
return msgHandler.create_JSON_message("danger", msg)
|
||||
|
|
|
@ -10,5 +10,5 @@ class MessageHandler:
|
|||
print("MessageHandler initialized...")
|
||||
|
||||
|
||||
def createMessageJSON(self, type, text):
|
||||
def create_JSON_message(self, type, text):
|
||||
return '{"message": { "type": "' + type + '", "text": "' + text + '" } }'
|
||||
|
|
|
@ -11,7 +11,7 @@ class WindowController:
|
|||
if window.id == win_id:
|
||||
return window
|
||||
|
||||
raise("No Window by ID {} found!".format(win_id))
|
||||
raise(f"No Window by ID {win_id} found!")
|
||||
|
||||
def get_windows(self):
|
||||
return self.windows
|
||||
|
@ -19,7 +19,7 @@ class WindowController:
|
|||
def add_window(self):
|
||||
window = Window()
|
||||
window.id = len(self.windows) + 1
|
||||
window.name = "window_" + str(window.id)
|
||||
window.name = f"window_{window.id}"
|
||||
window.create_view()
|
||||
self.windows.append(window)
|
||||
|
||||
|
@ -48,10 +48,10 @@ class WindowController:
|
|||
def list_windows(self):
|
||||
for window in self.windows:
|
||||
print("\n[ Window ]")
|
||||
print("ID: " + str(window.id))
|
||||
print("Name: " + window.name)
|
||||
print("Nickname: " + window.nickname)
|
||||
print("View Count: " + str( len(window.views) ))
|
||||
print("ID: {window.id}")
|
||||
print("Name: {window.name}")
|
||||
print("Nickname: {window.nickname}")
|
||||
print("View Count: {len(window.views)}")
|
||||
|
||||
|
||||
def list_views_from_window(self, win_id):
|
||||
|
|
|
@ -157,7 +157,7 @@ class View(Settings, Launcher, Path):
|
|||
if not os.path.exists(hashImgPth) :
|
||||
fullPath = join(current_directory, video[0])
|
||||
self.logger.debug(f"Hash Path: {hashImgPth}\nFile Path: {fullPath}")
|
||||
self.generateVideoThumbnail(fullPath, hashImgPth)
|
||||
self.generate_video_thumbnail(fullPath, hashImgPth)
|
||||
|
||||
return videos_set
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import os, subprocess, threading
|
|||
|
||||
|
||||
class Launcher:
|
||||
def openFilelocally(self, file):
|
||||
def open_file_locally(self, file):
|
||||
lowerName = file.lower()
|
||||
command = []
|
||||
|
||||
|
@ -38,14 +38,25 @@ class Launcher:
|
|||
subprocess.Popen(command, start_new_session=True, stdout=DEVNULL, stderr=DEVNULL, close_fds=True)
|
||||
|
||||
|
||||
def remuxVideo(self, hash, file):
|
||||
remux_vid_pth = self.REMUX_FOLDER + "/" + hash + ".mp4"
|
||||
def create_stream(self, hash, file):
|
||||
# ffmpeg -re -stream_loop -1 -i "<video file path>" -f rtsp -rtsp_transport udp rtsp://localhost:8554/live.stream
|
||||
command = ["ffmpeg", "-re", "-stream_loop", "-1", "-i", file, "-f", "rtsp", "-rtsp_transport", "udp", "rtsp://www.webfm.com:8554/{hash}.stream",]
|
||||
try:
|
||||
proc = subprocess.Popen(command)
|
||||
except Exception as e:
|
||||
self.logger.debug(message)
|
||||
self.logger.debug(e)
|
||||
return False
|
||||
|
||||
|
||||
def remux_video(self, hash, file):
|
||||
remux_vid_pth = f"{self.REMUX_FOLDER}/{hash}.mp4"
|
||||
self.logger.debug(remux_vid_pth)
|
||||
|
||||
if not os.path.isfile(remux_vid_pth):
|
||||
self.check_remux_space()
|
||||
|
||||
command = ["ffmpeg", "-i", file, "-hide_banner", "-movflags", "+faststart"]
|
||||
command = ["ffmpeg", "-i", file, "-hide_banner", "-movflags", "+faststart", "-sws_flags", "fast_bilinear",]
|
||||
if file.endswith("mkv"):
|
||||
command += ["-codec", "copy", "-strict", "-2"]
|
||||
if file.endswith("avi"):
|
||||
|
@ -67,7 +78,7 @@ class Launcher:
|
|||
return True
|
||||
|
||||
|
||||
def generateVideoThumbnail(self, fullPath, hashImgPth):
|
||||
def generate_video_thumbnail(self, fullPath, hashImgPth):
|
||||
try:
|
||||
proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", "65%", "-s", "300", "-c", "jpg", "-i", fullPath, "-o", hashImgPth])
|
||||
proc.wait()
|
||||
|
@ -83,7 +94,7 @@ class Launcher:
|
|||
self.logger.debug(e)
|
||||
return
|
||||
|
||||
usage = self.getRemuxFolderUsage(self.REMUX_FOLDER)
|
||||
usage = self.get_remux_folder_usage(self.REMUX_FOLDER)
|
||||
if usage > limit:
|
||||
files = os.listdir(self.REMUX_FOLDER)
|
||||
for file in files:
|
||||
|
@ -91,7 +102,7 @@ class Launcher:
|
|||
os.unlink(fp)
|
||||
|
||||
|
||||
def getRemuxFolderUsage(self, start_path = "."):
|
||||
def get_remux_folder_usage(self, start_path = "."):
|
||||
total_size = 0
|
||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||
for f in filenames:
|
||||
|
|
|
@ -14,21 +14,21 @@ class Settings:
|
|||
logger = None
|
||||
|
||||
USER_HOME = path.expanduser('~')
|
||||
CONFIG_PATH = USER_HOME + "/.config/webfm"
|
||||
CONFIG_FILE = CONFIG_PATH + "/settings.json"
|
||||
CONFIG_PATH = f"{USER_HOME}/.config/webfm"
|
||||
CONFIG_FILE = f"{CONFIG_PATH}/settings.json"
|
||||
HIDE_HIDDEN_FILES = True
|
||||
|
||||
GTK_ORIENTATION = 1 # HORIZONTAL (0) VERTICAL (1)
|
||||
DEFAULT_ICONS = CONFIG_PATH + "/icons"
|
||||
DEFAULT_ICON = DEFAULT_ICONS + "/text.png"
|
||||
FFMPG_THUMBNLR = CONFIG_PATH + "/ffmpegthumbnailer" # Thumbnail generator binary
|
||||
REMUX_FOLDER = USER_HOME + "/.remuxs" # Remuxed files folder
|
||||
DEFAULT_ICONS = f"{CONFIG_PATH}/icons"
|
||||
DEFAULT_ICON = f"{DEFAULT_ICONS}/text.png"
|
||||
FFMPG_THUMBNLR = f"{CONFIG_PATH}/ffmpegthumbnailer" # Thumbnail generator binary
|
||||
REMUX_FOLDER = f"{USER_HOME}/.remuxs" # Remuxed files folder
|
||||
|
||||
STEAM_BASE_URL = "https://steamcdn-a.akamaihd.net/steam/apps/"
|
||||
ICON_DIRS = ["/usr/share/pixmaps", "/usr/share/icons", USER_HOME + "/.icons" ,]
|
||||
BASE_THUMBS_PTH = USER_HOME + "/.thumbnails" # Used for thumbnail generation
|
||||
ABS_THUMBS_PTH = BASE_THUMBS_PTH + "/normal" # Used for thumbnail generation
|
||||
STEAM_ICONS_PTH = BASE_THUMBS_PTH + "/steam_icons"
|
||||
ICON_DIRS = ["/usr/share/pixmaps", "/usr/share/icons", f"{USER_HOME}/.icons" ,]
|
||||
BASE_THUMBS_PTH = f"{USER_HOME}/.thumbnails" # Used for thumbnail generation
|
||||
ABS_THUMBS_PTH = f"{BASE_THUMBS_PTH}/normal" # Used for thumbnail generation
|
||||
STEAM_ICONS_PTH = f"{BASE_THUMBS_PTH}/steam_icons"
|
||||
CONTAINER_ICON_WH = [128, 128]
|
||||
VIDEO_ICON_WH = [128, 64]
|
||||
SYS_ICON_WH = [56, 56]
|
||||
|
@ -74,7 +74,7 @@ class Settings:
|
|||
# Filters
|
||||
fvideos = ('.mkv', '.avi', '.flv', '.mov', '.m4v', '.mpg', '.wmv', '.mpeg', '.mp4', '.webm')
|
||||
foffice = ('.doc', '.docx', '.xls', '.xlsx', '.xlt', '.xltx', '.xlm', '.ppt', 'pptx', '.pps', '.ppsx', '.odt', '.rtf')
|
||||
fimages = ('.png', '.jpg', '.jpeg', '.gif', '.ico', '.tga')
|
||||
fimages = ('.png', '.jpg', '.jpeg', '.gif', '.ico', '.tga', '.webp')
|
||||
ftext = ('.txt', '.text', '.sh', '.cfg', '.conf')
|
||||
fmusic = ('.psf', '.mp3', '.ogg', '.flac', '.m4a')
|
||||
fpdf = ('.pdf')
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,281 @@
|
|||
|
||||
###############################################
|
||||
# General parameters
|
||||
|
||||
# Sets the verbosity of the program; available values are "error", "warn", "info", "debug".
|
||||
logLevel: info
|
||||
# Destinations of log messages; available values are "stdout", "file" and "syslog".
|
||||
logDestinations: [stdout]
|
||||
# If "file" is in logDestinations, this is the file which will receive the logs.
|
||||
logFile: rtsp-simple-server.log
|
||||
|
||||
# Timeout of read operations.
|
||||
readTimeout: 10s
|
||||
# Timeout of write operations.
|
||||
writeTimeout: 10s
|
||||
# Number of read buffers.
|
||||
# A higher number allows a wider throughput, a lower number allows to save RAM.
|
||||
readBufferCount: 2048
|
||||
|
||||
# HTTP URL to perform external authentication.
|
||||
# Every time a user wants to authenticate, the server calls this URL
|
||||
# with the POST method and a body containing:
|
||||
# {
|
||||
# "ip": "ip",
|
||||
# "user": "user",
|
||||
# "password": "password",
|
||||
# "path": "path",
|
||||
# "action": "read|publish"
|
||||
# "query": "url's raw query"
|
||||
# }
|
||||
# If the response code is 20x, authentication is accepted, otherwise
|
||||
# it is discarded.
|
||||
externalAuthenticationURL:
|
||||
|
||||
# Enable the HTTP API.
|
||||
api: no
|
||||
# Address of the API listener.
|
||||
apiAddress: 127.0.0.1:9997
|
||||
|
||||
# Enable Prometheus-compatible metrics.
|
||||
metrics: no
|
||||
# Address of the metrics listener.
|
||||
metricsAddress: 127.0.0.1:9998
|
||||
|
||||
# Enable pprof-compatible endpoint to monitor performances.
|
||||
pprof: no
|
||||
# Address of the pprof listener.
|
||||
pprofAddress: 127.0.0.1:9999
|
||||
|
||||
# Command to run when a client connects to the server.
|
||||
# This is terminated with SIGINT when a client disconnects from the server.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PORT: server port
|
||||
runOnConnect:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnConnectRestart: no
|
||||
|
||||
###############################################
|
||||
# RTSP parameters
|
||||
|
||||
# Disable support for the RTSP protocol.
|
||||
rtspDisable: no
|
||||
# List of enabled RTSP transport protocols.
|
||||
# UDP is the most performant, but doesn't work when there's a NAT/firewall between
|
||||
# server and clients, and doesn't support encryption.
|
||||
# UDP-multicast allows to save bandwidth when clients are all in the same LAN.
|
||||
# TCP is the most versatile, and does support encryption.
|
||||
# The handshake is always performed with TCP.
|
||||
protocols: [udp, multicast, tcp]
|
||||
# Encrypt handshake and TCP streams with TLS (RTSPS).
|
||||
# Available values are "no", "strict", "optional".
|
||||
encryption: "no"
|
||||
# Address of the TCP/RTSP listener. This is needed only when encryption is "no" or "optional".
|
||||
rtspAddress: :8554
|
||||
# Address of the TCP/TLS/RTSPS listener. This is needed only when encryption is "strict" or "optional".
|
||||
rtspsAddress: :8322
|
||||
# Address of the UDP/RTP listener. This is needed only when "udp" is in protocols.
|
||||
rtpAddress: :8000
|
||||
# Address of the UDP/RTCP listener. This is needed only when "udp" is in protocols.
|
||||
rtcpAddress: :8001
|
||||
# IP range of all UDP-multicast listeners. This is needed only when "multicast" is in protocols.
|
||||
multicastIPRange: 224.1.0.0/16
|
||||
# Port of all UDP-multicast/RTP listeners. This is needed only when "multicast" is in protocols.
|
||||
multicastRTPPort: 8002
|
||||
# Port of all UDP-multicast/RTCP listeners. This is needed only when "multicast" is in protocols.
|
||||
multicastRTCPPort: 8003
|
||||
# Path to the server key. This is needed only when encryption is "strict" or "optional".
|
||||
# This can be generated with:
|
||||
# openssl genrsa -out server.key 2048
|
||||
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
|
||||
serverKey: server.key
|
||||
# Path to the server certificate. This is needed only when encryption is "strict" or "optional".
|
||||
serverCert: server.crt
|
||||
# Authentication methods.
|
||||
authMethods: [basic, digest]
|
||||
|
||||
###############################################
|
||||
# RTMP parameters
|
||||
|
||||
# Disable support for the RTMP protocol.
|
||||
rtmpDisable: no
|
||||
# Address of the RTMP listener.
|
||||
rtmpAddress: :1935
|
||||
|
||||
###############################################
|
||||
# HLS parameters
|
||||
|
||||
# Disable support for the HLS protocol.
|
||||
hlsDisable: no
|
||||
# Address of the HLS listener.
|
||||
hlsAddress: :8888
|
||||
# By default, HLS is generated only when requested by a user.
|
||||
# This option allows to generate it always, avoiding the delay between request and generation.
|
||||
hlsAlwaysRemux: no
|
||||
# Variant of the HLS protocol to use. Available options are:
|
||||
# * mpegts - uses MPEG-TS segments, for maximum compatibility.
|
||||
# * fmp4 - uses fragmented MP4 segments, more efficient.
|
||||
# * lowLatency - uses Low-Latency HLS.
|
||||
hlsVariant: mpegts
|
||||
# Number of HLS segments to keep on the server.
|
||||
# Segments allow to seek through the stream.
|
||||
# Their number doesn't influence latency.
|
||||
hlsSegmentCount: 7
|
||||
# Minimum duration of each segment.
|
||||
# A player usually puts 3 segments in a buffer before reproducing the stream.
|
||||
# The final segment duration is also influenced by the interval between IDR frames,
|
||||
# since the server changes the duration in order to include at least one IDR frame
|
||||
# in each segment.
|
||||
hlsSegmentDuration: 1s
|
||||
# Minimum duration of each part.
|
||||
# A player usually puts 3 parts in a buffer before reproducing the stream.
|
||||
# Parts are used in Low-Latency HLS in place of segments.
|
||||
# Part duration is influenced by the distance between video/audio samples
|
||||
# and is adjusted in order to produce segments with a similar duration.
|
||||
hlsPartDuration: 200ms
|
||||
# Maximum size of each segment.
|
||||
# This prevents RAM exhaustion.
|
||||
hlsSegmentMaxSize: 50M
|
||||
# Value of the Access-Control-Allow-Origin header provided in every HTTP response.
|
||||
# This allows to play the HLS stream from an external website.
|
||||
hlsAllowOrigin: '*'
|
||||
# Enable TLS/HTTPS on the HLS server.
|
||||
# This is required for Low-Latency HLS.
|
||||
hlsEncryption: no
|
||||
# Path to the server key. This is needed only when encryption is yes.
|
||||
# This can be generated with:
|
||||
# openssl genrsa -out server.key 2048
|
||||
# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
|
||||
hlsServerKey: server.key
|
||||
# Path to the server certificate.
|
||||
hlsServerCert: server.crt
|
||||
|
||||
###############################################
|
||||
# Path parameters
|
||||
|
||||
# These settings are path-dependent, and the map key is the name of the path.
|
||||
# It's possible to use regular expressions by using a tilde as prefix.
|
||||
# For example, "~^(test1|test2)$" will match both "test1" and "test2".
|
||||
# For example, "~^prefix" will match all paths that start with "prefix".
|
||||
# The settings under the path "all" are applied to all paths that do not match
|
||||
# another entry.
|
||||
paths:
|
||||
all:
|
||||
# Source of the stream. This can be:
|
||||
# * publisher -> the stream is published by a RTSP or RTMP client
|
||||
# * rtsp://existing-url -> the stream is pulled from another RTSP server / camera
|
||||
# * rtsps://existing-url -> the stream is pulled from another RTSP server / camera with RTSPS
|
||||
# * rtmp://existing-url -> the stream is pulled from another RTMP server
|
||||
# * http://existing-url/stream.m3u8 -> the stream is pulled from another HLS server
|
||||
# * https://existing-url/stream.m3u8 -> the stream is pulled from another HLS server with HTTPS
|
||||
# * redirect -> the stream is provided by another path or server
|
||||
source: publisher
|
||||
|
||||
# If the source is an RTSP or RTSPS URL, this is the protocol that will be used to
|
||||
# pull the stream. available values are "automatic", "udp", "multicast", "tcp".
|
||||
sourceProtocol: automatic
|
||||
|
||||
# Tf the source is an RTSP or RTSPS URL, this allows to support sources that
|
||||
# don't provide server ports or use random server ports. This is a security issue
|
||||
# and must be used only when interacting with sources that require it.
|
||||
sourceAnyPortEnable: no
|
||||
|
||||
# If the source is a RTSPS or HTTPS URL, and the source certificate is self-signed
|
||||
# or invalid, you can provide the fingerprint of the certificate in order to
|
||||
# validate it anyway. It can be obtained by running:
|
||||
# openssl s_client -connect source_ip:source_port </dev/null 2>/dev/null | sed -n '/BEGIN/,/END/p' > server.crt
|
||||
# openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':'
|
||||
sourceFingerprint:
|
||||
|
||||
# If the source is an RTSP or RTMP URL, it will be pulled only when at least
|
||||
# one reader is connected, saving bandwidth.
|
||||
sourceOnDemand: no
|
||||
# If sourceOnDemand is "yes", readers will be put on hold until the source is
|
||||
# ready or until this amount of time has passed.
|
||||
sourceOnDemandStartTimeout: 10s
|
||||
# If sourceOnDemand is "yes", the source will be closed when there are no
|
||||
# readers connected and this amount of time has passed.
|
||||
sourceOnDemandCloseAfter: 10s
|
||||
|
||||
# If the source is "redirect", this is the RTSP URL which clients will be
|
||||
# redirected to.
|
||||
sourceRedirect:
|
||||
|
||||
# If the source is "publisher" and a client is publishing, do not allow another
|
||||
# client to disconnect the former and publish in its place.
|
||||
disablePublisherOverride: no
|
||||
|
||||
# If the source is "publisher" and no one is publishing, redirect readers to this
|
||||
# path. It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
|
||||
fallback:
|
||||
|
||||
# Username required to publish.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
publishUser:
|
||||
# Password required to publish.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
publishPass:
|
||||
# IPs or networks (x.x.x.x/24) allowed to publish.
|
||||
publishIPs: []
|
||||
|
||||
# Username required to read.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
readUser:
|
||||
# password required to read.
|
||||
# SHA256-hashed values can be inserted with the "sha256:" prefix.
|
||||
readPass:
|
||||
# IPs or networks (x.x.x.x/24) allowed to read.
|
||||
readIPs: []
|
||||
|
||||
# Command to run when this path is initialized.
|
||||
# This can be used to publish a stream and keep it always opened.
|
||||
# This is terminated with SIGINT when the program closes.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnInit:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnInitRestart: no
|
||||
|
||||
# Command to run when this path is requested.
|
||||
# This can be used to publish a stream on demand.
|
||||
# This is terminated with SIGINT when the path is not requested anymore.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnDemand:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnDemandRestart: no
|
||||
# Readers will be put on hold until the runOnDemand command starts publishing
|
||||
# or until this amount of time has passed.
|
||||
runOnDemandStartTimeout: 10s
|
||||
# The command will be closed when there are no
|
||||
# readers connected and this amount of time has passed.
|
||||
runOnDemandCloseAfter: 10s
|
||||
|
||||
# Command to run when the stream is ready to be read, whether it is
|
||||
# published by a client or pulled from a server / camera.
|
||||
# This is terminated with SIGINT when the stream is not ready anymore.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnReady:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnReadyRestart: no
|
||||
|
||||
# Command to run when a clients starts reading.
|
||||
# This is terminated with SIGINT when a client stops reading.
|
||||
# The following environment variables are available:
|
||||
# * RTSP_PATH: path name
|
||||
# * RTSP_PORT: server port
|
||||
# * G1, G2, ...: regular expression groups, if path name is
|
||||
# a regular expression.
|
||||
runOnRead:
|
||||
# Restart the command if it exits suddenly.
|
||||
runOnReadRestart: no
|
Loading…
Reference in New Issue