diff --git a/.gitignore b/.gitignore
index 729c745b..1da85220 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,26 +1 @@
-# build output
-dist/
-
-# generated types
-.astro/
-
-# dependencies
-node_modules/
-
-# logs
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-
-# environment variables
-.env
-.env.production
-
-# macOS-specific files
-.DS_Store
-
-# Code Editors
-.idea/
-
-.vercel
\ No newline at end of file
+.idea
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..5a8c1c0e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 MrBashyal
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/cleanup.css b/cleanup.css
new file mode 100644
index 00000000..fc21abcf
--- /dev/null
+++ b/cleanup.css
@@ -0,0 +1,9 @@
+/* Hide video thumbnail timestamps */
+ytd-thumbnail-overlay-time-status-renderer.ytd-thumbnail.style-scope {
+ display: none !important;
+}
+
+/* Hide rich sections in the grid (often contains shorts, ads or promoted content) */
+ytd-rich-section-renderer.ytd-rich-grid-renderer.style-scope {
+ display: none !important;
+}
\ No newline at end of file
diff --git a/content.js b/content.js
new file mode 100644
index 00000000..2a83d3f1
--- /dev/null
+++ b/content.js
@@ -0,0 +1,129 @@
+// content.js
+(function() {
+ 'use strict';
+
+ const CONFIG = {
+ CUSTOM_CLASS: 'youtube-enhancer-enabled',
+ FEATURE_FILES: {
+ vid: { js: 'features/vidplayer.js', css: 'features/vidplayer.css' },
+ shorts2long: { js: 'features/shorts2long.js', css: 'features/shorts2long.css' },
+ subsComment: { js: 'features/subs-comment.js', css: 'features/subs-comment.css' },
+ subsbutton: { js: 'features/subsbutton.js', css: 'features/subsbutton.css' },
+ sblock: { js: 'features/shortsblock.js', css: 'features/shortsblock.css' }
+ }
+ };
+
+ let isExtensionEnabled = true;
+ const injectedElements = {};
+ const activeFeatureCleanups = {};
+
+ async function initialize() {
+ console.log('Initializing content script');
+ await chrome.storage.sync.get(['extensionEnabled'], (result) => {
+ isExtensionEnabled = result.extensionEnabled !== false;
+ toggleExtensionFeatures(isExtensionEnabled);
+ });
+
+ await chrome.storage.sync.get(Object.keys(CONFIG.FEATURE_FILES), (features) => {
+ console.log('Feature states:', features);
+ Object.keys(CONFIG.FEATURE_FILES).forEach((feature) => {
+ if (features[feature] !== false) {
+ enableFeature(feature);
+ }
+ });
+ });
+
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
+ if (message.action === 'toggleExtension') {
+ handleToggleExtension(message.enabled);
+ } else if (message.action === 'toggleFeature') {
+ handleToggleFeature(message.feature, message.enabled);
+ }
+ sendResponse({ status: 'success' });
+ return true;
+ });
+ }
+
+ function handleToggleExtension(enabled) {
+ isExtensionEnabled = enabled;
+ toggleExtensionFeatures(enabled);
+ Object.keys(CONFIG.FEATURE_FILES).forEach((feature) => {
+ chrome.storage.sync.get([feature], (result) => {
+ if (result[feature] && isExtensionEnabled) {
+ enableFeature(feature);
+ } else {
+ disableFeature(feature);
+ }
+ });
+ });
+ }
+
+ function handleToggleFeature(feature, enabled) {
+ if (enabled && isExtensionEnabled) {
+ enableFeature(feature);
+ } else {
+ disableFeature(feature);
+ }
+ }
+
+ async function enableFeature(feature) {
+ if (!CONFIG.FEATURE_FILES[feature]) return;
+ try {
+ injectCSS(feature);
+ await loadFeatureJS(feature);
+ if (window.youtubeEnhancer?.[feature]?.init) {
+ const cleanup = window.youtubeEnhancer[feature].init();
+ if (cleanup) activeFeatureCleanups[feature] = cleanup;
+ }
+ console.log(`Enabled feature: ${feature}`);
+ } catch (error) {
+ console.error(`Error enabling ${feature}:`, error);
+ }
+ }
+
+ function disableFeature(feature) {
+ if (!CONFIG.FEATURE_FILES[feature]) return;
+ if (injectedElements[feature]) {
+ injectedElements[feature].remove();
+ delete injectedElements[feature];
+ }
+ if (activeFeatureCleanups[feature]) {
+ activeFeatureCleanups[feature]();
+ delete activeFeatureCleanups[feature];
+ }
+ console.log(`Disabled feature: ${feature}`);
+ }
+
+ function loadFeatureJS(feature) {
+ return new Promise((resolve, reject) => {
+ const script = document.createElement('script');
+ script.src = chrome.runtime.getURL(CONFIG.FEATURE_FILES[feature].js);
+ script.id = `youtube-enhancer-${feature}-script`;
+ script.onload = () => {
+ console.log(`Loaded ${feature}.js`);
+ resolve();
+ };
+ script.onerror = () => reject(new Error(`Failed to load ${feature}.js`));
+ document.head.appendChild(script);
+ });
+ }
+
+ function injectCSS(feature) {
+ const existingLink = document.getElementById(`youtube-enhancer-${feature}-css`);
+ if (existingLink) existingLink.remove();
+ const link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = chrome.runtime.getURL(CONFIG.FEATURE_FILES[feature].css) + '?v=' + Date.now();
+ link.id = `youtube-enhancer-${feature}-css`;
+ link.onload = () => console.log(`CSS loaded for ${feature}`);
+ link.onerror = () => console.error(`CSS failed to load for ${feature}`);
+ document.head.appendChild(link);
+ injectedElements[feature] = link;
+ }
+
+ function toggleExtensionFeatures(enabled) {
+ document.body.classList.toggle(CONFIG.CUSTOM_CLASS, enabled);
+ }
+
+ initialize();
+})();
\ No newline at end of file
diff --git a/element-hiding.css b/element-hiding.css
new file mode 100644
index 00000000..e69de29b
diff --git a/feature-handlers.js b/feature-handlers.js
new file mode 100644
index 00000000..ed4e1dde
--- /dev/null
+++ b/feature-handlers.js
@@ -0,0 +1,31 @@
+// Function to toggle features based on the checkbox state
+function toggleFeature(feature, isEnabled) {
+ chrome.storage.sync.set({ [feature]: isEnabled }, () => {
+ console.log(`${feature} is now ${isEnabled ? 'enabled' : 'disabled'}`);
+ // Send a message to the background script to relay to content scripts
+ chrome.runtime.sendMessage({
+ action: 'toggleFeature',
+ feature,
+ enabled: isEnabled
+ });
+ });
+}
+
+// This function will only run in popup.html context, not in the background
+function initFeatureHandlers() {
+ // Add event listeners to feature toggles
+ document.querySelectorAll('.feature-toggle').forEach(toggle => {
+ toggle.addEventListener('change', function() {
+ const feature = this.getAttribute('data-feature');
+ const isEnabled = this.checked;
+ toggleFeature(feature, isEnabled);
+ });
+ });
+}
+
+// Run the init function only if we're in a browser context with a DOM
+if (typeof document !== 'undefined' && document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initFeatureHandlers);
+} else if (typeof document !== 'undefined') {
+ initFeatureHandlers();
+}
\ No newline at end of file
diff --git a/icon128.png b/icon128.png
new file mode 100644
index 00000000..24fd43b1
Binary files /dev/null and b/icon128.png differ
diff --git a/icon16.png b/icon16.png
new file mode 100644
index 00000000..d2b19519
Binary files /dev/null and b/icon16.png differ
diff --git a/icon32.png b/icon32.png
new file mode 100644
index 00000000..fc7c9268
Binary files /dev/null and b/icon32.png differ
diff --git a/icon48.png b/icon48.png
new file mode 100644
index 00000000..cdd6c2ab
Binary files /dev/null and b/icon48.png differ
diff --git a/img.png b/img.png
new file mode 100644
index 00000000..e573668a
Binary files /dev/null and b/img.png differ
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 00000000..3ae4058e
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,45 @@
+{
+ "manifest_version": 3,
+ "name": "YouTube Enhancer",
+ "version": "1.0",
+ "description": "Enhances YouTube with additional features.",
+ "permissions": [
+ "storage",
+ "scripting",
+ "activeTab",
+ "webRequest"
+ ],
+ "background": {
+ "service_worker": "feature-handlers.js"
+ },
+ "action": {
+ "default_popup": "popup.html",
+ "default_icon": "icons/icon48.png"
+ },
+ "content_scripts": [
+ {
+ "matches": ["*://*.youtube.com/*"],
+ "js": ["content.js"]
+ }
+ ],
+ "web_accessible_resources": [
+ {
+ "resources": [
+ "features/vidplayer.js",
+ "features/vidplayer.css",
+ "features/shorts2long.js",
+ "features/shorts2long.css",
+ "features/subs-comment.js",
+ "features/subs-comment.css",
+ "features/subsbutton.js",
+ "features/subsbutton.css",
+ "features/shortsblock.js",
+ "features/shortsblock.css",
+ "styles/variables.css",
+ "styles/responsive.css",
+ "styles/themes.css"
+ ],
+ "matches": ["*://*.youtube.com/*"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/popup.html b/popup.html
new file mode 100644
index 00000000..1892fa2f
--- /dev/null
+++ b/popup.html
@@ -0,0 +1,84 @@
+
+
+
+
+
+ YouTube Enhancer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Shortz 2.Long
+
+
+
+
+
+
+
+
+ Extension Status
+
+
+
Loading status...
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/popup.js b/popup.js
new file mode 100644
index 00000000..51fc1d50
--- /dev/null
+++ b/popup.js
@@ -0,0 +1,211 @@
+const CONFIG = {
+ STATUS_UPDATE_DELAY: 2000,
+ DEFAULT_ENABLED: true,
+ ANIMATION_DURATION: 300,
+ // Feature names exactly matching content.js CONFIG.FEATURE_FILES keys
+ FEATURES: [
+ "vid",
+ "shorts2long",
+ "subsComment",
+ "sblock",
+ "subsbutton"
+ ]
+};
+
+// Set current year in footer
+document.addEventListener('DOMContentLoaded', () => {
+ document.getElementById('currentYear').textContent = new Date().getFullYear();
+
+ const toggle = document.getElementById('toggleSwitch');
+ const statusText = document.getElementById('statusText');
+ const container = document.querySelector('.container');
+ const settingsIcon = document.getElementById('openSettings');
+ const settingsPanel = document.getElementById('settings');
+
+ // Initially hide the settings panel
+ settingsPanel.style.display = 'none';
+
+ let isProcessingAction = false;
+
+ // Settings icon click handler - toggle settings panel visibility
+ settingsIcon.addEventListener('click', () => {
+ if (settingsPanel.style.display === 'none') {
+ // Show settings panel
+ settingsPanel.style.display = 'block';
+ // Load feature states when settings are opened
+ loadFeatureStates();
+ } else {
+ // Hide settings panel
+ settingsPanel.style.display = 'none';
+ }
+ });
+
+ // Function to load feature states from storage
+ function loadFeatureStates() {
+ // Get all feature keys
+ chrome.storage.sync.get(CONFIG.FEATURES, (result) => {
+ // Update each toggle based on stored state - use the exact same names as content.js
+ CONFIG.FEATURES.forEach(feature => {
+ const toggle = document.querySelector(`.feature-toggle[data-feature="${feature}"]`);
+ if (toggle) {
+ // Default to true if not explicitly set to false
+ toggle.checked = result[feature] !== false;
+ console.log(`Loading ${feature} state:`, toggle.checked);
+ }
+ });
+ });
+ }
+
+ // Add event listeners to feature toggles
+ document.querySelectorAll('.feature-toggle').forEach(toggle => {
+ toggle.addEventListener('change', (e) => {
+ const feature = e.target.dataset.feature;
+ const isEnabled = e.target.checked;
+
+ console.log(`Toggle ${feature}:`, isEnabled);
+
+ // Save feature state to storage
+ const storageUpdate = {};
+ storageUpdate[feature] = isEnabled;
+ chrome.storage.sync.set(storageUpdate, () => {
+ console.log(`Saved ${feature} state:`, isEnabled);
+ });
+
+ // Notify content script about feature change
+ chrome.tabs.query({ url: '*://*.youtube.com/*' }, function(tabs) {
+ tabs.forEach(tab => {
+ chrome.tabs.sendMessage(tab.id, {
+ action: 'toggleFeature',
+ feature: feature,
+ enabled: isEnabled
+ });
+ });
+ });
+
+ // Visual feedback
+ const featureName = document.querySelector(`.feature-card.${feature} .feature-title`)?.textContent || feature;
+ updateStatus(
+ statusText,
+ `${featureName} ${isEnabled ? 'enabled' : 'disabled'}`,
+ 'success'
+ );
+
+ // Reset status after delay
+ setTimeout(() => {
+ updateStatus(
+ statusText,
+ isExtensionEnabled ? 'Extension is Active 🟢' : 'Extension is Inactive 🔴',
+ isExtensionEnabled ? 'active' : 'inactive'
+ );
+ }, CONFIG.STATUS_UPDATE_DELAY);
+ });
+ });
+
+ // Check if current tab is YouTube
+ checkYouTubeTab()
+ .then(isYouTube => {
+ if (!isYouTube) {
+ disableControls(toggle);
+ updateStatus(statusText, 'Open YouTube to use this extension', 'warning');
+ return;
+ }
+
+ // Initialize extension state from storage
+ initializeExtensionState(toggle, statusText);
+ })
+ .catch(error => {
+ console.error('Tab access error:', error);
+ disableControls(toggle);
+ updateStatus(statusText, 'Error accessing tab information', 'error');
+ });
+
+ // Handle messages from runtime
+ chrome.runtime.onMessage.addListener((request) => {
+ if (request.action === "updateToggle") {
+ toggle.checked = request.enabled;
+ updateStatus(
+ statusText,
+ request.enabled ? 'Extension is Active 🟢' : 'Extension is Inactive 🔴',
+ request.enabled ? 'active' : 'inactive'
+ );
+ }
+ });
+
+ // Toggle event listener
+ toggle.addEventListener('change', () => {
+ if (isProcessingAction) return;
+ isProcessingAction = true;
+
+ const isEnabled = toggle.checked;
+ applyPulseAnimation(container);
+
+ // Save to storage
+ chrome.storage.sync.set({ extensionEnabled: isEnabled }, () => {
+ updateStatus(
+ statusText,
+ isEnabled ? 'Extension is Active 🟢' : 'Extension is Inactive 🔴',
+ isEnabled ? 'active' : 'inactive'
+ );
+
+ // Notify content script to enable/disable features
+ chrome.tabs.query({ url: '*://*.youtube.com/*' }, function(tabs) {
+ tabs.forEach(tab => {
+ chrome.tabs.sendMessage(tab.id, { action: 'toggleExtension', enabled: isEnabled });
+ });
+ });
+
+ isProcessingAction = false;
+ });
+ });
+});
+
+// Track global extension state
+let isExtensionEnabled = true;
+
+async function checkYouTubeTab() {
+ try {
+ const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
+ const tab = tabs[0];
+ return !!tab?.url?.match(/^https?:\/\/(www\.)?youtube\.com/);
+ } catch (error) {
+ console.error('Tab check error:', error);
+ return false;
+ }
+}
+
+function initializeExtensionState(toggle, statusText) {
+ toggle.disabled = false;
+
+ // Get extension state from storage
+ chrome.storage.sync.get(['extensionEnabled'], (result) => {
+ isExtensionEnabled = result.extensionEnabled !== false;
+ toggle.checked = isExtensionEnabled;
+ updateStatus(
+ statusText,
+ isExtensionEnabled ? 'Extension is Active 🟢' : 'Extension is Inactive 🔴',
+ isExtensionEnabled ? 'active' : 'inactive'
+ );
+ });
+}
+
+function updateStatus(statusElement, message, type) {
+ statusElement.classList.remove('status-active', 'status-inactive', 'status-warning', 'status-error', 'status-success', 'status-processing');
+ statusElement.classList.add(`status-${type}`);
+ statusElement.style.opacity = '0';
+ setTimeout(() => {
+ statusElement.textContent = message;
+ statusElement.style.opacity = '1';
+ }, 150);
+}
+
+function disableControls(toggle) {
+ toggle.disabled = true;
+ toggle.classList.add('disabled');
+}
+
+function applyPulseAnimation(element) {
+ element.classList.add('pulse');
+ setTimeout(() => {
+ element.classList.remove('pulse');
+ }, CONFIG.ANIMATION_DURATION);
+}
\ No newline at end of file
diff --git a/popup_styles.css b/popup_styles.css
new file mode 100644
index 00000000..1efa99d7
--- /dev/null
+++ b/popup_styles.css
@@ -0,0 +1,392 @@
+/* popup_styles.css */
+
+/* =======================================
+ VARIABLES AND BASE STYLES
+ ======================================= */
+:root {
+ --primary-color: #60f5ac;
+ --secondary-color: #60f5f5;
+ --background-color: #202124;
+ --text-color: #ffffff;
+ --danger-color: #cc0000;
+ --danger-hover: #ff0000;
+ --inactive-color: #f56060;
+ --success-color: #4caf50;
+ --warning-color: #ff9800;
+ --error-color: #f44336;
+ --processing-color: #2196f3;
+
+ --disabled-opacity: 0.6;
+ --border-radius: 8px;
+ --transition-speed: 0.3s;
+
+ --gradient-bg: linear-gradient(135deg, #181818 0%, #202124 100%);
+ --card-bg: rgba(255, 255, 255, 0.05);
+ --card-hover: rgba(255, 255, 255, 0.1);
+ --accent-pink: #ff5c8d;
+ --accent-green: #60f5ac;
+ --accent-blue: #60d5f5;
+}
+
+/* =======================================
+ GENERAL LAYOUT
+ ======================================= */
+body {
+ width: 320px;
+ font-family: 'Poppins', sans-serif;
+ background: var(--gradient-bg);
+ color: var(--text-color);
+ margin: 0;
+ padding: 0;
+ transition: all var(--transition-speed) ease;
+}
+
+.container {
+ padding: 20px;
+ transition: all var(--transition-speed) ease;
+}
+
+button, input[type="checkbox"] {
+ transition: all 0.2s ease;
+}
+
+/* =======================================
+ HEADER STYLING
+ ======================================= */
+/* =======================================
+ HEADER STYLING
+ ======================================= */
+.header {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ margin-bottom: 20px;
+ position: relative;
+ overflow: hidden;
+ border-radius: 12px;
+ padding: 15px;
+ background: rgba(0, 0, 0, 0.2);
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
+}
+
+.header::before {
+ content: "";
+ position: absolute;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ background: linear-gradient(
+ to right,
+ transparent,
+ rgba(255, 255, 255, 0.1),
+ transparent
+ );
+ transform: rotate(45deg);
+ animation: shine 3s infinite;
+}
+
+.header-icon {
+ width: 55px;
+ height: 55px;
+ border-radius: 14px;
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
+ transition: transform 0.3s ease;
+ z-index: 1;
+}
+
+.header-icon:hover {
+ transform: scale(1.05) rotate(5deg);
+}
+
+.header-text {
+ z-index: 1;
+}
+
+.header-title {
+ margin: 0;
+ color: var(--text-color);
+ font-size: 22px;
+ font-weight: 600;
+}
+
+.header-version {
+ font-size: 12px;
+ opacity: 0.7;
+ margin: 2px 0 0;
+}
+
+
+.settings-icon {
+ width: 44px;
+ height: 44px;
+ cursor: pointer;
+ transition: transform 0.3s ease; /* Smooth rotation */
+ fill: var(--accent-blue); /* Match feature card SVGs */
+}
+
+.settings-icon:hover {
+ transform: rotate(360deg);
+}
+
+/* =======================================
+ FEATURE CARDS
+ ======================================= */
+.feature-cards {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 12px;
+ margin-bottom: 20px;
+}
+
+.feature-card {
+ background: var(--card-bg);
+ border-radius: 12px;
+ padding: 12px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ transition: all 0.3s ease;
+ cursor: pointer;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+}
+
+.feature-card:hover {
+ background: var(--card-hover);
+ transform: translateY(-5px);
+ box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
+}
+
+.feature-card svg {
+ width: 24px;
+ height: 24px;
+ margin-bottom: 8px;
+ fill: var(--accent-blue);
+}
+
+.feature-title {
+ font-size: 13px;
+ font-weight: 500;
+ margin: 0;
+}
+
+/* =======================================
+ TOGGLE CONTAINER
+ ======================================= */
+.toggle-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: var(--card-bg);
+ padding: 15px;
+ border-radius: var(--border-radius);
+ transition: all 0.3s ease;
+ margin-bottom: 15px;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+}
+
+.toggle-container:hover {
+ background: var(--card-hover);
+}
+
+#toggle-label {
+ font-weight: 500;
+ color: var(--text-color);
+}
+
+/* =======================================
+ STATUS TEXT
+ ======================================= */
+#statusText {
+ text-align: center;
+ padding: 12px;
+ border-radius: 12px;
+ background: var(--card-bg);
+ margin-bottom: 15px;
+ font-size: 14px;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+ position: relative;
+ overflow: hidden;
+ color: var(--text-color); /* Ensure text is visible */
+}
+
+/* Status Indicators */
+.status-active {
+ color: var(--success-color);
+ font-weight: bold;
+}
+
+.status-inactive {
+ color: var(--inactive-color);
+}
+
+.status-warning {
+ color: var(--warning-color);
+ font-style: italic;
+}
+
+.status-error {
+ color: var(--error-color);
+ font-weight: bold;
+}
+
+.status-success {
+ color: var(--success-color);
+}
+
+.status-processing {
+ color: var(--processing-color);
+ font-style: italic;
+}
+
+#statusText.status-active::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 3px;
+ background: linear-gradient(to right, var(--accent-green), var(--accent-blue));
+ animation: pulse 2s infinite;
+}
+
+/* =======================================
+ ACTION BUTTONS
+ ======================================= */
+.actions {
+ display: flex;
+ gap: 10px;
+ margin-top: 10px;
+}
+
+.action-button {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ background: var(--card-bg);
+ color: var(--text-color);
+ border: none;
+ border-radius: 10px;
+ padding: 12px;
+ cursor: pointer;
+ font-size: 13px;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ font-family: 'Poppins', sans-serif;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+}
+
+.action-button:hover {
+ background: var(--card-hover);
+ transform: translateY(-3px);
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
+}
+
+/* =======================================
+ FOOTER
+ ======================================= */
+footer {
+ text-align: center;
+ font-size: 12px;
+ padding: 15px;
+ background: rgba(0, 0, 0, 0.2);
+ margin-top: 10px;
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+footer a {
+ color: var(--accent-blue);
+ text-decoration: none;
+ transition: color 0.3s ease;
+}
+
+footer a:hover {
+ color: var(--accent-green);
+ text-decoration: underline;
+}
+/* =======================================
+ ENHANCED TOGGLE SWITCH
+ ======================================= */
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 50px;
+ height: 26px;
+}
+
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #444; /* Default/off state */
+ transition: background-color 0.4s; /* Smooth color transition */
+ border-radius: 34px;
+ overflow: hidden;
+}
+
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 20px;
+ width: 20px;
+ left: 3px;
+ bottom: 3px;
+ background-color: white;
+ transition: transform 0.4s; /* Smooth knob movement */
+ border-radius: 50%;
+ z-index: 2;
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
+}
+
+input:checked + .slider {
+ background-color: var(--accent-green, #28a745); /* On state with fallback */
+}
+
+input:checked + .slider:before {
+ transform: translateX(24px); /* Move knob to the right */
+}
+
+/* Hover effect for better UX */
+.switch:hover .slider {
+ opacity: 0.9;
+}
+
+/* Disabled controls */
+input:disabled + .slider,
+.disabled {
+ opacity: var(--disabled-opacity, 0.5); /* Fallback opacity */
+ cursor: not-allowed;
+}
+
+/* Ensure input is hidden but functional */
+input {
+ position: absolute;
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+/* =======================================
+ ANIMATIONS
+ ======================================= */
+@keyframes shine {
+ 0% { transform: translateX(-100%) rotate(45deg); }
+ 50% { transform: translateX(100%) rotate(45deg); }
+ 100% { transform: translateX(-100%) rotate(45deg); }
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 0.6; }
+ 50% { opacity: 1; }
+}
+
+/* Pulse animation for container */
+.pulse {
+ animation: pulse-scale 0.3s
\ No newline at end of file
diff --git a/readme.md b/readme.md
new file mode 100644
index 00000000..12dfa90f
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,116 @@
+# YouTube Enhancer
+
+A lightweight, open-source browser extension that improves your YouTube experience with essential quality-of-life enhancements while respecting your privacy.
+
+
+
+> **Note**: This extension is currently in development and may not be fully functional. Please report any issues you encounter.
+
+## Features
+
+- **Cleaner Interface**: Removes ads, promotional content, and other distractions
+- **Enhanced Video Grid**: Improves the video browsing experience with a better grid layout
+- **Focus Mode**: One-click immersive viewing experience
+- **Advanced Playback Controls**: Easily adjust video speed with a convenient dropdown menu
+- **Performance Optimized**: Minimal impact on browser performance
+
+## Installation
+
+### From Chrome Web Store
+*(Coming soon)*
+
+### Manual Installation (Developer Mode)
+1. Download or clone this repository to your local machine
+2. Open Chrome/Edge/Brave and navigate to `chrome://extensions/`
+3. Enable "Developer mode" at the top-right corner
+4. Click "Load unpacked" and select the extension directory
+5. YouTube Enhancer is now installed!
+
+## Usage
+
+After installation, simply navigate to YouTube.com. The extension will automatically:
+
+- Apply a cleaner interface by removing ads and distractions
+- Implement a better video grid layout for easier browsing
+- Add enhanced playback controls to the video player
+
+### Focus Mode
+Click the "Focus" button (eye icon) in the player controls to toggle focus mode, which dims the page and centers the video for distraction-free viewing.
+
+### Speed Control
+Click the speed button (e.g., "1x") in the player controls to open a dropdown menu and select your preferred playback speed.
+
+## Known Issues
+
+- **Manifest V3 Compatibility**: Some features may not work correctly in Manifest V3 environments
+- **TrustedHTML Warnings**: Occasionally Chrome may log TrustedHTML warnings in the console (these don't affect functionality)
+- **YouTube Updates**: Since YouTube frequently updates its UI, occasional layout issues may occur
+- **Mobile Support**: This extension is designed for desktop browsers only
+
+## Roadmap
+
+- [ ] Add keyboard shortcuts for all features
+- [ ] Implement user preferences panel
+- [ ] Add dark mode toggle
+- [ ] Create more customization options
+- [ ] Add video bookmark feature
+- [ ] Migrate fully to Manifest V3 (Due to focus on Firefox)
+- [ ] Add Clear History system via 1 clcik
+
+## Contributing
+
+Contributions are welcome! If you'd like to help improve YouTube Enhancer:
+
+1. Fork the repository
+2. Create a feature branch (`git checkout -b feature/amazing-feature`)
+3. Commit your changes (`git commit -m 'Add some amazing feature'`)
+4. Push to the branch (`git push origin feature/amazing-feature`)
+5. Open a Pull Request
+
+### Development Setup
+
+```bash
+# Clone the repository
+git clone git@github.com:Prarambha369/YouTube_Enhancer.git
+
+# Navigate to the directory
+cd youtube-enhancer
+
+# Install dependencies (if using npm)
+npm install
+```
+
+## Code Structure
+
+```
+youtube-enhancer/
+├── manifest.json # Extension configuration
+├── background.js # Background service worker
+├── content.js # Main content script loader
+├── popup.html # Extension popup UI
+├── popup.js # Popup functionality
+├── feature-handlers.js # Feature toggle handlers
+├── features/ # Individual feature modules
+│ ├── vidplayer.js # Enhanced video player
+│ ├── shorts2long.js # Shorts to regular video converter
+│ └── shortsblock.js # Shorts blocking functionality
+├── styles/ # CSS styles
+│ └── theme.css # Main theme styles
+└── icons/ # Extension icons
+ ├── icon16.png
+ ├── icon48.png
+ └── icon128.png
+```
+
+## License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
+
+## Acknowledgments
+
+- Thanks to all contributors who have helped improve this extension
+- Inspired by various YouTube enhancement tools in the open-source community
+
+---
+
+**Made with ❤️ by Prarambha**
diff --git a/responsive.css b/responsive.css
new file mode 100644
index 00000000..4327417e
--- /dev/null
+++ b/responsive.css
@@ -0,0 +1,31 @@
+/* Responsive breakpoints using CSS variables */
+:root {
+ --breakpoint-xs: 480px;
+ --breakpoint-sm: 768px;
+ --breakpoint-md: 992px;
+ --breakpoint-lg: 1200px;
+
+ /* Responsive adjustments */
+ --video-radius-mobile: 6px;
+ --video-radius-desktop: 10px;
+}
+
+/* Apply different styles based on viewport */
+@media (max-width: 768px) {
+ :root {
+ --border-radius-video: var(--video-radius-mobile);
+ --feature-vidplayer-progress-height: 6px;
+ --feature-vidplayer-scrubber-size: 12px;
+ }
+
+ /* Mobile-specific styles */
+ .ytp-chrome-bottom {
+ padding-bottom: var(--spacing-xs) !important;
+ }
+}
+
+@media (min-width: 769px) {
+ :root {
+ --border-radius-video: var(--video-radius-desktop);
+ }
+}
\ No newline at end of file
diff --git a/shorts2long.css b/shorts2long.css
new file mode 100644
index 00000000..e69de29b
diff --git a/shorts2long.js b/shorts2long.js
new file mode 100644
index 00000000..732875f5
--- /dev/null
+++ b/shorts2long.js
@@ -0,0 +1,137 @@
+// features/shorts2long.js
+
+(function() {
+ 'use strict';
+
+ function init() {
+ // Handle current page if it's a shorts URL
+ redirectIfShortsPage();
+
+ // Set up observers to catch and modify any shorts links on the page
+ setupLinkObserver();
+
+ return cleanup; // Return cleanup function for content.js
+ }
+
+ // Redirect current page if it's a shorts URL
+ function redirectIfShortsPage() {
+ const currentUrl = new URL(window.location.href);
+ if (currentUrl.pathname.startsWith('/shorts/')) {
+ const videoId = currentUrl.pathname.split('/shorts/')[1].split('/')[0];
+ if (videoId) {
+ console.log('YouTube Enhancer: Converting shorts URL to standard video URL');
+ const newUrl = new URL('/watch', currentUrl.origin);
+ newUrl.searchParams.set('v', videoId);
+
+ // Preserve other search parameters
+ currentUrl.searchParams.forEach((value, key) => {
+ if (key !== 'v') {
+ newUrl.searchParams.set(key, value);
+ }
+ });
+
+ // Preserve hash if present
+ newUrl.hash = currentUrl.hash;
+
+ // Use history API instead of replace to avoid redirect loops
+ window.history.replaceState({}, '', newUrl.toString());
+ // Force page reload if needed
+ window.location.href = newUrl.toString();
+ }
+ }
+ }
+
+ // Intercept clicks on shorts links
+ function setupLinkObserver() {
+ // Intercept all clicks on the document
+ document.addEventListener('click', function(event) {
+ // Look for link elements in the event path
+ const path = event.composedPath();
+ for (const element of path) {
+ if (element.tagName === 'A' && element.href && element.href.includes('/shorts/')) {
+ // Prevent default navigation
+ event.preventDefault();
+ event.stopPropagation();
+
+ // Convert shorts URL to watch URL
+ const shortsUrl = new URL(element.href);
+ const videoId = shortsUrl.pathname.split('/shorts/')[1]?.split('/')[0];
+
+ if (videoId) {
+ // Create watch URL
+ const watchUrl = new URL('/watch', shortsUrl.origin);
+ watchUrl.searchParams.set('v', videoId);
+
+ // Preserve other parameters
+ shortsUrl.searchParams.forEach((value, key) => {
+ if (key !== 'v') {
+ watchUrl.searchParams.set(key, value);
+ }
+ });
+
+ // Navigate to the watch URL
+ window.location.href = watchUrl.toString();
+ return;
+ }
+ }
+ }
+ }, true); // Use capture phase to catch events before they reach the target
+
+ // Also modify all existing shorts links on the page
+ modifyExistingShortsLinks();
+
+ // Set up observer to modify shorts links as they're added to the DOM
+ const observer = new MutationObserver((mutations) => {
+ for (const mutation of mutations) {
+ if (mutation.type === 'childList' && mutation.addedNodes.length) {
+ modifyExistingShortsLinks();
+ }
+ }
+ });
+
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true
+ });
+ }
+
+ // Find and modify all shorts links on the page
+ function modifyExistingShortsLinks() {
+ const shortsLinks = document.querySelectorAll('a[href*="/shorts/"]');
+ shortsLinks.forEach(link => {
+ if (!link.hasAttribute('data-shorts-converted')) {
+ const shortsUrl = new URL(link.href);
+ const videoId = shortsUrl.pathname.split('/shorts/')[1]?.split('/')[0];
+
+ if (videoId) {
+ const watchUrl = new URL('/watch', shortsUrl.origin);
+ watchUrl.searchParams.set('v', videoId);
+
+ // Preserve other parameters
+ shortsUrl.searchParams.forEach((value, key) => {
+ if (key !== 'v') {
+ watchUrl.searchParams.set(key, value);
+ }
+ });
+
+ // Update the href
+ link.href = watchUrl.toString();
+
+ // Mark as converted
+ link.setAttribute('data-shorts-converted', 'true');
+ }
+ }
+ });
+ }
+
+ // Cleanup function for when feature is disabled
+ function cleanup() {
+ // Nothing specific to clean up as we're not adding persistent listeners
+ // that would need to be removed when the feature is disabled
+ console.log('Shorts2Long: Cleanup called');
+ }
+
+ // Register with YouTube Enhancer
+ window.youtubeEnhancer = window.youtubeEnhancer || {};
+ window.youtubeEnhancer.shorts2long = { init };
+})();
\ No newline at end of file
diff --git a/shortsblock.css b/shortsblock.css
new file mode 100644
index 00000000..a2102568
--- /dev/null
+++ b/shortsblock.css
@@ -0,0 +1,66 @@
+/* Shorts Blocker Feature */
+
+/* Hide Shorts in sidebar */
+ytd-guide-entry-renderer[aria-label="Shorts"] {
+ display: none !important;
+}
+
+/* Hide Shorts tab (3rd tab) */
+yt-tab-shape.yt-tab-shape-wiz--host-clickable.yt-tab-shape-wiz:nth-of-type(3) {
+ display: none !important;
+}
+
+/* Hide Shorts chip in the filter chips bar */
+yt-chip-cloud-chip-renderer[chip-style="STYLE_DEFAULT"][title="Shorts"] {
+ display: none !important;
+}
+
+/* Hide Shorts shelf on homepage */
+ytd-rich-shelf-renderer[is-shorts] {
+ display: none !important;
+}
+
+/* Hide Shorts in search results */
+ytd-video-renderer:has(a[href^="/shorts/"]) {
+ display: none !important;
+}
+
+/* Hide the Shorts button in the main navigation */
+ytd-mini-guide-entry-renderer[aria-label="Shorts"],
+ytd-guide-entry-renderer[aria-label="Shorts"] {
+ display: none !important;
+}
+
+/* Hide Shorts sections in recommendations */
+ytd-reel-shelf-renderer {
+ display: none !important;
+}
+
+/* Hide video thumbnail timestamps */
+ytd-thumbnail-overlay-time-status-renderer.ytd-thumbnail.style-scope {
+ display: none !important;
+}
+
+/* Hide rich sections in the grid (often contains shorts, ads or promoted content) */
+ytd-rich-section-renderer.ytd-rich-grid-renderer.style-scope {
+ display: none !important;
+}
+
+/* Hide shorts player when accessed directly */
+ytd-shorts, ytd-reel-video-renderer {
+ display: none !important;
+}
+
+/* Show message instead */
+ytd-shorts:after {
+ content: "Shorts have been blocked by YouTube Enhancer";
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 300px;
+ font-size: 18px;
+ color: var(--yt-text-primary, #fff);
+ background: var(--enhancer-bg-dark, #202124);
+ border-radius: var(--border-radius-md, 8px);
+ margin: 20px 0;
+}
diff --git a/shortsblock.js b/shortsblock.js
new file mode 100644
index 00000000..e69de29b
diff --git a/styles.css b/styles.css
new file mode 100644
index 00000000..8c2112b2
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,213 @@
+/* Existing styles */
+/* Video Player Styles */
+#movie_player,
+.html5-video-container,
+.video-stream {
+ border-radius: 10px !important;
+}
+
+ytd-watch-flexy[theater] #player-theater-container {
+ border-radius: 10px !important;
+}
+
+/* Progress Bar Styles */
+.ytp-chrome-bottom {
+ border-radius: 0 0 10px 10px !important;
+}
+
+.ytp-progress-bar-container {
+ height: 8px !important;
+}
+
+.ytp-progress-bar {
+ height: 8px !important;
+ border-radius: 10px !important;
+}
+
+.ytp-scrubber-button {
+ width: 16px !important;
+ height: 16px !important;
+ border-radius: 50% !important;
+ background-color: #ff0000 !important;
+}
+
+/* Layout Fixes */
+::-webkit-scrollbar {
+ display: none;
+}
+
+html, body {
+ overflow-x: hidden !important;
+}
+
+/* Element Visibility Control */
+#ytd-mini-guide-renderer {
+ display: block !important;
+ visibility: visible !important;
+}
+
+.ytd-guide-renderer,
+ytd-rich-section-renderer,
+ytd-reel-shelf-renderer {
+ display: none !important;
+ pointer-events: none !important;
+}
+
+/* Notification Button Positioning */
+ytd-mini-guide-entry-renderer:nth-of-type(4)
++ .ytd-notification-topbar-button-renderer {
+ margin-top: 8px !important;
+ order: 5 !important;
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+}
+
+.ytd-notification-topbar-button-renderer {
+ pointer-events: auto !important;
+ opacity: 1 !important;
+ visibility: visible !important;
+}
+
+/* Hide main guide renderer */
+ytd-guide-renderer,
+#guide-content,
+#guide-inner-content,
+tp-yt-app-drawer#guide {
+ display: none !important;
+ visibility: hidden !important;
+ opacity: 0 !important;
+}
+
+/* Existing styles */
+/* Video Player Styles */
+#movie_player,
+.html5-video-container,
+.video-stream {
+ border-radius: 10px !important;
+}
+
+ytd-watch-flexy[theater] #player-theater-container {
+ border-radius: 10px !important;
+}
+
+/* Progress Bar Styles */
+.ytp-chrome-bottom {
+ border-radius: 0 0 10px 10px !important;
+}
+
+.ytp-progress-bar-container {
+ height: 8px !important;
+}
+
+.ytp-progress-bar {
+ height: 8px !important;
+ border-radius: 10px !important;
+}
+
+.ytp-scrubber-button {
+ width: 16px !important;
+ height: 16px !important;
+ border-radius: 50% !important;
+ background-color: #ff0000 !important;
+}
+
+/* Layout Fixes */
+::-webkit-scrollbar {
+ display: none;
+}
+
+html, body {
+ overflow-x: hidden !important;
+}
+
+/* Element Visibility Control */
+#ytd-mini-guide-renderer {
+ display: block !important;
+ visibility: visible !important;
+}
+
+/* Use CSS classes for hiding elements instead of removing from DOM */
+.youtube-enhancer-hidden {
+ display: none !important;
+ pointer-events: none !important;
+ visibility: hidden !important;
+}
+
+/* Notification Button Positioning */
+.moved-notification-btn {
+ margin-top: 8px !important;
+ order: 5 !important;
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+}
+
+.ytd-notification-topbar-button-renderer {
+ pointer-events: auto !important;
+ opacity: 1 !important;
+ visibility: visible !important;
+}
+
+/* Hide main guide renderer */
+ytd-guide-renderer,
+#guide-content,
+#guide-inner-content,
+tp-yt-app-drawer#guide {
+ display: none !important;
+ visibility: hidden !important;
+ opacity: 0 !important;
+}
+
+/* PiP Theme Styles */
+.enhanced-pip .control-item.control-button {
+ background-color: rgba(0, 0, 0, 0) !important;
+}
+
+.enhanced-pip #close,
+.enhanced-pip #unpip {
+ fill: white !important;
+ padding: 5px !important;
+ top: 2px !important;
+}
+
+.enhanced-pip #close:hover,
+.enhanced-pip #unpip:hover {
+ background-color: rgba(255, 255, 255, 0.1) !important;
+}
+
+.enhanced-pip #controls {
+ margin: 0 !important;
+ height: 100% !important;
+ width: 100% !important;
+ transition: background-color 160ms linear;
+}
+
+.enhanced-pip #controls:hover {
+ background-color: rgba(0, 0, 0, 0.69) !important;
+}
+
+.enhanced-pip #controls-bottom-gradient {
+ display: none !important;
+}
+
+.enhanced-pip #controls-bottom {
+ display: flex;
+ flex-direction: column-reverse;
+ bottom: 5px !important;
+}
+
+.enhanced-pip #scrubber {
+ height: 5px !important;
+ transition: all 160ms linear !important;
+}
+
+.enhanced-pip #scrubber:hover {
+ height: 8px !important;
+}
+
+.enhanced-pip #scrubber::-moz-range-thumb {
+ width: 0 !important;
+ height: 0 !important;
+ border: none !important;
+}
\ No newline at end of file
diff --git a/subs-comment.css b/subs-comment.css
new file mode 100644
index 00000000..d6c4ec4e
--- /dev/null
+++ b/subs-comment.css
@@ -0,0 +1,19 @@
+.subscriber-count {
+ font-size: 1.1rem;
+ line-height: normal;
+ color: var(--yt-text-secondary);
+ background-color: var(--yt-secondary);
+ margin-left: var(--spacing-xs);
+ padding: 1px 3px;
+ border-radius: var(--border-radius-sm);
+}
+.youtube-enhancer-comments {
+ background: var(--feature-subscomment-bg) !important;
+ border-radius: var(--border-radius-md);
+ padding: var(--spacing-md) !important;
+}
+
+.youtube-enhancer-pinned-comment {
+ border-left: 3px solid var(--feature-subscomment-pinned-border) !important;
+ background: rgba(96, 245, 172, 0.1) !important;
+}
\ No newline at end of file
diff --git a/subs-comment.js b/subs-comment.js
new file mode 100644
index 00000000..30b387fa
--- /dev/null
+++ b/subs-comment.js
@@ -0,0 +1,81 @@
+// YouTube Enhancer - Subscription & Comments Feature
+ (function() {
+ 'use strict';
+
+ // Configuration for subscription and comments features
+ const SELECTORS = {
+ COMMENTS_SECTION: 'ytd-comments#comments',
+ SUBSCRIPTION_BUTTON: 'ytd-subscribe-button-renderer',
+ COMMENTS_COUNT: 'ytd-comments-header-renderer #count',
+ PINNED_COMMENT: 'ytd-comment-thread-renderer[is-pinned]'
+ };
+
+ // Initialize the feature
+ function init() {
+ // Check if extension is enabled
+ chrome.storage.local.get(['extensionEnabled', 'commentEnhancementsEnabled'], (result) => {
+ if (result.extensionEnabled === false) return;
+
+ // Apply enhancements if enabled
+ if (result.commentEnhancementsEnabled !== false) {
+ enhanceComments();
+ }
+
+ enhanceSubscriptions();
+ observeChanges();
+ });
+ }
+
+ // Enhance comments section
+ function enhanceComments() {
+ // Apply styling to comments section
+ const commentsSection = document.querySelector(SELECTORS.COMMENTS_SECTION);
+ if (commentsSection) {
+ commentsSection.classList.add('youtube-enhancer-comments');
+
+ // Highlight pinned comments
+ const pinnedComment = document.querySelector(SELECTORS.PINNED_COMMENT);
+ if (pinnedComment) {
+ pinnedComment.classList.add('youtube-enhancer-pinned-comment');
+ }
+ }
+ }
+
+ // Enhance subscription button
+ function enhanceSubscriptions() {
+ const subButton = document.querySelector(SELECTORS.SUBSCRIPTION_BUTTON);
+ if (subButton) {
+ subButton.classList.add('youtube-enhancer-subscription');
+ }
+ }
+
+ // Observe DOM changes to apply enhancements to dynamically loaded content
+ function observeChanges() {
+ const observer = new MutationObserver((mutations) => {
+ // Check if comments section is added
+ const commentsSection = document.querySelector(SELECTORS.COMMENTS_SECTION);
+ if (commentsSection && !commentsSection.classList.contains('youtube-enhancer-comments')) {
+ enhanceComments();
+ }
+
+ // Check for subscription button changes
+ const subButton = document.querySelector(SELECTORS.SUBSCRIPTION_BUTTON);
+ if (subButton && !subButton.classList.contains('youtube-enhancer-subscription')) {
+ enhanceSubscriptions();
+ }
+ });
+
+ // Start observing
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true
+ });
+ }
+
+ // Initialize on page load
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+ } else {
+ init();
+ }
+ })();
\ No newline at end of file
diff --git a/subsbutton.css b/subsbutton.css
new file mode 100644
index 00000000..6e068f2b
--- /dev/null
+++ b/subsbutton.css
@@ -0,0 +1,29 @@
+:root {
+ --sub-red-btn: #cc0000;
+ --sub-white-text-btn: #f2f2f2;
+ --sub-black-bg-btn: #303030;
+}
+
+/* General Subscribe Button Styling */
+#subscribe yt-button-shape button,
+#subscribe-button yt-button-shape button,
+ytd-subscribe-button-renderer yt-button-shape button,
+#subscribe yt-button-shape a[aria-label*="Subscribe"],
+#subscribe-button yt-button-shape a[aria-label*="Subscribe"],
+ytd-subscribe-button-renderer yt-button-shape a[aria-label*="Subscribe"] {
+ color: var(--yt-spec-static-brand-white) !important;
+ background-color: var(--yt-spec-brand-button-background) !important;
+}
+
+/* Dark Mode - Unsubscribe Button */
+html[dark] ytd-shorts #subscribe-button yt-button-shape button.yt-spec-button-shape-next.yt-spec-button-shape-next--tonal {
+ color: var(--yt-spec-text-secondary) !important;
+ background-color: var(--yt-spec-badge-chip-background) !important;
+}
+
+/* Subscribed Button on Channels - Optimized */
+[page-subtype="channels"] #subscribe yt-button-shape button[aria-label],
+#channel.ytd-grid-channel-renderer #subscribe yt-button-shape button[aria-label] {
+ color: var(--yt-spec-static-brand-white) !important;
+ background-color: var(--yt-spec-brand-button-background) !important;
+}
diff --git a/subsbutton.js b/subsbutton.js
new file mode 100644
index 00000000..e69de29b
diff --git a/themes.css b/themes.css
new file mode 100644
index 00000000..c206a1b0
--- /dev/null
+++ b/themes.css
@@ -0,0 +1,32 @@
+/* Theme system with CSS variables */
+:root {
+ /* Default theme (light) */
+ --theme-bg: #f9f9f9;
+ --theme-card: #ffffff;
+ --theme-text: #212121;
+ --theme-border: #e0e0e0;
+}
+
+/* Dark theme */
+.theme-dark {
+ --theme-bg: #202124;
+ --theme-card: #303134;
+ --theme-text: #e8eaed;
+ --theme-border: #5f6368;
+}
+
+/* Accent colors */
+.accent-green {
+ --enhancer-primary: #60f5ac;
+ --enhancer-secondary: #4acf8f;
+}
+
+.accent-blue {
+ --enhancer-primary: #60d5f5;
+ --enhancer-secondary: #4ab3cf;
+}
+
+.accent-pink {
+ --enhancer-primary: #ff5c8d;
+ --enhancer-secondary: #d84a74;
+}
\ No newline at end of file
diff --git a/trash.png b/trash.png
new file mode 100644
index 00000000..a6b73b62
Binary files /dev/null and b/trash.png differ
diff --git a/variables.css b/variables.css
new file mode 100644
index 00000000..7ae979ac
--- /dev/null
+++ b/variables.css
@@ -0,0 +1,74 @@
+/* YouTube Enhancer - CSS Variables */
+:root {
+ /* Color Palette */
+ --yt-primary: #f00;
+ --yt-primary-hover: #c00;
+ --yt-secondary: #606060;
+ --yt-bg-dark: #181818;
+ --yt-bg-light: #f9f9f9;
+ --yt-text-primary: #fff;
+ --yt-text-secondary: #aaa;
+
+ /* Extension Theme Colors */
+ --enhancer-primary: #60f5ac;
+ --enhancer-secondary: #60f5f5;
+ --enhancer-accent: #ff5c8d;
+ --enhancer-bg-dark: #202124;
+ --enhancer-bg-card: rgba(255, 255, 255, 0.05);
+ --enhancer-bg-card-hover: rgba(255, 255, 255, 0.1);
+
+ /* UI Elements */
+ --border-radius-sm: 4px;
+ --border-radius-md: 8px;
+ --border-radius-lg: 12px;
+ --border-radius-xl: 16px;
+ --border-radius-video: 10px;
+
+ /* Spacing */
+ --spacing-xs: 4px;
+ --spacing-sm: 8px;
+ --spacing-md: 16px;
+ --spacing-lg: 24px;
+ --spacing-xl: 32px;
+
+ /* Animations */
+ --transition-fast: 0.2s ease;
+ --transition-medium: 0.3s ease;
+ --transition-slow: 0.5s ease;
+
+ /* Shadows */
+ --shadow-sm: 0 2px 5px rgba(0, 0, 0, 0.1);
+ --shadow-md: 0 4px 10px rgba(0, 0, 0, 0.1);
+ --shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.1);
+
+ /* Status Colors */
+ --status-success: #4caf50;
+ --status-warning: #ff9800;
+ --status-error: #f44336;
+ --status-info: #2196f3;
+
+ /* Z-index layers */
+ --z-below: -1;
+ --z-normal: 1;
+ --z-above: 10;
+ --z-modal: 100;
+ --z-tooltip: 1000;
+
+ /* Feature-specific */
+ --feature-vidplayer-progress-height: 8px;
+ --feature-vidplayer-scrubber-size: 16px;
+ --feature-subscomment-bg: #1a1a1a;
+ --feature-subscomment-pinned-border: #60f5ac;
+}
+
+/* Dark theme specific variables */
+html[dark] {
+ --yt-bg: var(--yt-bg-dark);
+ --yt-text: var(--yt-text-primary);
+}
+
+/* Light theme specific variables */
+html:not([dark]) {
+ --yt-bg: var(--yt-bg-light);
+ --yt-text: var(--yt-secondary);
+}
\ No newline at end of file
diff --git a/vidplayer.css b/vidplayer.css
new file mode 100644
index 00000000..63de4895
--- /dev/null
+++ b/vidplayer.css
@@ -0,0 +1,686 @@
+/* features/vidplayer.css */
+#movie_player {
+ width: 100% !important;
+ height: auto !important;
+ min-height: 360px !important;
+ position: relative !important;
+ top: 0 !important;
+ left: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border-radius: 10px !important;
+ overflow: hidden !important;
+}
+
+.html5-video-container {
+ width: 100% !important;
+ height: 100% !important;
+ border-radius: 10px !important;
+ display: flex !important;
+ justify-content: center !important;
+ align-items: center !important;
+ overflow: hidden !important;
+}
+
+.video-stream {
+ width: 100% !important;
+ height: 100% !important;
+ border-radius: 10px !important;
+}
+
+ytd-watch-flexy[theater] #player-theater-container {
+ height: 100vh !important;
+ border-radius: 10px !important;
+}
+
+.ytp-chrome-bottom {
+ width: 100% !important;
+ border-radius: 0 0 10px 10px !important;
+}
+
+.ytp-progress-bar-container {
+ width: 100% !important;
+ height: 8px !important;
+ margin: 0 !important;
+ border-radius: 10px !important;
+ display: flex !important;
+ justify-content: center !important;
+}
+
+.ytp-progress-bar {
+ width: 100% !important;
+ height: 8px !important;
+ border-radius: 10px !important;
+}
+
+.ytp-scrubber-button {
+ width: 16px !important;
+ height: 16px !important;
+ border-radius: 50% !important;
+ background-color: #ff0000 !important;
+}
+
+::-webkit-scrollbar {
+ display: none !important;
+}
+
+html, body {
+ overflow-x: hidden !important;
+}
+
+/* Hide YouTube elements */
+ytd-app #header > ytd-rich-grid-renderer,
+ytd-app ytd-mini-guide-renderer,
+ytd-rich-section-renderer > ytd-rich-shelf-renderer,
+ytd-masthead #end,
+ytd-menu-renderer > .yt-spec-icon-shape > div,
+#voice-search-button > yt-button-shape,
+#guide-button,
+#country-code,
+#logo-icon > .yt-spec-icon-shape > div,
+ytd-comment-view-model > .yt-img-shadow,
+ytd-comment-view-model,
+#author-thumbnail > .yt-img-shadow,
+ytd-comment-simplebox-renderer,
+#avatar > .yt-img-shadow,
+ytd-video-owner-renderer,
+#like-button,
+#dislike-button,
+#menu > ytd-watch-metadata,
+#subscribe-button-shape,
+ytd-comments-header-renderer #title,
+ytd-merch-shelf-renderer,
+ytd-reel-shelf-renderer,
+.yt-chip-cloud-renderer,
+.YtVideoMetadataCarouselViewModelHost,
+#pinned-comment-badge,
+.ytd-live-chat-frame > yt-button-shape,
+#show-hide-button > .ytd-live-chat-frame,
+ytd-comment-view-model #published-time-text,
+#creator-heart-button,
+#actions,
+#foreground-content,
+ytd-reel-shelf-renderer,
+ytd-video-description-infocards-section-renderer,
+a.bold.yt-formatted-string,
+.ytp-next-button,
+.ytp-autonav-toggle-button,
+#playlist-actions,
+.ytd-playlist-panel-renderer.publisher,
+#header-top-row,
+ytd-comment-replies-renderer > .yt-img-shadow,
+ytd-comment-replies-renderer,
+.yt-spec-avatar-shape--cairo-refresh,
+#avatar-section,
+ytd-watch-next-secondary-results-renderer > ytd-item-section-renderer,
+#secondary,
+.YtSearchboxComponentClearButton,
+.yt-lockup-view-model-wiz--collection-stack-2 > .yt-lockup-view-model-wiz__content-image > .yt-collection-thumbnail-view-model > .collections-stack-wiz > div > .collections-stack-wiz__collection-stack2 {
+ display: none !important;
+}
+
+/* VidPlayz - Enhanced Video Player CSS */
+
+/* Make masthead semi-transparent */
+#movie_player.vidplayz-enhanced ~ ytd-app #masthead > .ytd-masthead.style-scope {
+ background-color: rgba(33, 33, 33, 0.4) !important; /* 40% opacity */
+ box-shadow: none !important;
+ transition: background-color 0.3s ease !important;
+}
+
+#movie_player.vidplayz-enhanced ~ ytd-app #masthead:hover > .ytd-masthead.style-scope {
+ background-color: rgba(33, 33, 33, 0.6) !important; /* Slightly more visible on hover */
+}
+
+/* Center the player controls and make them semi-transparent */
+#movie_player.vidplayz-enhanced .ytp-chrome-bottom {
+ display: flex !important;
+ justify-content: center !important;
+ width: 80% !important; /* Not 100% so it's centered */
+ left: 50% !important;
+ transform: translateX(-50%) !important;
+ background-color: rgba(0, 0, 0, 0.4) !important; /* 40% opacity */
+ border-radius: var(--border-radius-md) !important;
+ margin-bottom: 10px !important;
+}
+
+/* Make controls hover effect subtle */
+#movie_player.vidplayz-enhanced .ytp-chrome-bottom:hover {
+ background-color: rgba(0, 0, 0, 0.6) !important; /* Slightly more visible on hover */
+}
+
+/* Center the progress bar */
+#movie_player.vidplayz-enhanced .ytp-progress-bar-container {
+ width: 85% !important;
+ margin: 0 auto !important;
+ display: flex !important;
+ justify-content: center !important;
+}
+
+/* Make thumbnail overlays semi-transparent */
+.ytp-ce-element,
+.ytp-ce-covering-overlay,
+.ytp-ce-element-shadow,
+.ytp-ce-covering-image,
+.ytp-ce-expanding-image,
+.ytp-ce-element.ytp-ce-channel.ytp-ce-channel-this,
+.ytp-ce-element.ytp-ce-video.ytp-ce-element-show {
+ opacity: 0.4 !important;
+ transition: opacity 0.3s ease !important;
+}
+
+/* Increase opacity on hover */
+.ytp-ce-element:hover {
+ opacity: 0.8 !important;
+}
+
+/* Hide guide/sidebar when video player is enhanced */
+#movie_player.vidplayz-enhanced ~ ytd-app #guide-inner-content,
+#movie_player.vidplayz-enhanced ~ ytd-app tp-yt-app-drawer,
+#movie_player.vidplayz-enhanced ~ ytd-app ytd-mini-guide-renderer {
+ display: none !important;
+}
+
+/* Adjust the main content when guide is hidden */
+#movie_player.vidplayz-enhanced ~ ytd-app #contentContainer {
+ margin-left: 0 !important;
+}
+
+#movie_player.vidplayz-enhanced ~ ytd-app #page-manager {
+ margin-left: 0 !important;
+ width: 100% !important;
+}
+
+/* Center the player in the page */
+#movie_player.vidplayz-enhanced ~ ytd-app ytd-watch-flexy:not([theater]) #primary-inner {
+ display: flex !important;
+ justify-content: center !important;
+}
+
+/* Video container enhancements */
+#movie_player.vidplayz-enhanced {
+ --vidplayZ-primary: var(--enhancer-primary, #60f5ac);
+ --vidplayZ-secondary: var(--enhancer-secondary, #60f5f5);
+ --vidplayZ-background: rgba(0, 0, 0, 0.4); /* Set to 40% opacity */
+ --vidplayZ-radius: var(--border-radius-video, 10px);
+ --vidplayZ-controls-height: 48px;
+
+ border-radius: var(--vidplayZ-radius) !important;
+ overflow: hidden !important;
+ transition: all var(--transition-medium, 0.3s ease) !important;
+}
+
+/* Video element enhancements */
+#movie_player.vidplayz-enhanced .html5-video-container,
+#movie_player.vidplayz-enhanced .html5-main-video {
+ border-radius: var(--vidplayZ-radius) !important;
+}
+
+/* Controls visibility */
+#movie_player.vidplayz-enhanced.vidplayz-controls-hidden .ytp-chrome-bottom {
+ opacity: 0 !important;
+ transform: translateX(-50%) translateY(100%) !important;
+}
+
+/* Enhanced progress bar */
+#movie_player.vidplayz-enhanced .ytp-progress-bar-container {
+ height: 8px !important;
+ padding: 0 !important;
+ transition: height 0.2s ease !important;
+}
+
+#movie_player.vidplayz-enhanced:hover .ytp-progress-bar-container {
+ height: 12px !important;
+}
+
+#movie_player.vidplayz-enhanced .ytp-progress-bar {
+ background-color: rgba(255, 255, 255, 0.2) !important;
+}
+
+#movie_player.vidplayz-enhanced .ytp-progress-bar .ytp-play-progress {
+ background-color: var(--vidplayZ-primary) !important;
+}
+
+#movie_player.vidplayz-enhanced .ytp-scrubber-button {
+ background-color: var(--vidplayZ-primary) !important;
+ width: 16px !important;
+ height: 16px !important;
+ border-radius: 50% !important;
+ transform: scale(0.8) !important;
+ transition: transform 0.2s ease !important;
+}
+
+#movie_player.vidplayz-enhanced:hover .ytp-scrubber-button {
+ transform: scale(1) !important;
+}
+
+/* Center all control panel buttons */
+#movie_player.vidplayz-enhanced .ytp-left-controls,
+#movie_player.vidplayz-enhanced .ytp-right-controls {
+ display: flex !important;
+ align-items: center !important;
+}
+
+/* Preview thumbnail */
+.vidplayz-preview-container {
+ position: absolute;
+ bottom: 50px;
+ padding: 4px;
+ background-color: var(--vidplayZ-background);
+ border-radius: var(--border-radius, 8px);
+ display: none;
+ z-index: 100;
+ pointer-events: none;
+}
+
+.vidplayz-preview-thumbnail {
+ width: 160px;
+ height: 90px;
+ background-color: #000;
+ border-radius: 4px;
+}
+
+.vidplayz-preview-timestamp {
+ text-align: center;
+ color: #fff;
+ font-size: 12px;
+ padding: 2px 0;
+}
+
+/* Volume display */
+.vidplayz-volume-display {
+ position: absolute;
+ background-color: var(--vidplayZ-background);
+ border-radius: 4px;
+ padding: 2px 6px;
+ font-size: 11px;
+ color: #fff;
+ top: -24px;
+ left: 50%;
+ transform: translateX(-50%);
+ pointer-events: none;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.ytp-volume-panel:hover .vidplayz-volume-display {
+ opacity: 1;
+}
+
+/* Custom buttons */
+.vidplayz-screenshot-button,
+.vidplayz-loop-button,
+.vidplayz-speed-button {
+ opacity: 0.9;
+ transition: opacity 0.2s ease !important;
+}
+
+.vidplayz-screenshot-button:hover,
+.vidplayz-loop-button:hover,
+.vidplayz-speed-button:hover {
+ opacity: 1;
+}
+
+.vidplayz-screenshot-button svg,
+.vidplayz-loop-button svg {
+ fill: #fff;
+}
+
+.vidplayz-loop-button.active svg {
+ fill: var(--vidplayZ-primary) !important;
+}
+
+.vidplayz-speed-button span {
+ color: #fff;
+ font-size: 13px;
+ font-weight: 500;
+}
+
+/* Speed menu */
+.vidplayz-speed-menu {
+ position: fixed;
+ display: none;
+ background-color: var(--vidplayZ-background);
+ border-radius: var(--border-radius, 8px);
+ padding: 8px 0;
+ z-index: 1000;
+ min-width: 80px;
+}
+
+.vidplayz-speed-option {
+ padding: 6px 12px;
+ color: #fff;
+ font-size: 13px;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+}
+
+.vidplayz-speed-option:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+}
+
+/* Quality badge */
+.vidplayz-quality-badge {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ background-color: var(--vidplayZ-background);
+ color: #fff;
+ padding: 2px 6px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-weight: 500;
+ pointer-events: none;
+ transition: opacity 0.3s ease;
+}
+
+/* Notifications */
+.vidplayz-notification {
+ position: fixed;
+ bottom: 80px;
+ left: 50%;
+ transform: translateX(-50%) translateY(20px);
+ background-color: var(--vidplayZ-background);
+ color: #fff;
+ padding: 8px 16px;
+ border-radius: var(--border-radius, 8px);
+ font-size: 14px;
+ z-index: 9999;
+ opacity: 0;
+ transition: transform 0.3s ease, opacity 0.3s ease;
+ border-left: 3px solid var(--vidplayZ-primary);
+}
+
+.vidplayz-notification.visible {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
+}
+
+.vidplayz-notification.error {
+ border-left: 3px solid var(--status-error, #f44336);
+}
+
+/* Video end overlay */
+.vidplayz-end-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.6);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 50;
+}
+
+.vidplayz-replay-button {
+ background-color: var(--vidplayZ-primary);
+ color: #000;
+ border: none;
+ border-radius: 4px;
+ padding: 12px 24px;
+ font-size: 16px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: transform 0.2s ease, background-color 0.2s ease;
+}
+
+.vidplayz-replay-button:hover {
+ transform: scale(1.05);
+ background-color: var(--vidplayZ-secondary);
+}
+
+/* Theater mode enhancements */
+ytd-watch-flexy[theater] #movie_player.vidplayz-enhanced {
+ max-height: calc(100vh - var(--ytd-watch-flexy-masthead-height)) !important;
+ margin: 0 auto !important;
+ border-radius: 0 !important;
+}
+
+/* Additional theater mode enhancements with sidebar/guide removed */
+ytd-watch-flexy[theater] #movie_player.vidplayz-enhanced ~ ytd-app {
+ --ytd-watch-flexy-sidebar-width: 0px !important;
+}
+
+ytd-watch-flexy[theater] #movie_player.vidplayz-enhanced ~ ytd-app ytd-watch-flexy #columns {
+ max-width: 100% !important;
+ margin: 0 auto !important;
+}
+
+/* Fullscreen enhancements */
+#movie_player.vidplayz-enhanced.ytp-fullscreen {
+ border-radius: 0 !important;
+}
+
+/* Responsive styles */
+@media (max-width: 768px) {
+ .vidplayz-quality-badge {
+ font-size: 10px;
+ padding: 1px 4px;
+ }
+
+ .vidplayz-notification {
+ font-size: 12px;
+ padding: 6px 12px;
+ }
+
+ .vidplayz-replay-button {
+ padding: 8px 16px;
+ font-size: 14px;
+ }
+
+ #movie_player.vidplayz-enhanced .ytp-chrome-bottom {
+ width: 90% !important;
+ }
+}
+
+/* Center YouTube search bar */
+#masthead-container #search-container.ytd-searchbox {
+ display: flex;
+ justify-content: center;
+ margin: 0 auto;
+ max-width: 640px;
+ width: 100%;
+}
+
+/* Ensure the search input takes appropriate space */
+#masthead #search-container.ytd-searchbox #search-form {
+ width: 100%;
+}
+
+/* Ensure avatar and all its components are fully visible */
+.page-header-view-model-wiz__page-header-headline-image.yt-decorated-avatar-view-model-wiz,
+#avatar-link,
+#avatar-section,
+.ytd-topbar-menu-button-renderer,
+ytd-topbar-menu-button-renderer,
+ytd-masthead .ytd-masthead[slot="avatar-button"],
+#avatar,
+#img.yt-img-shadow,
+#avatar > img,
+.yt-img-shadow,
+ytd-topbar-menu-button-renderer > .yt-decorated-avatar-view-model-wiz,
+#avatar.ytd-masthead {
+ display: inline-block !important;
+ visibility: visible !important;
+ opacity: 1 !important;
+ pointer-events: auto !important;
+}
+
+/* Force avatar image to be visible with correct dimensions */
+#avatar > img.yt-img-shadow {
+ width: 32px !important;
+ height: 32px !important;
+ border-radius: 50% !important;
+ object-fit: cover !important;
+ display: block !important;
+}
+
+/* Make sure the avatar container is properly sized */
+#avatar {
+ width: 32px !important;
+ height: 32px !important;
+ display: block !important;
+ overflow: visible !important;
+}
+
+/* Ensure all parents of avatar are visible */
+#buttons.ytd-masthead,
+#end.ytd-masthead {
+ display: flex !important;
+ visibility: visible !important;
+ opacity: 1 !important;
+}
+
+/* Optional: adjust search container positioning on the page */
+#masthead #center {
+ display: flex;
+ justify-content: center;
+ flex: 1;
+}
+
+/* Responsive adjustments for smaller screens */
+@media (max-width: 768px) {
+ #masthead-container #search-container.ytd-searchbox {
+ max-width: 90%;
+ }
+}
+
+/* Restore thumbnail hover overlay elements */
+.ytThumbnailHoverOverlayViewModelHost,
+.ytThumbnailHoverOverlayViewModelStyleCover.ytThumbnailHoverOverlayViewModelScrim,
+.yt-spec-avatar-shape__button--button-giant.yt-spec-avatar-shape__button.yt-spec-avatar-shape > .yt-spec-avatar-shape--cairo-refresh {
+ display: block !important;
+ visibility: visible !important;
+ opacity: 1 !important;
+ pointer-events: auto !important;
+}
+
+/* Enhance hover effects */
+.ytThumbnailHoverOverlayViewModelHost {
+ transition: opacity 0.2s ease !important;
+}
+
+/* Make video player respect video dimensions */
+#movie_player.vidplayz-enhanced,
+#movie_player.vidplayz-enhanced .html5-video-container,
+#movie_player.vidplayz-enhanced .html5-main-video {
+ width: auto !important;
+ height: auto !important;
+ max-width: 100% !important;
+ max-height: calc(100vh - 120px) !important;
+ aspect-ratio: auto !important;
+ object-fit: contain !important;
+}
+
+/* Center the video in the player */
+#movie_player.vidplayz-enhanced .html5-video-container {
+ display: flex !important;
+ justify-content: center !important;
+ align-items: center !important;
+}
+
+/* Maintain aspect ratio for theater mode */
+ytd-watch-flexy[theater] #movie_player.vidplayz-enhanced {
+ max-height: calc(100vh - var(--ytd-watch-flexy-masthead-height) - 20px) !important;
+ margin: 0 auto !important;
+}
+
+/* Center YouTube search bar */
+#masthead-container #search-container.ytd-searchbox {
+ display: flex;
+ justify-content: center;
+ margin: 0 auto;
+ max-width: 640px;
+ width: 100%;
+}
+
+/* Ensure the search input takes appropriate space */
+#masthead #search-container.ytd-searchbox #search-form {
+ width: 100%;
+}
+
+/* Ensure avatar and all its components are fully visible */
+.page-header-view-model-wiz__page-header-headline-image.yt-decorated-avatar-view-model-wiz,
+#avatar-link,
+#avatar-section,
+.ytd-topbar-menu-button-renderer,
+ytd-topbar-menu-button-renderer,
+ytd-masthead .ytd-masthead[slot="avatar-button"],
+#avatar,
+#img.yt-img-shadow,
+#avatar > img,
+.yt-img-shadow,
+ytd-topbar-menu-button-renderer > .yt-decorated-avatar-view-model-wiz,
+#avatar.ytd-masthead {
+ display: inline-block !important;
+ visibility: visible !important;
+ opacity: 1 !important;
+ pointer-events: auto !important;
+}
+
+/* Force avatar image to be visible with correct dimensions */
+#avatar > img.yt-img-shadow {
+ width: 32px !important;
+ height: 32px !important;
+ border-radius: 50% !important;
+ object-fit: cover !important;
+ display: block !important;
+}
+
+/* Make sure the avatar container is properly sized */
+#avatar {
+ width: 32px !important;
+ height: 32px !important;
+ display: block !important;
+ overflow: visible !important;
+}
+
+/* Ensure all parents of avatar are visible */
+#buttons.ytd-masthead,
+#end.ytd-masthead {
+ display: flex !important;
+ visibility: visible !important;
+ opacity: 1 !important;
+}
+
+/* Optional: adjust search container positioning on the page */
+#masthead #center {
+ display: flex;
+ justify-content: center;
+ flex: 1;
+}
+
+/* Responsive adjustments for smaller screens */
+@media (max-width: 768px) {
+ #masthead-container #search-container.ytd-searchbox {
+ max-width: 90%;
+ }
+}
+
+/* Perfect video fit in container */
+.html5-main-video.video-stream {
+ width: 100% !important;
+ height: 100% !important;
+ object-fit: contain !important;
+ max-height: 100vh !important;
+ position: relative !important;
+ left: 0 !important;
+ top: 0 !important;
+}
+
+/* Hide rich shelf headers */
+#rich-shelf-header,
+ytd-rich-shelf-renderer #rich-shelf-header,
+#rich-section-header,
+.ytd-rich-shelf-renderer #rich-shelf-header,
+ytd-rich-grid-renderer #rich-shelf-header {
+ display: none !important;
+ visibility: hidden !important;
+ height: 0 !important;
+ opacity: 0 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ overflow: hidden !important;
+}
\ No newline at end of file
diff --git a/vidplayer.js b/vidplayer.js
new file mode 100644
index 00000000..a57ee486
--- /dev/null
+++ b/vidplayer.js
@@ -0,0 +1,424 @@
+// features/vidplayer.js
+(function() {
+ 'use strict';
+
+ // Configuration - simplified
+ const config = {
+ defaultVolume: 0.8,
+ speedOptions: [0.5, 0.75, 1, 1.25, 1.5, 2],
+ defaultGridSize: 4 // Fixed default grid size
+ };
+
+ // State management - simplified
+ let state = {
+ initialized: false,
+ player: null
+ };
+
+ // Initialize function that will be called when the feature is enabled
+ function initialize() {
+ if (state.initialized) return;
+
+ console.log('YouTube Enhancer: Initializing video player improvements');
+
+ // Apply clean UI
+ applyCleanUI();
+
+ // Apply default video grid layout
+ applyDefaultVideoGrid();
+
+ // Wait for YouTube player to be ready
+ waitForYouTubePlayer()
+ .then(setupEnhancedPlayer)
+ .catch(err => console.error('YouTube Enhancer: Error initializing player', err));
+
+ state.initialized = true;
+
+ return cleanup; // Return cleanup function for content.js
+ }
+
+ // Apply a cleaner UI by hiding distractions
+ function applyCleanUI() {
+ // Only add the style once
+ if (document.getElementById('youtube-enhancer-clean-view')) {
+ return;
+ }
+
+ // Create a style element to hold our custom hiding rules
+ const style = document.createElement('style');
+ style.id = 'youtube-enhancer-clean-view';
+ style.textContent = `
+ /* Hide masthead ad banners */
+ #masthead-ad {
+ display: none !important;
+ }
+
+ /* Hide promotional elements */
+ ytd-merch-shelf-renderer,
+ ytd-movie-offer-module-renderer,
+ .ytp-ce-element,
+ .ytp-cards-button,
+ #clarify-box,
+ ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-structured-description"],
+ ytd-info-panel-content-renderer {
+ display: none !important;
+ }
+
+ /* Hide rich shelf headers */
+ #rich-shelf-header,
+ ytd-rich-shelf-renderer #rich-shelf-header,
+ #rich-section-header,
+ .ytd-rich-shelf-renderer #rich-shelf-header,
+ ytd-rich-grid-renderer #rich-shelf-header {
+ display: none !important;
+ }
+
+ /* Perfect video fit in container */
+ .html5-main-video.video-stream {
+ width: 100% !important;
+ height: 100% !important;
+ object-fit: contain !important;
+ max-height: 100vh !important;
+ position: relative !important;
+ left: 0 !important;
+ top: 0 !important;
+ }
+ `;
+
+ document.head.appendChild(style);
+ }
+
+ // Apply a default grid layout with reasonable values
+ function applyDefaultVideoGrid() {
+ // Remove any existing grid style
+ const existingStyle = document.getElementById('youtube-enhancer-grid-layout');
+ if (existingStyle) {
+ existingStyle.remove();
+ }
+
+ const gridStyle = document.createElement('style');
+ gridStyle.id = 'youtube-enhancer-grid-layout';
+ gridStyle.textContent = `
+ /* Set video grid to a reasonable default */
+ ytd-rich-grid-renderer #contents.ytd-rich-grid-renderer {
+ display: grid !important;
+ grid-template-columns: repeat(${config.defaultGridSize}, 1fr) !important;
+ grid-gap: 16px !important;
+ padding: 16px !important;
+ }
+
+ /* Video item sizing adjustments */
+ ytd-rich-item-renderer,
+ ytd-grid-video-renderer,
+ ytd-compact-video-renderer {
+ width: 100% !important;
+ margin: 0 !important;
+ display: flex !important;
+ flex-direction: column !important;
+ height: auto !important;
+ }
+
+ /* Consistent thumbnail heights */
+ ytd-thumbnail {
+ width: 100% !important;
+ aspect-ratio: 16/9 !important;
+ margin-bottom: 8px !important;
+ }
+
+ /* Fix thumbnail images */
+ ytd-thumbnail #thumbnail {
+ width: 100% !important;
+ height: 100% !important;
+ object-fit: cover !important;
+ }
+
+ /* Responsive adjustments */
+ @media (max-width: 1200px) {
+ ytd-rich-grid-renderer #contents.ytd-rich-grid-renderer {
+ grid-template-columns: repeat(3, 1fr) !important;
+ }
+ }
+
+ @media (max-width: 768px) {
+ ytd-rich-grid-renderer #contents.ytd-rich-grid-renderer {
+ grid-template-columns: repeat(2, 1fr) !important;
+ }
+ }
+
+ @media (max-width: 480px) {
+ ytd-rich-grid-renderer #contents.ytd-rich-grid-renderer {
+ grid-template-columns: repeat(1, 1fr) !important;
+ }
+ }
+ `;
+
+ document.head.appendChild(gridStyle);
+ }
+
+ // Wait for YouTube player to be ready
+ function waitForYouTubePlayer() {
+ return new Promise((resolve) => {
+ const checkForPlayer = () => {
+ const player = document.querySelector('#movie_player');
+ if (player) {
+ resolve(player);
+ } else {
+ setTimeout(checkForPlayer, 200);
+ }
+ };
+ checkForPlayer();
+ });
+ }
+
+ // Setup enhanced player features - simplified
+ function setupEnhancedPlayer(player) {
+ state.player = player;
+
+ // Add focus mode button
+ addFocusModeButton();
+
+ // Add speed control
+ addSpeedControl();
+
+ // User-friendly notification
+ showNotification('YouTube Enhancer activated');
+ }
+
+ // Add focus mode toggle button
+ function addFocusModeButton() {
+ const controlsRight = state.player.querySelector('.ytp-right-controls');
+ if (!controlsRight) return;
+
+ // Create focus mode button
+ const focusModeButton = document.createElement('button');
+ focusModeButton.className = 'ytp-button youtube-enhancer-focus-button';
+ focusModeButton.title = 'Focus Mode (Hide Recommendations)';
+
+ // Create SVG icon for the button
+ const svgNS = "http://www.w3.org/2000/svg";
+ const svg = document.createElementNS(svgNS, "svg");
+ svg.setAttribute("height", "100%");
+ svg.setAttribute("viewBox", "0 0 36 36");
+
+ const path = document.createElementNS(svgNS, "path");
+ path.setAttribute("d", "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-5v2h5v3h2v-5h-2v0zm-5-5h5V7h-2v3h-3v2z");
+ path.setAttribute("fill", "white");
+
+ svg.appendChild(path);
+ focusModeButton.appendChild(svg);
+
+ let focusModeEnabled = false;
+
+ focusModeButton.addEventListener('click', () => {
+ focusModeEnabled = !focusModeEnabled;
+
+ // Add style for focus mode if not exists
+ if (!document.getElementById('youtube-enhancer-focus-mode-style')) {
+ const style = document.createElement('style');
+ style.id = 'youtube-enhancer-focus-mode-style';
+ style.textContent = `
+ body.youtube-enhancer-focus-mode ytd-watch-next-secondary-results-renderer {
+ display: none !important;
+ }
+ `;
+ document.head.appendChild(style);
+ }
+
+ if (focusModeEnabled) {
+ document.body.classList.add('youtube-enhancer-focus-mode');
+ focusModeButton.classList.add('active');
+ showNotification('Focus mode enabled');
+ } else {
+ document.body.classList.remove('youtube-enhancer-focus-mode');
+ focusModeButton.classList.remove('active');
+ showNotification('Focus mode disabled');
+ }
+ });
+
+ controlsRight.insertBefore(focusModeButton, controlsRight.firstChild);
+ }
+
+ // Add custom speed control
+ function addSpeedControl() {
+ const controlsRight = state.player.querySelector('.ytp-right-controls');
+ if (!controlsRight) return;
+
+ // Create speed button
+ const speedButton = document.createElement('button');
+ speedButton.className = 'ytp-button youtube-enhancer-speed-button';
+ speedButton.title = 'Playback Speed';
+
+ // Create text element for speed display
+ const speedText = document.createElement('span');
+ speedText.textContent = '1x';
+ speedButton.appendChild(speedText);
+
+ // Create speed menu
+ const speedMenu = document.createElement('div');
+ speedMenu.className = 'youtube-enhancer-speed-menu';
+ speedMenu.style.display = 'none';
+ speedMenu.style.position = 'absolute';
+ speedMenu.style.backgroundColor = 'rgba(28, 28, 28, 0.9)';
+ speedMenu.style.borderRadius = '4px';
+ speedMenu.style.padding = '8px 0';
+ speedMenu.style.zIndex = '2000';
+ speedMenu.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.3)';
+
+ // Add speed options
+ config.speedOptions.forEach(speed => {
+ const option = document.createElement('div');
+ option.className = 'youtube-enhancer-speed-option';
+ option.textContent = `${speed}x`;
+ option.style.padding = '8px 16px';
+ option.style.color = 'white';
+ option.style.cursor = 'pointer';
+ option.style.fontSize = '14px';
+
+ // Highlight on hover
+ option.addEventListener('mouseover', () => {
+ option.style.backgroundColor = 'rgba(255, 255, 255, 0.1)';
+ });
+
+ option.addEventListener('mouseout', () => {
+ option.style.backgroundColor = '';
+ });
+
+ option.addEventListener('click', (e) => {
+ e.stopPropagation();
+ const video = state.player.querySelector('video');
+ if (!video) return;
+
+ video.playbackRate = speed;
+ speedText.textContent = `${speed}x`;
+ speedMenu.style.display = 'none';
+ showNotification(`Speed: ${speed}x`);
+ });
+
+ speedMenu.appendChild(option);
+ });
+
+ // Toggle speed menu on click
+ speedButton.addEventListener('click', (e) => {
+ e.stopPropagation();
+
+ if (speedMenu.style.display === 'none') {
+ speedMenu.style.display = 'block';
+
+ // Position menu
+ const rect = speedButton.getBoundingClientRect();
+ speedMenu.style.top = `${rect.bottom}px`;
+ speedMenu.style.right = `${window.innerWidth - rect.right}px`;
+ } else {
+ speedMenu.style.display = 'none';
+ }
+ });
+
+ // Hide menu when clicking elsewhere
+ document.addEventListener('click', () => {
+ speedMenu.style.display = 'none';
+ });
+
+ // Update speed button text when speed changes
+ const video = state.player.querySelector('video');
+ if (video) {
+ video.addEventListener('ratechange', () => {
+ speedText.textContent = `${video.playbackRate}x`;
+ });
+ }
+
+ controlsRight.insertBefore(speedButton, controlsRight.firstChild);
+ document.body.appendChild(speedMenu);
+ }
+
+ // Show notification
+ function showNotification(message) {
+ // Remove existing notification if any
+ const existingNotification = document.querySelector('.youtube-enhancer-notification');
+ if (existingNotification) {
+ existingNotification.remove();
+ }
+
+ // Create notification element
+ const notification = document.createElement('div');
+ notification.className = 'youtube-enhancer-notification';
+ notification.textContent = message;
+
+ // Style the notification
+ notification.style.position = 'fixed';
+ notification.style.bottom = '24px';
+ notification.style.left = '50%';
+ notification.style.transform = 'translateX(-50%)';
+ notification.style.backgroundColor = 'rgba(33, 33, 33, 0.9)';
+ notification.style.color = 'white';
+ notification.style.padding = '8px 16px';
+ notification.style.borderRadius = '4px';
+ notification.style.zIndex = '9999';
+ notification.style.fontSize = '14px';
+ notification.style.fontWeight = 'bold';
+ notification.style.opacity = '0';
+ notification.style.transition = 'opacity 0.3s ease';
+
+ document.body.appendChild(notification);
+
+ // Animate in
+ setTimeout(() => {
+ notification.style.opacity = '1';
+ }, 10);
+
+ // Remove after delay
+ setTimeout(() => {
+ notification.style.opacity = '0';
+ setTimeout(() => {
+ notification.remove();
+ }, 300);
+ }, 3000);
+ }
+
+ // Cleanup function (called when feature is disabled)
+ function cleanup() {
+ if (!state.initialized) return;
+
+ console.log('YouTube Enhancer: Cleaning up video player improvements');
+
+ // Remove custom styles
+ const cleanViewStyle = document.getElementById('youtube-enhancer-clean-view');
+ if (cleanViewStyle) {
+ cleanViewStyle.remove();
+ }
+
+ const gridLayoutStyle = document.getElementById('youtube-enhancer-grid-layout');
+ if (gridLayoutStyle) {
+ gridLayoutStyle.remove();
+ }
+
+ const focusModeStyle = document.getElementById('youtube-enhancer-focus-mode-style');
+ if (focusModeStyle) {
+ focusModeStyle.remove();
+ }
+
+ // Remove focus mode class if active
+ document.body.classList.remove('youtube-enhancer-focus-mode');
+
+ // Remove custom buttons
+ document.querySelectorAll('.youtube-enhancer-focus-button, .youtube-enhancer-speed-button').forEach(el => el.remove());
+
+ // Remove speed menu
+ const speedMenu = document.querySelector('.youtube-enhancer-speed-menu');
+ if (speedMenu) {
+ speedMenu.remove();
+ }
+
+ // Remove notifications
+ const notification = document.querySelector('.youtube-enhancer-notification');
+ if (notification) {
+ notification.remove();
+ }
+
+ state.initialized = false;
+ }
+
+ // Return the public API
+ return {
+ init: initialize
+ };
+})();
\ No newline at end of file