How can I determine a public YouTube channel's username - youtube

The YouTube Data API allows us to query data about a channel using the channel id or the channel username.
I thought that a channel's username would be its "#handle" as listed below the channel title and (commonly) in the channel URL. But 3Blue1Brown is a clear counterexample to this.
from googleapiclient.discovery import build
# Instantiate a googleapiclient.discovery.Resource object for youtube
youtube = build(
serviceName='youtube',
version='v3',
developerKey='MYKEY'
)
# Define the request
request = youtube.channels().list(
part="contentDetails",
forUsername="3blue1brown"
)
# Execute the request and save the response
response = request.execute()
response
# {'kind': 'youtube#channelListResponse',
# 'etag': 'RuuXzTIr0OoDqI4S0RU6n4FqKEM',
# 'pageInfo': {'totalResults': 0, 'resultsPerPage': 5}}
I know I can just use the channel's id instead, but the id is becoming harder to find and frankly, it just bothers me that I don't know how to find a channel's username.

Related

How to avoid omissions in video information acquisition when using the YouTube Data API?

Assumption / What I want to achieve
I want to use YouTube Data API V3 to get the video ID without any omissions, and find out if the cause of the trouble is in the code or in the video settings of YouTube (API side).
Problem
The following code is used to get the video information from YouTube Data API, but the number of IDs I got did not match the number of videos that are actually posted.
from apiclient.discovery
import build
id = "UCD-miitqNY3nyukJ4Fnf4_A" #sampleID
token_check = None
nextPageToken = None
id_info = []
while True:
if token_check != None:
nextPageToken = token_check
Search_Video = youtube.search().list(
part = "id",
channelId = id,
maxResults = 50,
order = 'date',
safeSearch = "none",
pageToken = nextPageToken
).execute()
for ID_check in Search_Video.get("items", []):
if ID_check["id"]["kind"] == "youtube#video":
id_info.append(ID_check["id"]["videoId"])
try:
token_check = Search_Video["nextPageToken"]
except:
print(len(id_info)) #check number of IDs
break
I also used the YouTube Data API function to get the videoCount information of the channel, and noticed that the value of videoCount did not match the number of IDs obtained by the code above, which is why I posted this.
According to channels() API, this channel have 440 videos, but the above code gets only 412 videos (at 10:30 a.m. JST).
Supplemental Information
・Python 3.9.0
・YouTube Data API v3
You have to acknowledge that the Search.list API endpoint does not have a crisp behavior. That means you should not expect precise results from it. Google does not document this behavior as such, but this forum has many posts from users experiencing that.
If you want to obtain all the IDs of videos uploaded by a given channel then you should employ the following two-step procedure:
Step 1: Obtain the ID of the Uploads Playlist of a Channel.
Invoke the Channels.list API endpoint, queried with its request parameter id set to the ID of the channel of your interest (or, otherwise, with its request parameter mine set to true) for to obtain that channel's uploads playlist ID, contentDetails.relatedPlaylists.uploads.
def get_channel_uploads_playlist_id(youtube, channel_id):
response = youtube.channels().list(
fields = 'items/contentDetails/relatedPlaylists/uploads',
part = 'contentDetails',
id = channel_id,
maxResults = 1
).execute()
items = response.get('items')
if items:
return items[0] \
['contentDetails'] \
['relatedPlaylists'] \
.get('uploads')
else:
return None
Do note that the function get_channel_uploads_playlist_id should only be called once for to obtain the uploads playlist
ID of a given channel; subsequently use that ID as many times as needed.
Step 2: Retrieve All IDs of Videos of a Playlist.
Invoke the PlaylistItems.list API endpoint, queried with its request parameter playlistId set to the ID obtained from get_channel_uploads_playlist_id:
def get_playlist_video_ids(youtube, playlist_id):
request = youtube.playlistItems().list(
fields = 'nextPageToken,items/snippet/resourceId',
playlistId = playlist_id,
part = 'snippet',
maxResults = 50
)
videos = []
is_video = lambda item: \
item['snippet']['resourceId']['kind'] == 'youtube#video'
video_id = lambda item: \
item['snippet']['resourceId']['videoId']
while request:
response = request.execute()
items = response.get('items', [])
assert len(items) <= 50
videos.extend(map(video_id, filter(is_video, items)))
request = youtube.playlistItems().list_next(
request, response)
return videos
Do note that, when using the Google's APIs Client Library for Python (as you do), API result set pagination is trivially simple: just use the list_next method of the Python API object corresponding to the respective paginated API endpoint (as was shown above):
request = API_OBJECT.list(...)
while request:
response = request.execute()
...
request = API_OBJECT.list_next(
request, response)
Also note that above I used twice the fields request parameter. This is good practice: ask from the API only the info that is of actual use.
Yet an important note: the PlaylistItems.list endpoint would not return items that correspond to private videos of a channel when invoked with an API key. This happens when your youtube object was constructed by calling the function apiclient.discovery.build upon passing to it the parameter developerKey.
PlaylistItems.list returns items corresponding to private videos only to the channel owner. This happens when the youtube object is constructed by calling the function apiclient.discovery.build upon passing to it the parameter credentials and if credentials refer to the channel that owns the respective playlist.
An additional important note: according to Google staff, there's an upper 20000 limit set by design for the number of items returned via PlaylistItems.list endpoint when queried for a given channel's uploads playlist. This is unfortunate, but a fact.

Searching Channels with YouTube API returns incorrect results

Using this code allows me to search for YouTube channels and return their statistics; for some reason, however, only about half the channels return any results.
For example, if I use this link to test the API, you can see that I searched for Animal Planet, and that yields no results. However, if we go to YouTube's website and search for the same thing, we get the channel as needed.
Why does this happen and how can this be fixed?
This is the code I used:
#commands.command()
async def yt_test(self, ctx, *, search):
api_key = '...'
youtube = build('youtube', 'v3', developerKey=api_key)
request = youtube.channels().list(
part="snippet,statistics",
forUsername=search,
maxResults=1
)
response = request.execute()
try:
for item in response['items']:
link = item['id']
thumb_image = item['snippet']['thumbnails']['default']['url']
views = "{:,}".format(int(item['statistics']['viewCount']))
subs = "{:,}".format(int(item['statistics']['subscriberCount']))
vidCount = "{:,}".format(int(item['statistics']['videoCount']))
except:
Notfound = discord.Embed(description= "Sorry, but this Channel was not located, check spelling" , color=0xfc0328)
await ctx.send(embed=Notfound)
finalresult = discord.Embed(title = search, url='https://www.youtube.com/channel/' + link,
description= '**Statistics**\nViews: ' + views + '\nSubscribers: ' + subs + "\nVideos: " + vidCount, color=0x00ff00)
finalresult.set_thumbnail(url = thumb_image)
await ctx.send(embed=finalresult)
print(f"{ctx.author} looked up the profile of {search} --- in #{ctx.guild.name}")
You have to acknowledge that invoking the Channels.list API endpoint, by passing to it the parameter forUsername, is by no means the same thing as going to the YouTube's Web UI for to apply a manual search. For these things to come to light, do bear with me for a little while...
According to the official docs, the Channels.list endpoint invoked with forUsername=... produces the meta-data associated to the channel of which username is given as argument to the parameter forUsername:
forUsername (string)
The forUsername parameter specifies a YouTube username, thereby requesting the channel associated with that username.
According to an official Google staff post from 2013-07-11, it's not a requisite that every channel have attached an username. Hence, is perfectly possible for Channels.list endpoint to return no channel at all, if the argument of forUsername does not represent a YouTube username.
If you use one of my public (MIT licensed) Python 3 script -- youtube-search.py --, you'll see that it returns nothing for your queried username Animal Planet:
$ python3 youtube-search.py --user-name "Animal Planet"
youtube-search.py: error: user name "Animal Planet": no associated channel found
But if you invoke that script as follows:
$ python3 youtube-search.py --search-term "Animal Planet" --type channel
UCkEBDbzLyH-LbB2FgMoSMaQ: Animal Planet
UCuTSm59-_kap6J7Se-orUVA: Animal Planet Latinoamérica
UCpvajaPSWvFlyl83xvzs65A: animalplanet românia
UCgyOEJZJLPKclqrDyUkZ6Ag: Animal planet 1
UCypzlOdJyyOR2NhKv0o3zig: 動物星球頻道/ Animal Planet Taiwan
UCmMm-p51c14XJ1p294faCmA: Animal Planet Brasil
UCejVe2sNPjjvfCXg35q_EQQ: Animal Planet Videos
UClQLf_zw2A_nRaB45HWTbtA: Your Animal Planet
UChQEdt9dBiCkcCch6LyrjtA: Cute Animal Planet
UCRIS8Y1kfrFvFF_6vt6_3Cg: Animal Planet Videos
you'll see pretty much the same output as the YouTube's Web UI will show when queried for the same search term and filtered for TYPE being Channel.
Do note that your own results (the one obtained from youtube-search.py and the other provided by the YouTube Web UI) may very likely differ from the above ones; this is so because Google searches are tailored for the user that initiates the query.
The explanation behind the matching results sets obtained from youtube-search.py and YouTube Web UI is the following:
The API endpoint that corresponds to YouTube's own search feature accessible via its Web UI is none other than Search.list.
Do convince yourself about this claim by reading the code of youtube-search.py: when invoking it with --search-term "Animal Planet" --type channel, that script executes the function Service.search_term:
class Service:
...
def search_term(self, term, type = None):
resp = self.youtube.search().list(
q = term,
type = type,
part = 'id,snippet',
fields = 'items(id,snippet(title))',
maxResults = self.max_results
).execute()
...
with term equal to the string Animal Planet and type equal to the string channel. Consequently, this script is calling for the Search.list endpoint, passing to it the parameters q and type as mentioned.

youtube api - get timestamp of subscription

I am fetching all the Accounts that have subscribed to the authenticated user's channel.
Is there a way to obtain the timestamp of when the user has subscribed to the channel?
The default "publishedAt" sadly is of no help because that seems to be the timestamp of when the channel was created.
Also, that is not part of the "subscriberSnippet"-Section
Twitch and Mixer both provide "created_at", but the YouTube-API is a whole other story...
Thank you very much,
~ Daniel
Based from the official documentation of Youtube API:
Subscriptions: list
retrieves a list of channels that the specified channel subscribes to.
You can view here the subscriptions resource and publishedAt is part of it:
snippet.publishedAt datetime
The date and time that the subscription was created. The value is specified in ISO 8601
(YYYY-MM-DDThh:mm:ss.sZ) format.
You can try the try this section from the documentation and check your Youtube Subscibers List using Creator Studio by following the steps here.
Note: The list only shows subscribers who have chosen to make their
subscriptions public. When a user first joins YouTube, their
subscriptions list is set to private by default.
Yes, here's an example in Python
import requests
headers = {
'Authorization': 'Bearer {0}'.format(token),
'Client-Id': client_id
}
query = {
'part': 'subscriberSnippet,snippet',
'mySubscribers': 'true',
'key': youtube_key
}
if cursor is not None:
query['pageToken'] = cursor
response = requests.get(
'https://youtube.googleapis.com/youtube/v3/subscriptions',
headers=headers, params=query)
res = response.json()
weirdly, snippet contains your authenticated user channel description and title but publishedAt field corresponds to when the item from subscriberSnippet subscribed to your authenticated user

YouTube API: Get upload playlistID for YouTube Channel

I'm trying to figure out the best way to get a channel's upload playlistID given the channel's channelID. E.g. for the channel with channelID
UC9CuvdOVfMPvKCiwdGKL3cQ
The corresponding upload playlistID is
UU9CuvdOVfMPvKCiwdGKL3cQ
Notice the second character has changed from a "C" to a "U"
I can do this transformation via string manipulation, but I'm curious if there's a better, less hacky way to find the upload playlist ID through the official youtube api.
Here's some Kotlin code that shows my issue:
I can find the ChannelID for the "Game Grumps" channel through the following youtube api v3 search:
val req = youtube.search().list("snippet");
req.key = {API_KEY}
req.q = "Game Grumps"
req.type = "channel"
val response = req.execute();
The resulting Channel id (response.items[0].snippet.channelId) is UC9CuvdOVfMPvKCiwdGKL3cQ
But when I run the following to try to get the videos uploaded by the channel, I have to use the transformed ChannelID (starting with UU instead of UC)
val req = youtube.PlaylistItems().list("snippet")
req.playlistId = "UU9CuvdOVfMPvKCiwdGKL3cQ"
req.key = {API_KEY}
val response = req .execute()
If I use the untransformed "UC" channelID, I get the following error: The playlist identified with the requests playlistId parameter cannot be found.
Instead of just replacing the second character with a "U", what's the more robust way (e.g. using the youtube API) of translating a ChannelID to a PlaylistID (for the uploads from that channel)?
I would suggest using the official Youtube API, instead of trying to manipulate the strings. You can follow the instructions here:
Instructions to get video ids for all uploaded videos for a channel in V3
Get the channel id for the channel you want (you probably only need to do this once, then you can save it)
Use search.list
Set type to channel
Set q to the name of the channel you want
Grab the channel id (something like this: "channelId": "UC0X2VuXXXXXXXXXXXXXXXX")
Get the playlist id for the channel uploads using the channel id from step 1 (you probably only need to do this once, then you can save it)
Use channels.list
Set id to UC0X2VuXXXXXXXXXXXXXXXX from step 1
Grab the uploads key from contentDetails (something like this: "uploads": "UU0XXXXXXXXXXXXXXXXXXXX")
Get the videos via the playlistitems in the playlist using the playlist id from step 2
Use playlistItems.list
Set playlistId to UU0XXXXXXXXXXXXXXXXXXXX from step 2
Go through each PlaylistItem and pull out the video id

YouTube API PlaylistItems/list limited to 5000 results?

I am trying to get a list of all titles from YouTube channel machinima. Machinima currently has (I'm guessing here) tens of thousands of videos.
The following Python script is supposed to walk the chain of pages containing playlist items (the playlist being user's uploads).
channels_response = youtube.channels().list(
id=DEFAULT_CHANNEL,
part="contentDetails"
).execute()
if len(channels_response["items"]) == 0:
channels_response = youtube.channels().list(
forUsername=DEFAULT_CHANNEL,
part="contentDetails"
).execute()
for channel in channels_response["items"]:
# From the API response, extract the playlist ID that identifies the list
# of videos uploaded to the authenticated user's channel.
uploads_list_id = channel["contentDetails"]["relatedPlaylists"]["uploads"]
print "Videos in list %s" % uploads_list_id
# Retrieve the list of videos uploaded to the authenticated user's channel.
playlistitems_list_request = youtube.playlistItems().list(
playlistId=uploads_list_id,
part="snippet",
maxResults=50
)
while playlistitems_list_request:
playlistitems_list_response = playlistitems_list_request.execute()
# Print information about each video.
for playlist_item in playlistitems_list_response["items"]:
title = playlist_item["snippet"]["title"]
video_id = playlist_item["snippet"]["resourceId"]["videoId"]
print video_id
videos.append(video_id)
titles.append(title)
playlistitems_list_request = youtube.playlistItems().list_next(
playlistitems_list_request, playlistitems_list_response)
However, for some reason, I always end up getting no more than 5,000 videos.
I checked the API documentation, but there is no reference of limit of such sort. The code I'm using is from the official documentation too (slightly modified) and should work.
Does anyone know how could I get ALL the videos from Machinima?
EDIT: Update on October 20th. The limit seems to have disappeared.

Resources