Last active
November 26, 2024 23:01
-
-
Save jaredcat/a6623bd6f7137e28c03df533b3bbafe7 to your computer and use it in GitHub Desktop.
YouTube Playlist TVDB Data
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
// USE WITH https://greasyfork.org/en/scripts/518978-tvdb-episode-input-automation | |
const PLAYLIST_ID = 'ENTER A PLAYLIST ID HERE'; | |
async function getPlaylistVideos(playlistUrl) { | |
// Get playlist page | |
const response = await fetch(playlistUrl); | |
const html = await response.text(); | |
// Extract video IDs from playlist page | |
const videoIds = [ | |
...new Set([...html.matchAll(/"videoId":"([^"]+)"/g)].map((m) => m[1])), | |
]; | |
const videos = []; | |
let episodeCount = 0; | |
for (const videoId of videoIds) { | |
episodeCount++; | |
try { | |
const videoData = await getVideoDetails( | |
`https://www.youtube.com/watch?v=${videoId}`, | |
episodeCount, | |
); | |
videos.push(videoData); | |
// Respect YouTube's rate limiting | |
await new Promise((resolve) => setTimeout(resolve, 1000)); | |
} catch (error) { | |
console.error(`Error processing video ${videoId}:`, error); | |
} | |
} | |
return videos; | |
} | |
async function getVideoDetails(videoUrl, fallbackEpisodeNumber) { | |
const response = await fetch(videoUrl); | |
const html = await response.text(); | |
// Extract data from ytInitialData | |
const dataMatch = html.match(/ytInitialData\s*=\s*({.+?});/); | |
const playerDataMatch = html.match(/ytInitialPlayerResponse\s*=\s*({.+?});/); | |
if (!dataMatch || !playerDataMatch) { | |
throw new Error('Could not extract video data'); | |
} | |
const data = JSON.parse(dataMatch[1]); | |
const playerData = JSON.parse(playerDataMatch[1]); | |
// Get video details | |
const videoDetails = playerData.videoDetails; | |
const primaryInfo = | |
data.contents?.twoColumnWatchNextResults?.results?.results?.contents?.[0] | |
?.videoPrimaryInfoRenderer; | |
// Get episode number from title | |
const episodeMatch = videoDetails.title.match(/\[(\d+)\]/); | |
let episode = fallbackEpisodeNumber; | |
if (!episodeMatch) { | |
console.warn( | |
`Could not extract episode number in: ${videoDetails.title}\n\t Falling back to: ${fallbackEpisodeNumber}`, | |
); | |
} else { | |
episode = episodeMatch[1]; | |
} | |
// Get formatted title | |
const title = videoDetails.title.split(' |')[0].trim(); | |
// Get upload date from date text | |
const dateText = primaryInfo?.dateText?.simpleText?.trim() || ''; | |
// Get description | |
const description = videoDetails.shortDescription || ''; | |
const firstLine = description.split('\n')[0]?.trim(); | |
// Get length in minutes | |
const lengthSeconds = parseInt(videoDetails.lengthSeconds); | |
const lengthMinutes = Math.ceil(Math.round((lengthSeconds / 60) * 100) / 100); | |
return { | |
number: `${episode}`, | |
name: title, | |
overview: firstLine, | |
date: convertDate(dateText), | |
runtime: lengthMinutes, | |
}; | |
} | |
function convertDate(dateStr) { | |
const date = new Date(dateStr); | |
return date.toISOString().split('T')[0]; | |
} | |
getPlaylistVideos(`https://www.youtube.com/playlist?list=${PLAYLIST_ID}`) | |
.then((videos) => { | |
console.table(videos); | |
console.log(videos); | |
}) | |
.catch((error) => console.error('Error:', error)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment