IMDb Watcher

Adds buttons on IMDb to watch movies/series for free

目前為 2025-01-11 提交的版本,檢視 最新版本

// ==UserScript==
// @name         IMDb Watcher
// @namespace    https://www.imdb.com/
// @icon         https://vidsrc.net/template/vidsrc-ico.png
// @version      1.1.1
// @description  Adds buttons on IMDb to watch movies/series for free
// @author       cevoj35548 (orig. Undefined42)
// @license      MIT
// @match        https://*.imdb.com/title/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const sources = [
        {
            name: 'Embed.su',
            urls: {
                movie: 'https://embed.su/embed/movie/{id}/1/1',
                tv: 'https://embed.su/embed/tv/{id}/1/1',
                tvEp: 'https://embed.su/embed/tv/{id}/{season}/{episode}'
            },
            color: '#9038ED',
            textColor: '#f0f0f0'
        },
        {
            name: 'SuperEmbed',
            urls: {
                movie: 'https://multiembed.mov/?video_id={id}',
                tv: 'https://multiembed.mov/?video_id={id}',
                tvEp: 'https://multiembed.mov/?video_id={id}&s={season}&e={episode}'
            },
            color: '#5009DE',
            textColor: '#bad8eb'
        },
        {
            name: '2Embed',
            urls: {
                movie: 'https://www.2embed.stream/embed/movie/{id}',
                tv: 'https://www.2embed.stream/embed/tv/{id}',
                tvEp: 'https://www.2embed.stream/embed/tv/{id}/{season}/{episode}'
            },
            color: '#0058a4',
            textColor: '#bad8eb'
        },
        {
            name: 'VidSrc',
            urls: {
                movie: 'https://vidsrc.net/embed/movie/{id}',
                tv: 'https://vidsrc.net/embed/tv/{id}',
                tvEp: 'https://vidsrc.net/embed/tv/{id}/{season}/{episode}'
            },
            color: '#125784',
            textColor: '#bad8eb'
        },
    ];

    const cornerPadding = 20; // Distance from the corner of the screen
    const buttonSpacing = 2; // Space between each button in the stack

    // extract season and episode numbers from the IMDb page
    function extractSeasonEpisode() {
        const seasonEpisodeDiv = document.querySelector('[data-testid="hero-subnav-bar-season-episode-numbers-section"]');
        if (seasonEpisodeDiv) {
            const seasonEpisodeText = seasonEpisodeDiv.textContent.trim();
            const match = seasonEpisodeText.match(/S(\d+).E(\d+)/);
            if (match) {
                return {
                    season: match[1],
                    episode: match[2]
                };
            }
        }
        return null;
    }

    // extract the series ID from the IMDb page
    function extractSeriesId() {
        const seriesLink = document.querySelector('[data-testid="hero-title-block__series-link"]');
        if (seriesLink) {
            const href = seriesLink.getAttribute('href');
            const match = href.match(/\/title\/(tt\d+)\//);
            if (match) {
                return match[1];
            }
        }
        return null;
    }

    // generate the URL for a specific source
    function generateUrl(urls, isMovie, isEpisode, imdbId, seriesId, seasonEpisode) {
        if (isMovie && isEpisode) {
            return urls.movie.replace('{id}', imdbId);
        } else if (seasonEpisode && seriesId) {
            const { season, episode } = seasonEpisode;
            return urls.tvEp.replace('{id}', seriesId).replace('{season}', season).replace('{episode}', episode);
        } else {
            return urls.tv.replace('{id}', imdbId);
        }
    }

    sources.reverse().forEach((source, index) => {
        const button = document.createElement('button');
        button.textContent = `📽 Watch - ${source.name}`;
        button.style.fontFamily = 'Arial';
        button.style.position = 'fixed';
        button.style.bottom = `${cornerPadding + index * (40 + buttonSpacing)}px`;
        button.style.right = `${cornerPadding}px`;
        button.style.padding = '10px';
        button.style.background = source.color;
        button.style.color = source.textColor;
        button.style.border = 'none';
        button.style.cursor = 'pointer';
        button.style.fontWeight = 'bold';
        button.style.borderRadius = '6px';
        button.style.zIndex = '9999';
        button.style.filter = 'drop-shadow(0 10px 8px rgb(0 0 0 / 0.05)) drop-shadow(0 4px 3px rgb(0 0 0 / 0.1))';

        // add click event to redirect to the source URL
        button.addEventListener('click', () => {
            const imdbId = window.location.pathname.split('/')[2];
            const isMovie = document.title.indexOf('TV Series') === -1;
            const isEpisode = document.title.indexOf('TV Episode') === -1;
            const seasonEpisode = extractSeasonEpisode();
            const seriesId = extractSeriesId();
            const url = generateUrl(source.urls, isMovie, isEpisode, imdbId, seriesId, seasonEpisode);
            window.open(url, '_blank');
        });

        document.body.appendChild(button);
    });
})();