YouTube add Channel RSS Link

Add an RSS feed link to the video owner section on YouTube video pages

  1. // ==UserScript==
  2. // @name YouTube add Channel RSS Link
  3. // @namespace https://greasyfork.org/en/users/4612-gdorn
  4. // @version 1.0.1
  5. // @description Add an RSS feed link to the video owner section on YouTube video pages
  6. // @author GDorn
  7. // @license MIT
  8. // @match https://www.youtube.com/watch*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. let currentVideoId = null; // Tracks the video ID for which the RSS link was added
  16. let isRunning = false; // Semaphore to prevent concurrent execution
  17.  
  18. /**
  19. * Extracts the channel ID from available data sources.
  20. */
  21. const getChannelId = () => {
  22. let channelId = null;
  23.  
  24. // First, try to get it from ytInitialPlayerResponse
  25. if (window.ytInitialPlayerResponse && window.ytInitialPlayerResponse.videoDetails) {
  26. channelId = window.ytInitialPlayerResponse.videoDetails.channelId;
  27. }
  28.  
  29. // If not found, try to get it from ytInitialData
  30. if (!channelId && window.ytInitialData) {
  31. const data = window.ytInitialData;
  32. if (data && data.contents && data.contents.twoColumnWatchNextResults) {
  33. const owner = data.contents.twoColumnWatchNextResults.results.contents[0].videoOwnerRenderer;
  34. if (owner && owner.ownerEndpoint) {
  35. channelId = owner.ownerEndpoint.browseEndpoint.browseId.replace('UC', '');
  36. }
  37. }
  38. }
  39.  
  40. // If no channelId found yet, fall back to script tag parsing
  41. if (!channelId) {
  42. const scriptTags = Array.from(document.querySelectorAll('script'));
  43. for (const script of scriptTags) {
  44. if (script.innerHTML.includes('channelId":"UC')) {
  45. const match = script.innerHTML.match(/"channelId":"(UC[0-9A-Za-z-_]+)"/);
  46. if (match) {
  47. channelId = match[1].replace('UC', '');
  48. break;
  49. }
  50. }
  51. }
  52. }
  53.  
  54. return channelId;
  55. };
  56.  
  57. /**
  58. * Adds an RSS link to the video owner's section, with retry logic for when the owner box isn't found.
  59. */
  60. const addRssLink = () => {
  61. const ownerBox = document.querySelector('#owner');
  62. if (ownerBox) {
  63. const existingLink = ownerBox.querySelector('.rss-link');
  64. if (existingLink) existingLink.remove();
  65.  
  66. // Extract channel ID dynamically
  67. const channelId = getChannelId();
  68. if (!channelId) {
  69. console.log("Failed to find channel ID.");
  70. setTimeout(addRssLink, 500); // Retry if channelId is not found
  71. return;
  72. }
  73.  
  74. const rssLink = `https://www.youtube.com/feeds/videos.xml?channel_id=${channelId}`;
  75. const rssElement = document.createElement('a');
  76. rssElement.href = rssLink;
  77. rssElement.textContent = 'RSS Feed';
  78. rssElement.target = '_blank';
  79. rssElement.className = 'rss-link';
  80. rssElement.style.display = 'block';
  81. rssElement.style.marginTop = '10px';
  82.  
  83. ownerBox.appendChild(rssElement);
  84. isRunning = false;
  85. console.log("Added RSS link");
  86. } else {
  87. // Retry after 500ms if owner box is not found
  88. console.log("Owner box not found, retrying...");
  89. setTimeout(addRssLink, 500);
  90. }
  91. };
  92.  
  93. /**
  94. * Replaces the existing RSS link with a reload link.
  95. */
  96. const replaceWithReloadLink = () => {
  97. const ownerBox = document.querySelector('#owner');
  98. if (ownerBox) {
  99. const existingLink = ownerBox.querySelector('.rss-link');
  100. if (existingLink) existingLink.remove();
  101.  
  102. const reloadElement = document.createElement('a');
  103. reloadElement.href = location.href;
  104. reloadElement.textContent = 'Reload to update RSS link';
  105. reloadElement.target = '_self';
  106. reloadElement.className = 'rss-link';
  107. reloadElement.style.display = 'block';
  108. reloadElement.style.marginTop = '10px';
  109. ownerBox.appendChild(reloadElement);
  110. isRunning = false;
  111.  
  112. console.log("Added reload link because video ID changed.");
  113. } else {
  114. // Retry after 500ms if owner box is not found
  115. console.log("Owner box not found, retrying...");
  116. setTimeout(replaceWithReloadLink, 500);
  117. }
  118.  
  119. };
  120.  
  121. /**
  122. * Processes the current video page and handles RSS/reload link updates.
  123. * If the owner box is not yet present, it retries every 500ms.
  124. */
  125. const processVideo = () => {
  126. const videoId = new URLSearchParams(window.location.search).get('v');
  127. if (!videoId) return; // No valid video ID
  128.  
  129. // If the video ID has changed, replace the RSS link with the reload link.
  130. if (videoId !== currentVideoId) {
  131. if (currentVideoId !== null) {
  132. console.log("Video ID changed. Replacing RSS link with reload link.");
  133. replaceWithReloadLink(); // Replace the RSS link with a reload link
  134. currentVideoId = videoId;
  135. return;
  136. }
  137.  
  138. currentVideoId = videoId;
  139. isRunning = true;
  140.  
  141. addRssLink(); // Start the retry logic to wait for the #owner box and add the RSS link
  142. }
  143. };
  144.  
  145. // Observe DOM changes to detect navigation to a new video
  146. const observer = new MutationObserver(() => {
  147. if (!isRunning) {
  148. processVideo(); // Process the video when necessary
  149. }
  150. });
  151.  
  152. observer.observe(document.body, { childList: true, subtree: true });
  153.  
  154. console.log("YouTube Channel RSS Link script initialized.");
  155. processVideo(); // Initial run
  156. })();