Last active
August 11, 2024 11:42
-
-
Save antronic/b9eaeb6ad4a61ddff0190cade75c8572 to your computer and use it in GitHub Desktop.
Grab the GUID of a Plex entry on demand
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Plex GUID Grabber | |
// @namespace plex-guid-grabber | |
// @include http://plex.*/* | |
// @include https://plex.*/* | |
// @include *:32400/* | |
// @version 1.0.3 | |
// @description Grab the GUID of a Plex entry on demand | |
// @icon https://app.plex.tv/desktop/favicon.ico | |
// @homepageURL https://gist.githubusercontent.com/antronic/b9eaeb6ad4a61ddff0190cade75c8572/raw/70453287de9400e2677da29036d28b81402d77a7/try.js | |
// @downloadURL https://gist.githubusercontent.com/antronic/b9eaeb6ad4a61ddff0190cade75c8572/raw/70453287de9400e2677da29036d28b81402d77a7/try.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js | |
// @require https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js | |
// @resource TOASTR_CSS https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css | |
// @grant GM_addStyle | |
// @grant GM_getResourceText | |
// ==/UserScript== | |
(function () { | |
"use strict"; | |
const plexServer = window.location.origin; | |
let buttonContainer = null; | |
let clipboard = null; | |
// Add Toastr CSS | |
GM_addStyle(GM_getResourceText("TOASTR_CSS")); | |
// Configure Toastr | |
toastr.options = { | |
"closeButton": false, | |
"debug": false, | |
"newestOnTop": false, | |
"progressBar": true, | |
"positionClass": "toast-top-right", | |
"preventDuplicates": true, | |
"onclick": null, | |
"showDuration": "300", | |
"hideDuration": "1000", | |
"timeOut": "5000", | |
"extendedTimeOut": "1000", | |
"showEasing": "swing", | |
"hideEasing": "linear", | |
"showMethod": "fadeIn", | |
"hideMethod": "fadeOut" | |
}; | |
function log(message) { | |
console.log(`PLEX_GUID_GRABBER: ${message}`); | |
} | |
log("Script initialized"); | |
// Function to get GUID | |
async function getGUID() { | |
const posterElement = document.querySelector("[class^=MetadataSimplePosterCard-card-]"); | |
if (!posterElement) { | |
toastr.warning("No Plex item selected. Please select an item and try again."); | |
return null; | |
} | |
const details = extractMetadataDetails(posterElement); | |
if (!details) { | |
toastr.error("Unable to extract metadata details. Please try again with a different item."); | |
return null; | |
} | |
const { metadataKey, token } = details; | |
const metadataUrl = `${plexServer}${metadataKey}?X-Plex-Token=${token}`; | |
try { | |
const response = await fetch(metadataUrl); | |
const text = await response.text(); | |
const parser = new DOMParser(); | |
const xmlDoc = parser.parseFromString(text, "text/xml"); | |
const directoryElement = xmlDoc.querySelector("Directory"); | |
if (directoryElement) { | |
return directoryElement.getAttribute("guid"); | |
} else { | |
throw new Error("Directory element not found in XML"); | |
} | |
} catch (error) { | |
console.error("Plex GUID Grabber: Error fetching metadata:", error); | |
toastr.error("Error fetching GUID. Please try again."); | |
return null; | |
} | |
} | |
// Function to extract metadata key and token from poster element | |
function extractMetadataDetails(element) { | |
const linkElement = element.querySelector("a[class^=PosterCardLink-link-]"); | |
const imgElement = element.querySelector("img"); | |
if (linkElement && imgElement) { | |
const imgURL = new URL(imgElement.src); | |
const token = imgURL.searchParams.get("X-Plex-Token"); | |
const urlParam = imgURL.searchParams.get("url"); | |
const metadataKey = decodeURIComponent(urlParam).split("/thumb/")[0]; | |
return { metadataKey, token }; | |
} | |
return null; | |
} | |
// Function to add the GUID button | |
function addGUIDButton() { | |
if (buttonContainer && !document.getElementById("guid-button")) { | |
const button = document.createElement("button"); | |
button.id = "guid-button"; | |
button.setAttribute("aria-label", "Get and Copy GUID"); | |
button.className = "_1v4h9jl0 _76v8d62 _76v8d61 _76v8d68 tvbry61 _76v8d6g _76v8d6h _1v25wbq1g _1v25wbq18"; | |
button.innerHTML = ` | |
<div class="_1h4p3k00 _1v25wbq8 _1v25wbq1w _1v25wbqg _1v25wbq1g _1v25wbq1c _1v25wbq14 _1v25wbq3g _1v25wbq2g"> | |
<svg aria-hidden="true" class="rkbrtb0 rkbrtb1 rkbrtb3 _1v25wbq5k" height="24" width="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | |
<g transform="translate(2,2) scale(0.8333)"> | |
<path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" fill="currentColor" fill-rule="evenodd"></path> | |
</g> | |
</svg> | |
</div> | |
`; | |
button.addEventListener('click', handleButtonClick); | |
buttonContainer.prepend(button); | |
log("GUID button added successfully"); | |
} | |
} | |
// Function to handle button click | |
async function handleButtonClick() { | |
const guid = await getGUID(); | |
if (guid) { | |
if (!clipboard) { | |
clipboard = new ClipboardJS('#guid-button', { | |
text: function () { | |
return guid; | |
} | |
}); | |
clipboard.on('success', function (e) { | |
toastr.success(guid, "GUID copied successfully!"); | |
e.clearSelection(); | |
}); | |
log("Clipboard.js initialized"); | |
// Trigger the copy action for the first click | |
clipboard.onClick({ currentTarget: document.getElementById('guid-button') }); | |
} else { | |
// For subsequent clicks, manually trigger the copy and show toast | |
navigator.clipboard.writeText(guid).then(function () { | |
toastr.success(guid, "GUID copied successfully!"); | |
}).catch(function (err) { | |
toastr.error('Failed to copy GUID: ' + err); | |
}); | |
} | |
} | |
} | |
// Function to check for button container and add button | |
function checkForButtonContainer() { | |
buttonContainer = document.querySelector(".PageHeaderRight-pageHeaderRight-j9Yjqh"); | |
if (buttonContainer) { | |
addGUIDButton(); | |
observer.disconnect(); | |
log("Button container found and button added. Stopped observing for DOM changes."); | |
} | |
} | |
// Create and start the observer | |
const observer = new MutationObserver(checkForButtonContainer); | |
observer.observe(document.body, { childList: true, subtree: true }); | |
log("Started observing for DOM changes"); | |
// Initial check for button container | |
checkForButtonContainer(); | |
log("Script setup complete"); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment