]> git.gir.st - subscriptionfeed.git/blob - app/browse/lib.py
try /c/ before /user/ when canonicalizing user names
[subscriptionfeed.git] / app / browse / lib.py
1 import re
2 import requests
3 from datetime import datetime, timezone
4
5 from ..common.common import fetch_xml, parse_xml
6 from ..common.innertube import G
7
8 def fetch_ajax(endpoint, **kwargs):
9 """
10 fetch data using a continuation protobuf
11 """
12 # TODO: handle auto_generated!
13 today = datetime.now(timezone.utc).strftime("%Y%m%d")
14
15 # TODO: this is not cached any more! -> https://github.com/reclosedev/requests-cache/issues/154
16 # Note: this 'innertube' API key exists since at least 2015: https://stackoverflow.com/q/33511165
17 r = requests.post(f"https://www.youtube.com/youtubei/v1/{endpoint}?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", json={
18 **kwargs,
19 'context': {'client': {
20 'gl': 'US',
21 'hl': 'en',
22 'clientName': 'WEB',
23 'clientVersion': f'2.{today}.01.01',
24 }},
25 })
26
27 if not r.ok:
28 return None
29
30 return r.json()
31
32 def canonicalize_channel(name):
33 if re.fullmatch(r"(UC[A-Za-z0-9_-]{22})", name):
34 return name
35
36 # get UCID of /c/ (vanity URLs):
37 today = datetime.now(timezone.utc).strftime("%Y%m%d")
38 r = requests.get(f'https://www.youtube.com/c/{name}/about?pbj=1&hl=en_US', headers={
39 'x-youtube-client-name': '1',
40 'x-youtube-client-version': f'2.{today}.01.01', # see fetch_searchresults()
41 })
42 try:
43 return r.json()[1]['response']['metadata']['channelMetadataRenderer']['rssUrl'].split("=")[1]
44 except:
45 pass
46
47 # try /user/ (legacy URLs):
48 xmlfeed = fetch_xml("user", name)
49 if xmlfeed:
50 _, _, _, channel_id, _ = parse_xml(xmlfeed)
51 return channel_id
52
53 # unable to extract:
54 return None
55
56 def find_and_parse_error(result):
57 error_obj = (
58 result|G('responseContext')|G('errors')|G('error')|G(0)
59 or result|G('alerts')|G(0)|G('alertRenderer')
60 or result|G('error')
61 )
62 if error_obj is None:
63 return None
64
65 error_type = error_obj|G('code', 'type', 'status') or 'Error'
66 error = (
67 error_obj|G('debugInfo', 'externalErrorMessage')
68 or error_obj|G('text')|G.text
69 or error_obj|G('message')
70 or "unknown error"
71 )
72 return f"{error_type}: {error.rstrip('.')}"
Imprint / Impressum