您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
chronium based only
// ==UserScript== // @name YouTube - Live Recoder // @namespace https://greasyfork.org/ja/users/941284-ぐらんぴ // @version 2025-08-05 // @description chronium based only // @author ぐらんぴ // @match https://*.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @grant none // @run-at document-start // @require https://greasyfork.org/scripts/433051-trusted-types-helper/code/Trusted-Types%20Helper.js // @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; window.addEventListener("yt-navigate-finish", e => { if(e.detail.pageType == "watch"){ if(e.detail.response?.playerResponse?.videoDetails?.isLive){// live //filename const now = new Date(); const month = String(now.getMonth() + 1).padStart(2, '0'); const day = String(now.getDate()).padStart(2, '0'); let name = e.detail.response.playerResponse.videoDetails?.author let title = e.detail.response.playerResponse.videoDetails?.title //let videoId = e.detail.response.playerResponse.videoDetails?.videoId let btn = $c('button'); btn.textContent = ` [RECORD]`; btn.className = "GRMP"; btn.style.cursor = "pointer"; btn.style.color = "white"; btn.style.background = "none"; btn.style.border = "none"; 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; try{ a.download = name + "_" + title + "_" + month + "/" + day + ".webm"; }catch(e){ //alert('Could not get filename', e) a.download = location.search.replace('?v=', '') + "_" + 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]`; } }); setTimeout(()=>{ if(!$s('.GRMP')) $s('.ytp-right-controls').appendChild(btn) },5000); function formatTime(sec){ const m = String(Math.floor(sec / 60)).padStart(2, '0'); const s = String(sec % 60).padStart(2, '0'); return ` [${m}:${s}]`; } } } });