From a16268b2e9dff80ccb4ee398bda493e040554a84 Mon Sep 17 00:00:00 2001 From: girst Date: Wed, 15 Dec 2021 20:29:47 +0100 Subject: [PATCH] implement /watch?show=meta to get metadata removed from ANDROID api --- app/youtube/__init__.py | 10 ++++++-- app/youtube/lib.py | 56 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/app/youtube/__init__.py b/app/youtube/__init__.py index 62dd1e1..e2ef6b0 100644 --- a/app/youtube/__init__.py +++ b/app/youtube/__init__.py @@ -78,7 +78,8 @@ def watch(): } video_id = request.args.get('v') - video_url, stream_map, metadata, error, errdetails = get_video_info(video_id) + show = request.args.get("show") + video_url, stream_map, metadata, error, errdetails = get_video_info(video_id, metaOnly=(show == 'meta')) proxy_on = getattr(g, 'proxy_on', None) proxy_msg = { @@ -130,7 +131,6 @@ def watch(): Please try again in 30 seconds. """) - show = request.args.get("show") if show == "raw": if error: msg = errdetails if error=='player' else f"{error.upper()}: {errdetails}" @@ -154,6 +154,12 @@ def watch(): reverse=True )),{}).get('url') return redirect(stream) + elif show == "meta": + # this is the subset of (useful) keys that are not present in the + # Android API response. the special key '_' contains ready-to-use + # parsed versions of that data. + parsed = microformat_parser(metadata) + return {'microformat': metadata.get('microformat'),'cards':metadata.get('cards'), '_':parsed} else: if error and not metadata: # e.g. malformed, private/deleted video, ... return render_template('video-error.html.j2', video_id=video_id, diff --git a/app/youtube/lib.py b/app/youtube/lib.py index afcf8a2..0c83d91 100644 --- a/app/youtube/lib.py +++ b/app/youtube/lib.py @@ -62,3 +62,59 @@ def channel_exists(feed_id): feed_type: feed_id, }) return r.ok + +def microformat_parser(metadata): + """ parses additional metadata only available with get_video_info(metaOnly=True) """ + meta2 = metadata.get('microformat',{}).get('playerMicroformatRenderer',{}) + all_countries = """AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD + BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH + CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER + ES ET FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT + GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE + KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD + ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF + NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA + RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX + SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ + VA VC VE VG VI VN VU WF WS YE YT ZA ZM ZW""".split() + whitelisted = sorted(meta2.get('availableCountries',[])) + blacklisted = sorted(set(all_countries) - set(whitelisted)) + regions = ( + 'all' if not blacklisted else + 'none' if not whitelisted else + f"not in {' '.join(blacklisted)}" if len(blacklisted) < len(whitelisted) else + f"only in {' '.join(whitelisted)}" + ) + try: + poster = sorted(meta2['thumbnail']['thumbnails'], key=lambda t: t['width'], reverse=True)[0]['url'] + except: poster = None + infocards = prepare_infocards(metadata) + endcards = prepare_endcards(metadata) + # combine cards to weed out duplicates. for videos and playlists prefer + # infocards, for channels and websites prefer endcards, as those have more + # information than the other. + # if the card type is not in ident, we use the whole card for comparison + # (otherwise they'd all replace each other) + ident = { # ctype -> ident + 'VIDEO': 'video_id', + 'PLAYLIST': 'playlist_id', + 'CHANNEL': 'channel_id', + 'WEBSITE': 'url', + 'POLL': 'question', + } + getident = lambda c: c['content'].get(ident.get(c['type']), c) + mkexclude = lambda cards, types: [getident(c) for c in cards if c['type'] in types] + exclude = lambda cards, without: [c for c in cards if getident(c) not in without] + + allcards = exclude(infocards, mkexclude(endcards, ['CHANNEL','WEBSITE'])) + \ + exclude(endcards, mkexclude(infocards, ['VIDEO','PLAYLIST'])) + + return { + 'published': meta2.get('publishDate'), + #'uploaded': meta2.get('uploadDate'), + #'infocards': infocards, + #'endcards': endcards, + 'all_cards': allcards, + 'poster': poster, + 'regions': regions, + } -- 2.39.3