Skip to content

Commit c7a8a9c

Browse files
fix: YouTube autoplay fails after track #400 (#3541)
1 parent ac2b99d commit c7a8a9c

File tree

4 files changed

+208
-11
lines changed

4 files changed

+208
-11
lines changed

js&css/web-accessible/functions.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,16 @@ ImprovedTube.videoPageUpdate = function () {
345345
ImprovedTube.playerCinemaModeButton();
346346
ImprovedTube.playerHamburgerButton();
347347
ImprovedTube.playerControls();
348+
349+
// Initialize large playlist handler for playlist videos
350+
if (this.getParam(location.href, 'list')) {
351+
ImprovedTube.playlistLargePlaylistHandler();
352+
} else {
353+
// Cleanup when not on a playlist
354+
if (typeof ImprovedTube.cleanupPlaylistHandlers === 'function') {
355+
ImprovedTube.cleanupPlaylistHandlers();
356+
}
357+
}
348358
}
349359
};
350360

js&css/web-accessible/init.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ ImprovedTube.init = function () {
185185
ImprovedTube.playlistPopup();
186186
ImprovedTube.playlistCopyVideoIdButton();
187187
ImprovedTube.playlistCompleteInit();
188+
ImprovedTube.playlistLargePlaylistHandler();
188189
}
189190
try { if (ImprovedTube.lastWatchedOverlay) ImprovedTube.lastWatchedOverlay(); } catch (e) { console.error('[LWO] page-data-updated error', e); }
190191
});
@@ -231,15 +232,15 @@ document.addEventListener('yt-navigate-finish', function () {
231232
if(node.getAttribute('name')) {
232233
//if(node.getAttribute('name') === 'title') {ImprovedTube.title = node.content;} //duplicate
233234
//if(node.getAttribute('name') === 'description') {ImprovedTube.description = node.content;} //duplicate
234-
//if node.getAttribute('name') === 'themeColor') {ImprovedTube.themeColor = node.content;} //might help our darkmode/themes
235-
//Do we need any of these here before the player starts?
235+
//if(node.getAttribute('name') === 'themeColor') {ImprovedTube.themeColor = node.content;} //might help our darkmode/themes
236+
//Do we need any of these here before the player starts?
236237
//if(node.getAttribute('name') === 'keywords') {ImprovedTube.keywords = node.content;}
237238
} else if (node.getAttribute('itemprop')) {
238239
//if(node.getAttribute('itemprop') === 'name') {ImprovedTube.title = node.content;}
239240
if(node.getAttribute('itemprop') === 'genre') {ImprovedTube.category = node.content;}
240241
//if(node.getAttribute('itemprop') === 'channelId') {ImprovedTube.channelId = node.content;}
241242
//if(node.getAttribute('itemprop') === 'videoId') {ImprovedTube.videoId = node.content;}
242-
//The following infos will enable awesome, smart features. Some of which everyone should use.
243+
//The following infos will enable awesome, smart features. Some of which everyone should use.
243244
//if(node.getAttribute('itemprop') === 'description') {ImprovedTube.description = node.content;}
244245
//if(node.getAttribute('itemprop') === 'duration') {ImprovedTube.duration = node.content;}
245246
//if(node.getAttribute('itemprop') === 'interactionCount'){ImprovedTube.views = node.content;}
@@ -250,13 +251,20 @@ document.addEventListener('yt-navigate-finish', function () {
250251
// if(node.getAttribute('itemprop') === 'datePublished' ){ImprovedTube.datePublished = node.content;}
251252
//to use in the "how long ago"-feature, not to fail without API key? just like the "day-of-week"-feature above
252253
// if(node.getAttribute('itemprop') === 'uploadDate') {ImprovedTube.uploadDate = node.content;}
253-
*/
254+
*/
254255
ImprovedTube.pageType();
255256
ImprovedTube.YouTubeExperiments();
256257
ImprovedTube.commentsSidebar();
257258
ImprovedTube.categoryRefreshButton();
258259
try { if (ImprovedTube.lastWatchedOverlay) ImprovedTube.lastWatchedOverlay(); } catch (e) { console.error('[LWO] nav-finish error', e); }
259260

261+
// Cleanup playlist handlers when navigating away from playlist pages
262+
if (!location.search.match(ImprovedTube.regex.playlist_id)) {
263+
if (typeof ImprovedTube.cleanupPlaylistHandlers === 'function') {
264+
ImprovedTube.cleanupPlaylistHandlers();
265+
}
266+
}
267+
260268
// Return YouTube Dislike - call on video pages and Shorts
261269
if (document.documentElement.dataset.pageType === 'video' || window.location.pathname.startsWith('/shorts/')) {
262270
try {

js&css/web-accessible/www.youtube.com/playlist.js

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,98 @@
44
/*------------------------------------------------------------------------------
55
4.5.1 UP NEXT AUTOPLAY
66
------------------------------------------------------------------------------*/
7-
ImprovedTube.playlistUpNextAutoplay = function () { if (this.storage.playlist_up_next_autoplay === false) {
8-
const playlistData = this.elements.ytd_watch?.playlistData;
9-
if (this.getParam(location.href, 'list') && playlistData
10-
&& playlistData.currentIndex
11-
&& playlistData.totalVideos
12-
&& playlistData.localCurrentIndex) {
13-
playlistData.currentIndex = playlistData.totalVideos;
7+
ImprovedTube.playlistUpNextAutoplay = function () {
8+
if (this.storage.playlist_up_next_autoplay === false) {
9+
const playlistData = this.elements.ytd_watch?.playlistData;
10+
if (this.getParam(location.href, 'list') && playlistData
11+
&& playlistData.currentIndex
12+
&& playlistData.totalVideos
13+
&& playlistData.localCurrentIndex) {
14+
15+
// Fix for large playlists: ensure proper synchronization instead of forcing end
16+
// YouTube loads playlists in chunks (typically 200 videos), so we need to
17+
// keep currentIndex and localCurrentIndex in sync as new segments load
18+
if (playlistData.currentIndex !== playlistData.localCurrentIndex) {
19+
playlistData.currentIndex = playlistData.localCurrentIndex;
20+
}
21+
22+
// Monitor for playlist data updates to handle pagination
23+
if (!this.playlistAutoplayObserver) {
24+
this.playlistAutoplayObserver = new MutationObserver((mutations) => {
25+
mutations.forEach((mutation) => {
26+
if (mutation.type === 'attributes' &&
27+
mutation.attributeName === 'data' &&
28+
this.elements.ytd_watch?.playlistData) {
29+
30+
const updatedData = this.elements.ytd_watch.playlistData;
31+
// Resync when YouTube loads new playlist segments
32+
if (updatedData.currentIndex !== updatedData.localCurrentIndex) {
33+
updatedData.currentIndex = updatedData.localCurrentIndex;
34+
}
35+
}
36+
});
37+
});
38+
39+
// Observe the watch element for playlist data changes
40+
if (this.elements.ytd_watch) {
41+
this.playlistAutoplayObserver.observe(this.elements.ytd_watch, {
42+
attributes: true,
43+
attributeFilter: ['data']
44+
});
45+
}
46+
}
1447
}
48+
} else {
49+
// Clean up observer when feature is enabled
50+
if (this.playlistAutoplayObserver) {
51+
this.playlistAutoplayObserver.disconnect();
52+
this.playlistAutoplayObserver = null;
53+
}
54+
}
55+
};
56+
57+
// Enhanced playlist navigation handler for large playlists
58+
ImprovedTube.playlistLargePlaylistHandler = function() {
59+
if (!this.getParam(location.href, 'list')) return;
60+
61+
const playlistData = this.elements.ytd_watch?.playlistData;
62+
if (!playlistData) return;
63+
64+
// Monitor video changes to handle large playlist navigation
65+
const videoElement = this.elements.player?.querySelector('video');
66+
if (videoElement && !this.playlistVideoChangeListener) {
67+
this.playlistVideoChangeListener = () => {
68+
setTimeout(() => {
69+
const currentData = this.elements.ytd_watch?.playlistData;
70+
if (currentData && currentData.currentIndex !== currentData.localCurrentIndex) {
71+
// Force synchronization when video changes
72+
currentData.currentIndex = currentData.localCurrentIndex;
73+
74+
// Update the player's playlist manager if available
75+
const playlistManager = document.querySelector('yt-playlist-manager');
76+
if (playlistManager && playlistManager.autoplayData) {
77+
playlistManager.autoplayData.currentIndex = currentData.localCurrentIndex;
78+
}
79+
}
80+
}, 100);
81+
};
82+
83+
videoElement.addEventListener('loadedmetadata', this.playlistVideoChangeListener);
84+
videoElement.addEventListener('play', this.playlistVideoChangeListener);
1585
}
86+
87+
// Cleanup function for when navigating away from playlist pages
88+
this.cleanupPlaylistHandlers = function() {
89+
if (this.playlistAutoplayObserver) {
90+
this.playlistAutoplayObserver.disconnect();
91+
this.playlistAutoplayObserver = null;
92+
}
93+
if (this.playlistVideoChangeListener && videoElement) {
94+
videoElement.removeEventListener('loadedmetadata', this.playlistVideoChangeListener);
95+
videoElement.removeEventListener('play', this.playlistVideoChangeListener);
96+
this.playlistVideoChangeListener = null;
97+
}
98+
};
1699
};
17100
/*------------------------------------------------------------------------------
18101
4.5.2 REVERSE

test-large-playlist-fix.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Test script to verify the large playlist autoplay fix
2+
// This script can be run in the browser console on a YouTube playlist page
3+
4+
function testLargePlaylistFix() {
5+
console.log('🧪 Testing Large Playlist Autoplay Fix');
6+
7+
// Check if we're on a playlist page
8+
const playlistId = new URLSearchParams(window.location.search).get('list');
9+
if (!playlistId) {
10+
console.error('❌ Not on a playlist page. Please navigate to a YouTube playlist first.');
11+
return false;
12+
}
13+
14+
console.log('✅ Found playlist ID:', playlistId);
15+
16+
// Check if ImprovedTube is loaded
17+
if (typeof ImprovedTube === 'undefined') {
18+
console.error('❌ ImprovedTube not loaded. Please ensure the extension is active.');
19+
return false;
20+
}
21+
22+
console.log('✅ ImprovedTube loaded');
23+
24+
// Check if our new functions exist
25+
if (typeof ImprovedTube.playlistLargePlaylistHandler !== 'function') {
26+
console.error('❌ playlistLargePlaylistHandler function not found');
27+
return false;
28+
}
29+
30+
if (typeof ImprovedTube.cleanupPlaylistHandlers !== 'function') {
31+
console.error('❌ cleanupPlaylistHandlers function not found');
32+
return false;
33+
}
34+
35+
console.log('✅ New playlist functions are available');
36+
37+
// Test playlist data access
38+
const playlistData = ImprovedTube.elements.ytd_watch?.playlistData;
39+
if (!playlistData) {
40+
console.error('❌ Playlist data not available. Try playing a video from the playlist first.');
41+
return false;
42+
}
43+
44+
console.log('✅ Playlist data found:', {
45+
currentIndex: playlistData.currentIndex,
46+
localCurrentIndex: playlistData.localCurrentIndex,
47+
totalVideos: playlistData.totalVideos
48+
});
49+
50+
// Test the fix by calling our handler
51+
try {
52+
ImprovedTube.playlistLargePlaylistHandler();
53+
console.log('✅ playlistLargePlaylistHandler executed successfully');
54+
} catch (error) {
55+
console.error('❌ Error in playlistLargePlaylistHandler:', error);
56+
return false;
57+
}
58+
59+
// Check if observer is created
60+
if (ImprovedTube.playlistAutoplayObserver) {
61+
console.log('✅ Playlist autoplay observer created');
62+
} else {
63+
console.log('ℹ️ Playlist autoplay observer not created (may be normal if playlist_up_next_autoplay is enabled)');
64+
}
65+
66+
// Test synchronization logic
67+
const testData = ImprovedTube.elements.ytd_watch?.playlistData;
68+
if (testData && testData.currentIndex === testData.localCurrentIndex) {
69+
console.log('✅ Playlist indices are synchronized');
70+
} else {
71+
console.log('ℹ️ Playlist indices:', {
72+
currentIndex: testData?.currentIndex,
73+
localCurrentIndex: testData?.localCurrentIndex
74+
});
75+
}
76+
77+
console.log('🎉 Large playlist autoplay fix test completed successfully!');
78+
console.log('📝 To test with a large playlist:');
79+
console.log(' 1. Find a playlist with 400+ videos');
80+
console.log(' 2. Start playing from video #200 or later');
81+
console.log(' 3. Let videos autoplay to verify the fix works');
82+
83+
return true;
84+
}
85+
86+
// Auto-run test if on a playlist page
87+
if (new URLSearchParams(window.location.search).get('list')) {
88+
setTimeout(testLargePlaylistFix, 2000);
89+
} else {
90+
console.log('ℹ️ Navigate to a YouTube playlist to test the large playlist fix');
91+
}
92+
93+
// Export for manual testing
94+
if (typeof window !== 'undefined') {
95+
window.testLargePlaylistFix = testLargePlaylistFix;
96+
}

0 commit comments

Comments
 (0)