BYTS(Better YouTube Shorts) - Greasyfork Edition

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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;
        }
    }
}