Plex Random Movie Picker

Picks a random movie from the Plex library on localhost and displays additional information

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Plex Random Movie Picker
// @namespace    https://greasyfork.org/en/users/247131
// @author       ALi3naTEd0
// @version      1.6
// @license      MIT
// @description  Picks a random movie from the Plex library on localhost and displays additional information
// @match        http://localhost/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // History to track previously shown movies
    let movieHistory = [];
    let currentHistoryIndex = -1;

    // Create the main button for random movie selection
    const button = document.createElement("button");
    button.innerText = "Pick Random Movie";
    button.style.position = "fixed";
    button.style.top = "20px"; // Space from the top
    button.style.left = "50%";
    button.style.transform = "translateX(-50%)";
    button.style.padding = "10px 20px";
    button.style.fontSize = "16px";
    button.style.zIndex = 1000;
    button.style.backgroundColor = "#e5a00d";
    button.style.color = "#000";
    button.style.border = "none";
    button.style.borderRadius = "5px";
    button.style.fontWeight = "bold";
    button.style.cursor = "pointer";
    button.onclick = fetchRandomMovie;
    document.body.appendChild(button);

    // Function to display a movie from history
    function showMovieFromHistory(index) {
        if (index >= 0 && index < movieHistory.length) {
            currentHistoryIndex = index;
            displayMovie(movieHistory[index]);
        }
    }

    // Function to fetch a random movie from the Plex library
    async function fetchRandomMovie() {
        try {
            // Replace with your Plex token and server details
            const token = "TOKEN";
            const url = "http://localhost:32400/library/sections/1/all?X-Plex-Token=" + token;

            // Get server machine identifier
            const identityResponse = await fetch(`http://localhost:32400/identity?X-Plex-Token=${token}`);
            const identityData = await identityResponse.text();
            const identityParser = new DOMParser();
            const identityDoc = identityParser.parseFromString(identityData, "text/xml");
            const serverId = identityDoc.documentElement.getAttribute("machineIdentifier");

            const response = await fetch(url);
            const data = await response.text();
            const parser = new DOMParser();
            const xmlDoc = parser.parseFromString(data, "text/xml");

            // Get all movies in the library
            const movies = xmlDoc.getElementsByTagName("Video");
            if (movies.length === 0) {
                alert("No movies found.");
                return;
            }

            // Select a random movie
            const randomIndex = Math.floor(Math.random() * movies.length);
            const randomMovie = movies[randomIndex];

            // Retrieve movie details
            const title = randomMovie.getAttribute("title");
            const year = randomMovie.getAttribute("year");
            const durationMs = randomMovie.getAttribute("duration");
            const duration = durationMs ? Math.round(durationMs / 60000) + " mins" : "Unknown";
            const movieKey = randomMovie.getAttribute("ratingKey");
            const thumbPath = randomMovie.getAttribute("thumb");

            // Construct Plex movie URL on localhost using the actual server ID
            const plexUrl = `http://localhost:32400/web/index.html#!/server/${serverId}/details?key=%2Flibrary%2Fmetadata%2F${movieKey}`;

            // Construct IMDb search URL
            const imdbSearchUrl = `https://www.imdb.com/find?q=${encodeURIComponent(title + " " + year)}&s=tt`;

            // Create movie data object
            const movieData = {
                title, year, duration, movieKey, thumbPath, plexUrl, imdbSearchUrl, token, serverId
            };

            // Add to history (remove any items after current position if we went back)
            if (currentHistoryIndex < movieHistory.length - 1) {
                movieHistory = movieHistory.slice(0, currentHistoryIndex + 1);
            }
            movieHistory.push(movieData);
            currentHistoryIndex = movieHistory.length - 1;

            // Display the movie
            displayMovie(movieData);

        } catch (error) {
            console.error("Error fetching movie:", error);
            alert("Error fetching movie.");
        }
    }

    // Function to display movie details
    function displayMovie(movieData) {
        const { title, year, duration, movieKey, thumbPath, plexUrl, imdbSearchUrl, token, serverId } = movieData;

        // If dialog exists, remove it to refresh content
        const existingDialog = document.getElementById("movieDialog");
        if (existingDialog) existingDialog.remove();

            // Create custom dialog to display movie details
            const dialog = document.createElement("div");
            dialog.id = "movieDialog";
            dialog.style.position = "fixed";
            dialog.style.top = "50%";
            dialog.style.left = "50%";
            dialog.style.transform = "translate(-50%, -50%)";
            dialog.style.backgroundColor = "#1f1f1f";
            dialog.style.border = "2px solid #e5a00d";
            dialog.style.padding = "20px";
            dialog.style.zIndex = 1001;
            dialog.style.boxShadow = "0 8px 16px rgba(0,0,0,0.6)";
            dialog.style.width = "300px";
            dialog.style.textAlign = "center";
            dialog.style.borderRadius = "10px";

            // Add movie thumbnail if available
            if (thumbPath) {
                const thumbnail = document.createElement("img");
                thumbnail.src = `http://localhost:32400${thumbPath}?X-Plex-Token=${token}`;
                thumbnail.style.width = "100%";
                thumbnail.style.borderRadius = "5px";
                thumbnail.style.marginBottom = "10px";
                dialog.appendChild(thumbnail);
            }

            // Add movie details
            const message = document.createElement("p");
            message.innerText = `Title: ${title}\nYear: ${year}\nDuration: ${duration}`;
            message.style.color = "#ffffff";
            message.style.fontSize = "14px";
            message.style.lineHeight = "1.5";
            message.style.whiteSpace = "pre-line";
            dialog.appendChild(message);

            // Add link to open movie in current tab
            const goButton = document.createElement("a");
            goButton.href = plexUrl; // URL of the movie in localhost
            goButton.innerText = "Go to Movie";
            goButton.style.display = "block";
            goButton.style.marginTop = "15px";
            goButton.style.textDecoration = "none";
            goButton.style.color = "#4d9cff";
            goButton.style.fontWeight = "bold";
            goButton.onclick = () => {
                window.location.href = plexUrl; // Opens the URL in the same tab
            };
            dialog.appendChild(goButton);

            // Add IMDb link in yellow Plex color
            const imdbLink = document.createElement("a");
            imdbLink.href = imdbSearchUrl;
            imdbLink.innerText = "View on IMDb";
            imdbLink.target = "_blank"; // Open IMDb in a new tab
            imdbLink.style.display = "block";
            imdbLink.style.marginTop = "10px";
            imdbLink.style.marginBottom = "15px"; // Add space before the buttons
            imdbLink.style.textDecoration = "none";
            imdbLink.style.color = "#f5c518"; // IMDb yellow color
            imdbLink.style.fontWeight = "bold";
            dialog.appendChild(imdbLink);

            // Add 'Previous' button
            const previousButton = document.createElement("button");
            previousButton.innerText = "Previous";
            previousButton.onclick = () => {
                if (currentHistoryIndex > 0) {
                    showMovieFromHistory(currentHistoryIndex - 1);
                }
            };
            previousButton.style.marginTop = "10px";
            previousButton.style.marginRight = "10px";
            previousButton.style.backgroundColor = currentHistoryIndex > 0 ? "#e5a00d" : "#666";
            previousButton.style.color = currentHistoryIndex > 0 ? "#000" : "#999";
            previousButton.style.border = "none";
            previousButton.style.padding = "8px 16px";
            previousButton.style.borderRadius = "5px";
            previousButton.style.fontWeight = "bold";
            previousButton.style.cursor = currentHistoryIndex > 0 ? "pointer" : "not-allowed";
            previousButton.disabled = currentHistoryIndex <= 0;
            dialog.appendChild(previousButton);

            // Add 'Next' button for another random selection
            const nextButton = document.createElement("button");
            nextButton.innerText = "Next";
            nextButton.onclick = fetchRandomMovie; // Calls the function again for a new random movie
            nextButton.style.marginTop = "10px";
            nextButton.style.marginLeft = "10px";
            nextButton.style.backgroundColor = "#e5a00d";
            nextButton.style.color = "#000";
            nextButton.style.border = "none";
            nextButton.style.padding = "8px 16px";
            nextButton.style.borderRadius = "5px";
            nextButton.style.fontWeight = "bold";
            nextButton.style.cursor = "pointer";
            dialog.appendChild(nextButton);

            // Add line break for button layout
            const lineBreak = document.createElement("br");
            dialog.appendChild(lineBreak);

            // Add 'Close' button below Previous and Next
            const closeButton = document.createElement("button");
            closeButton.innerText = "Close";
            closeButton.onclick = () => {
                document.body.removeChild(dialog);
            };
            closeButton.style.marginTop = "10px";
            closeButton.style.backgroundColor = "#666";
            closeButton.style.color = "#fff";
            closeButton.style.border = "none";
            closeButton.style.padding = "8px 16px";
            closeButton.style.borderRadius = "5px";
            closeButton.style.fontWeight = "bold";
            closeButton.style.cursor = "pointer";
            dialog.appendChild(closeButton);

            // Add the dialog to the document
            document.body.appendChild(dialog);
    }
})();