Created
December 5, 2023 19:38
-
-
Save mkwsnyder/653f1e8df008af4c262bf296daa5d471 to your computer and use it in GitHub Desktop.
YouTube RSS Feed Generator
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
// @ts-nocheck | |
/** | |
* Build an RSS Feed from your YouTube Subscriptions | |
* | |
* It runs a little slow to prevent YouTube from blocking the requests. | |
* The entire process will take as many seconds as you have subscriptions (you can shorten this by changing the delay in getRssFeeds()). | |
* | |
* How to use: | |
* 1. Go to https://www.youtube.com/ (while signed in | |
* 2. Under Subscriptions click "Show more" | |
* 3. Open the console (F12) | |
* 4. Paste this script and run it | |
* 5. A file named "youtube-feed.opml" will be downloaded | |
*/ | |
async function buildRssFeed() { | |
let channels = getChannelList(); | |
await getRssFeeds(channels); | |
let rss = channelsToRssFeed(channels); | |
writeRssToFile(rss); | |
} | |
function getChannelList() { | |
let channels = []; | |
for (let e of Array.from( | |
document.querySelectorAll('.yt-simple-endpoint.style-scope.ytd-guide-entry-renderer'), | |
).filter((e) => e.href.includes('@'))) { | |
channels.push({ | |
title: e.title, | |
htmlUrl: e.href, | |
htmlUrlImg: e.querySelector('img').src, | |
}); | |
} | |
return channels; | |
} | |
async function getRssFeeds(channels) { | |
const delay = 1000; | |
let index = 0; | |
const processNextChannel = () => { | |
if (index < channels.length) { | |
return getFeed(channels[index++]) | |
.then(() => { | |
return new Promise((resolve) => setTimeout(resolve, delay)); | |
}) | |
.then(processNextChannel); | |
} else return Promise.resolve(); | |
}; | |
return processNextChannel(); | |
} | |
async function getFeed(channel) { | |
return fetch(channel.htmlUrl).then(async (response) => { | |
let html = await response.text(); | |
let parser = new DOMParser(); | |
let doc = parser.parseFromString(html, 'text/html'); | |
for (let arrScripts = doc.getElementsByTagName('script'), i = 0; i < arrScripts.length; i++) { | |
if (arrScripts[i].textContent.indexOf('externalId') != -1) { | |
let channelId = arrScripts[i].textContent.match(/\"externalId\"\s*\:\s*\"(.*?)\"/)[1]; | |
let channelRss = 'https://www.youtube.com/feeds/videos.xml?channel_id=' + channelId; | |
let channelTitle = doc.title.match(/\(?\d*\)?\s?(.*?)\s\-\sYouTube/)[1]; | |
console.log(`Acquired RSS feed for '${channelTitle}' (\n${channelRss})`); | |
channel.xmlUrl = channelRss; | |
break; | |
} | |
} | |
}); | |
} | |
function channelsToRssFeed(channels) { | |
return `<?xml version="1.0" encoding="UTF-8"?> | |
<opml version="1.0"> | |
<head> | |
<title>Script Export</title> | |
</head> | |
<body> | |
${channels.map(channelToString).join('\n')} | |
</body> | |
</opml>`; | |
} | |
function channelToString(channel) { | |
return ` <outline text="${channel.title}" type="rss" xmlUrl="${channel.xmlUrl}" htmlUrl="${channel.htmlUrl}" htmlUrlImg="${channel.htmlUrlImg}" />`; | |
} | |
function writeRssToFile(rss) { | |
const blob = new Blob([rss], { type: 'text/plain;charset=utf-8' }); | |
const link = document.createElement('a'); | |
link.download = 'youtube-feed.opml'; | |
link.href = URL.createObjectURL(blob); | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
} | |
buildRssFeed(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment