您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
chronium based only
// ==UserScript== // @name Twitch - Live Recoder // @namespace https://greasyfork.org/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, seconds = 0, timerInterval, 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.color = "red"; //$s('html.tw-root--theme-light') 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 const now = new Date(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); try{ 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/','') a.download = name + "_" + title + "_" + month + "/" + day + ".webm"; } }catch(e){ //alert('Could not get filename', e) a.download = location.pathname.replace('/', '') + "_" + 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); }