Twitch - Live Recoder

chronium based only

当前为 2025-08-03 提交的版本,查看 最新版本

// ==UserScript==
// @name         Twitch - Live Recoder
// @namespace    https://gf.qytechs.cn/ja/users/941284-ぐらんぴ
// @version      2025-08-04
// @description  chronium based only
// @author       ぐらんぴ
// @match        https://www.twitch.tv/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=twitch.tv
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

let $s = (el) => document.querySelector(el), $sa = (el) => document.querySelectorAll(el), $c = (el) => document.createElement(el)
let recorder, chunks = [], isRecording = false, log = console.log;

const origAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
    if(type === "loadstart"){
        const recordWrapper = function(e){
            if(location.href == "https://www.twitch.tv/") return;
            record();
        };
        origAddEventListener.call(this, type, recordWrapper, options);
    }
    return origAddEventListener.call(this, type, listener, options);
};

function record(){
    let awaitAddon = setInterval(() => {
        let addon = $s(".player-controls__right-control-group")
        clearInterval(awaitAddon);

        let btn = $c('button');
        btn.textContent = ` [RECORD]`;
        btn.className = "GRMP";
        btn.style.cursor = "pointer";
        btn.addEventListener("click", () => {
            const video = $s("video");
            if(!video){
                alert("Video element not found.");
                return;
            }

            if(video.paused || video.readyState < 3){
                video.play().catch(err => console.warn("Video play failed:", err));
            }

            if(!isRecording){
                try{
                    const stream = video.captureStream();
                    if(!stream){
                        alert("Failed to capture stream.");
                        return;
                    }

                    recorder = new MediaRecorder(stream);
                    chunks = [];

                    recorder.ondataavailable = e => chunks.push(e.data);
                    recorder.onstop = () => {
                        clearInterval(timerInterval);
                        btn.textContent = ` [RECORD]`;

                        const blob = new Blob(chunks, { type: 'video/webm' });
                        const url = URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.href = url;

                        //filename
                        let name
                        let title = $s('[data-a-target="stream-title"]').textContent

                        
                            if(location.pathname.startsWith('/videos/')){// archive
                                name = $s('h1.tw-title').textContent
                                let videoId = location.pathname.replace('/videos/', '')

                                a.download = name + "_" + title + "_" + videoId + ".webm";
                            }else{// live
                                name = $s('[aria-label="Channel Avatar Picture"] > a').href.replace('https://www.twitch.tv/','')
                                const now = new Date();
                                const month = String(now.getMonth() + 1).padStart(2, '0');
                                const day = String(now.getDate()).padStart(2, '0');

                                a.download = name + "_" + title + "_" + month + "/" + day + ".webm";
                            }
                            
                        a.click();
                    };

                    recorder.start();
                    isRecording = true;
                    seconds = 0;
                    btn.textContent = formatTime(seconds);

                    timerInterval = setInterval(() => {
                        seconds++;
                        btn.textContent = formatTime(seconds);
                    }, 1000);

                }catch(e){ alert("Recording failed: " + e);
                         }
            }else{
                recorder.stop();
                isRecording = false;
                clearInterval(timerInterval);
                btn.textContent = ` [RECORD]`;
            }
        });
        if(!$s('.GRMP')) addon.appendChild(btn);

        function formatTime(sec){
            const m = String(Math.floor(sec / 60)).padStart(2, '0');
            const s = String(sec % 60).padStart(2, '0');
            return ` [${m}:${s}]`;
        }
    }, 500);
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址