-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#60] Add examples for listing comments for a video using /next endpoint
- Loading branch information
Showing
2 changed files
with
176 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
from innertube import InnerTube | ||
|
||
# YouTube Web CLient | ||
CLIENT = InnerTube("WEB", "2.20240105.01.00") | ||
|
||
|
||
def parse_text(text): | ||
return "".join(run["text"] for run in text["runs"]) | ||
|
||
|
||
def flatten(items): | ||
flat_items = {} | ||
|
||
for item in items: | ||
key = next(iter(item)) | ||
val = item[key] | ||
|
||
flat_items.setdefault(key, []).append(val) | ||
|
||
return flat_items | ||
|
||
|
||
def flatten_item_sections(item_sections): | ||
return { | ||
item_section["sectionIdentifier"]: item_section | ||
for item_section in item_sections | ||
} | ||
|
||
|
||
def extract_comments(next_continuation_data): | ||
return [ | ||
continuation_item["commentThreadRenderer"] | ||
for continuation_item in next_continuation_data["onResponseReceivedEndpoints"][ | ||
1 | ||
]["reloadContinuationItemsCommand"]["continuationItems"][:-1] | ||
] | ||
|
||
|
||
def extract_comments_continuation_token(next_data): | ||
contents = flatten( | ||
next_data["contents"]["twoColumnWatchNextResults"]["results"]["results"][ | ||
"contents" | ||
] | ||
) | ||
item_sections = flatten_item_sections(contents["itemSectionRenderer"]) | ||
comment_item_section_content = item_sections["comment-item-section"]["contents"][0] | ||
comments_continuation_token = comment_item_section_content[ | ||
"continuationItemRenderer" | ||
]["continuationEndpoint"]["continuationCommand"]["token"] | ||
|
||
return comments_continuation_token | ||
|
||
|
||
def get_comments(video_id, params=None): | ||
video = CLIENT.next(video_id, params=params) | ||
|
||
continuation_token = extract_comments_continuation_token(video) | ||
|
||
comments_continuation = CLIENT.next(continuation=continuation_token) | ||
|
||
return extract_comments(comments_continuation) | ||
|
||
|
||
def print_comment(comment): | ||
comment_renderer = comment["comment"]["commentRenderer"] | ||
|
||
comment_author = comment_renderer["authorText"]["simpleText"] | ||
comment_content = parse_text(comment_renderer["contentText"]) | ||
|
||
print(f"[{comment_author}]") | ||
print(comment_content) | ||
print() | ||
|
||
|
||
video_id = "BV1O7RR-VoA" | ||
|
||
# Get comments for a given video | ||
comments = get_comments(video_id) | ||
|
||
# Select a comment to highlight (in this case the 3rd one) | ||
comment = comments[2] | ||
|
||
# Print the comment we're going to highlight | ||
print("### Highlighting Comment: ###") | ||
print() | ||
print_comment(comment) | ||
print("---------------------") | ||
print() | ||
|
||
# Extract the 'params' to highlight this comment | ||
params = comment["comment"]["commentRenderer"]["publishedTimeText"]["runs"][0][ | ||
"navigationEndpoint" | ||
]["watchEndpoint"]["params"] | ||
|
||
# Get comments, but highlighting the selected comment | ||
highlighted_comments = get_comments(video_id, params=params) | ||
|
||
print("### Comments: ###") | ||
print() | ||
|
||
for comment in highlighted_comments: | ||
print_comment(comment) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from innertube import InnerTube | ||
|
||
ENGAGEMENT_SECTION_COMMENTS = "engagement-panel-comments-section" | ||
C0MMENTS_TOP = "Top comments" | ||
COMMENTS_NEWEST = "Newest first" | ||
|
||
|
||
def parse_text(text): | ||
return "".join(run["text"] for run in text["runs"]) | ||
|
||
|
||
def extract_engagement_panels(next_data): | ||
engagement_panels = {} | ||
raw_engagement_panels = next_data.get("engagementPanels", []) | ||
|
||
for raw_engagement_panel in raw_engagement_panels: | ||
engagement_panel = raw_engagement_panel.get( | ||
"engagementPanelSectionListRenderer", {} | ||
) | ||
target_id = engagement_panel.get("targetId") | ||
|
||
engagement_panels[target_id] = engagement_panel | ||
|
||
return engagement_panels | ||
|
||
|
||
def parse_sort_filter_sub_menu(menu): | ||
menu_items = menu["sortFilterSubMenuRenderer"]["subMenuItems"] | ||
|
||
return {menu_item["title"]: menu_item for menu_item in menu_items} | ||
|
||
|
||
def extract_comments(next_continuation_data): | ||
return [ | ||
continuation_item["commentThreadRenderer"] | ||
for continuation_item in next_continuation_data["onResponseReceivedEndpoints"][ | ||
1 | ||
]["reloadContinuationItemsCommand"]["continuationItems"][:-1] | ||
] | ||
|
||
|
||
# YouTube Web CLient | ||
client = InnerTube("WEB", "2.20240105.01.00") | ||
|
||
# ShortCircuit - Dell just DESTROYED the Surface Pro! - Dell XPS 13 2-in-1 | ||
video = client.next("BV1O7RR-VoA") | ||
|
||
engagement_panels = extract_engagement_panels(video) | ||
comments = engagement_panels[ENGAGEMENT_SECTION_COMMENTS] | ||
comments_header = comments["header"]["engagementPanelTitleHeaderRenderer"] | ||
comments_title = parse_text(comments_header["title"]) | ||
comments_context = parse_text(comments_header["contextualInfo"]) | ||
comments_menu_items = parse_sort_filter_sub_menu(comments_header["menu"]) | ||
comments_top = comments_menu_items[C0MMENTS_TOP] | ||
comments_top_continuation = comments_top["serviceEndpoint"]["continuationCommand"][ | ||
"token" | ||
] | ||
|
||
print(f"{comments_title} ({comments_context})...") | ||
print() | ||
|
||
comments_continuation = client.next(continuation=comments_top_continuation) | ||
|
||
comments = extract_comments(comments_continuation) | ||
|
||
for comment in comments: | ||
comment_renderer = comment["comment"]["commentRenderer"] | ||
|
||
comment_author = comment_renderer["authorText"]["simpleText"] | ||
comment_content = parse_text(comment_renderer["contentText"]) | ||
|
||
print(f"[{comment_author}]") | ||
print(comment_content) | ||
print() |