Web Skipper for Plex

Automatically skip intros, credits, and auto-play the next item on Plex Web.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name               Web Skipper for Plex
// @namespace          https://github.com/x1ao4/web-skipper-for-plex
// @version            1.2
// @description        Automatically skip intros, credits, and auto-play the next item on Plex Web.
// @description:zh-CN  在 Plex Web 上实现自动跳过片头、片尾和自动播放下一个项目功能。
// @description:zh-HK  在 Plex Web 上實現自動跳過片頭、片尾和自動播放下一個項目功能。
// @description:zh-TW  在 Plex Web 上實現自動跳過介紹、名單和自動播放下一個項目功能。
// @author             x1ao4
// @match              https://app.plex.tv/*
// @match              http://localhost:32400/*
// @match              http://127.0.0.1:32400/*
// @license            MIT
// @grant              GM_setValue
// @grant              GM_getValue
// @grant              GM_registerMenuCommand
// @grant              GM_unregisterMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    // 设置检查按钮和 “开启自动播放” 元素的间隔
    const interval = 1000;

    // 设置按钮的选择器
    const buttonSelector = 'button.AudioVideoFullPlayer-overlayButton-D2xSex';

    // 保存菜单命令的 ID
    let skipIntroCmd, skipCreditsCmd, autoPlayNextCmd;

    // 获取用户语言
    const userLang = navigator.language || navigator.userLanguage;

    // 根据用户语言设置按钮文本
    const buttonTexts = {
        'skipIntro': {
            'af-ZA': 'Slaan inleiding oor',
            'bg-BG': 'Прескачане на интродукцията',
            'ca-ES': 'Salteu la Introducció',
            'cs-CZ': 'Přeskočit úvod',
            'da-DK': 'Spring over intro',
            'de-DE': 'Überspringe Intro',
            'el-GR': 'Παράλειψη Προλόγου',
            'en-US': 'Skip Intro',
            'es-419': 'Omitir Opening',
            'es-ES': 'Saltar intro',
            'et-EE': 'Jäta intro vahele',
            'fi-FI': 'Ohita intro',
            'fr-CA': 'Passer l\'intro',
            'fr-FR': 'Passer l\'intro',
            'he-IL': 'דלג על הפתיח',
            'hr-HR': 'Preskakanje uvoda',
            'hu-HU': 'Intro átugrása',
            'is-IS': 'Sleppa inngangi',
            'it-IT': 'Salta Intro',
            'ja-JP': 'イントロをスキップ',
            'ko-KR': '인트로 건너뛰기',
            'lt-LT': 'Praleisti intro',
            'nl-NL': 'Intro overslaan',
            'nb-NO': 'Hopp over intro',
            'pl-PL': 'Pomiń wstęp',
            'pt-BR': 'Pular Intro',
            'pt-PT': 'Saltar introdução',
            'ro-RO': 'Sari peste introducere',
            'ru-RU': 'Пропустить интро',
            'sk-SK': 'Preskočiť intro',
            'sl-SI': 'Preskoči predstavitev',
            'sv-SE': 'Hoppa över intro',
            'th-TH': 'ข้ามตอนต้น',
            'tr-TR': 'Jeneriği Atla',
            'uk-UA': 'Пропустити вступ',
            'zh-TW': '略過介紹',
            'zh-CN': '跳过片头',
        },
        'skipCredits': {
            'af-ZA': 'Slaan eindkrediete oor',
            'cs-CZ': 'Přeskočit titulky',
            'da-DK': 'Spring rulletekster over',
            'de-DE': 'Abspann überspringen',
            'el-GR': 'Παράλειψη των τίτλων τέλους',
            'en-US': 'Skip Credits',
            'es-ES': 'Saltar créditos',
            'fi-FI': 'Ohita lopputekstit',
            'fr-CA': 'Sauter le générique',
            'fr-FR': 'Passer le générique',
            'he-IL': 'דלג על כותרות סיום',
            'hr-HR': 'Preskoči odjavnu špicu',
            'hu-HU': 'Stáblista Átugrása',
            'it-IT': 'Salta Crediti',
            'ja-JP': 'クレジットをスキップ',
            'ko-KR': '크레딧 건너뛰기',
            'lt-LT': 'Praleisti subtitrus',
            'nl-NL': 'Credits overslaan',
            'nb-NO': 'Hopp over rulleteksten',
            'pl-PL': 'Pomiń napisy końcowe',
            'pt-BR': 'Pular créditos',
            'pt-PT': 'Saltar créditos',
            'ro-RO': 'Sari peste credite',
            'ru-RU': 'Пропустить титры',
            'sk-SK': 'Preskočiť titulky',
            'sl-SI': 'Preskoči napise',
            'sv-SE': 'Hoppa över eftertexter',
            'tr-TR': 'Kredileri Atla',
            'uk-UA': 'Пропустити титри',
            'zh-TW': '跳過名單',
            'zh-CN': '跳过片尾',
        },
        'autoSkipIntro': {
            'en-US': 'Auto Skip Intro',
            'zh-TW': '自動跳過介紹',
            'zh-CN': '自动跳过片头',
        },
        'autoSkipCredits': {
            'en-US': 'Auto Skip Credits',
            'zh-TW': '自動跳過名單',
            'zh-CN': '自动跳过片尾',
        },
        'autoPlayNext': {
            'en-US': 'Auto Play Next',
            'zh-TW': '自動播放下一個',
            'zh-CN': '自动播放下一个',
        },
        'enabled': {
            'en-US': 'On',
            'zh-TW': '開',
            'zh-CN': '开',
        },
        'disabled': {
            'en-US': 'Off',
            'zh-TW': '關',
            'zh-CN': '关',
        }
    };

    // 获取对应语言的文本,如果没有找到,则使用英语作为默认语言
    function getText(textType) {
        return buttonTexts[textType][userLang] || buttonTexts[textType]['en-US'];
    }

    // 获取按钮类型
    function getButtonType(buttonText) {
        for (let lang in buttonTexts['skipIntro']) {
            if (buttonTexts['skipIntro'][lang] === buttonText) {
                return 'skipIntro';
            }
        }
        for (let lang in buttonTexts['skipCredits']) {
            if (buttonTexts['skipCredits'][lang] === buttonText) {
                return 'skipCredits';
            }
        }
        return null;
    }

    // 更新菜单命令
    function updateMenuCommands() {
        // 移除旧的菜单命令
        GM_unregisterMenuCommand(skipIntroCmd);
        GM_unregisterMenuCommand(skipCreditsCmd);
        GM_unregisterMenuCommand(autoPlayNextCmd);

        // 为每个功能注册新的菜单命令
        skipIntroCmd = GM_registerMenuCommand(getText('autoSkipIntro') + ' · ' + (GM_getValue('skipIntro', true) ? getText('enabled') : getText('disabled')), function() {
            GM_setValue('skipIntro', !GM_getValue('skipIntro', true));
            updateMenuCommands();
        });

        skipCreditsCmd = GM_registerMenuCommand(getText('autoSkipCredits') + ' · ' + (GM_getValue('skipCredits', true) ? getText('enabled') : getText('disabled')), function() {
            GM_setValue('skipCredits', !GM_getValue('skipCredits', true));
            updateMenuCommands();
        });

        autoPlayNextCmd = GM_registerMenuCommand(getText('autoPlayNext') + ' · ' + (GM_getValue('autoPlayNext', true) ? getText('enabled') : getText('disabled')), function() {
            GM_setValue('autoPlayNext', !GM_getValue('autoPlayNext', true));
            updateMenuCommands();
        });
    }

    // 初始化菜单命令
    updateMenuCommands();

    // 如果存在按钮,则点击按钮
    function clickButton(selector, isEnabled) {
        if (isEnabled) {
            const buttons = document.querySelectorAll(selector);
            for (const button of buttons) {
                let buttonType = getButtonType(button.innerText);
                if (buttonType && GM_getValue(buttonType, true)) {
                    button.click();
                    break;
                }
            }
        }
    }

    // 检查元素是否存在
    function elementExists(selector) {
        return document.querySelector(selector) !== null;
    }

    // 设置一个间隔来检查按钮和 “开启自动播放” 元素,如果它们存在,则点击它们或按空格键
    setInterval(() => {
        clickButton(buttonSelector, true);
        if (GM_getValue('autoPlayNext', true) && elementExists('label.AudioVideoUpNext-autoPlayOn-FMTHL1')) {
            window.dispatchEvent(new KeyboardEvent('keydown', {
                key: ' ',
                code: 'Space',
                keyCode: 32,
                which: 32,
                charCode: 32
            }));
        }
    }, interval);
})();