// ==UserScript== // @name Plex GUID Grabber // @namespace @soitora/plex-guid-grabber // @description Grab the GUID of a Plex entry on demand // @version 3.2.0 // @license MPL-2.0 // @icon https://app.plex.tv/desktop/favicon.ico // @homepageURL https://soitora.com/Plex-GUID-Grabber/ // @include *:32400/* // @include *://plex.*/* // @include https://app.plex.tv/* // @require https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_getResourceText // @run-at document-end // @downloadURL none // ==/UserScript== GM_addStyle(`button[id$="-guid-button"], button[id$="-yaml-button"] { margin-right: 4px; } button[id$="-guid-button"]:not([id="imdb-guid-button"]):hover img, button[id$="-yaml-button"]:not([id="imdb-yaml-button"]):hover img { filter: invert(100%) grayscale(100%) contrast(120%); } button[id="imdb-guid-button"]:hover img, button[id="imdb-yaml-button"]:hover img { filter: grayscale(100%) contrast(120%); } button[id="imdb-guid-button"] img, button[id="imdb-yaml-button"] img { width: 30px !important; height: 30px !important; } `); // SweetAlert2 Toast const Toast = Swal.mixin({ toast: true, position: "bottom-right", showConfirmButton: false, timer: 5000, timerProgressBar: true, }); // Initialize GM values if they don't exist function initializeGMValues() { // Only set if the values don't already exist if (GM_getValue("TMDB_API_KEY") === undefined) { GM_setValue("TMDB_API_KEY", ""); console.log("\x1b[36mPGG", "Created TMDB_API_KEY storage"); } if (GM_getValue("TVDB_API_KEY") === undefined) { GM_setValue("TVDB_API_KEY", ""); console.log("\x1b[36mPGG", "Created TVDB_API_KEY storage"); } if (GM_getValue("USE_PAS") === undefined) { GM_setValue("USE_PAS", false); console.log("\x1b[36mPGG", "Created USE_PAS storage"); } } // Initialize console.log("\x1b[36mPGG", "🔍 Plex GUID Grabber"); initializeGMValues(); // Variables let rightButtonContainer = null; let clipboard = null; // User configuration - Set these values in your userscript manager const TMDB_API_KEY = GM_getValue("TMDB_API_KEY", ""); // Default empty const TVDB_API_KEY = GM_getValue("TVDB_API_KEY", ""); // Default empty const USE_PAS = GM_getValue("USE_PAS", false); // Default false const siteConfig = { plex: { id: "plex-guid-button", name: "Plex", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/plex.webp", buttonLabel: "Copy Plex GUID", visible: ["album", "artist", "movie", "season", "episode", "show"], }, imdb: { id: "imdb-guid-button", name: "IMDb", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/imdb.webp", buttonLabel: "Open IMDB", visible: ["movie", "show"], }, tmdb: { id: "tmdb-guid-button", name: "TMDB", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/tmdb-small.webp", buttonLabel: "Open TMDB", visible: ["movie", "show"], }, tvdb: { id: "tvdb-guid-button", name: "TVDB", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/tvdb.webp", buttonLabel: "Open TVDB", visible: ["movie", "show"], }, mbid: { id: "musicbrainz-guid-button", name: "MusicBrainz", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/musicbrainz.webp", buttonLabel: "Open MusicBrainz", visible: ["album", "artist"], }, anidb: { id: "anidb-guid-button", name: "AniDB", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/anidb.webp", buttonLabel: "Open AniDB", visible: ["show", "movie"], }, youtube: { id: "youtube-guid-button", name: "YouTube", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/youtube.webp", buttonLabel: "Open YouTube", visible: ["movie", "show", "episode"], }, tmdbYaml: { id: "tmdb-yaml-button", name: "TMDB YAML", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/tmdb-small.webp", buttonLabel: "Copy TMDB YAML", visible: ["movie", "show"], isYamlButton: true, }, tvdbYaml: { id: "tvdb-yaml-button", name: "TVDB YAML", icon: "https://raw.githubusercontent.com/Soitora/Plex-GUID-Grabber/main/.github/images/tvdb.webp", buttonLabel: "Copy TVDB YAML", visible: ["movie", "show"], isYamlButton: true, }, }; function handleButtons(metadata, pageType, guid) { const leftButtonContainer = $(document).find(".PageHeaderLeft-pageHeaderLeft-GB_cUK"); const rightButtonContainer = $(document).find(".PageHeaderRight-pageHeaderRight-j9Yjqh"); console.debug("\x1b[36mPGG \x1b[32mDebug", "Button container found:", rightButtonContainer.length > 0); // Check if container exists or button already exists if (!rightButtonContainer.length || $("#" + siteConfig.plex.id).length) return; // Get title once for all buttons const $directory = $(metadata).find("Directory, Video").first(); const title = $directory.attr("parentTitle") || $directory.attr("title"); const buttons = Object.keys(siteConfig).reduce((acc, site) => { acc[site] = { handler: (event) => handleButtonClick(event, site, guid[site], pageType, metadata), config: siteConfig[site], }; return acc; }, {}); Object.entries(buttons).forEach(([site, { handler, config }]) => { if (config.visible.includes(pageType)) { // Skip YAML buttons if USE_PAS is false if (config.isYamlButton && !USE_PAS) { return; } // For YAML buttons, check if the corresponding API ID exists let shouldShow = true; if (config.isYamlButton) { const apiSite = site === "tmdbYaml" ? "tmdb" : "tvdb"; shouldShow = !!guid[apiSite]; } const $button = $("