Media Stop Visual Alert Window

미디어가 15초 이상 정지되면 별도 창으로 시각 알림 + 정지 시간 표시

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

Advertisement:

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

Advertisement:

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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);

})();