방송 중임에도 영상이 10초 이상 멈춰있거나(리방 오류 등), 새로 시작될 때 알림을 띄웁니다.
// ==UserScript==
// @name Chzzk Auto Refresh
// @namespace http://tampermonkey.net/
// @version 3.4
// @description 방송 중임에도 영상이 10초 이상 멈춰있거나(리방 오류 등), 새로 시작될 때 알림을 띄웁니다.
// @author 떱_
// @match https://chzzk.naver.com/live/*
// @icon https://ssl.pstatic.net/static/nng/glive/icon/favicon.png
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 설정
const CHECK_INTERVAL = 3000;
const COOLDOWN_TIME = 120000;
const AUTO_REFRESH_SECONDS = 5;
const STUCK_THRESHOLD = 4; // 약 12초
let isPageLoaded = false;
let hasAlerted = false;
let lastPlayingTime = 0;
let cooldownUntil = 0;
let consecutiveStuckCount = 0;
let previousApiStatus = null;
let currentChannelId = null;
let worker = null;
// 페이지 로드 후 5초 대기
setTimeout(() => {
isPageLoaded = true;
currentChannelId = getChannelId(); // 초기 채널 ID 세팅
startWorkerEngine();
}, 5000);
// --- Web Worker 엔진 시동 (백그라운드 생존용) ---
function startWorkerEngine() {
const workerScript = `
self.onmessage = function(e) {
if (e.data === 'start') {
setInterval(function() {
self.postMessage('tick');
}, ${CHECK_INTERVAL});
}
};
`;
const blob = new Blob([workerScript], { type: 'application/javascript' });
worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = function(e) {
if (e.data === 'tick') checkLiveStatus();
};
worker.postMessage('start');
console.log("🟢 [Auto Refresh] Web Worker 모드 시작 (Tampermonkey 최적화)");
}
// --- 유틸리티 ---
function isValidLiveUrl() {
return /^\/live\/[^/]+$/.test(window.location.pathname);
}
function getChannelId() {
const path = window.location.pathname.split('/');
const liveIndex = path.indexOf('live');
if (liveIndex !== -1 && path[liveIndex + 1]) return path[liveIndex + 1];
return null;
}
function isVideoPlaying() {
const video = document.querySelector('video');
if (!video) return false;
return !video.paused && video.readyState > 2 && video.currentTime > 0;
}
// 초강력 새로고침 (Form Submit 방식)
function forceReload() {
console.warn("🔄 강제 새로고침(하드 리프레시)을 실행합니다.");
const url = new URL(window.location.href);
url.searchParams.set('_t', Date.now());
const form = document.createElement('form');
form.method = 'GET';
form.action = url.toString();
document.body.appendChild(form);
form.submit();
}
function clearCustomModal() {
const existingModal = document.getElementById('czk_custom_modal');
if (existingModal) existingModal.remove();
}
// --- 커스텀 알림창 ---
function showCustomModal(reason) {
clearCustomModal();
const modalStyle = `
position: fixed; top: 20%; left: 50%; transform: translate(-50%, -50%);
background: #1e1e1e; color: white; padding: 25px; border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.7); z-index: 999999;
text-align: center; font-family: 'Pretendard', sans-serif; min-width: 350px;
border: 1px solid #444; font-size: 16px;
`;
const btnBaseStyle = `
padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer;
font-weight: bold; margin: 0 5px; font-size: 14px;
`;
const modal = document.createElement('div');
modal.id = 'czk_custom_modal';
modal.style.cssText = modalStyle;
modal.innerHTML = `
<h2 style="margin: 0 0 10px; font-size: 20px; color: #00ffa3;">📢 방송 상태 확인</h2>
<p style="margin: 5px 0; font-weight: bold;">${reason}</p>
<p style="margin: 5px 0; font-size: 13px; color: #ccc;">오류 해결을 위해 강제 새로고침합니다.</p>
<p id="czk_timer_msg" style="margin: 15px 0; font-size: 14px; color: #ffcc00;">${AUTO_REFRESH_SECONDS}초 뒤 자동으로 새로고침됩니다.</p>
<div style="margin-top: 20px;">
<button id="czk_refresh_btn" style="${btnBaseStyle} background: #00ffa3; color: #000;">새로고침</button>
<button id="czk_cancel_btn" style="${btnBaseStyle} background: #555; color: #fff;">취소</button>
</div>
`;
document.body.appendChild(modal);
let timeLeft = AUTO_REFRESH_SECONDS;
const countdownInterval = setInterval(() => {
timeLeft--;
const msgEl = document.getElementById('czk_timer_msg');
if (msgEl) msgEl.innerText = `${timeLeft}초 뒤 자동으로 새로고침됩니다.`;
if (timeLeft <= 0) {
clearInterval(countdownInterval);
forceReload();
}
}, 1000);
document.getElementById('czk_refresh_btn').onclick = () => {
clearInterval(countdownInterval);
forceReload();
};
document.getElementById('czk_cancel_btn').onclick = () => {
clearInterval(countdownInterval);
if (worker) worker.terminate(); // Web Worker 중단
modal.remove();
alert("자동 감지가 취소되었습니다. 다시 켜려면 페이지를 수동으로 새로고침하세요.");
};
}
// --- 메인 감지 로직 ---
async function checkLiveStatus() {
if (!isValidLiveUrl()) return;
const channelId = getChannelId();
if (!channelId) return;
// 채널 이동 감지 로직 (SPA 라우팅 오탐 방지)
if (currentChannelId && currentChannelId !== channelId) {
console.log(`🔄 [Auto Refresh] 채널 이동 감지됨 (${currentChannelId} -> ${channelId}). 스크립트 상태 리셋.`);
currentChannelId = channelId;
hasAlerted = false;
lastPlayingTime = 0;
cooldownUntil = 0;
consecutiveStuckCount = 0;
previousApiStatus = null;
clearCustomModal();
isPageLoaded = false;
setTimeout(() => { isPageLoaded = true; }, 4000);
return;
}
if (!isPageLoaded || hasAlerted) return;
if (Date.now() < cooldownUntil) return;
if (isVideoPlaying()) {
lastPlayingTime = Date.now();
consecutiveStuckCount = 0;
previousApiStatus = 'OPEN';
return;
}
if (lastPlayingTime > 0 && (Date.now() - lastPlayingTime < 30000)) {
console.warn("🛑 방송 종료 감지. 2분 쿨다운.");
cooldownUntil = Date.now() + COOLDOWN_TIME;
lastPlayingTime = 0;
previousApiStatus = 'CLOSE';
return;
}
try {
const response = await fetch(`https://api.chzzk.naver.com/polling/v2/channels/${channelId}/live-status`);
const data = await response.json();
const currentStatus = data.content?.status;
if (currentStatus === 'OPEN') {
if (previousApiStatus === 'CLOSE') {
console.warn("🚨 [EVENT] 방송 시작 감지 (즉시)");
hasAlerted = true;
document.title = "🔴 방송 시작!!";
showCustomModal("방송이 시작되었습니다!");
return;
}
consecutiveStuckCount++;
if (consecutiveStuckCount >= STUCK_THRESHOLD) {
console.warn("🚨 [EVENT] 장시간 멈춤 감지");
hasAlerted = true;
showCustomModal("방송 중이나 영상 재생이 멈춰있습니다.");
}
} else {
consecutiveStuckCount = 0;
}
previousApiStatus = currentStatus;
} catch (error) {
// 에러 무시
}
}
// 탭 복귀 시 즉각 확인
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === 'visible' && !hasAlerted) {
checkLiveStatus();
}
});
console.log("🟢 [Auto Refresh] v3.4 로드됨 (Tampermonkey 최적화 버전)");
})();