YouTube multi-view

multi-view video and streaming

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         YouTube multi-view
// @name:en      YouTube multi-view
// @name:ja      YouTube 複窓視聴
// @name:zh-cn      YouTube 多视图
// @namespace    https://www.youtube.com/
// @version      2024-07-20
// @description       multi-view video and streaming
// @description:en    multi-view video and streaming
// @description:ja    生放送や動画を複窓で見れます
// @description:zh-cn 多视图视频和流媒体
// @author       ぐらんぴ
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        none
// @require      https://greasyfork.org/scripts/433051-trusted-types-helper/code/Trusted-Types%20Helper.js
// @license      MIT
// ==/UserScript==

divide = 2

let addBtn = setInterval(()=>{
    if(document.querySelectorAll("#end > div").length !== 2){
        clearInterval(addBtn)
    }else{
        let btns = document.querySelector("#buttons"),
            div = document.createElement('div')
        div.innerHTML = `<button style="color: #ff00dd; position: relative; background: transparent; border: none; outline: none; box-shadow: none; cursor: pointer;"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path fill-rule="evenodd" clip-rule="evenodd" d="M11 17H4C2.34315 17 1 15.6569 1 14V6C1 4.34315 2.34315 3 4 3H20C21.6569 3 23 4.34315 23 6V14C23 15.6569 21.6569 17 20 17H13V19H16C16.5523 19 17 19.4477 17 20C17 20.5523 16.5523 21 16 21H8C7.44772 21 7 20.5523 7 20C7 19.4477 7.44772 19 8 19H11V17ZM4 5H20C20.5523 5 21 5.44772 21 6V14C21 14.5523 20.5523 15 20 15H4C3.44772 15 3 14.5523 3 14V6C3 5.44772 3.44772 5 4 5Z"
        fill="currentColor" /></svg></button>`
        btns.before(div)

        let div2 = document.createElement('div')
        div2.innerHTML = `<input type="text" placeholder="URL" style="position: absolute; display: flex; width: 500px; right: 10px; top: 50px;" input>
        <button class="add" style="position: absolute; display: flex; right: 10px; top: 50px;" id='submit'>add</button>
        <button class="switch" style="position: absolute; display: flex; width: 54px; right: 516px; top: 50px;" id='submit'>switch</button>`
        div.appendChild(div2)

        let hideDiv2 = document.querySelector("#end > div:nth-child(2) > div")
        hideDiv2.style.display = 'none'
        document.querySelector("#end > div:nth-child(2) > button").addEventListener('click', ()=>{
            let del = document.querySelectorAll(".del")
            if(hideDiv2.style.display == 'none'){
                hideDiv2.style.display = ''
                del.forEach(elm => {
                    elm.style.display = "";
                })
            }else{
                hideDiv2.style.display = 'none'
                del.forEach(elm => {
                    elm.style.display = "none";
                })
            }
        });
        a()
    }
},1000)
function a(){
    document.querySelector(".add").onclick =()=>{
        if(location.href.match('/watch')){// /watch
            parent = document.querySelector("ytd-watch-flexy") //document.querySelector("ytd-watch-grid")
        }else if(location.href.match('/results')){// /results
            parent = document.querySelector("ytd-search")
        }else{// /home, /subscriptions, /you
            let parents = document.querySelectorAll("ytd-browse")
            for(let i = 0; i < parents.length; i++){
                if(parents[i].getAttribute('role') !== null){
                    parent = parents[i]
                }
            }
        }
        let ytd = document.createElement('div')
        ytd.innerHTML = `
            <div class="area" style="display: none;">
                <div class="content_class" style="display: flex; flex-wrap: wrap;"></div>
            </div>`
        parent.before(ytd)
        if(location.href.match('/watch')){
            Vwidth = (document.querySelector("#masthead").clientWidth-(document.querySelector("yt-icon > span > div > svg").width.animVal.value*3))/2-1 // document.querySelector("#icon > yt-icon-shape > icon-shape > div > svg").width.animVal.value
            console.log(Vwidth)
        }else{
            Vwidth = document.querySelector("#page-manager").clientWidth / divide -1
            console.log(Vwidth)
        }
        Vval = document.querySelector("#end > div:nth-child(2) > div > input[type=text]").value
        if(Vval.match('&')){
            Vsrc = Vval.substring(0, Vval.indexOf("&")).replace("watch?v=", "embed/")
        }else{
            Vsrc = Vval.replace("watch?v=", "embed/")
        }
        let divWatch = document.createElement('div')
        divWatch.innerHTML = `
        <iframe width="${Vwidth}" height="${Math.floor(Vwidth/1.77)}" src="${Vsrc}"
        title="" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture;
        web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
        <div style="text-align: center;">
        <button class="del" style="position: absolute;" id='submit'>delete</button>
        </div>`
        divWatch.addEventListener('click', (e)=>{
            //console.log(e.target.closest('div').parentNode);
            e.target.closest('div').parentNode.remove()
        });
        document.querySelector(".content_class").appendChild(divWatch)
    }
    document.querySelector(".switch").onclick =()=>{
        if(location.href.match('/watch')){// /watch
            parent = document.querySelector("ytd-watch-flexy")//document.querySelector("ytd-watch-grid")
        }else if(location.href.match('/results')){// /results
            parent = document.querySelector("ytd-search")
        }else{// /home, /subscriptions, /you
            let parents = document.querySelectorAll("ytd-browse")
            for(let i = 0; i < parents.length; i++){
                if(parents[i].getAttribute('role') !== null){
                    parent = parents[i]
                }
            }
        }
        if(parent.style.display == ""){
            parent.style.display = 'none'
            if(document.querySelector(".area") !== null){
                document.querySelector(".area").style.display = ''
            }
        }else{
            parent.style.display = ''
            if(document.querySelector(".area") !== null){
                document.querySelector(".area").style.display = 'none'
            }
        }
    }
}
var oldHref = window.location.href;
var observer = new MutationObserver(()=>{
    if(oldHref != window.location.href){
        oldHref = window.location.href;
        var currHref = window.location.href;
        a()
        if(document.querySelector(".area") !== null && location.href.match('/results')){
            (async () => {
                await new Promise(resolve => setTimeout(resolve, 800)); //Without this code, display = 'none' is unstable.
                document.querySelector("ytd-search").style.display = ''
                document.querySelector(".area").style.display = 'none'
                console.log('async')
            })();
            }else if(document.querySelector(".area") !== null){
                (async () => {
                    await new Promise(resolve => setTimeout(resolve, 800));
                    let parents = document.querySelectorAll("ytd-browse")
                    for(let i = 0; i < parents.length; i++){
                        if(parents[i].getAttribute('role') !== null){
                            if(parents[i].style.display !== 'none' || document.querySelectorAll(".area") !== null){
                                parents[i].style.display = ''
                                document.querySelector(".area").style.display = 'none'
                            }
                        }
                    }
                })();
                }

    }
})
observer.observe(document.body, {childList: true,
                                 subtree: true,
                                });