YouTube Playlist Enhancer - Play All & Sort

Adds a Play All button and oldest-to-newest sorting for YouTube playlists

Size

6.2 KB

Version

1.0.1

Created

Oct 22, 2025

Updated

about 17 hours ago

1// ==UserScript==
2// @name		YouTube Playlist Enhancer - Play All & Sort
3// @description		Adds a Play All button and oldest-to-newest sorting for YouTube playlists
4// @version		1.0.1
5// @match		https://www.youtube.com/*
6// @match		https://m.youtube.com/*
7// @icon		https://robomonkey.io/icon.png?adc3438f5fbb5315
8// ==/UserScript==
9(function() {
10    'use strict';
11
12    console.log('YouTube Playlist Enhancer loaded');
13
14    // Debounce function to prevent excessive calls
15    function debounce(func, wait) {
16        let timeout;
17        return function executedFunction(...args) {
18            const later = () => {
19                clearTimeout(timeout);
20                func(...args);
21            };
22            clearTimeout(timeout);
23            timeout = setTimeout(later, wait);
24        };
25    }
26
27    // Check if we're on a playlist page
28    function isPlaylistPage() {
29        return window.location.href.includes('/playlist?list=');
30    }
31
32    // Add Play All button
33    function addPlayAllButton() {
34        if (!isPlaylistPage()) return;
35
36        // Check if button already exists
37        if (document.querySelector('#rm-play-all-button')) return;
38
39        // Find the playlist header actions area
40        const playlistHeader = document.querySelector('ytd-playlist-header-renderer #top-level-buttons-computed');
41        if (!playlistHeader) {
42            console.log('Playlist header not found yet');
43            return;
44        }
45
46        console.log('Adding Play All button');
47
48        // Create Play All button
49        const playAllButton = document.createElement('button');
50        playAllButton.id = 'rm-play-all-button';
51        playAllButton.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m';
52        playAllButton.innerHTML = `
53            <div class="yt-spec-button-shape-next__button-text-content">
54                <span class="yt-core-attributed-string yt-core-attributed-string--white-space-no-wrap">▶ Play All</span>
55            </div>
56        `;
57        playAllButton.style.marginLeft = '8px';
58
59        playAllButton.addEventListener('click', async () => {
60            console.log('Play All button clicked');
61            const firstVideo = document.querySelector('ytd-playlist-video-renderer a#video-title');
62            if (firstVideo) {
63                const videoUrl = firstVideo.href;
64                // Add playlist parameter to play all videos
65                const playlistId = new URLSearchParams(window.location.search).get('list');
66                window.location.href = `${videoUrl}&list=${playlistId}`;
67            }
68        });
69
70        playlistHeader.appendChild(playAllButton);
71    }
72
73    // Add Sort button (Oldest to Newest)
74    function addSortButton() {
75        if (!isPlaylistPage()) return;
76
77        // Check if button already exists
78        if (document.querySelector('#rm-sort-button')) return;
79
80        // Find the playlist header actions area
81        const playlistHeader = document.querySelector('ytd-playlist-header-renderer #top-level-buttons-computed');
82        if (!playlistHeader) {
83            console.log('Playlist header not found yet');
84            return;
85        }
86
87        console.log('Adding Sort button');
88
89        // Create Sort button
90        const sortButton = document.createElement('button');
91        sortButton.id = 'rm-sort-button';
92        sortButton.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m';
93        sortButton.innerHTML = `
94            <div class="yt-spec-button-shape-next__button-text-content">
95                <span class="yt-core-attributed-string yt-core-attributed-string--white-space-no-wrap">↑ Oldest First</span>
96            </div>
97        `;
98        sortButton.style.marginLeft = '8px';
99
100        sortButton.addEventListener('click', async () => {
101            console.log('Sort button clicked');
102            
103            // Click the sort dropdown
104            const sortDropdown = document.querySelector('ytd-playlist-header-renderer paper-menu-button button[aria-label*="sort"], ytd-playlist-header-renderer yt-sort-filter-sub-menu-renderer button');
105            if (sortDropdown) {
106                sortDropdown.click();
107                
108                // Wait for menu to appear and click "Date added (oldest)" option
109                setTimeout(() => {
110                    const oldestOption = Array.from(document.querySelectorAll('tp-yt-paper-listbox ytd-menu-service-item-renderer, tp-yt-paper-listbox yt-formatted-string'))
111                        .find(el => el.textContent.includes('oldest') || el.textContent.includes('Oldest'));
112                    
113                    if (oldestOption) {
114                        oldestOption.click();
115                        console.log('Clicked oldest first option');
116                    } else {
117                        console.log('Oldest option not found');
118                    }
119                }, 300);
120            } else {
121                console.log('Sort dropdown not found');
122            }
123        });
124
125        playlistHeader.appendChild(sortButton);
126    }
127
128    // Initialize buttons
129    function init() {
130        if (!isPlaylistPage()) return;
131        
132        console.log('Initializing YouTube Playlist Enhancer');
133        addPlayAllButton();
134        addSortButton();
135    }
136
137    // Watch for URL changes (YouTube is a SPA)
138    let lastUrl = location.href;
139    const urlObserver = new MutationObserver(debounce(() => {
140        const currentUrl = location.href;
141        if (currentUrl !== lastUrl) {
142            lastUrl = currentUrl;
143            console.log('URL changed to:', currentUrl);
144            setTimeout(init, 1000);
145        }
146    }, 500));
147
148    // Watch for DOM changes to detect when playlist loads
149    const contentObserver = new MutationObserver(debounce(() => {
150        init();
151    }, 500));
152
153    // Start observing
154    if (document.body) {
155        urlObserver.observe(document.body, { childList: true, subtree: true });
156        contentObserver.observe(document.body, { childList: true, subtree: true });
157    }
158
159    // Initial load
160    if (document.readyState === 'loading') {
161        document.addEventListener('DOMContentLoaded', () => {
162            setTimeout(init, 2000);
163        });
164    } else {
165        setTimeout(init, 2000);
166    }
167
168})();
YouTube Playlist Enhancer - Play All & Sort | Robomonkey