aboutsummaryrefslogtreecommitdiffstats
path: root/youtube/comments.py
diff options
context:
space:
mode:
Diffstat (limited to 'youtube/comments.py')
-rw-r--r--youtube/comments.py129
1 files changed, 67 insertions, 62 deletions
diff --git a/youtube/comments.py b/youtube/comments.py
index d47c826..c4e30dd 100644
--- a/youtube/comments.py
+++ b/youtube/comments.py
@@ -8,9 +8,6 @@ import settings
import json
import base64
-import urllib
-import re
-import traceback
import flask
from flask import request
@@ -34,7 +31,7 @@ def make_comment_ctoken(video_id, sort=0, offset=0, lc='', secret_key=''):
secret_key = proto.as_bytes(secret_key)
- page_info = proto.string(4,video_id) + proto.uint(6, sort)
+ page_info = proto.string(4, video_id) + proto.uint(6, sort)
offset_information = proto.nested(4, page_info) + proto.uint(5, offset)
if secret_key:
offset_information = proto.string(1, secret_key) + offset_information
@@ -47,25 +44,23 @@ def make_comment_ctoken(video_id, sort=0, offset=0, lc='', secret_key=''):
return base64.urlsafe_b64encode(result).decode('ascii')
-mobile_headers = {
- 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1',
- 'Accept': '*/*',
- 'Accept-Language': 'en-US,en;q=0.5',
- 'X-YouTube-Client-Name': '2',
- 'X-YouTube-Client-Version': '2.20180823',
-}
-
-
def request_comments(ctoken, replies=False):
- base_url = 'https://m.youtube.com/watch_comment?'
- if replies:
- base_url += 'action_get_comment_replies=1&ctoken='
- else:
- base_url += 'action_get_comments=1&ctoken='
- url = base_url + ctoken.replace("=", "%3D") + "&pbj=1"
+ url = 'https://m.youtube.com/youtubei/v1/next'
+ url += '?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8'
+ data = json.dumps({
+ 'context': {
+ 'client': {
+ 'hl': 'en',
+ 'gl': 'US',
+ 'clientName': 'MWEB',
+ 'clientVersion': '2.20210804.02.00',
+ },
+ },
+ 'continuation': ctoken.replace('=', '%3D'),
+ })
content = util.fetch_url(
- url, headers=mobile_headers,
+ url, headers=util.mobile_xhr_headers + util.json_header, data=data,
report_text='Retrieved comments', debug_name='request_comments')
content = content.decode('utf-8')
@@ -83,7 +78,7 @@ def single_comment_ctoken(video_id, comment_id):
def post_process_comments_info(comments_info):
for comment in comments_info['comments']:
- comment['author'] = strip_non_ascii(comment['author'])
+ comment['author'] = strip_non_ascii(comment['author']) if comment.get('author') else ""
comment['author_url'] = concat_or_none(
'/', comment['author_url'])
comment['author_avatar'] = concat_or_none(
@@ -102,26 +97,26 @@ def post_process_comments_info(comments_info):
ctoken = comment['reply_ctoken']
ctoken, err = proto.set_protobuf_value(
ctoken,
- 'base64p', 6, 3, 9, value=250)
+ 'base64p', 6, 3, 9, value=200)
if err:
print('Error setting ctoken value:')
print(err)
comment['replies_url'] = None
comment['replies_url'] = concat_or_none(
util.URL_ORIGIN,
- '/comments?replies=1&ctoken=' + ctoken)
+ f'/comments?replies=1&ctoken={ctoken}')
if reply_count == 0:
comment['view_replies_text'] = 'Reply'
elif reply_count == 1:
comment['view_replies_text'] = '1 reply'
else:
- comment['view_replies_text'] = str(reply_count) + ' replies'
+ comment['view_replies_text'] = f'{reply_count} replies'
- if comment['like_count'] == 1:
+ if comment['approx_like_count'] == '1':
comment['likes_text'] = '1 like'
else:
- comment['likes_text'] = str(comment['like_count']) + ' likes'
+ comment['likes_text'] = f"{comment['approx_like_count']} likes"
comments_info['include_avatars'] = settings.enable_comment_avatars
if comments_info['ctoken']:
@@ -131,7 +126,7 @@ def post_process_comments_info(comments_info):
# change max_replies field to 250 in ctoken
new_ctoken, err = proto.set_protobuf_value(
ctoken,
- 'base64p', 6, 3, 9, value=250)
+ 'base64p', 6, 3, 9, value=200)
if err:
print('Error setting ctoken value:')
print(err)
@@ -142,7 +137,10 @@ def post_process_comments_info(comments_info):
comments_info['more_comments_url'] = concat_or_none(
util.URL_ORIGIN, '/comments?ctoken=', ctoken, replies_param)
- comments_info['page_number'] = page_number = str(int(comments_info['offset']/20) + 1)
+ if comments_info['offset'] is None:
+ comments_info['page_number'] = None
+ else:
+ comments_info['page_number'] = int(comments_info['offset']/20) + 1
if not comments_info['is_replies']:
comments_info['sort_text'] = 'top' if comments_info['sort'] == 0 else 'newest'
@@ -151,54 +149,53 @@ def post_process_comments_info(comments_info):
util.URL_ORIGIN, '/watch?v=', comments_info['video_id'])
comments_info['video_thumbnail'] = concat_or_none(
settings.img_prefix, 'https://i.ytimg.com/vi/',
- comments_info['video_id'], '/mqdefault.jpg'
+ comments_info['video_id'], '/hqdefault.jpg'
)
def video_comments(video_id, sort=0, offset=0, lc='', secret_key=''):
+ if not settings.comments_mode:
+ return {}
+
+ # Initialize the result dict up-front so that any exception path below
+ # can safely attach an 'error' field without risking UnboundLocalError.
+ comments_info = {'error': None}
try:
- if settings.comments_mode:
- comments_info = {'error': None}
- other_sort_url = (
- util.URL_ORIGIN + '/comments?ctoken='
- + make_comment_ctoken(video_id, sort=1 - sort, lc=lc)
- )
- other_sort_text = 'Sort by ' + ('newest' if sort == 0 else 'top')
+ other_sort_url = (
+ f"{util.URL_ORIGIN}/comments?ctoken="
+ f"{make_comment_ctoken(video_id, sort=1 - sort, lc=lc)}"
+ )
+ other_sort_text = f'Sort by {"newest" if sort == 0 else "top"}'
- this_sort_url = (util.URL_ORIGIN
- + '/comments?ctoken='
- + make_comment_ctoken(video_id, sort=sort, lc=lc))
+ this_sort_url = (f"{util.URL_ORIGIN}/comments?ctoken="
+ f"{make_comment_ctoken(video_id, sort=sort, lc=lc)}")
- comments_info['comment_links'] = [
- (other_sort_text, other_sort_url),
- ('Direct link', this_sort_url)
- ]
+ comments_info['comment_links'] = [
+ (other_sort_text, other_sort_url),
+ ('Direct link', this_sort_url)
+ ]
- comments_info.update(yt_data_extract.extract_comments_info(
- request_comments(
- make_comment_ctoken(video_id, sort, offset, lc, secret_key)
- )
- ))
- post_process_comments_info(comments_info)
+ ctoken = make_comment_ctoken(video_id, sort, offset, lc)
+ comments_info.update(yt_data_extract.extract_comments_info(
+ request_comments(ctoken), ctoken=ctoken
+ ))
+ post_process_comments_info(comments_info)
- return comments_info
- else:
- return {}
+ return comments_info
except util.FetchError as e:
if e.code == '429' and settings.route_tor:
- comments_info['error'] = 'Error: Youtube blocked the request because the Tor exit node is overutilized.'
+ comments_info['error'] = 'Error: YouTube blocked the request because the Tor exit node is overutilized.'
if e.error_message:
- comments_info['error'] += '\n\n' + e.error_message
- comments_info['error'] += '\n\nExit node IP address: %s' % e.ip
+ comments_info['error'] += f'\n\n{e.error_message}'
+ comments_info['error'] += f'\n\nExit node IP address: {e.ip}'
else:
- comments_info['error'] = traceback.format_exc()
+ comments_info['error'] = f'YouTube blocked the request. Error: {e}'
except Exception as e:
- comments_info['error'] = traceback.format_exc()
+ comments_info['error'] = f'YouTube blocked the request. Error: {e}'
if comments_info.get('error'):
- print('Error retrieving comments for ' + str(video_id) + ':\n' +
- comments_info['error'])
+ print(f'Error retrieving comments for {video_id}:\n{comments_info["error"]}')
return comments_info
@@ -208,12 +205,20 @@ def get_comments_page():
ctoken = request.args.get('ctoken', '')
replies = request.args.get('replies', '0') == '1'
- comments_info = yt_data_extract.extract_comments_info(request_comments(ctoken, replies))
+ comments_info = yt_data_extract.extract_comments_info(
+ request_comments(ctoken, replies), ctoken=ctoken
+ )
post_process_comments_info(comments_info)
if not replies:
- other_sort_url = util.URL_ORIGIN + '/comments?ctoken=' + make_comment_ctoken(comments_info['video_id'], sort=1 - comments_info['sort'])
- other_sort_text = 'Sort by ' + ('newest' if comments_info['sort'] == 0 else 'top')
+ if comments_info['sort'] is None or comments_info['video_id'] is None:
+ other_sort_url = None
+ else:
+ other_sort_url = (
+ f'{util.URL_ORIGIN}/comments?ctoken='
+ f'{make_comment_ctoken(comments_info["video_id"], sort=1-comments_info["sort"])}'
+ )
+ other_sort_text = f'Sort by {"newest" if comments_info["sort"] == 0 else "top"}'
comments_info['comment_links'] = [(other_sort_text, other_sort_url)]
return flask.render_template(