Play Single Video From YouTube Playlist

Adds a button on YouTube playlist video (non-Shorts) entries, which links to a clean watch page for that video that does not include the playlist.

// ==UserScript==
// @name         Play Single Video From YouTube Playlist
// @namespace    http://tampermonkey.net/
// @version      2025.08.07
// @description  Adds a button on YouTube playlist video (non-Shorts) entries, which links to a clean watch page for that video that does not include the playlist.
// @license      MIT
// @author       provigz (Vankata453)
// @match        *://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?domain=youtube.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function createPlaylistPlaySingleButtons() {
        if (location.href.indexOf("/playlist?") <= 0) return;

        const videoEntries = document.querySelectorAll("ytd-playlist-video-renderer");
        videoEntries.forEach((videoEntry) => {
            const videoEntryURLSplit = videoEntry.querySelector("a#thumbnail").getAttribute("href").split("&list=");
            if (videoEntryURLSplit.length <= 1) return; // The link to the video is clean by default, entry is a part of the "Recommended videos" section.

            const videoWatchURL = videoEntryURLSplit[0];

            let button = videoEntry.querySelector("button-view-model#button-play-single");
            if (button) {
                // Button exists, simply update the video URL
                button.querySelector("a").setAttribute("href", videoWatchURL);
                return;
            }

            button = document.createElement("button-view-model");
            button.className = "yt-spec-button-view-model";
            button.id = "button-play-single";

            const anchor = document.createElement("a");
            anchor.className = "yt-spec-button-shape-next yt-spec-button-shape-next--filled yt-spec-button-shape-next--overlay yt-spec-button-shape-next--size-m yt-spec-button-shape-next--icon-leading yt-spec-button-shape-next--enable-backdrop-filter-experiment";
            anchor.setAttribute("aria-haspopup", "false");
            anchor.setAttribute("force-new-state", "true");
            anchor.setAttribute("aria-disabled", "false");
            anchor.setAttribute("href", videoWatchURL);
            anchor.setAttribute("aria-label", "Play Single");
            anchor.style.paddingRight = "0";

            const iconWrapper = document.createElement("div");
            iconWrapper.className = "yt-spec-button-shape-next__icon";
            iconWrapper.setAttribute("aria-hidden", "true");
            const icon = document.createElement("img");
            icon.setAttribute("src", "https://static.thenounproject.com/png/open-link-icon-1395731-512.png");
            icon.style.width = "24px";
            icon.style.height = "24px";
            iconWrapper.appendChild(icon);

            anchor.appendChild(iconWrapper);
            button.appendChild(anchor);

            videoEntry.insertBefore(button, videoEntry.querySelector("div#menu"));
        });
    }

    document.addEventListener("yt-navigate-finish", createPlaylistPlaySingleButtons);
    document.addEventListener("yt-action", (ev) => {
        if (event.detail && event.detail.actionName &&
            (event.detail.actionName.indexOf("yt-append-continuation") >= 0 ||
             event.detail.actionName == "yt-update-playlist-action")) {
            createPlaylistPlaySingleButtons();
        }
    });
})();