Browser-back / video.py
Nrighton233j
Add yt-dlp video extraction, page scraper, and speed optimizations (session reuse, parallel related-links search, caching)
080713a
Raw
History Blame Contribute Delete
2.84 kB
"""
video.py — yt-dlp integration for B24 Browser.
Two modes, matching what the user picks in the app:
- "play": return a direct stream URL for the video player to use
- "download": stream the video bytes through this backend to the client
Note: merging separate video+audio streams (needed for >720p on most
sites) requires ffmpeg, which must be present in the container.
Pre-muxed formats (usually up to 720p) work without ffmpeg.
"""
import subprocess
import yt_dlp
YDL_BASE_OPTS = {
"quiet": True,
"no_warnings": True,
"skip_download": True,
}
def get_video_info(url: str):
"""
Returns { title, thumbnail, duration, formats: [...] } or {"error": ...}
Each format: { format_id, resolution, ext, filesize, needs_merge, note }
"""
try:
with yt_dlp.YoutubeDL(YDL_BASE_OPTS) as ydl:
info = ydl.extract_info(url, download=False)
except yt_dlp.utils.DownloadError as e:
return {"error": f"could not extract video info: {e}"}
raw_formats = info.get("formats", [])
formats = []
seen_resolutions = set()
for f in raw_formats:
height = f.get("height")
vcodec = f.get("vcodec", "none")
acodec = f.get("acodec", "none")
if vcodec == "none":
continue
resolution = f"{height}p" if height else f.get("format_note", "unknown")
if resolution in seen_resolutions:
continue
seen_resolutions.add(resolution)
formats.append({
"format_id": f.get("format_id"),
"resolution": resolution,
"ext": f.get("ext"),
"filesize": f.get("filesize") or f.get("filesize_approx"),
"needs_merge": acodec == "none",
"url": f.get("url"),
})
formats.sort(key=lambda x: (x["resolution"] != "unknown", x["resolution"]), reverse=True)
return {
"title": info.get("title"),
"thumbnail": info.get("thumbnail"),
"duration": info.get("duration"),
"formats": formats[:8],
}
def stream_download(url: str, format_id: str = None):
"""
Yields raw video bytes for streaming to the client, via yt-dlp CLI
piping to stdout. Handles merging (ffmpeg) automatically if the
chosen format needs it. Falls back to best pre-muxed format.
"""
fmt = format_id if format_id else "best[ext=mp4]/best"
cmd = [
"yt-dlp",
"-f", fmt,
"--merge-output-format", "mkv",
"-o", "-",
"--quiet",
"--no-warnings",
url,
]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
try:
while True:
chunk = process.stdout.read(65536)
if not chunk:
break
yield chunk
finally:
process.stdout.close()
process.wait()