IMDB Streaming Service

Allows people to watch any movie or TV show directly from the IMDb website

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         IMDB Streaming Service
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Allows people to watch any movie or TV show directly from the IMDb website
// @author       Tristan Reeves
// @match        https://www.imdb.com/title/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=imdb.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let currentKeyIndex = 0;

    function getColor() {
        return hexCode[currentKeyIndex].substring(1);
    }

    function rotateKey() {
        currentKeyIndex = (currentKeyIndex + 1) % hexCode.length;
    }

    // Inject Font Awesome CSS
    function injectFontAwesome() {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css';
        document.head.appendChild(link);
    }

    function getIframe(url) {
        const iframe = document.createElement('iframe');
        iframe.src = url;
        iframe.width = '100%';
        iframe.height = '600';
        iframe.frameBorder = '0';
        iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
        iframe.allowFullscreen = true;
        return iframe;
    }
    const hexCode = ['#3a00944e', '#c9e1059a', '#594b3352', '#3fb62173', '#6afacd8a'];

    function createControlButtons() {
        const container = document.createElement('div');
        container.style.marginTop = '10px';
        container.style.position = 'relative';

        // Previous Episode Button
        const prevButton = document.createElement('button');
        prevButton.innerHTML = '<i class="fas fa-backward"></i>';
        prevButton.title = 'Previous Episode';
        prevButton.style.fontSize = '20px';
        prevButton.style.border = 'none';
        prevButton.style.backgroundColor = 'transparent';
        prevButton.style.cursor = 'pointer';
        prevButton.style.marginRight = '10px';

        // Next Episode Button
        const nextButton = document.createElement('button');
        nextButton.innerHTML = '<i class="fas fa-forward"></i>';
        nextButton.title = 'Next Episode';
        nextButton.style.fontSize = '20px';
        nextButton.style.border = 'none';
        nextButton.style.backgroundColor = 'transparent';
        nextButton.style.cursor = 'pointer';

        container.appendChild(prevButton);
        container.appendChild(nextButton);

        return { container, prevButton, nextButton };
    }

    function createInputFields() {
        const container = document.createElement('div');
        container.style.marginTop = '10px';
        container.style.position = 'relative';

        const seasonLabel = document.createElement('label');
        seasonLabel.textContent = 'Season: ';
        const seasonInput = document.createElement('input');
        seasonInput.type = 'number';
        seasonInput.min = 1; // Keep minimum as 1 but allow any number initially
        seasonLabel.appendChild(seasonInput);

        const episodeLabel = document.createElement('label');
        episodeLabel.textContent = ' Episode: ';
        const episodeInput = document.createElement('input');
        episodeInput.type = 'number';
        episodeInput.min = 1; // Keep minimum as 1 but allow any number initially
        episodeLabel.appendChild(episodeInput);

        const messageContainer = document.createElement('span');
        messageContainer.style.marginLeft = '10px';
        messageContainer.style.transition = 'opacity 2s';
        messageContainer.style.display = 'inline-block';
        messageContainer.style.position = 'absolute';

        container.appendChild(seasonLabel);
        container.appendChild(episodeLabel);
        container.appendChild(messageContainer);

        return { container, seasonInput, episodeInput, messageContainer };
    }

    function showMessage(messageContainer, message) {
        messageContainer.textContent = message;
        messageContainer.style.opacity = '1';
        setTimeout(() => {
            messageContainer.style.opacity = '0';
        }, 3000);
    }

    async function fetchShowDetails(imdbCode) {
        let data;
        try {
            const response = await fetch(`https://www.omdbapi.com/?i=${imdbCode}&apikey=${getColor()}`);
            data = await response.json();
        } catch (error) {
            rotateKey();
            return fetchShowDetails(imdbCode);
        }
        return data;
    }

    async function fetchMaxEpisodesPerSeason(imdbCode, totalSeasons) {
        const maxEpisodesPerSeason = [];
        for (let season = 1; season <= totalSeasons; season++) {
            try {
                const response = await fetch(`https://www.omdbapi.com/?i=${imdbCode}&Season=${season}&apikey=${getColor()}`);
                const seasonData = await response.json();
                maxEpisodesPerSeason[season] = seasonData.Episodes.length;
            } catch (error) {
                rotateKey();
                season--;
            }
        }
        return maxEpisodesPerSeason;
    }

    function saveLastWatched(imdbCode, season, episode) {
        localStorage.setItem(`lastWatched_${imdbCode}`, JSON.stringify({ season, episode }));
    }

    function getLastWatched(imdbCode) {
        const lastWatched = localStorage.getItem(`lastWatched_${imdbCode}`);
        return lastWatched ? JSON.parse(lastWatched) : null;
    }

    // Inject Font Awesome CSS
    injectFontAwesome();

    const currentUrl = window.location.href;
    const idMatch = currentUrl.match(/title\/(tt\d+)\//);
    const imdbCode = idMatch ? idMatch[1] : null;

    if (!imdbCode) { return }

    const videoTypeMeta = document.querySelector('meta[property="og:type"]');
    const videoType = videoTypeMeta ? videoTypeMeta.getAttribute('content') : null;

    if (!videoType) { return }

    let embedUrl = '';

    if (videoType === 'video.movie') {
        embedUrl = `https://multiembed.mov/directstream.php?video_id=${imdbCode}`;
    } else if (videoType === 'video.tv_show') {
        embedUrl = `https://multiembed.mov/directstream.php?video_id=${imdbCode}&s=1&e=1`;
    }

    if (embedUrl) {
        const result = document.evaluate(
            '//*[@id="__next"]/main/div/section[1]/section/div[3]/section/section/div[2]',
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
        );

        const topDiv = result.singleNodeValue;
        if (topDiv) {
            const iframe = getIframe(embedUrl);
            topDiv.insertAdjacentElement("afterend", iframe);

            if (videoType === 'video.tv_show') {
                // Create control buttons and input fields
                const { container: buttonsContainer, prevButton, nextButton } = createControlButtons();
                iframe.insertAdjacentElement("afterend", buttonsContainer);

                const { container, seasonInput, episodeInput, messageContainer } = createInputFields();
                buttonsContainer.insertAdjacentElement("afterend", container);

                // Load last watched season and episode if available
                const lastWatched = getLastWatched(imdbCode);
                if (lastWatched) {
                    seasonInput.value = lastWatched.season;
                    episodeInput.value = lastWatched.episode;
                    iframe.src = `https://multiembed.mov/directstream.php?video_id=${imdbCode}&s=${lastWatched.season}&e=${lastWatched.episode}`;
                } else {
                    // Set default values if no last watched data is found
                    seasonInput.value = 1;
                    episodeInput.value = 1;
                }

                // Update iframe src and save state when inputs change (immediate effect without validation)
                function updateIframeSrcAndSave() {
                    const season = parseInt(seasonInput.value, 10);
                    const episode = parseInt(episodeInput.value, 10);
                    iframe.src = `https://multiembed.mov/directstream.php?video_id=${imdbCode}&s=${season}&e=${episode}`;
                    saveLastWatched(imdbCode, season, episode);
                }

                // Fetch details and validate inputs
                fetchShowDetails(imdbCode).then(data => {
                    if (data.Type === 'series') {
                        const totalSeasons = parseInt(data.totalSeasons, 10);
                        fetchMaxEpisodesPerSeason(imdbCode, totalSeasons).then(maxEpisodesPerSeason => {
                            // Validate season input
                            seasonInput.addEventListener('input', () => {
                                let season = parseInt(seasonInput.value, 10);
                                if (season < 1) {
                                    season = 1;
                                    showMessage(messageContainer, 'Number of seasons must be at least 1.');
                                } else if (season > totalSeasons) {
                                    season = totalSeasons;
                                    showMessage(messageContainer, `Season cannot exceed ${totalSeasons}.`);
                                }
                                seasonInput.value = season;
                                updateIframeSrcAndSave();
                            });

                            // Validate episode input
                            episodeInput.addEventListener('input', () => {
                                let season = parseInt(seasonInput.value, 10);
                                let episode = parseInt(episodeInput.value, 10);
                                if (episode < 1) {
                                    episode = 1;
                                    showMessage(messageContainer, 'Number of episodes must be at least 1.');
                                } else if (episode > maxEpisodesPerSeason[season]) {
                                    episode = maxEpisodesPerSeason[season];
                                    showMessage(messageContainer, `Season ${season} contains only ${maxEpisodesPerSeason[season]} episodes.`);
                                }
                                episodeInput.value = episode;
                                updateIframeSrcAndSave();
                            });

                            // Button event listeners
                            prevButton.addEventListener('click', () => {
                                let season = parseInt(seasonInput.value, 10);
                                let episode = parseInt(episodeInput.value, 10);
                                if (episode > 1) {
                                    episode--;
                                } else if (season > 1) {
                                    season--;
                                    episode = maxEpisodesPerSeason[season]; // Reset to the first episode of the previous season
                                } else {
                            showMessage(messageContainer, 'This is the first episode of the first season.');
                            return;
                        }
                                if (season < 1) {
                                    season = 1;
                                }
                                if (episode < 1) {
                                    episode = 1;
                                }
                                seasonInput.value = season;
                                episodeInput.value = episode;
                                updateIframeSrcAndSave();
                            });

                            nextButton.addEventListener('click', () => {
                                let season = parseInt(seasonInput.value, 10);
                                let episode = parseInt(episodeInput.value, 10);
                                if (episode < maxEpisodesPerSeason[season]) {
                                    episode++;
                                } else if (season < totalSeasons) {
                                    season++;
                                    episode = 1; // Reset to the first episode of the next season
                                } else {
                            showMessage(messageContainer, 'This is the last episode of the last season.');
                            return;
                        }
                                if (season > totalSeasons) {
                                    season = totalSeasons;
                                }
                                if (episode > maxEpisodesPerSeason[season]) {
                                    episode = maxEpisodesPerSeason[season];
                                }
                                seasonInput.value = season;
                                episodeInput.value = episode;
                                updateIframeSrcAndSave();
                            });
                        });
                    }
                });
            }
        }
    }
})();