display error as video

This commit is contained in:
Lamp 2023-06-22 20:47:07 -07:00
parent 5ea0cccc13
commit 81ef6df1cf
6 changed files with 83 additions and 22 deletions

4
.gitignore vendored

@ -1 +1,3 @@
.vscode
.vscode
__pycache__
test.mp4

@ -10,6 +10,7 @@ A YouTube video file resolver thingy via search query, made to let you watch You
- Limited to one YoutubeDL invocation per IP address
- Results cached for 5 hours or until expiry found in extracted URL
- Extracted URLs proxied in Caddy so that they work in all countries
- Errors displayed as a video
### Planned

BIN
bg.png Normal file

Binary file not shown.

After

(image error) Size: 257 B

BIN
requirements.txt Normal file

Binary file not shown.

@ -1,4 +1,6 @@
from yt_dlp import YoutubeDL
from importlib.metadata import version
print("yt-dlp version", version("yt_dlp"))
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import unquote, urlparse, parse_qs
from threading import Event
@ -6,16 +8,15 @@ from datetime import datetime, timedelta
from os import environ
import logging
import re
from textvid import generate_video_from_text
def getBestMp4UrlFromInfo(info):
def best_mp4(info):
formats = info['entries'][0].get('formats') if info.get("entries") else info.get("formats")
if not formats: return None
valid = list(filter(lambda x: x['ext'] == "mp4" and x['vcodec'] != 'none' and x['acodec'] != 'none', formats))
if not valid: return None
best = max(valid, key=lambda x: x['height'])
return best.get('url')
return best['url']
def getExpireDatetime(url):
def get_expire(url):
alt_expire = datetime.now() + timedelta(hours=5)
if not url: return alt_expire
q = parse_qs(urlparse(url).query)
@ -24,7 +25,7 @@ def getExpireDatetime(url):
expire = datetime.fromtimestamp(int(expire[0])) #this seems to always be +6 hours
return min([expire, alt_expire])
query_dicts = {}
ctx_cache = {}
ips_running_ytdl = []
class Handler(BaseHTTPRequestHandler):
@ -49,37 +50,50 @@ class Handler(BaseHTTPRequestHandler):
match = re.match("\/(?:id\/|(?:https?:\/\/)?(?:(?:www\.)?youtube\.com\/(?:watch\?v=|shorts\/)|youtu\.be\/))([A-Za-z0-9_-]{11})", path)
query = match[1] if match else "ytsearch:" + path[1:]
dict = query_dicts.get(query)
ctx = ctx_cache.get(query)
if not dict or 'expire' in dict and datetime.now() >= dict['expire']:
if not ctx or 'expire' in ctx and datetime.now() >= ctx['expire']:
client_ip = self.address_string()
if client_ip in ips_running_ytdl:
self.send_error(429)
return
try:
ips_running_ytdl.append(client_ip)
query_dicts[query] = dict = {'event': Event()}
ctx_cache[query] = ctx = {'event': Event()}
with YoutubeDL() as ydl:
info = ydl.extract_info(query, download=False)
url = getBestMp4UrlFromInfo(info)
dict['url'] = url
dict['expire'] = getExpireDatetime(url)
try:
url = best_mp4(info)
except Exception as e:
logging.exception(e)
#todo these videos are rare but need one to test
raise Exception("ERROR: Couldn't find a suitable format for this video.")
ctx['url'] = url
ctx['expire'] = get_expire(url)
except Exception as e:
logging.exception(e)
dict['exception'] = e
#logging.exception(e)
ctx['exception'] = e
ctx['error_vid'] = generate_video_from_text(re.sub("(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]", '', str(e)))
finally:
ips_running_ytdl.remove(client_ip)
dict['event'].set()
elif 'url' not in dict:
dict['event'].wait(60)
ctx['event'].set()
elif 'url' not in ctx:
ctx['event'].wait(60)
if not dict.get('url'):
if 'exception' in dict:
self.send_error(500, message=str(dict['exception']))
if not ctx.get('url'):
if 'exception' in ctx:
if 'error_vid' in ctx:
self.send_response(200)
self.send_header("Content-Type", "video/mp4")
self.send_header("Content-Length", str(len(ctx['error_vid'])))
self.end_headers()
self.wfile.write(ctx['error_vid'])
else:
self.send_error(500, message=str(ctx['exception']))
else:
self.send_error(404)
else:
url = dict['url']
url = ctx['url']
if 'PROXY' in environ:
url = environ['PROXY'] + url.replace("https://",'')
self.send_response(302)

44
textvid.py Normal file

@ -0,0 +1,44 @@
from ffmpeg import FFmpeg
import textwrap
from tempfile import mktemp
from os import remove
def generate_video_from_text(text, test=False) -> bytes:
""" generate a single-frame ten-second mp4 displaying the text """
text = text.replace("\\", "\\\\").replace('"', '""').replace("'", "''").replace("%", "\\%").replace(":", "\\:")
text = textwrap.fill(text, 90)
file = mktemp()
peg = FFmpeg().option("y").input("bg.png", {
"framerate": "0.1"
}).output(file, {
"f": "mp4",
"t": "10",
"c:v": "libx264",
"pix_fmt": "yuv420p",
"vf": "drawtext=font=monospace:fontsize=24:x=10:y=10:text='"+text+"':"
})
if test:
@peg.on("start")
def on_start(arguments): print("cmd:", ' '.join(arguments))
@peg.on("stderr")
def on_stderr(line): print("stderr:", line)
peg.execute()
fp = open(file, "rb")
data = fp.read()
fp.close()
remove(file)
return data
if __name__ == "__main__":
print("test")
v = generate_video_from_text("""Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut sem viverra aliquet eget sit amet. Senectus et netus et malesuada fames ac. Gravida quis blandit turpis cursus in hac habitasse platea. Sed ullamcorper morbi tincidunt ornare massa eget egestas purus. Tristique risus nec feugiat in. Malesuada bibendum arcu vitae elementum curabitur vitae nunc sed velit. Porta lorem mollis aliquam ut porttitor leo a. Tellus rutrum tellus pellentesque eu tincidunt. Enim diam vulputate ut pharetra sit amet. Platea dictumst vestibulum rhoncus est. Sed sed risus pretium quam vulputate dignissim suspendisse. Viverra maecenas accumsan lacus vel facilisis volutpat est velit egestas. Lorem ipsum dolor sit amet consectetur. Netus et malesuada fames ac turpis egestas integer eget. Tellus elementum sagittis vitae et leo duis ut. Ipsum a arcu cursus vitae. Amet aliquam id diam maecenas ultricies mi. Mattis vulputate enim nulla aliquet porttitor lacus luctus accumsan. Magna ac placerat vestibulum lectus mauris ultrices eros in.
Dui sapien eget mi proin sed libero enim sed faucibus. Hac habitasse platea dictumst quisque sagittis purus sit. Mi eget mauris pharetra et ultrices neque ornare. Sagittis aliquam malesuada bibendum arcu vitae elementum curabitur vitae. Eget arcu dictum varius duis. Purus in massa tempor nec feugiat nisl pretium. Ipsum nunc aliquet bibendum enim facilisis gravida neque convallis. Adipiscing diam donec adipiscing tristique risus. Pulvinar neque laoreet suspendisse interdum consectetur libero id faucibus. Non quam lacus suspendisse faucibus.
Sed libero enim sed faucibus. Ut etiam sit amet nisl purus in mollis nunc sed. Cursus eget nunc scelerisque viverra mauris in aliquam sem fringilla. Eget aliquet nibh praesent tristique magna sit amet purus. Dui accumsan sit amet nulla facilisi morbi tempus. Lacus laoreet non curabitur gravida. Mi eget mauris pharetra et ultrices neque. Volutpat est velit egestas dui id ornare arcu odio. Porttitor lacus luctus accumsan tortor posuere ac. Morbi quis commodo odio aenean. Accumsan in nisl nisi scelerisque eu. Tincidunt dui ut ornare lectus sit amet est placerat in. Libero enim sed faucibus turpis in eu mi bibendum neque. At lectus urna duis convallis convallis.
Vel risus commodo viverra maecenas accumsan lacus. Mauris pharetra et ultrices neque ornare aenean euismod elementum. Non enim praesent elementum facilisis leo. Amet massa vitae tortor condimentum lacinia. Ornare aenean euismod elementum nisi quis eleifend. Diam donec adipiscing tristique risus nec. Volutpat diam ut venenatis tellus. Mauris nunc congue nisi vitae. Sit amet nisl suscipit adipiscing bibendum est ultricies integer quis. Turpis massa tincidunt dui ut ornare lectus sit amet. Libero enim sed faucibus turpis in. Sit amet porttitor eget dolor morbi non arcu risus quis. Sem integer vitae justo eget magna fermentum iaculis eu. Mattis molestie a iaculis at. Amet volutpat consequat mauris nunc congue. Et tortor at risus viverra adipiscing at in tellus integer. Amet mattis vulputate enim nulla.
Dignissim convallis aenean et tortor. Vitae congue eu consequat ac felis donec et odio. Risus at ultrices mi tempus imperdiet. Amet massa vitae tortor condimentum lacinia quis. Consectetur adipiscing elit ut aliquam purus. Integer quis auctor elit sed vulputate mi sit amet. Tellus id interdum velit laoreet. Sed risus ultricies tristique nulla aliquet. Fermentum dui faucibus in ornare quam. Lobortis elementum nibh tellus molestie nunc non blandit. Amet dictum sit amet justo donec. Iaculis urna id volutpat lacus.""", True)
with open("test.mp4", "wb") as f:
f.write(v)