Feat/force autoplay on refresh - #4259
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an opt-in “Force autoplay / auto-resume on page reload” player setting and wires it into initialization so the extension attempts to resume playback after refresh/navigation, with a muted fallback path for autoplay-policy blocking. The PR also includes Shorts autoplay/loop behavior changes and Firefox keyboard shortcut handling updates.
Changes:
- Add
force_autoplay_on_refreshsetting (menu + locale) and implementImprovedTube.forceAutoplayOnRefresh()with init wiring. - Update Shorts playback behavior (stop autoloop + click-next on ended) and add unit tests around Shorts + new force-autoplay behavior.
- Adjust keyboard shortcut event listener registration for Firefox compatibility and add a Firefox shortcuts test guide.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/shorts-autoplay.test.js | Adds unit coverage for Shorts autoloop stopping and Shorts “next” behavior on ended. |
| tests/unit/force-autoplay-on-refresh.test.js | Adds unit coverage asserting the new setting exists, is localized, is wired, and attempts playback with a muted fallback. |
| TEST_FIREFOX_SHORTCUTS.md | Adds a manual test guide for Firefox shortcut focus behavior. |
| menu/skeleton-parts/player.js | Adds the force_autoplay_on_refresh switch to the Player settings UI. |
| js&css/web-accessible/www.youtube.com/shortcuts.js | Adds document-level key listeners and adjusts “ignore typing targets” logic. |
| js&css/web-accessible/www.youtube.com/player.js | Implements ImprovedTube.forceAutoplayOnRefresh() and autoplay-policy handling. |
| js&css/web-accessible/init.js | Calls forceAutoplayOnRefresh() on init/navigation and changes Shorts autoloop gating; comments out YouTubeExperiments() on SPA navigation. |
| js&css/web-accessible/functions.js | Refactors Shorts autoloop stopping, adds Shorts “next” click on ended, and calls forceAutoplayOnRefresh() during initPlayer(). |
| _locales/en/messages.json | Adds the forceAutoplayOnRefresh locale message. |
Suppressed comments (2)
js&css/web-accessible/init.js:286
- Same default-enabling issue here:
up_next_autoplay !== falsemakes Shorts behavior change even when the setting is unset. Use an explicit=== truecheck (orisset-based check) to avoid enabling by default.
ImprovedTube.redirectShortsToWatch();
if (ImprovedTube.storage.prevent_shorts_autoloop || ImprovedTube.storage.up_next_autoplay !== false) {
ImprovedTube.stop_shorts_autoloop();
}
js&css/web-accessible/functions.js:605
ImprovedTube.storage.up_next_autoplay !== falseenables Shorts “next video” clicks by default when the setting is unset. If the intention is opt-in (or at least consistent withupNextAutoplay()’sissetbehavior), this should check for=== trueinstead.
if (document.documentElement.dataset.pageType === 'shorts' && ImprovedTube.storage.up_next_autoplay !== false) {
const nextButton = document.querySelector('#navigation-button-down button') ||
document.querySelector('button[aria-label="Next video"]');
if (nextButton) {
nextButton.click();
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| window.addEventListener(name, handler, {passive: false, capture: true}); | ||
| // Firefox compatibility: also listen on document for keyboard events | ||
| if (name === 'keydown' || name === 'keyup') { | ||
| document.addEventListener(name, handler, {passive: false, capture: true}); | ||
| } |
| window.removeEventListener(name, handler, {passive: false, capture: true}); | ||
| // Firefox compatibility: also remove from document | ||
| if (name === 'keydown' || name === 'keyup') { | ||
| document.removeEventListener(name, handler, {passive: false, capture: true}); | ||
| } |
| // fallback check for activeElement (for nested elements) | ||
| if (document.activeElement && ImprovedTube.input.ignoreElements.includes(document.activeElement.tagName) && document.activeElement.isContentEditable) return; |
| if (player && typeof player.playVideo === 'function') { | ||
| try { | ||
| playPromise = player.playVideo(); | ||
| } catch (e) { | ||
| if (video && typeof video.play === 'function') { | ||
| playPromise = video.play(); | ||
| } | ||
| } | ||
| } else if (video && typeof video.play === 'function') { | ||
| playPromise = video.play(); | ||
| } |
| playPromise.catch(function (error) { | ||
| if (player && typeof player.mute === 'function') { | ||
| try { player.mute(); } catch (e) {} |
| ImprovedTube.pageType(); | ||
| ImprovedTube.YouTubeExperiments(); | ||
| // ImprovedTube.YouTubeExperiments(); | ||
| ImprovedTube.commentsSidebar(); |
| if (ImprovedTube.storage.prevent_shorts_autoloop || ImprovedTube.storage.up_next_autoplay !== false) { | ||
| ImprovedTube.stop_shorts_autoloop(); | ||
| } |
| if (document.documentElement.dataset.pageType === 'shorts') { | ||
| if (ImprovedTube.storage.prevent_shorts_autoloop || ImprovedTube.storage.up_next_autoplay !== false) { | ||
| ImprovedTube.stop_shorts_autoloop(this); | ||
| } | ||
| } |
|
hi! @MoriMomo thank you! Sort by duration too?
|
|
( Reviewing PRs quickly in 2026, i tend to wish the code came classified line by line
) |
Summary of Changes
force_autoplay_on_refreshswitch in the player options menu.forceAutoplayOnRefreshkey ("Force autoplay / auto-resume on page reload") in_locales/en/messages.json.ImprovedTube.forceAutoplayOnRefreshinjs&css/web-accessible/www.youtube.com/player.js. If browser autoplay policies block unmuted playback (NotAllowedError), it falls back to muted autoplay.forceAutoplayOnRefreshininit.jsandinitPlayerinfunctions.js.tests/unit/force-autoplay-on-refresh.test.js(all 22 test suites / 97 tests passing).Closes #4239