您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
You can record a live stream.
// ==UserScript== // @name CHZZK - Recorder (HLS) // @name:en CHZZK - Recorder (HLS) // @name:ko 치지직 - 레코더 (HLS) // @namespace https://greasyfork.org/ja/users/941284-ぐらんぴ // @version 2025-08-03 // @description You can record a live stream. // @description:en You can record a live stream. // @description:ko 라이브 스트림을 녹화할 수 있습니다. // @author ぐらんぴ // @match https://*.naver.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=naver.com // @grant none // @run-at document-start // @license MIT // ==/UserScript== let m3u8, lastHref, recorder, chunks = [], isRecording = false, log = console.log; const origSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function (){ this.addEventListener("load", function (){ try{ const url = this.responseURL; const currHref = location.href; // detect location changed if(currHref !== lastHref){ lastHref = currHref; m3u8 = undefined; } // reassign if((!m3u8 || currHref !== lastHref) && url?.includes(".m3u8") && currHref.startsWith('https://chzzk.naver.com/live/')){ m3u8 = url; log("m3u8:", m3u8); } }catch(e){//log("err", e) } }); return origSend.apply(this, arguments); }; const origAppendChild = Element.prototype.appendChild; Element.prototype.appendChild = function (...args){ const el = args[0]; if(el?.className === "video_information_control__UTm8Z"){ if(!location.href.startsWith('https://chzzk.naver.com/live/')) return; if(el.querySelector(".GRMP")) return; const button = document.createElement("button"); button.className = "GRMP"; button.textContent = "RECORD"; button.addEventListener("click", () => { if(!m3u8){ alert("No m3u8 URL found."); return; } const video = document.querySelector("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 = () => { const blob = new Blob(chunks, { type: 'video/webm' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = document.querySelector(".name_text__yQG50").textContent.trim() + " - " + document.querySelector(".video_information_title__jrLfG").textContent.trim() + ".webm"; a.click(); }; recorder.start(); isRecording = true; button.textContent = "SAVE"; }catch(e){ alert("Recording failed: " + e); } }else{ recorder.stop(); isRecording = false; button.textContent = "RECORD"; } }); el.appendChild(button); } return origAppendChild.apply(this, args); };