New:Socket for Asana Is Now Available.Learn more
Get Started

YouTube Detox

Package Overview
Versions
2
Alerts
File Explorer
Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

yt-detox@example.com - firefox Package Compare versions

Comparing version
1.1.1
to
1.2.0
+172
content-scripts/return-youtube-dislike.js
// Basic test to verify script loading
console.log('YouTube Detox: RYD script starting');
document.addEventListener('DOMContentLoaded', function() {
console.log('YouTube Detox: DOM Content Loaded');
});
// Also try with load event
window.addEventListener('load', function() {
console.log('YouTube Detox: Window Loaded');
});
// Return YouTube Dislike API integration
const RYD_API_URL = 'https://returnyoutubedislikeapi.com/votes';
// Cache to store dislike counts and reduce API calls
const dislikeCache = new Map();
const CACHE_EXPIRY = 1000 * 60 * 60; // 1 hour in milliseconds
// Fetch dislike count for a video
async function fetchDislikes(videoId) {
try {
const cachedData = dislikeCache.get(videoId);
if (cachedData) return cachedData.dislikes;
const response = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', `${RYD_API_URL}?videoId=${videoId}`);
xhr.onload = () => xhr.status === 200 ? resolve(xhr.responseText) : reject();
xhr.onerror = reject;
xhr.send();
});
const data = JSON.parse(response);
if (!data?.dislikes) return null;
dislikeCache.set(videoId, { dislikes: data.dislikes });
return data.dislikes;
} catch {
return null;
}
}
// Format number for display (e.g., 1.5K, 1.2M, or 1,5 млн, 1,2 тыс. for Russian)
function formatNumber(number) {
const isRussian = document.documentElement.lang === 'ru-RU';
if (isRussian) {
if (number >= 1000000) {
// Use comma as decimal separator for Russian locale
return (number / 1000000).toFixed(1).replace('.', ',') + ' млн';
}
if (number >= 1000) {
return (number / 1000).toFixed(1).replace('.', ',') + ' тыс.';
}
return number.toString();
} else {
if (number >= 1000000) return (number / 1000000).toFixed(1) + 'M';
if (number >= 1000) return (number / 1000).toFixed(1) + 'K';
return number.toString();
}
}
// Wait for an element to be present in the DOM
function waitForElement(selector, timeout = 5000) {
return new Promise((resolve, reject) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(() => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
setTimeout(() => {
observer.disconnect();
reject(new Error(`Timeout waiting for ${selector}`));
}, timeout);
});
}
// Update dislike count in the UI
async function updateDislikeUI(dislikeCount) {
try {
// Wait for the dislike button view model
const dislikeButton = await waitForElement('dislike-button-view-model');
if (!dislikeButton) return;
// Find the button element within the view model structure
const button = dislikeButton.querySelector('button.yt-spec-button-shape-next');
if (!button) return;
// Find existing text content container or create new one
let textContainer = button.querySelector('.yt-spec-button-shape-next__button-text-content');
if (!textContainer) {
textContainer = document.createElement('div');
textContainer.className = 'yt-spec-button-shape-next__button-text-content';
textContainer.style.marginLeft = '0px';
// Insert after the icon
const iconDiv = button.querySelector('.yt-spec-button-shape-next__icon');
if (iconDiv && iconDiv.nextSibling) {
button.insertBefore(textContainer, iconDiv.nextSibling);
} else {
button.appendChild(textContainer);
}
}
// Find or create the dislike count span
let dislikeText = textContainer.querySelector('.ytd-dislike-count');
if (!dislikeText) {
dislikeText = document.createElement('span');
dislikeText.className = 'ytd-dislike-count';
textContainer.appendChild(dislikeText);
}
// Update the text and aria-label
if (dislikeCount !== null) {
const formattedCount = formatNumber(dislikeCount);
dislikeText.textContent = formattedCount;
button.setAttribute('aria-label', `${button.getAttribute('aria-label')} и ещё ${formattedCount} пользователям`);
}
} catch (error) {
console.error('[RYD] Error updating UI:', error);
}
}
// Extract video ID from URL
function getVideoId(url = window.location.href) {
try {
const urlObj = new URL(url);
return urlObj.searchParams.get('v');
} catch {
return null;
}
}
// Main function to handle dislike display
async function handleDislikes() {
const videoId = getVideoId();
if (!videoId) {
console.log('[RYD] No video ID found');
return;
}
console.log('[RYD] Processing video:', videoId);
try {
await waitForElement('ytd-watch-flexy');
const dislikes = await fetchDislikes(videoId);
await updateDislikeUI(dislikes);
} catch (error) {
console.error('[RYD] Error:', error);
}
}
// Initialize
document.addEventListener('yt-navigate-finish', handleDislikes);
// Initial load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', handleDislikes);
} else {
handleDislikes();
}
+11
-0

@@ -7,2 +7,13 @@ # Changelog

## [1.2.0] - 2025-05-18
### Added
- Restored dislike counter functionality using Return YouTube Dislike API
- Seamless integration of dislike counts into YouTube's native interface
- Efficient caching system for dislike data
## [1.1.2] - 2025-05-17
### Fixed
- Enhanced Shorts removal by hiding additional rich shelf elements in feeds
- Added comprehensive selectors to catch all variations of Shorts shelves
## [1.1.1] - 2025-05-17

@@ -9,0 +20,0 @@ ### Fixed

+4
-17

@@ -100,18 +100,5 @@ // Redirect shorts, home page and trending to subscription feed

// Block programmatic navigation to shorts and handle home redirects
const pushState = history.pushState;
history.pushState = function() {
pushState.apply(history, arguments);
setTimeout(handleNavigation, 0);
};
const replaceState = history.replaceState;
replaceState = function() {
replaceState.apply(history, arguments);
setTimeout(handleNavigation, 0);
};
// Also handle popstate events (browser back/forward buttons)
window.addEventListener('popstate', () => {
setTimeout(handleNavigation, 0);
});
// Handle history changes without modifying the original methods
window.addEventListener('pushState', handleNavigation);
window.addEventListener('replaceState', handleNavigation);
window.addEventListener('popstate', handleNavigation);
{
"manifest_version": 2,
"name": "YouTube Detox",
"version": "1.1.1",
"version": "1.2.0",
"homepage_url": "https://github.com/lemjoe/yt-detox",

@@ -19,3 +19,4 @@ "description": "Transform YouTube into a focused, distraction-free space. Remove Shorts, recommendations, and stay in control of your viewing experience.",

"permissions": [
"*://*.youtube.com/*"
"*://*.youtube.com/*",
"*://*.returnyoutubedislikeapi.com/*"
],

@@ -31,7 +32,8 @@ "content_scripts": [

"js": [
"content-scripts/youtube-detox.js"
"content-scripts/youtube-detox.js",
"content-scripts/return-youtube-dislike.js"
],
"run_at": "document_start"
"run_at": "document_end"
}
]
}

@@ -21,2 +21,8 @@ # YouTube Detox

- **Restored Dislike Counter**
- Brings back the dislike count using Return YouTube Dislike API
- Seamlessly integrates with YouTube's native interface
- Shows accurate community feedback for better content evaluation
- Includes efficient caching for faster loading
- **Clean, Focused Interface**

@@ -45,3 +51,3 @@ - Centers video content for better viewing experience

For permanent installation, check the Firefox Add-ons store - this extension might already be available there!
For permanent installation, check the [Firefox Add-ons store](https://addons.mozilla.org/addon/yt-detox/)!

@@ -53,3 +59,4 @@ ## Development

- `styles/youtube-detox.css`: CSS rules for hiding elements and layout modifications
- `content-scripts/youtube-detox.js`: JavaScript for handling dynamic content, navigation, and functionality
- `content-scripts/youtube-detox.js`: JavaScript for handling dynamic content and navigation
- `content-scripts/return-youtube-dislike.js`: JavaScript for dislike counter functionality

@@ -56,0 +63,0 @@ ## Contributing

@@ -49,2 +49,24 @@ /* Hide the entire secondary content area */

/* Hide Shorts shelves and sections */
ytd-rich-shelf-renderer:has(#dismissible),
ytd-rich-shelf-renderer:has(#rich-shelf-header-container),
ytd-rich-shelf-renderer:has(#title-text[title="Shorts"]),
ytd-rich-section-renderer:has(#title-text[title="Shorts"]),
ytd-reel-shelf-renderer {
display: none !important;
}
/* Style for dislike button */
dislike-button-view-model button.yt-spec-button-shape-next {
width: auto !important;
min-width: 32px;
}
/* Style for dislike button text content */
.yt-spec-button-shape-next__button-text-content {
margin-left: 0;
min-width: 24px;
padding: 0 6px;
}
/* Hide unwanted sidebar elements */

@@ -51,0 +73,0 @@ ytd-guide-entry-renderer[aria-label="Home"],

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet