Skip to content

Instantly share code, notes, and snippets.

@JonnyWong16
Last active January 31, 2024 19:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JonnyWong16/ec660094e4cd7233f1dd96b4b4d62e21 to your computer and use it in GitHub Desktop.
Save JonnyWong16/ec660094e4cd7233f1dd96b4b4d62e21 to your computer and use it in GitHub Desktop.
Open the Plex metadata XML info for any media page with Ctrl+I
// ==UserScript==
// @name Open Plex Metadata XML
// @namespace https://app.plex.tv
// @version 1.3
// @description Open the Plex metadata XML info for any media page with Ctrl+I.
// @author JonnyWong16
// @homepage https://gist.github.com/JonnyWong16/ec660094e4cd7233f1dd96b4b4d62e21
// @downloadURL https://gist.github.com/JonnyWong16/ec660094e4cd7233f1dd96b4b4d62e21/raw/openPlexMetadataXML.user.js
// @updateURL https://gist.github.com/JonnyWong16/ec660094e4cd7233f1dd96b4b4d62e21/raw/openPlexMetadataXML.user.js
// @match https://app.plex.tv/*
// @grant none
// ==/UserScript==
// Based on https://github.com/piplongrun/piplongrun.github.io/blob/master/plxdwnld/bookmarklet.js
(function() {
'use strict';
const clientIdRegex = new RegExp("(?:server|media)\/([a-f0-9]{40})|provider\/(tv\.plex\.provider\.(?:discover|metadata|vod))");
const apiResourceUrl = "https://plex.tv/api/resources?includeHttps=1&X-Plex-Token={token}";
const accessTokenXpath = "//Device[@clientIdentifier='{clientId}']/@accessToken";
const baseUriXpath = "//Device[@clientIdentifier='{clientId}']/Connection[@local='0']/@uri";
const includeUrlParams = {
checkFiles: 1,
includeAllConcerts: 1,
includeBandwidths: 1,
includeChapters: 1,
includeChildren: 1,
includeConcerts: 1,
includeExternalMedia: 1,
includeExtras: 1,
includeGeolocation: 1,
includeLoudnessRamps: 1,
includeMarkers: 1,
includeOnDeck: 1,
includePopularLeaves: 1,
includePreferences: 1,
includeRelated: 1,
includeRelatedCount: 1,
includeReviews: 1,
includeStations: 1
}
const includeUrlParamsServer = {
includeFields: 'thumbBlurHash,artBlurHash'
}
let myPlexAccessToken = localStorage.myPlexAccessToken;
const getXML = function(url, callback) {
const request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
callback(request.responseXML);
}
};
request.open('GET', url);
request.send();
};
const openXML = function(baseUri, accessToken, urlKey, server) {
let XMLUrlParams = includeUrlParams;
if (server) {
XMLUrlParams = {
...XMLUrlParams,
...includeUrlParamsServer
}
}
XMLUrlParams['X-Plex-Token'] = accessToken;
const [uriKey, uriParams] = decodeURIComponent(urlKey).split('?');
if (uriParams) {
XMLUrlParams = {
...Object.fromEntries(new URLSearchParams(uriParams)),
...XMLUrlParams
}
}
const XMLUrl = new URL(baseUri);
XMLUrl.pathname = uriKey;
XMLUrl.search = new URLSearchParams(XMLUrlParams).toString();
window.open(XMLUrl.toString());
};
const viewXML = function() {
const clientId = clientIdRegex.exec(window.location.href);
const urlParams = new URLSearchParams(window.location.href.split('?')[1]);
const urlKey = urlParams.get('key');
if (clientId && clientId.length == 3 && urlKey) {
if (clientId[2] === 'tv.plex.provider.discover' || clientId[2] === 'tv.plex.provider.metadata' || clientId[2] === 'tv.plex.provider.vod') {
const baseUri = 'https://metadata.provider.plex.tv';
const accessToken= myPlexAccessToken;
openXML(baseUri, accessToken, urlKey);
} else {
getXML(apiResourceUrl.replace('{token}', myPlexAccessToken), function(xml) {
const baseUriNode = xml.evaluate(baseUriXpath.replace('{clientId}', clientId[1]), xml, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
const accessTokenNode = xml.evaluate(accessTokenXpath.replace('{clientId}', clientId[1]), xml, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
if (accessTokenNode.singleNodeValue && baseUriNode.singleNodeValue) {
const baseUri = baseUriNode.singleNodeValue.textContent;
const accessToken = accessTokenNode.singleNodeValue.textContent;
openXML(baseUri, accessToken, urlKey, true);
} else {
alert('Unable to find a valid accessToken.');
}
});
}
} else {
alert('You are currently not viewing a media page.');
}
}
document.addEventListener('keydown', function(e) {
// Press Ctrl+I
if (e.keyCode == 73 && !e.shiftKey && e.ctrlKey && !e.altKey && !e.metaKey) {
if (typeof myPlexAccessToken != 'undefined') {
viewXML();
}
}
}, false);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment