Add an RSS feed link to the video owner section on YouTube video pages
// ==UserScript==
// @name YouTube add Channel RSS Link
// @namespace https://greasyfork.org/en/users/4612-gdorn
// @version 1.0.1
// @description Add an RSS feed link to the video owner section on YouTube video pages
// @author GDorn
// @license MIT
// @match https://www.youtube.com/watch*
// @grant none
// ==/UserScript==
(function () {
'use strict';
let currentVideoId = null; // Tracks the video ID for which the RSS link was added
let isRunning = false; // Semaphore to prevent concurrent execution
/**
* Extracts the channel ID from available data sources.
*/
const getChannelId = () => {
let channelId = null;
// First, try to get it from ytInitialPlayerResponse
if (window.ytInitialPlayerResponse && window.ytInitialPlayerResponse.videoDetails) {
channelId = window.ytInitialPlayerResponse.videoDetails.channelId;
}
// If not found, try to get it from ytInitialData
if (!channelId && window.ytInitialData) {
const data = window.ytInitialData;
if (data && data.contents && data.contents.twoColumnWatchNextResults) {
const owner = data.contents.twoColumnWatchNextResults.results.contents[0].videoOwnerRenderer;
if (owner && owner.ownerEndpoint) {
channelId = owner.ownerEndpoint.browseEndpoint.browseId.replace('UC', '');
}
}
}
// If no channelId found yet, fall back to script tag parsing
if (!channelId) {
const scriptTags = Array.from(document.querySelectorAll('script'));
for (const script of scriptTags) {
if (script.innerHTML.includes('channelId":"UC')) {
const match = script.innerHTML.match(/"channelId":"(UC[0-9A-Za-z-_]+)"/);
if (match) {
channelId = match[1].replace('UC', '');
break;
}
}
}
}
return channelId;
};
/**
* Adds an RSS link to the video owner's section, with retry logic for when the owner box isn't found.
*/
const addRssLink = () => {
const ownerBox = document.querySelector('#owner');
if (ownerBox) {
const existingLink = ownerBox.querySelector('.rss-link');
if (existingLink) existingLink.remove();
// Extract channel ID dynamically
const channelId = getChannelId();
if (!channelId) {
console.log("Failed to find channel ID.");
setTimeout(addRssLink, 500); // Retry if channelId is not found
return;
}
const rssLink = `https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`;
const rssElement = document.createElement('a');
rssElement.href = rssLink;
rssElement.textContent = 'RSS Feed';
rssElement.target = '_blank';
rssElement.className = 'rss-link';
rssElement.style.display = 'block';
rssElement.style.marginTop = '10px';
ownerBox.appendChild(rssElement);
isRunning = false;
console.log("Added RSS link");
} else {
// Retry after 500ms if owner box is not found
console.log("Owner box not found, retrying...");
setTimeout(addRssLink, 500);
}
};
/**
* Replaces the existing RSS link with a reload link.
*/
const replaceWithReloadLink = () => {
const ownerBox = document.querySelector('#owner');
if (ownerBox) {
const existingLink = ownerBox.querySelector('.rss-link');
if (existingLink) existingLink.remove();
const reloadElement = document.createElement('a');
reloadElement.href = location.href;
reloadElement.textContent = 'Reload to update RSS link';
reloadElement.target = '_self';
reloadElement.className = 'rss-link';
reloadElement.style.display = 'block';
reloadElement.style.marginTop = '10px';
ownerBox.appendChild(reloadElement);
isRunning = false;
console.log("Added reload link because video ID changed.");
} else {
// Retry after 500ms if owner box is not found
console.log("Owner box not found, retrying...");
setTimeout(replaceWithReloadLink, 500);
}
};
/**
* Processes the current video page and handles RSS/reload link updates.
* If the owner box is not yet present, it retries every 500ms.
*/
const processVideo = () => {
const videoId = new URLSearchParams(window.location.search).get('v');
if (!videoId) return; // No valid video ID
// If the video ID has changed, replace the RSS link with the reload link.
if (videoId !== currentVideoId) {
if (currentVideoId !== null) {
console.log("Video ID changed. Replacing RSS link with reload link.");
replaceWithReloadLink(); // Replace the RSS link with a reload link
currentVideoId = videoId;
return;
}
currentVideoId = videoId;
isRunning = true;
addRssLink(); // Start the retry logic to wait for the #owner box and add the RSS link
}
};
// Observe DOM changes to detect navigation to a new video
const observer = new MutationObserver(() => {
if (!isRunning) {
processVideo(); // Process the video when necessary
}
});
observer.observe(document.body, { childList: true, subtree: true });
console.log("YouTube Channel RSS Link script initialized.");
processVideo(); // Initial run
})();