$http.get("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=PLFgquLnL59alCl_2TQvOiD5Vgm1hCaGSI&key={mykey}&maxResults=10")
I used the playlistItems but couldn't get the statistic part which contain duration of the video. Do I need to call twice? Get the video Id and make another call? or I'm missing something in this case?
For whatever reason, playlistItems do not include some things like statistics or category. You'll need to make a separate call using the video ID and https://developers.google.com/youtube/v3/docs/videos/list in order to get those fields.
This is how I do it (using Python but you can adapt it for whatever language you are using with http requests and JSON parsing)
url = "https://www.googleapis.com/youtube/v3/videos?id=" + videoId
+ "&key=" + DEVELOPER_KEY + "&part=snippet,contentDetails"
r = requests.get(url)
metadata = r.json()["items"][0]
channelName = metadata["snippet"]["channelTitle"]
publishedTime = metadata["snippet"]["publishedAt"]
duration = metadata["contentDetails"]["duration"]
duration is in a strange format that looks like
PT4M11S
meaning 4 minutes 11 seconds. You will have to "parse" this.
Related
I just want to fetch all my liked videos ~25k items. as far as my research goes this is not possible via the YouTube v3 API.
I have already found multiple issues (issue, issue) on the same problem, though some claim to have fixed it, but it only works for them as they don't have < 5000 items in their liked video list.
playlistItems list API endpoint with playlist id set to "liked videos" (LL) has a limit of 5000.
videos list API endpoint has a limit of 1000.
Unfortunately those endpoints don't provide me with parameters that I could use to paginate the requests myself (e.g. give me all the liked videos between date x and y), so I'm forced to take the provided order (which I can't get past 5k entries).
Is there any possibility I can fetch all my likes via the API?
more thoughts to the reply from #Yarin_007
if there are deleted videos in the timeline they appear as "Liked https://...url" , the script doesnt like that format and fails as the underlying elements dont have the same structure as existing videos
can be easily fixed with a try catch
function collector(all_cards) {
var liked_videos = {};
all_cards.forEach(card => {
try {
// ignore Dislikes
if (card.innerText.split("\n")[1].startsWith("Liked")) {
....
}
}
catch {
console.log("error, prolly deleted video")
}
})
return liked_videos;
}
to scroll down to the bottom of the page ive used this simple script, no need to spin up something big
var millisecondsToWait = 1000;
setInterval(function() {
window.scrollTo(0, document.body.scrollHeight);
console.log("scrolling")
}, millisecondsToWait);
when more ppl want to retrive this kind of data, one could think about building a proper script that is more convenient to use. If you check the network requests you can find the desired data in the response of requests called batchexecute. One could copy the authentification of one of them provide them to a script that queries those endpoints and prepares the data like the other script i currently manually inject.
Hmm. perhaps Google Takeout?
I have verified the youtube data contains a csv called "liked videos.csv". The header is Video Id,Time Added, and the rows are
dQw4w9WgXcQ,2022-12-18 23:42:19 UTC
prvXCuEA1lw,2022-12-24 13:22:13 UTC
for example.
So you would need to retrieve video metadata per video ID. Not too bad though.
Note: the export could take a while, especially with 25k videos. (select only YouTube data)
I also had an idea that involves scraping the actual liked videos page (which would save you 25k HTTP Requests). But I'm unsure if it breaks with more than 5000 songs. (also, emulating the POST requests on that page may prove quite difficult, albeit not impossible. (they fetch /browse?key=..., and have some kind of obfuscated / encrypted base64 strings in the request-body, among other parameters)
EDIT:
Look. There's probably a normal way to get a complete dump of all you google data. (i mean, other than takeout. Email them? idk.)
anyway, the following is the other idea...
Follow this deep link to your liked videos history.
Scroll to the bottom... maybe with selenium, maybe with autoit, maybe put something on the "end" key of your keyboard until you reach your first liked video.
Hit f12 and run this in the developer console
// https://www.youtube.com/watch?v=eZPXmCIQW5M
// https://myactivity.google.com/page?utm_source=my-activity&hl=en&page=youtube_likes
// go over all "cards" in the activity webpage. (after scrolling down to the absolute bottom of it)
// create a dictionary - the key is the Video ID, the value is a list of the video's properties
function collector(all_cards) {
var liked_videos = {};
all_cards.forEach(card => {
// ignore Dislikes
if (card.innerText.split("\n")[1].startsWith("Liked")) {
// horrible parsing. your mileage may vary. I Tried to avoid using any gibberish class names.
let a_links = card.querySelectorAll("a")
let details = a_links[0];
let url = details.href.split("?v=")[1]
let video_length = a_links[3].innerText;
let time = a_links[2].parentElement.innerText.split(" • ")[0];
let title = details.innerText;
let date = card.closest("[data-date]").getAttribute("data-date")
liked_videos[url] = [title,video_length, date, time];
// console.log(title, video_length, date, time, url);
}
})
return liked_videos;
}
// https://stackoverflow.com/questions/57709550/how-to-download-text-from-javascript-variable-on-all-browsers
function download(filename, text, type = "text/plain") {
// Create an invisible A element
const a = document.createElement("a");
a.style.display = "none";
document.body.appendChild(a);
// Set the HREF to a Blob representation of the data to be downloaded
a.href = window.URL.createObjectURL(
new Blob([text], { type })
);
// Use download attribute to set set desired file name
a.setAttribute("download", filename);
// Trigger the download by simulating click
a.click();
// Cleanup
window.URL.revokeObjectURL(a.href);
document.body.removeChild(a);
}
function main() {
// gather relevant elements
var all_cards = document.querySelectorAll("div[aria-label='Card showing an activity from YouTube']")
var liked_videos = collector(all_cards)
// download json
download("liked_videos.json", JSON.stringify(liked_videos))
}
main()
Basically it gathers all the liked videos' details and creates a key: video_ID - Value: [title,video_length, date, time] object for each liked video.
It then automatically downloads the json as a file.
I am using YouTube data API and trying to differentiate prior livestreams vs premiered content. The liveStreamingDetails in the video list is populated for both livestreams and premiered content. Is there a way I can differentiate between the two?
Below is my python code for getting live stream start time. If its not populated, then I know that video is not live stream. But the problem is that this value is getting populated for premiered content as well.
vid_request = youtube.videos().list(part = 'contentDetails, statistics, snippet, liveStreamingDetails, status',id = ','.join(vid_ids))
vid_response = vid_request.execute()
for videoitem in vid_response['items']:
try:
livestreamStartTime = videoitem['liveStreamingDetails']['actualStartTime']
except:
livestreamStartTime = ''
Any pointers on what could work would really help?
Goal: Get all (non-live) videos uploaded on October 2020 order by viewCount
My first try was like this:
request = youtube.search().list(
part="snippet",
order="viewCount",
publishedAfter="2020-10-01T00:00:00Z",
publishedBefore="2020-10-31T23:59:59Z",
type="video",
maxResults=50
)
It returns me YpUR1rHXbqs video which is live (and still running). IMHO it doesn't fit publishedBefore="2020-10-31T23:59:59Z" since it still running, but never mind.
I try to filter out live videos with:
response = request.execute()
for item in response["items"]:
if item["snippet"]["liveBroadcastContent"] != 'live':
It returns o6zDG9jYpC0 with 7 624 725 views today and 'liveBroadcastContent'='none' Seems too little to me.
So I try add q="a" to check if o6zDG9jYpC0 is the most viewed one:
request = youtube.search().list(
q='a',
part="snippet",
order="viewCount",
publishedAfter="2020-10-01T00:00:00Z",
publishedBefore="2020-10-31T23:59:59Z",
type="video",
maxResults=50
)
Returns uy30PB5BpV0 with 192 861 990 views :-( and 'liveBroadcastContent'='none'
I try to experiment with q="", q="*", q="%2A", q="+", q=" ", q=None ... , but no luck.
Adding eventType="completed" seems to work well with missing/not set q but it omits 'liveBroadcastContent'='none' videos.
How can I query all videos please?
Side note: I'm aware of Video: list (most popular videos) chart='mostPopular' but it doesn't support publishedAfter/Before which is required
I have a script that uses the YouTube API (v3) to find a video of a music from the name of the artist and the name of the music.
This works, however, in some cases, the first choice (sorted by relevance) is not the official video VEVO.
I tried adding VEVO in my query (after name of the artist and name of the music), but when there is no video VEVO, the API returns no results.
Is it possible to force to choose VEVO videos, if they exist?
Thank you.
Vincent
var request = gapi.client.youtube.search.list
({
q: artiste+' '+track,
part: 'snippet',
order: 'relevance'
});
request.execute(function(response)
{
idVideo=response.result.items[0].id.videoId;
});
This is the part that allows to select the id of a video based on the artist's name and the name of the music
UPDATE: I don't think the syndicated video suggesting I put below would work well but I'll leave it there just in case you want to explore it. What might work better, again not guaranteed but should be more accurate just hoping for the best would be to simply sort it by viewCount instead of relevance... Generally speaking, the VEVO videos have the most views.
Example: https://developers.google.com/apis-explorer/#p/youtube/v3/youtube.search.list?part=snippet&order=viewCount&q=nicki+minaj+anaconda&type=video&_h=3&
GET https://www.googleapis.com/youtube/v3/search?part=snippet&order=viewCount&q=nicki+minaj+anaconda&type=video&key={YOUR_API_KEY}
--
ORIGINAL ANSWER
I haven't been able to test it yet and it won't necessarily restrict it to ONLY vevo videos but you can try the syndicated option https://developers.google.com/youtube/v3/docs/search/list#videoSyndicated
string
The videoSyndicated parameter lets you to restrict a search to only videos that can be played outside youtube.com. If you specify a value for this parameter, you must also set the type parameter's value to video.
Acceptable values are:
any – Return all videos, syndicated or not.
true – Only retrieve syndicated videos.
If that returns nothing, than do the same search without syndicated and use the first option from that.
It is actually pretty easy. What you need to do is add 'VEVO' to you search query. This will make sure that anything from a VEVO channel will be the first result. It should look something like this.
var request = gapi.client.youtube.search.list
({
q: artiste+' '+track + 'VEVO',
part: 'snippet',
order: 'relevance'
});
If you wan't to make sure you are getting a VEVO video the easiest thing to do is parse the channel title to make sure it contains the word "VEVO". The Code would then look something like this
var request = gapi.client.youtube.search.list
({
q: artiste+' '+track + 'VEVO',
part: 'snippet',
order: 'relevance'
});
var obj = JSON.parse(result.content);
var findChannelTitle = obj.items[0].snippet.channelTitle;
var isVevo = findChannelTitle.match(/VEVO/g); //checks to see if this is VEVO content. We only wan't to use Vevo videos.
if (isVevo){ //returns true if VEVO is found in the channel title
var youtubeVideoId = obj.items[0].id.videoId; //finds the video ID
return youtubeVideoId;
}else{
return null;
}
I am accessing data from YouTube's API, I have everything working fine but the problem I'm having is that when there's a dash (-) at the beginning of the videoID that it's not returning the json data.
$videoID = -FIHqoTcZog;
$json = json_decode(file_get_contents("http://gdata.youtube.com/feeds/api/videos?q={$videoID}&alt=json"));
I am however able to return the thumbnail as always with it using this:
$thumbnail = "http://i4.ytimg.com/vi/".$videoID."/mqdefault.jpg";
This is the code that I use to pull the information from the above json that I want.
$title = $json->{'feed'}->{'entry'}[0]->{'title'}->{'$t'};
$description = $json->{'feed'}->{'entry'}[0]->{'media$group'}->{'media$description'}->{'$t'};
$thumbnail = "http://i4.ytimg.com/vi/".$videoID."/mqdefault.jpg";
$ratings = ((round($json->{'feed'}->{'entry'}[0]->{'gd$rating'}->{'average'}, 1)/$json->{'feed'}->{'entry'}[0]->{'gd$rating'}->{'max'})*100)."%";
$views = number_format($json->{'feed'}->{'entry'}[0]->{'yt$statistics'}->{'viewCount'});
$duration = $json->{'feed'}->{'entry'}[0]->{'media$group'}->{'yt$duration'}->{'seconds'};
Are you sure you're only getting a problem with IDs that have a dash in front of it? The code you pasted shouldn't be working with any Youtube ID, because the gdata feed returns, as part of the JSON, some text with the '$' character in it. That character is a PHP identifier, so you'll get 500 errors trying to run the json_decode function on whatever the feed returns.
One way to solve the problem is to use json_decode's 2nd parameter to give you an associative array rather than an object, like this:
$json = json_decode(file_get_contents("http://gdata.youtube.com/feeds/api/videos?q={$videoID}&alt=json"),true);
Of course, that requires you to work with an array, too, but the subsequent code changes should be minimal.
If you aren't getting errors with other videos using the exact same code, perhaps you could post it here?