diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1c8169f36..5858fd3cf 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -87,6 +87,15 @@ "appearance": { "message": "Appearance" }, + "audioOnlyMode": { + "message": "Audio-only mode (hide video)" + }, + "audioOnlyButton": { + "message": "Audio-only button" + }, + "autoSwitchForMusic": { + "message": "Auto-switch for music category" + }, "areYouSureYouWantToExportTheData": { "message": "Are you sure you want to export the data?" }, diff --git a/js&css/extension/www.youtube.com/appearance/player/player.css b/js&css/extension/www.youtube.com/appearance/player/player.css index 8273547cc..52fbc4bea 100644 --- a/js&css/extension/www.youtube.com/appearance/player/player.css +++ b/js&css/extension/www.youtube.com/appearance/player/player.css @@ -807,3 +807,97 @@ html[it-player-hide-pause-overlay='true'] .ytp-pause-overlay { -moz-background-size: contain !important; background-color: #000 !important; } + +/*-------------------------------------------------------------- +# AUDIO-ONLY MODE +--------------------------------------------------------------*/ +html[it-player-audio-only='true'] .html5-video-container video { + opacity: 0 !important; + visibility: hidden !important; +} + +#it-audio-only-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 10; + display: none; + align-items: center; + justify-content: center; + background: #0f0f0f; + overflow: hidden; + pointer-events: none; + user-select: none; +} + +html[it-player-audio-only='true'] #it-audio-only-overlay { + display: flex !important; +} + +.it-audio-only-bg { + position: absolute; + top: -10%; + left: -10%; + width: 120%; + height: 120%; + filter: blur(35px) brightness(0.3); + object-fit: cover; + z-index: 1; +} + +.it-audio-only-content { + position: relative; + z-index: 2; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 14px; + padding: 24px; + max-width: 80%; + text-align: center; +} + +.it-audio-only-art { + width: clamp(140px, 35vh, 280px); + height: clamp(140px, 35vh, 280px); + object-fit: cover; + border-radius: 12px; + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +.it-audio-only-badge { + display: inline-flex; + align-items: center; + gap: 6px; + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(8px); + color: #fff; + padding: 5px 14px; + border-radius: 20px; + font-size: 13px; + font-weight: 500; + letter-spacing: 0.3px; + border: 1px solid rgba(255, 255, 255, 0.15); +} + +.it-audio-only-title { + color: #ffffff; + font-size: clamp(16px, 2.5vh, 22px); + font-weight: 600; + text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6); + max-width: 100%; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.it-audio-only-author { + color: rgba(255, 255, 255, 0.75); + font-size: clamp(13px, 1.8vh, 16px); + font-weight: 400; +} diff --git a/js&css/web-accessible/core.js b/js&css/web-accessible/core.js index c744c1625..f48a4978f 100644 --- a/js&css/web-accessible/core.js +++ b/js&css/web-accessible/core.js @@ -243,6 +243,21 @@ document.addEventListener('it-message-from-extension', function () { ImprovedTube.blocklistInit(); break + case 'playerAudioOnly': + case 'playerAudioOnlyAutoMusic': + if (typeof ImprovedTube.playerAudioOnlyUpdate === 'function') { + ImprovedTube.playerAudioOnlyUpdate(); + } + break + + case 'playerAudioOnlyButton': + if (ImprovedTube.storage.player_audio_only_button === false) { + ImprovedTube.elements.buttons['it-audio-only-button']?.remove(); + } else if (typeof ImprovedTube.playerAudioOnlyButton === 'function') { + ImprovedTube.playerAudioOnlyButton(); + } + break + case 'playerPlaybackSpeed': case 'playerForcedPlaybackSpeed': if (ImprovedTube.storage.player_forced_playback_speed === true && isFinite(Number(ImprovedTube.storage.player_playback_speed))) { diff --git a/js&css/web-accessible/functions.js b/js&css/web-accessible/functions.js index dd293eee8..d1d5d0072 100644 --- a/js&css/web-accessible/functions.js +++ b/js&css/web-accessible/functions.js @@ -473,6 +473,8 @@ ImprovedTube.initPlayer = function () { if (this.storage.player_always_repeat === true) { ImprovedTube.playerRepeat(); } ImprovedTube.playerPlaybackSpeedButton(); + ImprovedTube.playerAudioOnlyButton(); + ImprovedTube.playerAudioOnlyUpdate(); ImprovedTube.playerScreenshotButton(); ImprovedTube.playerRepeatButton(); ImprovedTube.playerRotateButton(); diff --git a/js&css/web-accessible/init.js b/js&css/web-accessible/init.js index 9f763d5e7..fc31c336f 100644 --- a/js&css/web-accessible/init.js +++ b/js&css/web-accessible/init.js @@ -188,6 +188,7 @@ ImprovedTube.init = function () { ImprovedTube.playlistLargePlaylistHandler(); } try { if (ImprovedTube.lastWatchedOverlay) ImprovedTube.lastWatchedOverlay(); } catch (e) { console.error('[LWO] page-data-updated error', e); } + try { if (ImprovedTube.playerAudioOnlyUpdate) ImprovedTube.playerAudioOnlyUpdate(); } catch (e) {} }); this.pageType(); this.playerOnPlay(); @@ -276,6 +277,7 @@ document.addEventListener('yt-navigate-finish', function () { if (ImprovedTube.elements.player && ImprovedTube.elements.player.setPlaybackRate) { ImprovedTube.videoPageUpdate(); ImprovedTube.initPlayer(); + try { if (ImprovedTube.playerAudioOnlyUpdate) ImprovedTube.playerAudioOnlyUpdate(); } catch (e) {} } if (ImprovedTube.elements.shorts_player) { ImprovedTube.redirectShortsToWatch(); diff --git a/js&css/web-accessible/www.youtube.com/player.js b/js&css/web-accessible/www.youtube.com/player.js index 2709ecf37..aed742b12 100644 --- a/js&css/web-accessible/www.youtube.com/player.js +++ b/js&css/web-accessible/www.youtube.com/player.js @@ -982,6 +982,126 @@ ImprovedTube.playerScreenshotButton = function () { }); } }; + +/*------------------------------------------------------------------------------ +AUDIO-ONLY MODE +------------------------------------------------------------------------------*/ +ImprovedTube.isMusicVideo = function () { + const genre = document.querySelector('meta[itemprop=genre]')?.content || ''; + if (genre === 'Music') return true; + try { + const microformat = JSON.parse(document.querySelector('#microformat script')?.textContent || '{}'); + if (microformat.genre === 'Music' || microformat.category === 'Music') return true; + } catch (e) {} + const category = ImprovedTube.category || ''; + if (category === 'Music') return true; + const author = document.querySelector('ytd-video-owner-renderer ytd-channel-name a')?.textContent?.trim() || ''; + if (author && (/VEVO$/i.test(author) || / - Topic$/i.test(author))) return true; + return false; +}; + +ImprovedTube.playerAudioOnlyToggle = function () { + ImprovedTube.storage.player_audio_only = !ImprovedTube.storage.player_audio_only; + ImprovedTube.messages.send({ + action: 'set', + key: 'player_audio_only', + value: ImprovedTube.storage.player_audio_only + }); + ImprovedTube.playerAudioOnlyUpdate(); +}; + +ImprovedTube.renderAudioOnlyOverlay = function () { + const playerContainer = ImprovedTube.elements.player || document.querySelector('#movie_player'); + if (!playerContainer) return; + + let overlay = document.querySelector('#it-audio-only-overlay'); + if (!overlay) { + overlay = document.createElement('div'); + overlay.id = 'it-audio-only-overlay'; + playerContainer.insertBefore(overlay, playerContainer.firstChild); + } + + const videoId = ImprovedTube.videoId(); + const title = ImprovedTube.videoTitle() || 'Audio-Only Mode'; + const author = document.querySelector('ytd-video-owner-renderer ytd-channel-name a')?.textContent?.trim() || ''; + const thumbUrl = videoId ? `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg` : ''; + + overlay.innerHTML = ` + ${thumbUrl ? `` : ''} +
+ ${thumbUrl ? `` : ''} +
+ + + + Audio-Only Mode +
+
${title}
+ ${author ? `
${author}
` : ''} +
+ `; +}; + +ImprovedTube.playerAudioOnlyUpdate = function () { + const isActive = ImprovedTube.storage.player_audio_only === true || (ImprovedTube.storage.player_audio_only_auto_music === true && ImprovedTube.isMusicVideo()); + + const btn = document.querySelector('#it-audio-only-button'); + if (btn) { + btn.style.opacity = isActive ? '1' : '0.55'; + btn.classList.toggle('it-audio-only-active', isActive); + } + + if (isActive) { + document.documentElement.setAttribute('it-player-audio-only', 'true'); + const player = ImprovedTube.elements.player || document.querySelector('#movie_player'); + if (player && typeof player.setPlaybackQualityRange === 'function') { + if (!ImprovedTube._preAudioQuality) { + ImprovedTube._preAudioQuality = player.getPlaybackQuality?.() || 'default'; + } + try { + player.setPlaybackQualityRange('tiny', 'tiny'); + player.setPlaybackQuality('tiny'); + } catch (e) {} + } + ImprovedTube.renderAudioOnlyOverlay(); + } else { + document.documentElement.removeAttribute('it-player-audio-only'); + const player = ImprovedTube.elements.player || document.querySelector('#movie_player'); + if (player && ImprovedTube._preAudioQuality && ImprovedTube._preAudioQuality !== 'tiny') { + try { + player.setPlaybackQualityRange(ImprovedTube._preAudioQuality, ImprovedTube._preAudioQuality); + player.setPlaybackQuality(ImprovedTube._preAudioQuality); + } catch (e) {} + ImprovedTube._preAudioQuality = null; + } + const overlay = document.querySelector('#it-audio-only-overlay'); + if (overlay) overlay.remove(); + } +}; + +ImprovedTube.playerAudioOnlyButton = function () { + if (this.storage.player_audio_only_button !== false) { + var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), + path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + + svg.setAttributeNS(null, 'viewBox', '0 0 24 24'); + path.setAttributeNS(null, 'd', 'M12 3a9 9 0 0 0-9 9v7c0 1.1.9 2 2 2h4v-8H5v-1a7 7 0 0 1 14 0v1h-4v8h4c1.1 0 2-.9 2-2v-7a9 9 0 0 0-9-9z'); + + svg.appendChild(path); + + const isActive = this.storage.player_audio_only === true || (this.storage.player_audio_only_auto_music === true && this.isMusicVideo()); + + this.createPlayerButton({ + id: 'it-audio-only-button', + child: svg, + opacity: isActive ? 1 : 0.55, + onclick: function () { + ImprovedTube.playerAudioOnlyToggle(); + }, + title: 'Audio-Only Mode' + }); + } +}; /*------------------------------------------------------------------------------ REPEAT -------------------------------------------------------------------------------*/ diff --git a/menu/skeleton-parts/player.js b/menu/skeleton-parts/player.js index b3880cef1..821b42eb1 100644 --- a/menu/skeleton-parts/player.js +++ b/menu/skeleton-parts/player.js @@ -111,6 +111,17 @@ extension.skeleton.main.layers.section.player.on.click = { text: 'ambientLighting', value: true }, + player_audio_only: { + component: 'switch', + text: 'audioOnlyMode', + storage: 'player_audio_only', + id: 'player_audio_only' + }, + player_audio_only_auto_music: { + component: 'switch', + text: 'autoSwitchForMusic', + storage: 'player_audio_only_auto_music' + }, player_autoPip: { component: 'switch', text: 'Auto_PiP_picture_in_picture', @@ -1649,6 +1660,13 @@ extension.skeleton.main.layers.section.player.on.click = { text: 'screenshot', id: 'player_screenshot_button' }, + player_audio_only_button: { + component: 'switch', + text: 'audioOnlyButton', + id: 'player_audio_only_button', + storage: 'player_audio_only_button', + value: true + }, embed_subtitle: { component: 'switch', text: 'Subtitle_Capture_including_the_current_words',