- // ==UserScript==
- // @name YouTube Enhancer (Reveal Channel ID)
- // @description Revealing the channel ID, displayed next to the channel handle.
- // @icon https://raw.githubusercontent.com/exyezed/youtube-enhancer/refs/heads/main/extras/youtube-enhancer.png
- // @version 1.3
- // @author exyezed
- // @namespace https://github.com/exyezed/youtube-enhancer/
- // @supportURL https://github.com/exyezed/youtube-enhancer/issues
- // @license MIT
- // @match https://www.youtube.com/*
- // @grant GM_xmlhttpRequest
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- let lastProcessedChannelName = '';
- let isRequestInProgress = false;
- let channelIdCache = {};
-
- const CACHE_DURATION = 7 * 24 * 60 * 60 * 1000;
-
- function loadCacheFromStorage() {
- try {
- const cachedData = localStorage.getItem('youtubeEnhancerCache');
- if (cachedData) {
- const parsedCache = JSON.parse(cachedData);
- const now = Date.now();
- Object.keys(parsedCache).forEach(key => {
- if (now - parsedCache[key].timestamp > CACHE_DURATION) {
- delete parsedCache[key];
- }
- });
- channelIdCache = parsedCache;
- return parsedCache;
- }
- } catch (error) {
- console.error('Error loading cache:', error);
- }
- return {};
- }
-
- function saveToCache(channelName, channelId) {
- try {
- channelIdCache[channelName] = {
- id: channelId,
- timestamp: Date.now()
- };
- localStorage.setItem('youtubeEnhancerCache', JSON.stringify(channelIdCache));
- } catch (error) {
- console.error('Error saving to cache:', error);
- }
- }
-
- function getFromCache(channelName) {
- const cached = channelIdCache[channelName];
- if (cached) {
- const now = Date.now();
- if (now - cached.timestamp <= CACHE_DURATION) {
- return cached.id;
- } else {
- delete channelIdCache[channelName];
- localStorage.setItem('youtubeEnhancerCache', JSON.stringify(channelIdCache));
- }
- }
- return null;
- }
-
- function getChannelNameElement() {
- const selectors = [
- 'yt-content-metadata-view-model .yt-core-attributed-string',
- '#channel-header #channel-name .ytd-channel-name',
- '#channel-header #text.ytd-channel-name',
- '#owner-name a',
- '#channel-name.ytd-video-owner-renderer'
- ];
-
- for (const selector of selectors) {
- const element = document.querySelector(selector);
- if (element) return element;
- }
- return null;
- }
-
- function isChannelPage() {
- const path = window.location.pathname;
- const channelTabs = [
- '/featured', '/videos', '/streams', '/shorts', '/courses',
- '/playlists', '/community', '/podcasts', '/store', '/about',
- '/membership', '/channels', '/search', '/@'
- ];
-
- return channelTabs.some(tab => path.includes(tab));
- }
-
- function isWatchPage() {
- return window.location.pathname.startsWith('/watch');
- }
-
- function waitForElement(selector, timeout = 5000) {
- return new Promise((resolve, reject) => {
- const element = getChannelNameElement();
- if (element) {
- return resolve(element);
- }
-
- const observer = new MutationObserver((mutations, obs) => {
- const element = getChannelNameElement();
- if (element) {
- obs.disconnect();
- resolve(element);
- }
- });
-
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
-
- setTimeout(() => {
- observer.disconnect();
- reject('Timeout waiting for element');
- }, timeout);
- });
- }
-
- function createLoadingElement() {
- const loadingSpan = document.createElement('span');
- loadingSpan.className = 'YouTubeEnhancerLoading';
- loadingSpan.textContent = ' (Loading...)';
- loadingSpan.style.fontSize = '1em';
- loadingSpan.style.color = '#aaaaaa';
- return loadingSpan;
- }
-
- async function addChannelId() {
- if (isWatchPage()) {
- return;
- }
-
- try {
- const channelNameElement = await waitForElement();
-
- if (!channelNameElement || !channelNameElement.textContent ||
- channelNameElement.querySelector('.YouTubeEnhancerRevealChannelID')) {
- return;
- }
-
- const channelName = channelNameElement.textContent.trim().replace('@', '');
-
- if (channelName.length === 0) {
- return;
- }
-
- const cachedChannelId = getFromCache(channelName);
- if (cachedChannelId) {
- appendChannelIdToElement(channelNameElement, cachedChannelId);
- return;
- }
-
- if (channelName === lastProcessedChannelName || isRequestInProgress) {
- return;
- }
-
- isRequestInProgress = true;
- lastProcessedChannelName = channelName;
-
- const loadingElement = createLoadingElement();
- channelNameElement.appendChild(loadingElement);
-
- GM_xmlhttpRequest({
- method: 'GET',
- url: `https://exyezed.vercel.app/api/channel/${channelName}`,
- onload: function(response) {
- isRequestInProgress = false;
- try {
- const loadingIndicator = channelNameElement.querySelector('.YouTubeEnhancerLoading');
- if (loadingIndicator) {
- loadingIndicator.remove();
- }
-
- const data = JSON.parse(response.responseText);
- const channelId = data.channel_id;
-
- saveToCache(channelName, channelId);
-
- appendChannelIdToElement(channelNameElement, channelId);
- } catch (error) {
- console.error('Error parsing API response:', error);
- const loadingIndicator = channelNameElement.querySelector('.YouTubeEnhancerLoading');
- if (loadingIndicator) {
- loadingIndicator.remove();
- }
- }
- },
- onerror: function(error) {
- isRequestInProgress = false;
- console.error('Error fetching channel ID:', error);
- const loadingIndicator = channelNameElement.querySelector('.YouTubeEnhancerLoading');
- if (loadingIndicator) {
- loadingIndicator.remove();
- }
- }
- });
- } catch (error) {
- console.error('Error in addChannelId:', error);
- }
- }
-
- function appendChannelIdToElement(element, channelId) {
- if (!element.querySelector('.YouTubeEnhancerRevealChannelID')) {
- const channelIdLink = document.createElement('a');
- channelIdLink.className = 'YouTubeEnhancerRevealChannelID';
- channelIdLink.textContent = ` (${channelId})`;
- channelIdLink.href = `https://www.youtube.com/channel/${channelId}`;
- channelIdLink.style.fontSize = '1em';
- channelIdLink.style.color = '#3ea6ff';
- channelIdLink.style.textDecoration = 'none';
- channelIdLink.style.cursor = 'pointer';
-
- channelIdLink.addEventListener('mouseover', function() {
- this.style.textDecoration = 'none';
- });
-
- element.appendChild(channelIdLink);
- }
- }
-
- function handleNavigation() {
- lastProcessedChannelName = '';
- isRequestInProgress = false;
-
- if (isChannelPage() && !isWatchPage()) {
- addChannelId();
- }
- }
-
- loadCacheFromStorage();
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.target.nodeName === 'YTD-APP') {
- handleNavigation();
- }
- else if (isChannelPage() && !isWatchPage()) {
- addChannelId();
- }
- }
- });
-
- const urlObserver = new MutationObserver((mutations) => {
- handleNavigation();
- });
-
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
-
- urlObserver.observe(document.querySelector('title'), {
- childList: true
- });
-
- handleNavigation();
-
- document.addEventListener('yt-navigate-start', handleNavigation);
- document.addEventListener('yt-navigate-finish', handleNavigation);
- console.log('YouTube Enhancer (Reveal Channel ID) is running');
- })();