From 7081c4ef94d8b838d5a9e734c57f410f04fc1f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Rigal?= Date: Fri, 15 Apr 2022 17:15:17 +0200 Subject: [PATCH] Improved Plex Discover support (#8) --- README.md | 12 +++++- background.js | 13 ++++++ js/content-scripts/plex.js | 88 ++++++++++++++++++++++---------------- 3 files changed, 75 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 253fe8b..69a328f 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,22 @@ [![Chrome Web Store Downloads](https://img.shields.io/chrome-web-store/d/hopnjiadheaagfhpipecoamoegijhnij.svg?style=flat-square)](https://chrome.google.com/webstore/detail/hopnjiadheaagfhpipecoamoegijhnij/reviews) [![Chrome Web Store Rating](https://img.shields.io/chrome-web-store/stars/hopnjiadheaagfhpipecoamoegijhnij.svg?style=flat-square)](https://chrome.google.com/webstore/detail/hopnjiadheaagfhpipecoamoegijhnij/reviews) -Overseerr Assistant is a browser extension for [Overseerr](https://github.com/sct/overseerr). It provides an integration for TMDB and IMDb websites. +Overseerr Assistant is a browser extension for [Overseerr](https://github.com/sct/overseerr). It provides an integration for most popular media websites. Features: -- One-click Overseerr requests directly from TMDB and IMDb websites +- One-click Overseerr requests directly from media websites - One-click access to available media on Plex - Monitor requests status - Support for both movies and TV shows +Supported media websites: +- [TMDB](https://www.themoviedb.org/) +- [IMDb](https://www.imdb.com/) +- [TVDB](https://thetvdb.com/) +- [Plex Discover](https://app.plex.tv/desktop/#!/media/tv.plex.provider.discover?source=home) +- [Letterboxd](https://letterboxd.com/) +- [Allociné](https://www.allocine.fr/) + ## Install **[Available on the Chrome Web Store](https://chrome.google.com/webstore/detail/hopnjiadheaagfhpipecoamoegijhnij)** diff --git a/background.js b/background.js index 3153d0b..e8d13d9 100644 --- a/background.js +++ b/background.js @@ -51,8 +51,21 @@ chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { return true; } + else if (request.contentScriptQuery === 'plexQueryMedia') { + let mediaKey = encodeURIComponentSafe(request.mediaKey); + let plexToken = encodeURIComponentSafe(request.plexToken); + console.log(`Requesting Plex media '${mediaKey}'`); + const options = {headers: {'Accept': 'application/json'}}; + fetch(`https://metadata.provider.plex.tv/library/metadata/${mediaKey}?X-Plex-Token=${plexToken}`, options) + .then(response => response.json()) + .then(json => sendResponse(json)) + .catch(error => console.error(error)); + return true; + } + else if (request.contentScriptQuery === 'openOptionsPage') { chrome.runtime.openOptionsPage(); return true; } + return false; }); diff --git a/js/content-scripts/plex.js b/js/content-scripts/plex.js index a29d05c..3f88c85 100644 --- a/js/content-scripts/plex.js +++ b/js/content-scripts/plex.js @@ -4,9 +4,6 @@ containerOptions.anchorElement = 'div.PrePlayActionBar-container-US01pp'; containerOptions.containerClass = 'mb-3 py-2'; containerOptions.badgeBackground = '#00000099'; -const titleElementSelector = 'div.PrePlayLeftTitle-leftTitle-ewTpwH'; -const dateElementSelector = 'div.PrePlaySecondaryTitle-secondaryTitle-BA3QVn'; - function isHashValid(hash) { return hash.startsWith('#!/provider/'); @@ -41,48 +38,67 @@ function arrangeMargins() { }); } +function getPlexToken() { + let imageElements = $('img').filter(function() { + return $(this).attr('src').includes('X-Plex-Token'); + }); + if (imageElements.length > 0) { + let pattern = /.X-Plex-Token=(\w+)/; + let matches = imageElements.attr('src').match(pattern); + if (matches !== null && matches.length > 1) { + return matches[1]; + } + } + return null; +} + +function getMediaKey() { + let pattern = /key=%2Flibrary%2Fmetadata%2F(\w+)/; + let matches = document.location.hash.match(pattern); + if (matches !== null && matches.length > 1) { + return matches[1]; + } + return null; +} + function processPage() { if (overseerrContainer) overseerrContainer.remove(); - waitForElm(titleElementSelector).then((titleElement) => { - let title = titleElement.textContent; - let releaseYear = parseInt($(dateElementSelector).text()) || null; - console.log(title, releaseYear); + waitForElm('div.PrePlayLeftTitle-leftTitle-ewTpwH').then(() => { + waitForElm('div.ImagePoster-flex-Ry0HC5 > img').then(() => { + initializeContainer(); + insertSpinner(); + arrangeMargins(); + + pullStoredData(function () { + if (!userId) { + removeSpinner(); + insertNotLoggedInButton(); + return; + } - initializeContainer(); - insertSpinner(); - arrangeMargins(); + let plexToken = getPlexToken(); + let mediaKey = getMediaKey(); - pullStoredData(function () { - if (!userId) { - removeSpinner(); - insertNotLoggedInButton(); - return; - } - - chrome.runtime.sendMessage({contentScriptQuery: 'search', title: title}, json => { - json.results = json.results - .filter((result) => result.mediaType === 'movie' || result.mediaType === 'tv') - .filter((result) => { - if(!releaseYear) { - return true; - } - let date = result.releaseDate || result.firstAirDate || null; - return date && parseInt(date.slice(0, 4)) === releaseYear; - }); - if (json.results.length === 0) { + if (plexToken === null || mediaKey === null) { removeSpinner(); insertStatusButton('Media not found', 0); return; } - const firstResult = json.results[0]; - mediaType = firstResult.mediaType; - chrome.runtime.sendMessage({contentScriptQuery: 'queryMedia', tmdbId: firstResult.id, mediaType: mediaType}, json => { - mediaInfo = json; - tmdbId = json.id; - console.log(`TMDB id: ${tmdbId}`); - removeSpinner(); - fillContainer(json.mediaInfo); + + chrome.runtime.sendMessage({contentScriptQuery: 'plexQueryMedia', plexToken: plexToken, mediaKey: mediaKey}, json => { + let guids = json.MediaContainer.Metadata[0].Guid.filter(guid => guid.id.startsWith('tmdb')); + if (guids.length > 0) { + tmdbId = parseInt(guids[0].id.replace('tmdb://', '')); + mediaType = json.MediaContainer.Metadata[0].type === 'movie' ? 'movie' : 'tv'; + console.log(`TMDB id: ${tmdbId}`); + chrome.runtime.sendMessage({contentScriptQuery: 'queryMedia', tmdbId: tmdbId, mediaType: mediaType}, json => { + mediaInfo = json; + console.log(json.name); + removeSpinner(); + fillContainer(json.mediaInfo); + }); + } }); }); });