Files
DepressedCat 5f2aad00f4
Check extension and helper / check (push) Canceled after 0s
Publish YouTubeDL 1.4.5 and website on Gitea
2026-10-10 23:53:17 +03:00

61 lines
4.0 KiB
Python

"""Validate YouTube-only cookies and attach them to an in-memory yt-dlp jar."""
from __future__ import annotations
import math
import re
from http.cookiejar import Cookie
DOMAIN = re.compile(r"^(?:[a-z0-9-]+\.)*youtube\.com$", re.I)
NAME = re.compile(r"^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,256}$")
def validate_cookies(value):
if value is None:
return []
if not isinstance(value, list) or len(value) > 300:
raise ValueError("Некорректные данные входа YouTube.")
result, size = [], 0
for entry in value:
if not isinstance(entry, dict):
raise ValueError("Некорректные данные входа YouTube.")
name, content, domain, path = (entry.get(key) for key in ("name", "value", "domain", "path"))
if not isinstance(domain, str) or len(domain) > 253 or not DOMAIN.fullmatch(domain.lstrip(".")) or domain.startswith(".."):
raise ValueError("Данные входа разрешены только для YouTube.")
if (not isinstance(name, str) or not NAME.fullmatch(name) or not isinstance(content, str) or len(content) > 16384
or any(char in content for char in "\r\n\x00") or not isinstance(path, str) or not path.startswith("/")
or len(path) > 2048 or any(char in path for char in "\r\n\x00")):
raise ValueError("Некорректные данные входа YouTube.")
expires = entry.get("expirationDate")
if expires is not None and (isinstance(expires, bool) or not isinstance(expires, (int, float)) or not math.isfinite(expires) or not 0 <= expires < 2**63):
raise ValueError("Некорректный срок действия входа YouTube.")
size += len((name + content + domain + path).encode("utf-8"))
if size > 512 * 1024:
raise ValueError("Превышен размер данных входа YouTube.")
result.append({"name": name, "value": content, "domain": domain.lower(), "path": path,
"secure": bool(entry.get("secure", True)), "httpOnly": bool(entry.get("httpOnly")),
"hostOnly": bool(entry.get("hostOnly", not domain.startswith("."))), "expirationDate": expires})
return result
def apply_cookies(ydl, cookies):
for entry in validate_cookies(cookies):
expires = entry["expirationDate"]
ydl.cookiejar.set_cookie(Cookie(
version=0, name=entry["name"], value=entry["value"], port=None, port_specified=False,
domain=entry["domain"], domain_specified=not entry["hostOnly"], domain_initial_dot=entry["domain"].startswith("."),
path=entry["path"], path_specified=True, secure=entry["secure"],
expires=int(expires) if expires is not None else None, discard=expires is None,
comment=None, comment_url=None, rest={"HttpOnly": None} if entry["httpOnly"] else {}, rfc2109=False))
def is_access_error(error):
return bool(re.search(r"sign in to confirm|not a bot|cookies.*authentication|login required|log in to|HTTP Error (?:403|410)", str(error), re.I))
def public_error(error, authenticated=False):
message = str(error)
if re.search(r"sign in to confirm|not a bot|cookies.*authentication|login required|log in to", message, re.I):
if authenticated:
return "YouTube отклонил вход браузера или просит проверку. Откройте этот ролик на YouTube, пройдите проверку, затем нажмите «Проверить вход» и повторите загрузку."
return "YouTube отклонил запрос загрузчика и просит проверку на бота. Повторите запрос или обновите yt-dlp в компонентах. Дополнительно можно подключить вход кнопкой «Использовать вход в YouTube». Помощник работает; переустановка не нужна."
return message[:2000]