您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
testing...
// ==UserScript== // @name Twitch - Enable DVR for Live Stream // @namespace https://gf.qytechs.cn/ja/users/941284-ぐらんぴ // @version 2025-08-21 // @description testing... // @author ぐらんぴ // @match https://www.twitch.tv/* // @icon https://www.google.com/s2/favicons?sz=64&domain=twitch.tv // @grant GM_xmlhttpRequest // @connect gql.twitch.tv // @run-at document-start // @license MIT // ==/UserScript== const log = console.log; let $S = el => document.querySelector(el), $SA = el => document.querySelectorAll(el), $C = el => document.createElement(el); let channelName, m3u8Url, origVideo; function isStreamPage(){ if(location.href == "https://www.twitch.tv/directory") return; const pathSegments = location.pathname.split('/').filter(Boolean); return pathSegments.length === 1; } function observeUrlChanges(){ let lastUrl = location.href; const observer = new MutationObserver(() => { if(location.href !== lastUrl){ lastUrl = location.href; checkPageChange(); } }); observer.observe(document.body, { subtree: true, childList: true, }); window.addEventListener('hashchange', () => { if(location.href !== lastUrl){ lastUrl = location.href; checkPageChange(); } }); } // ページ変更チェック function checkPageChange(){ if(isStreamPage()){ if(!$S('#GRMP')) setOrigVideo() setTimeout(()=>{ getM3u8() },1000) } } // 初期実行 & 監視開始 if(isStreamPage()){ setTimeout(()=>{ setOrigVideo(); loadHlsJs(()=>{ getM3u8() }) },1000) } observeUrlChanges(); ///------------------------------------------------------------------------------/// ///------------------------------------------------------------------------------/// ///------------------------------------------------------------------------------/// function setOrigVideo(){ const intervalId = setInterval(() => { let v = $S('video') if(!v) return; clearInterval(intervalId); v.pause(); v.src = ""; v.load(); $S('[data-a-target="video-ref"]').remove() origVideo = $C('video'); origVideo.id = "GRMP"; origVideo.controls = true; origVideo.style.width = "100%"; origVideo.style.height = "100%"; $S('[data-test-selector="video-player__video-container"]').appendChild(origVideo) },500); } function getM3u8(){ channelName = location.pathname.slice(1).toLowerCase(); const query = { "operationName": "PlaybackAccessToken", "extensions": { "persistedQuery": { "version": 1, "sha256Hash": "0828119ded1c13477966434e15800ff57ddacf13ba1911c129dc2200705b0712" } }, "variables": { "isLive": true, "login": "", "isVod": false, "vodID": "", "playerType": "site" } }; query.variables.login = channelName; GM_xmlhttpRequest({ method: "POST", url: "https://gql.twitch.tv/gql", headers: { "Client-ID": "kimne78kx3ncx6brgo4mv6wki5h1ko", "Content-Type": "application/json" }, data: JSON.stringify(query), onload: function(response){ const data = JSON.parse(response.responseText); const token = data.data.streamPlaybackAccessToken; if(token){ m3u8Url = `https://usher.ttvnw.net/api/channel/hls/${channelName}.m3u8?client_id=${token.signature}&token=${encodeURIComponent(token.value)}&sig=${token.signature}&allow_source=true`; if(typeof Hls !== 'undefined' && Hls.isSupported()){ const hls = new Hls(); hls.loadSource(m3u8Url); hls.attachMedia(origVideo); hls.on(Hls.Events.MANIFEST_PARSED, () => { origVideo.play().catch(err => { }); }); }else if (origVideo.canPlayType('application/vnd.apple.mpegurl')){ origVideo.src = m3u8Url; origVideo.play().catch(err => console.error('Playback failed:', err)); }else{ alert('HLS is not supported in this browser'); } }else{ console.error('Token not found'); } }, onerror: function(error){ console.error('Request failed', error); } }); } function loadHlsJs(callback){ if(unsafeWindow.Hls){ callback(); return; } const script = $C('script'); script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest'; script.onload = callback; document.head.appendChild(script); } //Pop-up remover const origFetch = unsafeWindow.fetch; unsafeWindow.fetch = async function(url, init) { let res = await origFetch(url, init); let data; if(res.status === 204 || res.status === 205) return res; try{ const text = await res.text(); data = text ? JSON.parse(text) : {}; }catch(err){//log('Failed to parse JSON:', err); return res; } try{ if(url.startsWith('https://edge.ads.twitch.tv/ads')) data = '' } catch(err){//log('err modifying data:', err); } return new Response(JSON.stringify(data), { headers: res.headers, status: res.status, statusText: res.statusText, }); };
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址