BYTS(Better YouTube Shorts) - Greasyfork Edition

Gives you a seekbar, autoplay option, volume options, etc.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         BYTS(Better YouTube Shorts) - Greasyfork Edition
// @version      1.2.9
// @description  Gives you a seekbar, autoplay option, volume options, etc.
// @author       Taki7o7 aka. WaGi-Coding
// @match        *://www.youtube.com/shorts/*
// @require      http://code.jquery.com/jquery-latest.js
// @run-at       document-idle
// @grant        GM_addStyle
// @grant        GM.getValue
// @grant        GM.setValue
// @license      GPLv3
// @namespace    https://greasyfork.org/users/772124
// ==/UserScript==



GM_addStyle(`
input[type=range].volslider {
    height: 14px;
    -webkit-appearance: none;
    margin: 10px 0;
    }
    input[type=range].volslider:focus {
    outline: none;
    }
    input[type=range].volslider::-webkit-slider-runnable-track {
    height: 8px;
    cursor: pointer;
    box-shadow: 0px 0px 0px #000000;
    background: rgb(50 50 50);
    border-radius: 25px;
    border: 1px solid #000000;
    }
    input[type=range].volslider::-webkit-slider-thumb {
        -webkit-appearance: none;
        width: 20px;
        height: 20px;
        margin-top: -7px;
        border-radius: 0px;
        background-image: url('https://i.imgur.com/vcQoCVS.png');
        background-size:20px;
        background-repeat: no-repeat;
        background-position: 50%;
    }
    input[type=range]:focus::-webkit-slider-runnable-track {
    background: rgb(50 50 50);
    }





    .switch {
    position: relative;
    display: inline-block;
    width: 46px;
    height: 20px;
    }
    .switch input {
    opacity: 0;
    width: 0;
    height: 0;
    }
    .slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    -webkit-transition: .4s;
    transition: .4s;
    }
    .slider:before {
    position: absolute;
    content: "";
    height: 12px;
    width: 12px;
    left: 4px;
    bottom: 4px;
    background-color: white;
    -webkit-transition: .4s;
    transition: .4s;
    }
    input:checked + .slider {
    background-color: #FF0000;
    }
    input:focus + .slider {
    box-shadow: 0 0 1px #FF0000;
    }
    input:checked + .slider:before {
    -webkit-transform: translateX(26px);
    -ms-transform: translateX(26px);
    transform: translateX(26px);
    }
    /* Rounded sliders */
    .slider.round {
    border-radius: 12px;
    }

    .slider.round:before {
    border-radius: 50%;
    }
`);

var $ = window.jQuery;
var vid = null;
var reel = null;
var udTimer = null;

var progbar = null;

var seekMouseDown = false;

var bytsVol = null;

var bytsTimeInfo = null;


var lastCurSeconds = 0;

// var xhr = new XMLHttpRequest();
// var windloc = '';

// Storage
var savedVolume = 1.0;
var autoScrollVal = true;
// -------

async function LoadSettings() {
    savedVolume = await GM.getValue('bytsVolume', 1.0);
    autoScrollVal = await GM.getValue('bytsAutoscroll', true);
}

function findValues(obj, key) {
    return findValuesHelper(obj, key, []);
}

function findValuesHelper(obj, key, list) {
    if (!obj) return list;
    if (obj instanceof Array) {
        for (var i in obj) {
            list = list.concat(findValuesHelper(obj[i], key, []));
        }
        return list;
    }
    if (obj[key]) list.push(obj[key]);

    if ((typeof obj == "object") && (obj !== null)) {
        var children = Object.keys(obj);
        if (children.length > 0) {
            for (i = 0; i < children.length; i++) {
                list = list.concat(findValuesHelper(obj[children[i]], key, []));
            }
        }
    }
    return list;
}

window.onload = function () {
    var checkExist = setInterval(() => {
        // wait until any video elements rendered
        if ($('ytd-shorts').length && $('.html5-video-player').length) {
            clearInterval(checkExist);

            LoadSettings();

            setInterval(updateVidElem, 50);

//             windloc = window.location.href;

//             setInterval(() => {
//                 if(windloc != window.location.href){
//                     windloc = window.location.href;
//                     xhr.abort();
//                 }
//             }, 20);

            udTimer = setInterval(AddUploadDateIfNeeded, 50);

            addEventListener("keydown", function (e) {
                switch (e.key.toUpperCase()) {

                    case "ARROWLEFT":
                        $(vid).prop('currentTime', $(vid).prop('currentTime') - 2)
                        break;
                    case "ARROWRIGHT":
                        $(vid).prop('currentTime', $(vid).prop('currentTime') + 2)
                        break;

                    default:
                        break;
                }
            });
        }
    }, 100);
}

function padTo2Digits(num) {
    return num.toString().padStart(2, '0');
}

function updateVidElem() {

    if ($('ytd-reel-video-renderer').length >= 28) {
        $('ytd-reel-video-renderer:lt(9)').remove();
    }

    vid = $('.html5-video-player').first().find('video').first();

    if ($(vid).length === 0) {
        return;
    }

    $(vid).prop('volume', $('#byts-vol').val());

    if (autoScrollVal == true) {
        $(vid).removeAttr('loop');
        $(vid).unbind('ended');
        $(vid).on('ended', function () {
            //console.log("ended");
            $('#navigation-button-down').find("button").first().click();
        });
    }
    else {
        $(vid).attr('loop', true);
        $(vid).unbind('ended');
    }


    reel = $(vid).closest('ytd-reel-video-renderer');
    if ($(reel).length === 0) {
        return;
    }
    if ($(reel).find('#byts-progbar').length === 0) {
        if ($('#byts-progbar').length === 0) {
            $(reel).append('<div id="byts-progbar" style="user-select: none; cursor: pointer; width: 100%; height: 14px; background-color: #343434; position: absolute; margin-top: -20px; border-radius: 10px;"></div>');

        } else {
            $(reel).append($('#byts-progbar'));
        }
        progbar = $('#byts-progbar').first();
        $(progbar).mousemove((e) => {
            if (seekMouseDown) {
                $(vid).prop('currentTime', (e.offsetX * 1 / $(reel).outerWidth()) * $(vid).prop('duration'));
            }
        });
        $(progbar).mousedown(() => {
            seekMouseDown = true;
        });
        $(progbar).mouseleave(() => {
            seekMouseDown = false;
        });
        $(progbar).mouseup((e) => {
            seekMouseDown = false;
            $(vid).prop('currentTime', (e.offsetX * 1 / $(reel).outerWidth()) * $(vid).prop('duration'));
        });
    }
    let time = ($(vid).prop('currentTime') / $(vid).prop('duration')) * 100;


    var progress = document.getElementById('byts-progress');
    if (progress == null) {
        progress = document.createElement("div");
        progress.setAttribute("id", "byts-progress");

        progress.style.userSelect = "none";
        progress.style.backgroundColor = "#FF0000";
        progress.style.height = "100%";
        progress.style.borderRadius = "10px";
        progress.style.width = time + "%";


        progress.onmouseup = function (e) {
            var selected_val = e.offsetX * 1 / $(reel).outerWidth();
            $(vid).prop('currentTime', selected_val * $(vid).prop('duration'));
        }

        $(progbar).append(progress);

    }
    else {
        progress.style.width = time + "%";
    }


    // Time Info
    let durSecs = Math.floor($(vid).prop('duration'));
    let durMinutes = Math.floor(durSecs / 60);
    let durSeconds = durSecs % 60;

    let curSecs = Math.floor($(vid).prop('currentTime'));
    if (curSecs != lastCurSeconds || $(reel).find('#byts-timeinfo').length === 0) {
        lastCurSeconds = curSecs;

        let curMinutes = Math.floor(curSecs / 60);
        let curSeconds = curSecs % 60;

        // TimeInfo Element
        if ($(reel).find('#byts-timeinfo').length === 0) {
            if ($('#byts-timeinfo').length === 0) {
                $(reel).append('<div id="byts-timeinfo" style="user-select: none; display: flex; right: auto; left: auto; position: absolute; margin-top: ' + ($(reel).height() + 2) + 'px;"><div id="byts-timeinfo-textdiv" style="display: flex; margin-right: 5px; margin-top: 4px; color: white; font-size: 1.2rem;">' + `${curMinutes}:${padTo2Digits(curSeconds)} / ${durMinutes}:${padTo2Digits(durSeconds)}` + '</div></div>');
            } else {
                $(reel).append($('#byts-timeinfo'));
            }
            bytsTimeInfo = $('#byts-timeinfo');
        }

        $('#byts-timeinfo-textdiv').text(`${curMinutes}:${padTo2Digits(curSeconds)} / ${durMinutes}:${padTo2Digits(durSeconds)}`);
    }

    $('#byts-timeinfo').css('margin-top', $(reel).height() + 2);


    // Volume Slide
    if ($(reel).find('#byts-vol').length === 0) {
        if ($('#byts-vol').length === 0) {
            $(reel).append('<input style="user-select: none; width: 100px; left: 0px; background-color: transparent; position: absolute; margin-top: ' + ($(reel).height() + 5) + 'px;" type="range" id="byts-vol" class="volslider" name="vol" min="0.0" max="1.0" step="0.01" value="' + savedVolume + '"></input>');
        } else {
            $(reel).append($('#byts-vol'));
        }
        bytsVol = $('#byts-vol');

        $('#byts-vol').on('input change', function () {
            $(vid).prop('volume', $(this).val());

            GM.setValue('bytsVolume', $(this).val());
        });
    }

    $('#byts-vol').css('margin-top', $(reel).height() + 5);


    // AutoScroll
    if ($(reel).find('#byts-autoscroll-div').length === 0) {
        if ($('#byts-autoscroll-div').length === 0) {
            let astc = '';
            if (autoScrollVal) {
                astc = ' checked';
            }
            $(reel).append('<div id="byts-autoscroll-div" style="user-select: none; display: flex; right: 0px; position: absolute; margin-top: ' + ($(reel).height() + 2) + 'px;"><div style="display: flex; margin-right: 5px; margin-top: 4px; color: white; font-size: 1.2rem;">Auto-Scroll: </div><label class="switch"><input id="byts-autoscroll-input" type="checkbox"' + astc + '><span class="slider round"></span></label></div>');
        } else {
            $(reel).append($('#byts-autoscroll-div'));
        }
        bytsVol = $('#byts-autoscroll-div');

        $('#byts-autoscroll-input').on('input change', function () {
            // console.log($(this).is(':checked'));
            GM.setValue('bytsAutoscroll', $(this).is(':checked'));

            if ($(this).is(':checked')) {
                autoScrollVal = true;
            }
            else {
                autoScrollVal = false;
            }
            if (autoScrollVal == true) {
                $(vid).removeAttr('loop');
                $(vid).unbind('ended');
                $(vid).on('ended', function () {
                    //console.log("ended");
                    $('#navigation-button-down').find("button").first().click();
                });
            }
            else {
                $(vid).attr('loop', true);
                $(vid).unbind('ended');
            }
            // $(vid).prop('volume', $(this).val());
        });
    }

    $('#byts-autoscroll-div').css('margin-top', $(reel).height() + 2);
}

async function AddUploadDateIfNeeded() {
    if ($(vid).length === 0) {
        return;
    }
    if ($(reel).length === 0) {
        return;
    }

    if ($(reel).find('#channel-name').find('#byts-uploaddate').length === 0) {
        clearInterval(udTimer);
        try {
            let reelId = $(reel).attr('id'); // for skipping when we switch to another video before the promise is done

            let html = '';
            //xhr.abort();
            // xhr = await $.get(window.location.href, function (data) {
            await $.get(window.location.href, function (data) {
                try{
                    if(reelId != $(reel).attr('id') || $(reel).find('#channel-name').find('#byts-uploaddate').length !== 0){
                        //xhr.abort();
                        udTimer = setInterval(AddUploadDateIfNeeded, 50);
                        return;
                    }

                    html = data;

                    // let jsonString = html.match('(?<=var ytInitialData = ).*(?=;</script>)');
                    let jsonString = html.substring(html.indexOf('var ytInitialData = '));

                    jsonString = jsonString.substring("var ytInitialData = ".length, jsonString.indexOf(';</script>'));

                    let jObj = JSON.parse(jsonString);
                    let ulDate = findValues(jObj, 'publishTimeText')[0].runs[1].text;
                    let viewObj = findValues(jObj, 'viewCountText')[0].runs;
                    let views = viewObj[0].text;
                    let viewsText = viewObj[1].text;

                    let displayText = '';

                    views = views.replaceAll(',', '.');

                    if (typeof ulDate == 'undefined') {
                        ulDate = '';
                    }

                    if(typeof viewObj != 'undefined'){
                        displayText = '<br>' + views + ' ' + viewsText;
                    }

                    if(typeof ulDate != 'undefined' || typeof viewObj != 'undefined'){
                        $(reel).find('#channel-name').find('#text').append('<span id="byts-uploaddate"><br>' + ulDate + displayText + '</span>');
                    }
                }catch{
                    udTimer = setInterval(AddUploadDateIfNeeded, 50);
                    return;
                }
            });

            udTimer = setInterval(AddUploadDateIfNeeded, 50);

        } catch (error) {

            udTimer = setInterval(AddUploadDateIfNeeded, 50);
            return;
        }
    }
}