From 19a2ae51ef57af10c8e2b9b4fbc840b04ecc59ac Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 11 Nov 2025 21:36:10 +0530 Subject: [PATCH] media-skip --- .../player/media-keys-content.js | 123 ++++++++++++++++++ .../www.youtube.com/player/media-keys.js | 50 +++++++ manifest.json | 31 ++++- 3 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 js&css/extension/www.youtube.com/player/media-keys-content.js create mode 100644 js&css/extension/www.youtube.com/player/media-keys.js diff --git a/js&css/extension/www.youtube.com/player/media-keys-content.js b/js&css/extension/www.youtube.com/player/media-keys-content.js new file mode 100644 index 000000000..33f5c5b16 --- /dev/null +++ b/js&css/extension/www.youtube.com/player/media-keys-content.js @@ -0,0 +1,123 @@ +// Media key handler for YouTube player (content script) + +class YouTubeMediaKeyController { + constructor() { + this.video = null; + this.initialize(); + } + + initialize() { + // Wait for the video element to be available + this.waitForVideoElement().then(() => { + this.setupEventListeners(); + }); + + // Listen for messages from background script + chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.type === 'MEDIA_KEY_ACTION') { + this.handleMediaKeyAction(request.action, request.value); + } + }); + } + + waitForVideoElement() { + return new Promise((resolve) => { + const checkVideo = () => { + this.video = document.querySelector('video'); + if (this.video) { + resolve(); + } else { + setTimeout(checkVideo, 500); + } + }; + checkVideo(); + }); + } + + setupEventListeners() { + // Re-initialize if video element changes (e.g., when navigating between videos) + const observer = new MutationObserver((mutations) => { + if (!document.body.contains(this.video)) { + this.video = document.querySelector('video'); + } + }); + + observer.observe(document.body, { childList: true, subtree: true }); + } + + handleMediaKeyAction(action, value) { + if (!this.video) { + this.video = document.querySelector('video'); + if (!this.video) return; + } + + try { + switch (action) { + case 'skip': + this.video.currentTime += value; + this.showSkipFeedback(value > 0 ? 'forward' : 'backward', Math.abs(value)); + break; + case 'playPause': + if (this.video.paused) { + this.video.play(); + } else { + this.video.pause(); + } + break; + } + } catch (error) { + console.error('Error handling media key action:', error); + } + } + + showSkipFeedback(direction, seconds) { + // Create or update feedback element + let feedback = document.getElementById('improvedtube-skip-feedback'); + if (!feedback) { + feedback = document.createElement('div'); + feedback.id = 'improvedtube-skip-feedback'; + feedback.style.position = 'fixed'; + feedback.style.top = '50%'; + feedback.style.left = '50%'; + feedback.style.transform = 'translate(-50%, -50%)'; + feedback.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; + feedback.style.color = 'white'; + feedback.style.padding = '10px 20px'; + feedback.style.borderRadius = '20px'; + feedback.style.fontSize = '24px'; + feedback.style.zIndex = '9999'; + feedback.style.display = 'flex'; + feedback.style.alignItems = 'center'; + feedback.style.justifyContent = 'center'; + document.body.appendChild(feedback); + + // Auto-remove after animation + setTimeout(() => { + if (feedback) { + feedback.style.opacity = '0'; + feedback.style.transition = 'opacity 0.5s'; + setTimeout(() => { + if (feedback && feedback.parentNode) { + feedback.parentNode.removeChild(feedback); + } + }, 500); + } + }, 1000); + } + + // Update feedback content + const icon = direction === 'forward' ? '⏩' : '⏪'; + feedback.textContent = `${icon} ${seconds}s`; + feedback.style.opacity = '1'; + feedback.style.transition = 'none'; + } +} + +// Initialize when page is loaded +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + new YouTubeMediaKeyController(); + }); +} else { + new YouTubeMediaKeyController(); +} diff --git a/js&css/extension/www.youtube.com/player/media-keys.js b/js&css/extension/www.youtube.com/player/media-keys.js new file mode 100644 index 000000000..acb8913da --- /dev/null +++ b/js&css/extension/www.youtube.com/player/media-keys.js @@ -0,0 +1,50 @@ +// Media key handler for YouTube player + +class MediaKeyHandler { + constructor() { + this.skipSeconds = 10; // Default skip duration in seconds + this.initialize(); + } + + initialize() { + // Load saved skip duration from storage + chrome.storage.local.get(['mediaKeySkipSeconds'], (result) => { + if (result.mediaKeySkipSeconds) { + this.skipSeconds = parseInt(result.mediaKeySkipSeconds, 10); + } + }); + + // Listen for media key commands + chrome.commands.onCommand.addListener((command) => { + if (command === 'mediaNextTrack') { + this.forward(); + } else if (command === 'mediaPrevTrack') { + this.rewind(); + } + }); + } + + // Send message to content script to control video + sendMessage(action, value) { + chrome.tabs.query({url: '*://*.youtube.com/*'}, (tabs) => { + if (tabs && tabs.length > 0) { + chrome.tabs.sendMessage(tabs[0].id, { + type: 'MEDIA_KEY_ACTION', + action: action, + value: value + }); + } + }); + } + + forward() { + this.sendMessage('skip', this.skipSeconds); + } + + rewind() { + this.sendMessage('skip', -this.skipSeconds); + } +} + +// Initialize media key handler when the script loads +const mediaKeyHandler = new MediaKeyHandler(); diff --git a/manifest.json b/manifest.json index 589992ce9..4ab61e18a 100644 --- a/manifest.json +++ b/manifest.json @@ -51,6 +51,8 @@ "js&css/extension/www.youtube.com/general/general.js", "js&css/extension/www.youtube.com/appearance/sidebar/sidebar.js", "js&css/extension/www.youtube.com/appearance/comments/comments.js", + "js&css/extension/www.youtube.com/player/media-keys.js", + "js&css/extension/www.youtube.com/player/media-keys-content.js", "js&css/extension/init.js" ], "matches": ["https://www.youtube.com/*"], @@ -58,8 +60,31 @@ } ], "host_permissions": ["https://www.youtube.com/*"], - "optional_permissions": ["downloads"], - "permissions": ["contextMenus", "storage"], + "optional_permissions": [ + "downloads" + ], + "permissions": [ + "contextMenus", + "storage", + "tabs", + "scripting", + "webNavigation", + "commands" + ], + "commands": { + "mediaPrevTrack": { + "suggested_key": { + "default": "MediaPrevTrack" + }, + "description": "Rewind video by 10 seconds" + }, + "mediaNextTrack": { + "suggested_key": { + "default": "MediaNextTrack" + }, + "description": "Fast forward video by 10 seconds" + } + }, "web_accessible_resources": [ { "resources": [ @@ -68,6 +93,8 @@ "js&css/web-accessible/functions.js", "js&css/web-accessible/www.youtube.com/appearance.js", "js&css/web-accessible/www.youtube.com/player.js", + "js&css/web-accessible/www.youtube.com/player/media-keys.js", + "js&css/web-accessible/www.youtube.com/player/media-keys-content.js", "js&css/web-accessible/www.youtube.com/themes.js", "js&css/web-accessible/www.youtube.com/playlist.js", "js&css/web-accessible/www.youtube.com/playlist-complete-playlist.js",