미디어가 15초 이상 정지되면 별도 창으로 시각 알림 + 정지 시간 표시
// ==UserScript==
// @name Media Stop Visual Alert Window
// @namespace media-stop-alert-window
// @version 2.4
// @description 미디어가 15초 이상 정지되면 별도 창으로 시각 알림 + 정지 시간 표시
// @match *://*/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const STOP_DELAY = 15000;
let stopTimer = null;
let wasPlaying = false;
let alertWindow = null;
let alertActive = false;
let stoppedAt = null;
function formatTime(date) {
return date.toLocaleTimeString('ko-KR', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
function openAlertWindow() {
if (alertWindow && !alertWindow.closed) return;
alertWindow = window.open(
'',
'MediaStopAlertWindow',
'width=460,height=300,left=100,top=100'
);
if (!alertWindow) {
console.warn('팝업이 차단되었습니다.');
return;
}
alertWindow.document.write(`
<html>
<head>
<title>미디어 정지 알림</title>
<style>
body {
margin: 0;
background: #300;
color: white;
font-family: sans-serif;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
text-align: center;
}
#box {
padding: 20px;
}
.alert {
font-size: 34px;
font-weight: bold;
color: #ff5555;
animation: blink 0.8s infinite;
}
.time {
font-size: 20px;
margin-top: 14px;
color: #ffdada;
}
.title {
font-size: 15px;
margin-top: 12px;
opacity: 0.85;
word-break: break-all;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.25; }
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
`);
}
function setAlertWindowStopped() {
openAlertWindow();
if (!alertWindow || alertWindow.closed) return;
alertActive = true;
const stoppedTimeText = stoppedAt ? formatTime(stoppedAt) : '알 수 없음';
alertWindow.document.title = '⏸ 미디어 정지됨!';
alertWindow.document.getElementById('box').innerHTML = `
<div class="alert">미디어 정지됨!</div>
<p>15초 이상 재생이 재개되지 않았습니다.</p>
<div class="time">정지된 시간: ${stoppedTimeText}</div>
<div class="title">정지된 탭: ${document.title}</div>
`;
alertWindow.focus();
}
function setAlertWindowNormal() {
alertActive = false;
stoppedAt = null;
if (!alertWindow || alertWindow.closed) return;
alertWindow.close();
alertWindow = null;
}
function getMediaElements() {
return [...document.querySelectorAll('video, audio')];
}
function isAnyMediaPlaying() {
return getMediaElements().some(media =>
!media.paused &&
!media.ended &&
media.currentTime > 0 &&
media.readyState > 2
);
}
function startStopTimer() {
clearTimeout(stopTimer);
if (!stoppedAt) {
stoppedAt = new Date();
}
stopTimer = setTimeout(() => {
stopTimer = null;
if (!isAnyMediaPlaying()) {
setAlertWindowStopped();
}
}, STOP_DELAY);
}
function checkMediaState() {
const playing = isAnyMediaPlaying();
if (playing) {
wasPlaying = true;
clearTimeout(stopTimer);
stopTimer = null;
if (alertActive || stoppedAt) {
setAlertWindowNormal();
}
return;
}
if (wasPlaying && !playing && !stopTimer && !alertActive) {
startStopTimer();
}
}
document.addEventListener('play', () => {
wasPlaying = true;
clearTimeout(stopTimer);
stopTimer = null;
setAlertWindowNormal();
}, true);
document.addEventListener('pause', () => {
if (wasPlaying && !alertActive) {
startStopTimer();
}
}, true);
document.addEventListener('ended', () => {
if (wasPlaying && !alertActive) {
startStopTimer();
}
}, true);
setInterval(checkMediaState, 1000);
})();