"""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]