From e3fac7da54356c448bd244b578da244b62f44e9e Mon Sep 17 00:00:00 2001 From: James Taylor Date: Mon, 31 Dec 2018 02:52:27 -0800 Subject: Ability to delete comments --- youtube/comments.css | 7 +++++-- youtube/comments.py | 27 ++++++++++++++++++++++++--- youtube/post_comment.py | 41 +++++++++++++++++++++++++++++++++++++++-- youtube/youtube.py | 20 ++++++++++++++++++++ 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/youtube/comments.css b/youtube/comments.css index 5fd9063..baa8ac7 100644 --- a/youtube/comments.css +++ b/youtube/comments.css @@ -113,10 +113,13 @@ white-space: nowrap; } -.comment .replies{ - grid-column:2 / span 2; +.comment .bottom-row{ + grid-column:2 / span 3; grid-row:4; justify-self:start; + display: grid; + grid-auto-flow: column; + grid-column-gap: 10px; } .more-comments{ diff --git a/youtube/comments.py b/youtube/comments.py index 58a03ac..1d96545 100644 --- a/youtube/comments.py +++ b/youtube/comments.py @@ -26,10 +26,14 @@ $avatar
$author
- $text + $text + $likes +
$replies +$action_buttons +
@@ -162,6 +166,8 @@ def parse_comments_ajax(content, replies=False): comment = { 'author': comment_raw['author']['runs'][0]['text'], 'author_url': comment_raw['author_endpoint']['url'], + 'author_channel_id': '', + 'author_id': '', 'author_avatar': comment_raw['author_thumbnail']['url'], 'likes': comment_raw['like_count'], 'published': comment_raw['published_time']['runs'][0]['text'], @@ -207,6 +213,7 @@ def parse_comments_polymer(content, replies=False): video_title = comment_raw['commentTargetTitle']['runs'][0]['text'] parent_id = comment_raw['comment']['commentRenderer']['commentId'] + # TODO: move this stuff into the comments_html function if 'replies' in comment_raw: #reply_ctoken = comment_raw['replies']['commentRepliesRenderer']['continuations'][0]['nextContinuationData']['continuation'] #comment_id, video_id = get_ids(reply_ctoken) @@ -226,12 +233,16 @@ def parse_comments_polymer(content, replies=False): comment = { 'author': common.get_plain_text(comment_raw['authorText']), 'author_url': comment_raw['authorEndpoint']['commandMetadata']['webCommandMetadata']['url'], + 'author_channel_id': comment_raw['authorEndpoint']['browseEndpoint']['browseId'], + 'author_id': comment_raw['authorId'], 'author_avatar': comment_raw['authorThumbnail']['thumbnails'][0]['url'], 'likes': comment_raw['likeCount'], 'published': common.get_plain_text(comment_raw['publishedTimeText']), 'text': comment_raw['contentText'].get('runs', ''), 'view_replies_text': view_replies_text, 'replies_url': replies_url, + 'video_id': video_id, + 'comment_id': comment_raw['commentId'], } comments.append(comment) except Exception as e: @@ -256,6 +267,16 @@ def get_comments_html(comments): ) else: avatar = '' + if comment['author_channel_id'] in accounts.accounts: + delete_url = (URL_ORIGIN + '/delete_comment?video_id=' + + comment['video_id'] + + '&channel_id='+ comment['author_channel_id'] + + '&author_id=' + comment['author_id'] + + '&comment_id=' + comment['comment_id']) + + action_buttons = '''Delete''' + else: + action_buttons = '' html_result += comment_template.substitute( author=comment['author'], author_url = URL_ORIGIN + comment['author_url'], @@ -264,8 +285,8 @@ def get_comments_html(comments): published = comment['published'], text = format_text_runs(comment['text']), datetime = '', #TODO - replies=replies, - #replies='', + replies = replies, + action_buttons = action_buttons, ) return html_result diff --git a/youtube/post_comment.py b/youtube/post_comment.py index 45e9f4b..587a258 100644 --- a/youtube/post_comment.py +++ b/youtube/post_comment.py @@ -70,7 +70,7 @@ def _post_comment_reply(text, video_id, parent_comment_id, session_token, cookie '''with open('debug/post_comment_response', 'wb') as f: f.write(content)''' -def delete_comment(video_id, comment_id, author_id, session_token, cookiejar): +def _delete_comment(video_id, comment_id, author_id, session_token, cookiejar): 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': '*/*', @@ -91,7 +91,9 @@ def delete_comment(video_id, comment_id, author_id, session_token, cookiejar): data = urllib.parse.urlencode(data_dict).encode() content = common.fetch_url("https://m.youtube.com/service_ajax?name=performCommentActionEndpoint", headers=headers, data=data, cookiejar_send=cookiejar) - + code = json.loads(content)['code'] + print("Comment deletion code: " + code) + return code xsrf_token_regex = re.compile(r'''XSRF_TOKEN"\s*:\s*"([\w-]*(?:=|%3D){0,2})"''') def get_session_token(video_id, cookiejar): @@ -107,6 +109,12 @@ def get_session_token(video_id, cookiejar): else: raise Exception("Couldn't find xsrf_token") +def delete_comment(parameters, fields): + video_id = fields['video_id'][0] + cookiejar = accounts.account_cookiejar(fields['channel_id'][0]) + token = get_session_token(video_id, cookiejar) + return _delete_comment(video_id, fields['comment_id'][0], fields['author_id'][0], token, cookiejar) + def post_comment(parameters, fields): channel_id = fields['channel_id'][0] cookiejar = accounts.account_cookiejar(channel_id) @@ -144,6 +152,35 @@ def post_comment(parameters, fields): return response''' return code +def get_delete_comment_page(query_string): + parameters = urllib.parse.parse_qs(query_string) + + style = ''' + main{ + display: grid; + grid-template-columns: minmax(0px, 3fr) 640px 40px 500px minmax(0px,2fr); + align-content: start; + } + main > div, main > form{ + margin-top:20px; + grid-column:2; + } + ''' + + page = ''' +
Are you sure you want to delete this comment?
+
''' + for parameter in ('video_id', 'channel_id', 'author_id', 'comment_id'): + page += '''\n ''' + page += ''' + +
''' + return common.yt_basic_template.substitute( + page_title = "Delete comment?", + style = style, + header = common.get_header(), + page = page, + ) def get_post_comment_page(query_string): parameters = urllib.parse.parse_qs(query_string) diff --git a/youtube/youtube.py b/youtube/youtube.py index 8260faf..c4a0e5b 100644 --- a/youtube/youtube.py +++ b/youtube/youtube.py @@ -78,6 +78,18 @@ def youtube(env, start_response): start_response('200 OK', (('Content-type','text/html'),) ) return accounts.get_account_login_page(query_string=query_string).encode() + elif path == "/delete_comment": + start_response('200 OK', (('Content-type','text/html'),) ) + return post_comment.get_delete_comment_page(query_string).encode() + + elif path == "/comment_delete_success": + start_response('200 OK', () ) + return b'Successfully deleted comment' + + elif path == "/comment_delete_fail": + start_response('200 OK', () ) + return b'Failed to deleted comment' + else: start_response('200 OK', (('Content-type','text/html'),) ) return channel.get_channel_page_general_url(path, query_string=query_string).encode() @@ -116,6 +128,14 @@ def youtube(env, start_response): start_response('303 See Other', (('Location', common.URL_ORIGIN + '/comments?ctoken=' + comments.make_comment_ctoken(video_id, sort=1)),) ) return '' + elif path == "/delete_comment": + parameters = urllib.parse.parse_qs(query_string) + code = post_comment.delete_comment(parameters, fields) + if code == "SUCCESS": + start_response('303 See Other', (('Location', common.URL_ORIGIN + '/comment_delete_success'),) ) + else: + start_response('303 See Other', (('Location', common.URL_ORIGIN + '/comment_delete_fail'),) ) + elif path == "/login": if 'save' in fields and fields['save'][0] == "on": save_account = True -- cgit v1.2.3