From e820d75fc40f02416b837c07a6fd1efb432dfcd1 Mon Sep 17 00:00:00 2001 From: Tom Bulled <26026015+tombulled@users.noreply.github.com> Date: Sun, 30 Jul 2023 13:13:14 +0100 Subject: [PATCH 1/3] Start writing exemplar script to list videos for channel --- innertube/clients.py | 7 +---- list-channel-videos.py | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 list-channel-videos.py diff --git a/innertube/clients.py b/innertube/clients.py index 059ca50..d627190 100644 --- a/innertube/clients.py +++ b/innertube/clients.py @@ -106,16 +106,11 @@ def browse( ) -> dict: return self( Endpoint.BROWSE, - params=utils.filter( - dict( - continuation=continuation, - ctoken=continuation, - ) - ), body=utils.filter( dict( browseId=browse_id, params=params, + continuation=continuation, ) ), ) diff --git a/list-channel-videos.py b/list-channel-videos.py new file mode 100644 index 0000000..6e8ce39 --- /dev/null +++ b/list-channel-videos.py @@ -0,0 +1,61 @@ +import random +import time + +from innertube import InnerTube + + +def delay(): + # A random duration between 2s and 5s + secs = random.randint(2000, 5000) / 1000 + + time.sleep(secs) + + +channel_browse_id = "UCXuqSBlHAE6Xw-yeJA0Tunw" # Linus Tech Tips + +# Client for YouTube (Web) +client = InnerTube("WEB", "2.20230728.00.00") + +# Fetch the browse data for the channel +channel_data = client.browse(channel_browse_id) + +# Extract the tab renderer for the "Videos" tab of the channel +videos_tab_renderer = channel_data["contents"]["twoColumnBrowseResultsRenderer"][ + "tabs" +][1]["tabRenderer"] + +# Make sure this tab is the "Videos" tab +assert videos_tab_renderer["title"] == "Videos" + +# Extract the browse params for the "Videos" tab of the channel +videos_params = videos_tab_renderer["endpoint"]["browseEndpoint"]["params"] + +# Wait a bit so that Google doesn't suspect us of being a bot +delay() + +# Fetch the browse data for the channel's videos +videos_data = client.browse(channel_browse_id, params=videos_params) + +# Extract the rich video items and the continuation item +*rich_items, continuation_item = videos_data["contents"][ + "twoColumnBrowseResultsRenderer" +]["tabs"][1]["tabRenderer"]["content"]["richGridRenderer"]["contents"] + +# Loop through each video and log out its details +for rich_item in rich_items: + video_renderer = rich_item["richItemRenderer"]["content"]["videoRenderer"] + + video_id = video_renderer["videoId"] + video_title = video_renderer["title"]["runs"][0]["text"] + + print(f"[{video_id}] {video_title}") + +# Extract the continuation token +continuation_token = continuation_item["continuationItemRenderer"][ + "continuationEndpoint" +]["continuationCommand"]["token"] + +# Fetch more videos by using the continuation token +more_videos_data = client.browse(continuation=continuation_token) + + From 167256992caa22ae90d7a861e71dbc39928e0bfc Mon Sep 17 00:00:00 2001 From: Tom Bulled <26026015+tombulled@users.noreply.github.com> Date: Sun, 30 Jul 2023 13:27:44 +0100 Subject: [PATCH 2/3] Finish example script to list videos for a channel --- examples/list-channel-videos.py | 83 +++++++++++++++++++++++++++++++++ list-channel-videos.py | 61 ------------------------ 2 files changed, 83 insertions(+), 61 deletions(-) create mode 100644 examples/list-channel-videos.py delete mode 100644 list-channel-videos.py diff --git a/examples/list-channel-videos.py b/examples/list-channel-videos.py new file mode 100644 index 0000000..4bbab3d --- /dev/null +++ b/examples/list-channel-videos.py @@ -0,0 +1,83 @@ +import random +import time + +from innertube import InnerTube + +def delay(): + # A random duration between 2s and 5s + secs = random.randint(2000, 5000) / 1000 + + time.sleep(secs) + + +def list_videos(channel_id=None, /, *, continuation=None): + # Client for YouTube (Web) + client = InnerTube("WEB", "2.20230728.00.00") + + # If this is the first video listing, browse the "Videos" page + if continuation is None: + # Fetch the browse data for the channel + channel_data = client.browse(channel_id) + + # Extract the tab renderer for the "Videos" tab of the channel + videos_tab_renderer = channel_data["contents"][ + "twoColumnBrowseResultsRenderer" + ]["tabs"][1]["tabRenderer"] + + # Make sure this tab is the "Videos" tab + assert videos_tab_renderer["title"] == "Videos" + + # Extract the browse params for the "Videos" tab of the channel + videos_params = videos_tab_renderer["endpoint"]["browseEndpoint"]["params"] + + # Wait a bit so that Google doesn't suspect us of being a bot + delay() + + # Fetch the browse data for the channel's videos + videos_data = client.browse(channel_id, params=videos_params) + + # Extract the contents list + contents = videos_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"][1][ + "tabRenderer" + ]["content"]["richGridRenderer"]["contents"] + else: + # Fetch more videos by using the continuation token + continued_videos_data = client.browse(continuation=continuation) + + contents = continued_videos_data[ + "onResponseReceivedActions" + ][0]["appendContinuationItemsAction"]["continuationItems"] + + # Extract the rich video items and the continuation item + *rich_items, continuation_item = contents + + # Loop through each video and log out its details + for rich_item in rich_items: + video_renderer = rich_item["richItemRenderer"]["content"]["videoRenderer"] + + video_id = video_renderer["videoId"] + video_title = video_renderer["title"]["runs"][0]["text"] + + print(f"[{video_id}] {video_title}") + + # Extract the continuation token + continuation_token = continuation_item["continuationItemRenderer"][ + "continuationEndpoint" + ]["continuationCommand"]["token"] + + return continuation_token + + +channel_browse_id = "UCXuqSBlHAE6Xw-yeJA0Tunw" # Linus Tech Tips + +# List the initial videos +continuation = list_videos(channel_browse_id) + +# Wait a bit so that Google doesn't suspect us of being a bot +delay() + +# Print an empty line to demarcate the initial items from the continuation +print() + +# List the videos from the first continuation +list_videos(continuation=continuation) diff --git a/list-channel-videos.py b/list-channel-videos.py deleted file mode 100644 index 6e8ce39..0000000 --- a/list-channel-videos.py +++ /dev/null @@ -1,61 +0,0 @@ -import random -import time - -from innertube import InnerTube - - -def delay(): - # A random duration between 2s and 5s - secs = random.randint(2000, 5000) / 1000 - - time.sleep(secs) - - -channel_browse_id = "UCXuqSBlHAE6Xw-yeJA0Tunw" # Linus Tech Tips - -# Client for YouTube (Web) -client = InnerTube("WEB", "2.20230728.00.00") - -# Fetch the browse data for the channel -channel_data = client.browse(channel_browse_id) - -# Extract the tab renderer for the "Videos" tab of the channel -videos_tab_renderer = channel_data["contents"]["twoColumnBrowseResultsRenderer"][ - "tabs" -][1]["tabRenderer"] - -# Make sure this tab is the "Videos" tab -assert videos_tab_renderer["title"] == "Videos" - -# Extract the browse params for the "Videos" tab of the channel -videos_params = videos_tab_renderer["endpoint"]["browseEndpoint"]["params"] - -# Wait a bit so that Google doesn't suspect us of being a bot -delay() - -# Fetch the browse data for the channel's videos -videos_data = client.browse(channel_browse_id, params=videos_params) - -# Extract the rich video items and the continuation item -*rich_items, continuation_item = videos_data["contents"][ - "twoColumnBrowseResultsRenderer" -]["tabs"][1]["tabRenderer"]["content"]["richGridRenderer"]["contents"] - -# Loop through each video and log out its details -for rich_item in rich_items: - video_renderer = rich_item["richItemRenderer"]["content"]["videoRenderer"] - - video_id = video_renderer["videoId"] - video_title = video_renderer["title"]["runs"][0]["text"] - - print(f"[{video_id}] {video_title}") - -# Extract the continuation token -continuation_token = continuation_item["continuationItemRenderer"][ - "continuationEndpoint" -]["continuationCommand"]["token"] - -# Fetch more videos by using the continuation token -more_videos_data = client.browse(continuation=continuation_token) - - From ac904811ee2e60bae787b6fb66b41ad596a519b1 Mon Sep 17 00:00:00 2001 From: Tom Bulled <26026015+tombulled@users.noreply.github.com> Date: Sun, 30 Jul 2023 13:32:56 +0100 Subject: [PATCH 3/3] examples/list-channel-videos.py: code formatting --- examples/list-channel-videos.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/list-channel-videos.py b/examples/list-channel-videos.py index 4bbab3d..5efb506 100644 --- a/examples/list-channel-videos.py +++ b/examples/list-channel-videos.py @@ -3,6 +3,7 @@ from innertube import InnerTube + def delay(): # A random duration between 2s and 5s secs = random.randint(2000, 5000) / 1000 @@ -44,9 +45,9 @@ def list_videos(channel_id=None, /, *, continuation=None): # Fetch more videos by using the continuation token continued_videos_data = client.browse(continuation=continuation) - contents = continued_videos_data[ - "onResponseReceivedActions" - ][0]["appendContinuationItemsAction"]["continuationItems"] + contents = continued_videos_data["onResponseReceivedActions"][0][ + "appendContinuationItemsAction" + ]["continuationItems"] # Extract the rich video items and the continuation item *rich_items, continuation_item = contents