fix: backport fixes from a sister project
This commit is contained in:
@@ -3,6 +3,7 @@ db_file_path = "/path/to/ythdd_db.sqlite" # Preferably stored on an SSD
|
|||||||
video_storage_directory_path = "/path/to/videos/" # Path to video vault.
|
video_storage_directory_path = "/path/to/videos/" # Path to video vault.
|
||||||
is_proxied = false # Set to true if running behind reverse proxy.
|
is_proxied = false # Set to true if running behind reverse proxy.
|
||||||
public_facing_url = "http://localhost:5000/" # Used for URL rewriting. Note the trailing backslash /.
|
public_facing_url = "http://localhost:5000/" # Used for URL rewriting. Note the trailing backslash /.
|
||||||
|
debug = false # Whether to print verbose, debug info on API endpoints.
|
||||||
|
|
||||||
[api]
|
[api]
|
||||||
api_key = "" # Leave empty API key for public access to non-sensitive backend
|
api_key = "" # Leave empty API key for public access to non-sensitive backend
|
||||||
|
|||||||
3
ythdd.py
3
ythdd.py
@@ -53,6 +53,9 @@ def setup():
|
|||||||
sanity_string += f" If you're running a reverse proxy, set {colors.OKCYAN}is_proxied{colors.ENDC} to true to silence this message.\n"
|
sanity_string += f" If you're running a reverse proxy, set {colors.OKCYAN}is_proxied{colors.ENDC} to true to silence this message.\n"
|
||||||
print(sanity_string)
|
print(sanity_string)
|
||||||
|
|
||||||
|
# Should work around disconnects: https://stackoverflow.com/a/61739721
|
||||||
|
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {"pool_pre_ping": True}
|
||||||
|
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{config['general']['db_file_path']}"
|
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{config['general']['db_file_path']}"
|
||||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||||
app.add_url_rule('/', view_func=views.index)
|
app.add_url_rule('/', view_func=views.index)
|
||||||
|
|||||||
15
ythdd_api.py
15
ythdd_api.py
@@ -4,6 +4,7 @@ from markupsafe import escape
|
|||||||
import requests, time, json
|
import requests, time, json
|
||||||
import ythdd_globals
|
import ythdd_globals
|
||||||
import ythdd_api_v1, ythdd_inv_tl
|
import ythdd_api_v1, ythdd_inv_tl
|
||||||
|
import traceback
|
||||||
|
|
||||||
def api_greeting():
|
def api_greeting():
|
||||||
string = {'status': 200, 'msg': f"ok (ythdd {ythdd_globals.version})", 'latest_api': f"v{ythdd_globals.apiVersion}"}
|
string = {'status': 200, 'msg': f"ok (ythdd {ythdd_globals.version})", 'latest_api': f"v{ythdd_globals.apiVersion}"}
|
||||||
@@ -25,10 +26,15 @@ def api_global_catchall(received_request):
|
|||||||
#return api_greeting()
|
#return api_greeting()
|
||||||
resp = api_greeting()
|
resp = api_greeting()
|
||||||
try:
|
try:
|
||||||
status, received, data = ythdd_api_v1.lookup(request_list)
|
status, received, data = ythdd_api_v1.lookup(request_list, request)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ythdd_globals.apiFailedRequests += 1
|
ythdd_globals.apiFailedRequests += 1
|
||||||
|
stripped_filename = __file__[max(__file__.rfind("/"), __file__.rfind("\\")) + 1:]
|
||||||
|
print(f"\n{c.FAIL}Error! /api/{received_request} -> {stripped_filename}:L{e.__traceback__.tb_lineno} -> {type(e).__name__}{c.ENDC}:"
|
||||||
|
+ f"{traceback.format_exc()}")
|
||||||
status, received, data = 500, f"internal server error: call ended in failure: {e}", []
|
status, received, data = 500, f"internal server error: call ended in failure: {e}", []
|
||||||
|
if ythdd_globals.config["general"]["debug"]:
|
||||||
|
status, received, data = 500, f"internal server error: call ended in failure: {e} ({stripped_filename}:L{e.__traceback__.tb_lineno})", []
|
||||||
resp = Response(json.dumps({'status': status, 'msg': received, 'data': data}), mimetype='application/json', status=status)
|
resp = Response(json.dumps({'status': status, 'msg': received, 'data': data}), mimetype='application/json', status=status)
|
||||||
elif request_list[0] == 'invidious':
|
elif request_list[0] == 'invidious':
|
||||||
# drop 'invidious' from the list
|
# drop 'invidious' from the list
|
||||||
@@ -43,12 +49,17 @@ def api_global_catchall(received_request):
|
|||||||
# if a path has been supplied try to get appropriate data
|
# if a path has been supplied try to get appropriate data
|
||||||
try:
|
try:
|
||||||
# lookup and construct a response
|
# lookup and construct a response
|
||||||
resp = ythdd_inv_tl.lookup(request_list)
|
resp = ythdd_inv_tl.lookup(request_list, request)
|
||||||
#print(resp) # for debugging purposes
|
#print(resp) # for debugging purposes
|
||||||
# unless an error occurs
|
# unless an error occurs
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ythdd_globals.apiFailedRequests += 1
|
ythdd_globals.apiFailedRequests += 1
|
||||||
|
stripped_filename = __file__[max(__file__.rfind("/"), __file__.rfind("\\")) + 1:]
|
||||||
|
print(f"\n{c.FAIL}Error! /api/{received_request} -> {stripped_filename}:L{e.__traceback__.tb_lineno} -> {type(e).__name__}{c.ENDC}:"
|
||||||
|
+ f"{traceback.format_exc()}")
|
||||||
status, received, data = 500, f"internal server error: invidious translation call ended in failure: {e}", []
|
status, received, data = 500, f"internal server error: invidious translation call ended in failure: {e}", []
|
||||||
|
if ythdd_globals.config["general"]["debug"]:
|
||||||
|
status, received, data = 500, f"internal server error: invidious translation call ended in failure: {e} ({stripped_filename}:L{e.__traceback__.tb_lineno})", []
|
||||||
resp = Response(json.dumps({'status': status, 'msg': received, 'data': data}), mimetype='application/json', status=status)
|
resp = Response(json.dumps({'status': status, 'msg': received, 'data': data}), mimetype='application/json', status=status)
|
||||||
else:
|
else:
|
||||||
ythdd_globals.apiFailedRequests += 1
|
ythdd_globals.apiFailedRequests += 1
|
||||||
|
|||||||
@@ -8,6 +8,20 @@ import ythdd_globals, ythdd_extractor
|
|||||||
#from flask_sqlalchemy import SQLAlchemy
|
#from flask_sqlalchemy import SQLAlchemy
|
||||||
#import ythdd_api_v1_stats, ythdd_api_v1_user, ythdd_api_v1_info, ythdd_api_v1_query, ythdd_api_v1_meta, ythdd_api_v1_admin
|
#import ythdd_api_v1_stats, ythdd_api_v1_user, ythdd_api_v1_info, ythdd_api_v1_query, ythdd_api_v1_meta, ythdd_api_v1_admin
|
||||||
|
|
||||||
|
def requireAuthentication(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
token = kwargs["r"].args.get('token')
|
||||||
|
if token == lewy_globals.config['api']['api_key']:
|
||||||
|
try:
|
||||||
|
status, received, data = func(*args, **kwargs)
|
||||||
|
return status, received, data
|
||||||
|
except:
|
||||||
|
raise AssertionError(f"Function \"{func.__name__}\" does not return status, code, and data as it should!")
|
||||||
|
else:
|
||||||
|
return 401, "error", {'error_msg': "Unauthorized"}
|
||||||
|
return wrapper
|
||||||
|
|
||||||
def incrementBadRequests():
|
def incrementBadRequests():
|
||||||
ythdd_globals.apiFailedRequests += 1
|
ythdd_globals.apiFailedRequests += 1
|
||||||
|
|
||||||
@@ -29,7 +43,7 @@ def stats():
|
|||||||
"outside_api_requests": ythdd_globals.outsideApiHits,
|
"outside_api_requests": ythdd_globals.outsideApiHits,
|
||||||
"local_api_requests": ythdd_globals.apiRequests - ythdd_globals.outsideApiHits
|
"local_api_requests": ythdd_globals.apiRequests - ythdd_globals.outsideApiHits
|
||||||
}
|
}
|
||||||
return 200, "OK", data_to_send
|
return 200, "ok", data_to_send
|
||||||
|
|
||||||
def videoIdSanityCheck(videoId: str):
|
def videoIdSanityCheck(videoId: str):
|
||||||
if len(videId) != 11:
|
if len(videId) != 11:
|
||||||
@@ -129,7 +143,7 @@ def hot(data):
|
|||||||
incrementBadRequests()
|
incrementBadRequests()
|
||||||
return notImplemented([data[1]]) # workaround before notImplemented is reworked
|
return notImplemented([data[1]]) # workaround before notImplemented is reworked
|
||||||
|
|
||||||
def lookup(data):
|
def lookup(data, request):
|
||||||
match data[0]:
|
match data[0]:
|
||||||
case 'stats':
|
case 'stats':
|
||||||
return stats()
|
return stats()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ def getConfig(configfile):
|
|||||||
global randomly_generated_passcode
|
global randomly_generated_passcode
|
||||||
|
|
||||||
if not os.path.exists(configfile):
|
if not os.path.exists(configfile):
|
||||||
dummy_config = {'general': {'db_file_path': 'ythdd_db.sqlite', 'video_storage_directory_path': 'videos/', 'is_proxied': False, 'public_facing_url': 'http://localhost:5000/'}, 'api': {'api_key': 'CHANGEME'}, 'extractor': {'user-agent': '', 'cookies_path': ''}, 'admin': {'admins': ['admin']}, 'yt_dlp': {}, 'postprocessing': {'presets': [{'name': 'recommended: [N][<=720p] best V+A', 'format': 'bv[height<=720]+ba', 'reencode': ''}, {'name': '[N][1080p] best V+A', 'format': 'bv[height=1080]+ba', 'reencode': ''}, {'name': '[R][1080p] webm', 'format': 'bv[height=1080]+ba', 'reencode': 'webm'}, {'name': '[N][720p] best V+A', 'format': 'bv[height=720]+ba', 'reencode': ''}, {'name': '[R][720p] webm', 'format': 'bv[height=720]+ba', 'reencode': 'webm'}, {'name': '[N][480p] best V+A', 'format': 'bv[height=480]+ba', 'reencode': ''}, {'name': '[480p] VP9 webm/reencode', 'format': 'bv*[height=480][ext=webm]+ba/bv[height=480]+ba', 'reencode': 'webm'}, {'name': '[N][1080p] best video only', 'format': 'bv[height=1080]', 'reencode': ''}, {'name': '[N][opus] best audio only', 'format': 'ba', 'reencode': 'opus'}]}}
|
dummy_config = {'general': {'db_file_path': 'ythdd_db.sqlite', 'video_storage_directory_path': 'videos/', 'is_proxied': False, 'public_facing_url': 'http://localhost:5000/', 'debug': False}, 'api': {'api_key': 'CHANGEME'}, 'extractor': {'user-agent': '', 'cookies_path': ''}, 'admin': {'admins': ['admin']}, 'yt_dlp': {}, 'postprocessing': {'presets': [{'name': 'recommended: [N][<=720p] best V+A', 'format': 'bv[height<=720]+ba', 'reencode': ''}, {'name': '[N][1080p] best V+A', 'format': 'bv[height=1080]+ba', 'reencode': ''}, {'name': '[R][1080p] webm', 'format': 'bv[height=1080]+ba', 'reencode': 'webm'}, {'name': '[N][720p] best V+A', 'format': 'bv[height=720]+ba', 'reencode': ''}, {'name': '[R][720p] webm', 'format': 'bv[height=720]+ba', 'reencode': 'webm'}, {'name': '[N][480p] best V+A', 'format': 'bv[height=480]+ba', 'reencode': ''}, {'name': '[480p] VP9 webm/reencode', 'format': 'bv*[height=480][ext=webm]+ba/bv[height=480]+ba', 'reencode': 'webm'}, {'name': '[N][1080p] best video only', 'format': 'bv[height=1080]', 'reencode': ''}, {'name': '[N][opus] best audio only', 'format': 'ba', 'reencode': 'opus'}]}}
|
||||||
# if a passcode has not been provided by the user (config file doesn't exist, and user didn't specify it using an argument)
|
# if a passcode has not been provided by the user (config file doesn't exist, and user didn't specify it using an argument)
|
||||||
print(f"{colors.WARNING}WARNING{colors.ENDC}: Using default, baked in config data. {colors.ENDL}"
|
print(f"{colors.WARNING}WARNING{colors.ENDC}: Using default, baked in config data. {colors.ENDL}"
|
||||||
f" Consider copying and editing the provided example file ({colors.OKCYAN}config.default.toml{colors.ENDC}).")
|
f" Consider copying and editing the provided example file ({colors.OKCYAN}config.default.toml{colors.ENDC}).")
|
||||||
@@ -99,8 +99,15 @@ def safeTraverse(obj: dict, path: list, default=None):
|
|||||||
for x in path:
|
for x in path:
|
||||||
#print(f"traversing {result} with respect to {x}")
|
#print(f"traversing {result} with respect to {x}")
|
||||||
result = result[x]
|
result = result[x]
|
||||||
except KeyError:
|
except (KeyError, TypeError):
|
||||||
result = default
|
result = default
|
||||||
print(f"error reading: {' -> '.join(path)} - returning: {default}")
|
print(f"error reading: {' -> '.join(path)} - returning: {default}")
|
||||||
finally:
|
finally:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def getCommit() -> str | None:
|
||||||
|
try:
|
||||||
|
return Repo(search_parent_directories=True).head.object.hexsha
|
||||||
|
except Exception as e:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ def videos(data):
|
|||||||
|
|
||||||
return send(status_code, response)
|
return send(status_code, response)
|
||||||
|
|
||||||
def lookup(data):
|
def lookup(data, request):
|
||||||
# possibly TODO: rewrite this mess
|
# possibly TODO: rewrite this mess
|
||||||
if len(data) > 2:
|
if len(data) > 2:
|
||||||
if (data[0], data[1]) == ("api", "v1"):
|
if (data[0], data[1]) == ("api", "v1"):
|
||||||
|
|||||||
Reference in New Issue
Block a user