Skip to content

Instantly share code, notes, and snippets.

@oyale
Last active December 29, 2022 15:27
Show Gist options
  • Save oyale/3b2246c2ddd255e3d1d17c86212d1069 to your computer and use it in GitHub Desktop.
Save oyale/3b2246c2ddd255e3d1d17c86212d1069 to your computer and use it in GitHub Desktop.
You can get an OPML file with your YouTube subscriptions by running this script on the console while you are logged in at YouTube's main page. The downloaded file can be imported at any invidious instance.
function downloadOPML(opml) {
// Create a new anchor element
const anchor = document.createElement('a');
// Set the anchor's download attribute
anchor.download = 'subscriptions.opml';
// Set the anchor's href attribute to the OPML data
anchor.href = `data:text/xml;charset=utf-8,${encodeURIComponent(opml)}`;
// Click the anchor element to trigger the download
anchor.click();
console.log("Downloaded OPML file");
}
async function getChannelId(url) {
let channelId = url.split('/').pop();
if (!url.startsWith('https://www.youtube.com/channel/')) {
// Fetch the source code of the URL
const response = await fetch(url);
const html = await response.text();
// Parse the source code into a DOM tree
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Find the <link> element with the correct attributes
const linkElement = doc.querySelector("link[rel='canonical'][href^='https://www.youtube.com/channel/']");
// console.log (linkElement)
// Extract the channel ID from the href attribute
const href = linkElement.getAttribute('href');
channelId = href.split('/').pop();
console.info(`Found channel ID ${channelId} for URL ${url}`);
}
return channelId;
}
var start = async function () {
// Firefox requires that the slide out is visible to get the list of
// subscriptions
if (getShowMoreElement() === undefined) {
// click on hamburger menu to show slide out
document.querySelector("#guide-icon").click();
while (getShowMoreElement() === undefined) {
await new Promise(r => setTimeout(r, 500));
}
}
getShowMoreElement().click();
let subscriptions = Array.from(document.querySelectorAll(
"a#endpoint")).filter(
ytTextElement => ytTextElement.innerHTML.includes(
"style-scope ytd-guide-entry-renderer no-transition")).map(elem => elem.title + "\t" +
elem.href);
subscriptions.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
function getShowMoreElement() {
let subscriptionsPanel = document.querySelectorAll("ytd-guide-section-renderer")[1];
if (subscriptionsPanel === undefined) return undefined; // it's still loading
let subscriptionsPanelInner = subscriptionsPanel.querySelector("#items > ytd-guide-collapsible-entry-renderer");
// it's loaded and there are enough subscriptions to show the "Show
// more" button
if (subscriptionsPanelInner !== null) {
return subscriptionsPanel.querySelector("#items > ytd-guide-collapsible-entry-renderer")
.querySelector("#endpoint");
} else {
// it's loaded, but there are just a few subscriptions it doesn't
// really matter what we click on
return document.createElement("foo");
}
}
}
async function main() {
start();
// Get all channel elements
let subscriptions = Array.from(document.querySelectorAll(
"a#endpoint")).filter(
ytTextElement => ytTextElement.innerHTML.includes(
"style-scope ytd-guide-entry-renderer no-transition")).map(
elem => [elem['title'] = elem.title, elem['url'] = elem.href]
);
console.log("Channels obtained: "+subscriptions.length)
const channelData = [];
// Iterate over the channels
for (const channel of subscriptions) {
// Extract the title and URL of the channel
const title = channel[0];
let url = channel[1];
// Convert the URL to the correct format if necessary
url = await getChannelId(url);
url = `https://www.youtube.com/feeds/videos.xml?channel_id=${url}`;
channelData.push({ title, url });
}
console.table(channelData)
// Generate the OPML document
const opml = `
<opml version="1.1">
<body>
<outline text="YouTube Subscriptions" title="YouTube Subscriptions">
${channelData.map(({ title, url }) => `
<outline text="${title}" title="${title}" type="rss" xmlUrl="${url}" />
`).join('')}
</outline>
</body>
</opml>
`;
downloadOPML(opml);
}
main();
@oyale
Copy link
Author

oyale commented Dec 29, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment