您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
생방송 시청자 수를 제외한 다시보기 VOD의 고유 조회수를 표시합니다
// ==UserScript== // @name SOOP (숲) - 다시보기 고유 조회수 // @name:ko SOOP (숲) - 다시보기 고유 조회수 // @namespace https://greasyfork.org/ko/users/651882-askld // @version 20250717 // @description 생방송 시청자 수를 제외한 다시보기 VOD의 고유 조회수를 표시합니다 // @author askld // @match https://ch.sooplive.co.kr/* // @icon https://res.sooplive.co.kr/afreeca.ico // @run-at document-start // @grant none // ==/UserScript== (function() { 'use strict'; const updateDOMWithViewCounts = (apiData) => { if (!apiData || !apiData.data) return; const vodDataMap = new Map(apiData.data.map(item => [item.title_no.toString(), item.count])); const vodElements = document.querySelectorAll('.vod-list li'); vodElements.forEach(item => { const linkElement = item.querySelector('.thum a'); if (!linkElement) return; const vodId = linkElement.href.split('/').pop(); if (vodDataMap.has(vodId)) { const counts = vodDataMap.get(vodId); const viewsElement = item.querySelector('.info .views'); if (viewsElement) { const formattedReadCnt = counts.read_cnt.toLocaleString('en-US'); const formattedVodReadCnt = counts.vod_read_cnt.toLocaleString('en-US'); viewsElement.innerHTML = `<em></em> ${formattedReadCnt} (${formattedVodReadCnt})`; } } }); }; const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url, ...args) { // 이 XHR 인스턴스에 url 정보를 저장해 둡니다. this._url = url; return originalOpen.apply(this, [method, url, ...args]); }; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function(...args) { const xhr = this; const originalOnLoad = xhr.onload; // load 이벤트 (요청이 성공적으로 완료되었을 때)를 가로챕니다. xhr.onload = function(...loadArgs) { // 저장된 url이 우리가 감시하려는 API가 맞는지 확인합니다. if (typeof xhr._url === 'string' && xhr._url.includes('/api/') && xhr._url.includes('/vods/review') && xhr.status === 200) { try { const apiData = JSON.parse(xhr.responseText); // DOM이 업데이트된 후 값을 덮어쓰기 위해 약간의 지연을 줍니다. setTimeout(() => updateDOMWithViewCounts(apiData), 500); } catch (e) { console.error('[XHR] JSON 파싱 오류', e); } } // 원래의 onload 핸들러가 있었다면 실행시켜 줍니다. if (originalOnLoad) { return originalOnLoad.apply(this, loadArgs); } }; return originalSend.apply(this, args); }; })();