import re import requests from datetime import datetime, timezone from ..common.innertube import G def fetch_ajax(endpoint, **kwargs): """ fetch data using a continuation protobuf """ # TODO: handle auto_generated! today = datetime.now(timezone.utc).strftime("%Y%m%d") # TODO: this is not cached any more! -> https://github.com/reclosedev/requests-cache/issues/154 # TODO: replace host with youtubei.googleapis.com (used by android)? r = requests.post(f"https://www.youtube.com/youtubei/v1/{endpoint}?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", json={ **kwargs, 'context': {'client': { 'gl': 'US', 'hl': 'en', 'clientName': 'WEB', 'clientVersion': f'2.{today}.01.01', }}, }) if not r.ok: return None return r.json() def canonicalize_channel(path): if re.fullmatch(r"(UC[A-Za-z0-9_-]{22})", path): return path # Note: for /watch, append query string, then return .endpoint.watchEndpoint.videoId resolved = fetch_ajax("navigation/resolve_url", url=f"https://www.youtube.com/{path}") channel_id = resolved.get('endpoint',{}).get('browseEndpoint',{}).get('browseId') return channel_id def find_and_parse_error(result): error_obj = ( result|G('responseContext')|G('errors')|G('error')|G(0) or result|G('alerts')|G(0)|G('alertRenderer') or result|G('error') ) if error_obj is None: return None error_type = error_obj|G('code', 'type', 'status') or 'Error' error = ( error_obj|G('debugInfo', 'externalErrorMessage') or error_obj|G('text')|G.text or error_obj|G('message') or "unknown error" ) return f"{error_type}: {error.rstrip('.')}"