From 5f3b90ad45993f99bf2813ef2fefaa3a59a2694f Mon Sep 17 00:00:00 2001 From: Astound Date: Mon, 22 Jan 2024 06:29:42 +0800 Subject: Fix channel about tab --- youtube/channel.py | 52 ++++++++++++++-- youtube/proto.py | 11 ++++ youtube/templates/channel.html | 11 +++- youtube/util.py | 79 +++++++++++++++++++++++++ youtube/watch.py | 66 +-------------------- youtube/yt_data_extract/common.py | 7 ++- youtube/yt_data_extract/everything_else.py | 95 ++++++++++++++++++++++++------ 7 files changed, 231 insertions(+), 90 deletions(-) diff --git a/youtube/channel.py b/youtube/channel.py index 75b0a15..e177c38 100644 --- a/youtube/channel.py +++ b/youtube/channel.py @@ -84,6 +84,40 @@ def channel_ctoken_v5(channel_id, page, sort, tab, view=1): return base64.urlsafe_b64encode(pointless_nest).decode('ascii') + +def channel_about_ctoken(channel_id): + return proto.make_protobuf( + ('base64p', + [ + [2, 80226972, + [ + [2, 2, channel_id], + [2, 3, + ('base64p', + [ + [2, 110, + [ + [2, 3, + [ + [2, 19, + [ + [2, 1, b'66b0e9e9-0000-2820-9589-582429a83980'], + ] + ], + ] + ], + ] + ], + ] + ) + ], + ] + ], + ] + ) + ) + + # https://github.com/user234683/youtube-local/issues/151 def channel_ctoken_v4(channel_id, page, sort, tab, view=1): new_sort = (2 if int(sort) == 1 else 1) @@ -359,7 +393,7 @@ def post_process_channel_info(info): util.add_extra_html_info(item) if info['current_tab'] == 'about': for i, (text, url) in enumerate(info['links']): - if util.YOUTUBE_URL_RE.fullmatch(url): + if isinstance(url, str) and util.YOUTUBE_URL_RE.fullmatch(url): info['links'][i] = (text, util.prefix_url(url)) @@ -469,7 +503,13 @@ def get_channel_page_general_url(base_url, tab, request, channel_id=None): number_of_videos, polymer_json = tasks[0].value, tasks[1].value elif tab == 'about': - polymer_json = util.fetch_url(base_url + '/about?pbj=1', headers_desktop, debug_name='gen_channel_about') + # polymer_json = util.fetch_url(base_url + '/about?pbj=1', headers_desktop, debug_name='gen_channel_about') + channel_id = get_channel_id(base_url) + ctoken = channel_about_ctoken(channel_id) + polymer_json = util.call_youtube_api('web', 'browse', { + 'continuation': ctoken, + }) + continuation=True elif tab == 'playlists' and page_number == 1: polymer_json = util.fetch_url(base_url+ '/playlists?pbj=1&view=1&sort=' + playlist_sort_codes[sort], headers_desktop, debug_name='gen_channel_playlists') elif tab == 'playlists': @@ -491,6 +531,9 @@ def get_channel_page_general_url(base_url, tab, request, channel_id=None): json.loads(polymer_json), tab, continuation=continuation ) + if info['error'] is not None: + return flask.render_template('error.html', error_message=info['error']) + if channel_id: info['channel_url'] = 'https://www.youtube.com/channel/' + channel_id info['channel_id'] = channel_id @@ -498,7 +541,7 @@ def get_channel_page_general_url(base_url, tab, request, channel_id=None): channel_id = info['channel_id'] # Will have microformat present, cache metadata while we have it - if channel_id and default_params and tab != 'videos': + if channel_id and default_params and tab not in ('videos', 'about'): metadata = extract_metadata_for_caching(info) set_cached_metadata(channel_id, metadata) # Otherwise, populate with our (hopefully cached) metadata @@ -515,9 +558,6 @@ def get_channel_page_general_url(base_url, tab, request, channel_id=None): for item in info['items']: item.update(additional_info) - if info['error'] is not None: - return flask.render_template('error.html', error_message = info['error']) - if tab in ('videos', 'shorts', 'streams'): info['number_of_videos'] = number_of_videos info['number_of_pages'] = math.ceil(number_of_videos/page_size) diff --git a/youtube/proto.py b/youtube/proto.py index d8b1fcd..924e983 100644 --- a/youtube/proto.py +++ b/youtube/proto.py @@ -141,6 +141,17 @@ base64_enc_funcs = { def _make_protobuf(data): + ''' + Input: Recursive list of protobuf objects or base-64 encodings + Output: Protobuf bytestring + Each protobuf object takes the form [wire_type, field_number, field_data] + If a string protobuf has a list/tuple of length 2, this has the form + (base64 type, data) + The base64 types are + - base64 means a base64 encode with equals sign paddings + - base64s means a base64 encode without padding + - base64p means a url base64 encode with equals signs replaced with %3D + ''' # must be dict mapping field_number to [wire_type, value] if isinstance(data, dict): new_data = [] diff --git a/youtube/templates/channel.html b/youtube/templates/channel.html index 8d2249c..c43f488 100644 --- a/youtube/templates/channel.html +++ b/youtube/templates/channel.html @@ -51,8 +51,11 @@