Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(new option) playlist popup button #1832

Merged
merged 7 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions js&css/web-accessible/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var ImprovedTube = {
channel_home_page_postfix: new RegExp('\/(featured)?\/?$'),
thumbnail_quality: new RegExp('(default\.jpg|mqdefault\.jpg|hqdefault\.jpg|hq720\.jpg|sddefault\.jpg|maxresdefault\.jpg)+'),
video_id: new RegExp('[?&]v=([^&]+)'),
video_time: new RegExp('[?&](?:t|start)=([^&]+)'),
playlist_id: new RegExp('[?&]list=([^&]+)'),
channel_link: new RegExp('https:\/\/www.youtube.com\/@|((channel|user|c)\/)')
},
Expand Down
5 changes: 4 additions & 1 deletion js&css/web-accessible/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ ImprovedTube.ytElementsHandler = function (node) {
this.playlistReverse();
}
}
this.playlistPopupUpdate();
} else if (name === 'YTD-GUIDE-SECTION-RENDERER') {
if (!this.elements.sidebar_section) {
this.elements.sidebar_section = node;
Expand All @@ -120,7 +121,9 @@ ImprovedTube.ytElementsHandler = function (node) {
if(document.documentElement.dataset.pageType === 'video'){
this.hideDetailButton(node.$['flexible-item-buttons'].children);
}
} else if (name === 'YTD-SUBSCRIBE-BUTTON-RENDERER') {
} else if (name === 'YTD-PLAYLIST-HEADER-RENDERER' || (name === 'YTD-MENU-RENDERER' && node.classList.contains('ytd-playlist-panel-renderer'))) {
this.playlistPopupUpdate();
} else if (name === 'YTD-SUBSCRIBE-BUTTON-RENDERER') {
if (node.className.indexOf('ytd-c4-tabbed-header-renderer') !== -1) {
ImprovedTube.blacklist('channel', node);
}
Expand Down
4 changes: 4 additions & 0 deletions js&css/web-accessible/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ ImprovedTube.observer = new MutationObserver(function (mutationList) {
for (var j = 0, k = mutation.addedNodes.length; j < k; j++) {
ImprovedTube.childHandler(mutation.addedNodes[j]);
}
for (const node of mutation.removedNodes){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might should fix our other playlist buttons like this too @MAZ01001 👍

Copy link
Contributor Author

@MAZ01001 MAZ01001 Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, or really every element that gets inserted into the DOM

That's how I wrote this playlist-popup button:

  1. One function that only creates the button (as an object) and returns it (does not insert it into the DOM)
  2. and another that checks if it should add a button to the current page (and adds it via 1.) or update any existing buttons

Then 2. can be called anytime since it checks for itself if it should insert something to the DOM, so I added it to every page where a button might be needed (in the ImprovedTube.childHandler) and whenever any existing buttons got deleted by YT (the for-loop you highlighted).

if(node.nodeName === 'BUTTON' && node.id === 'it-popup-playlist-button') ImprovedTube.playlistPopupUpdate();
}
}
}
}).observe(document.documentElement, {
Expand Down Expand Up @@ -105,6 +108,7 @@ document.addEventListener('yt-page-data-updated', function (event) {
ImprovedTube.playlistShuffle();
ImprovedTube.playlistReverse();
}
ImprovedTube.playlistPopupUpdate();
});

window.addEventListener('load', function () {
Expand Down
90 changes: 90 additions & 0 deletions js&css/web-accessible/www.youtube.com/playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,94 @@ ImprovedTube.playlistShuffle = function () {
{ button.click(); }
}, 5000);
}
};

/*------------------------------------------------------------------------------
4.5.5 POPUP
------------------------------------------------------------------------------*/
/**
* ## Creates a playlist popup button (with ID `it-popup-playlist-button`)
* - used by/in {@linkcode ImprovedTube.playlistPopupUpdate}
* - checks {@linkcode ImprovedTube.storage.player_autoplay} if to autoplay the popuped playlist/video
* - checks {@linkcode ImprovedTube.elements.player} to get video ID and current time, if available, otherwise starts first video of playlist
* - popup has video players width/height or window (inner) width/height when video player is not available
* - the button has the playlist ID as `list` in its dataset and reads from it to open the popup
* @param {string | null} playlistID - the playlist ID or `null`
* @param {boolean} [altButtonStyle] - [optional] changes styling of the playlist popup button - `true` for minplayer and playlist panel and `false` for the playlist page - default `false`
* @param {boolean} [checkVideo] - [optional] if `true` checks the {@linkcode ImprovedTube.elements.player} to get the video ID, time, and size, if available, otherwise starts first video of playlist - default `false` (starts first video of playlist)
* @returns {HTMLButtonElement | null} the playlist popup button to insert into the DOM or `null` if the {@linkcode playlistID} is `null`
*/
ImprovedTube.playlistPopupCreateButton = function (playlistID, altButtonStyle, checkVideo) {
"use strict";
if (playlistID == null) return null;
const button = document.createElement('button'),
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
path = document.createElementNS('http://www.w3.org/2000/svg', 'path');

button.id = 'it-popup-playlist-button';
button.className = `yt-spec-button-shape-next yt-spec-button-shape-next--${(altButtonStyle ?? false) ? 'text' : 'tonal'} yt-spec-button-shape-next--overlay yt-spec-button-shape-next--size-m yt-spec-button-shape-next--icon-button style-scope ytd-playlist-header-renderer`;
button.title = 'Popup playlist';
button.dataset.list = playlistID;
button.style.opacity = '0.8';
button.addEventListener(
'click',
(checkVideo ?? false) ? function (event) {
"use strict";
const videoURL = ImprovedTube.elements.player?.getVideoUrl();
if (videoURL != null && ImprovedTube.regex.video_id.test(videoURL)) {
ImprovedTube.elements.player.pauseVideo();
window.open(`${location.protocol}//www.youtube.com/embed/${videoURL.match(ImprovedTube.regex.video_id)[1]}?autoplay=${(ImprovedTube.storage.player_autoplay ?? true) ? '1' : '0'}&start=${videoURL.match(ImprovedTube.regex.video_time)?.[1] ?? '0'}&list=${this.dataset.list}`, '_blank', `directories=no,toolbar=no,location=no,menubar=no,status=no,titlebar=no,scrollbars=no,resizable=no,width=${ImprovedTube.elements.player.offsetWidth ?? innerWidth},height=${ImprovedTube.elements.player.offsetHeight ?? innerHeight}`);
//! If the video is not in the playlist or not within the first 200 entries, then it automatically selects the first video in the list.
//! But this is okay since this button is mainly for the playlist, not the video (see the video popup button in player.js).
} else window.open(`${location.protocol}//www.youtube.com/embed/videoseries?autoplay=${(ImprovedTube.storage.player_autoplay ?? true) ? '1' : '0'}&list=${this.dataset.list}`, '_blank', `directories=no,toolbar=no,location=no,menubar=no,status=no,titlebar=no,scrollbars=no,resizable=no,width=${innerWidth},height=${innerHeight}`);
} : function (event) {
"use strict";
window.open(`${location.protocol}//www.youtube.com/embed/videoseries?autoplay=${(ImprovedTube.storage.player_autoplay ?? true) ? '1' : '0'}&list=${this.dataset.list}`, '_blank', `directories=no,toolbar=no,location=no,menubar=no,status=no,titlebar=no,scrollbars=no,resizable=no,width=${innerWidth},height=${innerHeight}`);
},
true
);

svg.style.width = '24px';
svg.style.height = '24px';
svg.style.pointerEvents = 'none';
svg.style.fill = 'currentColor';
svg.setAttribute('viewBox', '0 0 24 24');
path.setAttribute('d', 'M19 7h-8v6h8V7zm2-4H3C2 3 1 4 1 5v14c0 1 1 2 2 2h18c1 0 2-1 2-2V5c0-1-1-2-2-2zm0 16H3V5h18v14z');

svg.append(path);
button.append(svg);

return button;
};
/**
* ## Adds a playlist popup button to each playlist panel found or update the links of existing popup buttons
* - buttons will be added on the playlist page (next to the share button), in the playlist panel (after the loop and shuffle buttons), and/or the mini playlist section of the mini player (after the loop and shuffle buttons)
* - uses {@linkcode ImprovedTube.playlistPopupCreateButton} to create each button
* - saves each button in {@linkcode ImprovedTube.elements.buttons} as `it-popup-playlist-button-playlist`, `it-popup-playlist-button-mini`, and `it-popup-playlist-button-panel`
* - called from {@linkcode ImprovedTube.ytElementsHandler} and {@linkcode ImprovedTube.hrefObserver} when DOM changes (somewhat related to playlist renderers)
*/
ImprovedTube.playlistPopupUpdate = function () {
"use strict";
if (!(this.storage.playlist_popup ?? false)) return;

const playlistID = location.search.match(this.regex.playlist_id)?.[1],
playlistIDMini = this.elements.player?.getPlaylistId?.();

if (!document.contains(this.elements.buttons['it-popup-playlist-button-playlist'])) {
const playlistShareButton = document.body.querySelector('ytd-app>div#content>ytd-page-manager>ytd-browse>ytd-playlist-header-renderer ytd-button-renderer.ytd-playlist-header-renderer:has(button[title])');
if (playlistShareButton == null) this.elements.buttons['it-popup-playlist-button-playlist'] = null;
else playlistShareButton.insertAdjacentElement('afterend', this.elements.buttons['it-popup-playlist-button-playlist'] = this.playlistPopupCreateButton(playlistID));
} else if (playlistID != null && this.elements.buttons['it-popup-playlist-button-playlist'].dataset.list !== playlistID) this.elements.buttons['it-popup-playlist-button-playlist'].dataset.list = playlistID;

if (!document.contains(this.elements.buttons['it-popup-playlist-button-mini'])) {
const miniItemButtons = document.body.querySelector('ytd-app>ytd-miniplayer ytd-playlist-panel-renderer div#top-level-buttons-computed');
if (miniItemButtons == null) this.elements.buttons['it-popup-playlist-button-mini'] = null;
else miniItemButtons.appendChild(this.elements.buttons['it-popup-playlist-button-mini'] = this.playlistPopupCreateButton(playlistIDMini, true, true));
} else if (playlistIDMini != null && this.elements.buttons['it-popup-playlist-button-mini'].dataset.list !== playlistIDMini) this.elements.buttons['it-popup-playlist-button-mini'].dataset.list = playlistIDMini;

if (!document.contains(this.elements.buttons['it-popup-playlist-button-panel'])) {
const panelItemButtons = document.body.querySelector('ytd-app>div#content>ytd-page-manager>ytd-watch-flexy ytd-playlist-panel-renderer div#top-level-buttons-computed');
if (panelItemButtons == null) this.elements.buttons['it-popup-playlist-button-panel'] = null;
else panelItemButtons.appendChild(this.elements.buttons['it-popup-playlist-button-panel'] = this.playlistPopupCreateButton(playlistID, true, true));
} else if (playlistID != null && this.elements.buttons['it-popup-playlist-button-panel'].dataset.list !== playlistID) this.elements.buttons['it-popup-playlist-button-panel'].dataset.list = playlistID;
};
4 changes: 4 additions & 0 deletions menu/skeleton-parts/playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ extension.skeleton.main.layers.section.playlist = {
playlist_shuffle: {
component: 'switch',
text: 'shuffle'
},
playlist_popup: {
component: 'switch',
text: 'popupPlayer'
}
}
}
Expand Down