NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @namespace https://openuserjs.org/users/SB100 // @name PTP TMDb Movie Recommendations // @description Show recommendations from TMDb on the PTP movie page // @updateURL https://openuserjs.org/meta/SB100/PTP_TMDb_Movie_Recommendations.meta.js // @version 1.2.0 // @author SB100 // @copyright 2021, SB100 (https://openuserjs.org/users/SB100) // @license MIT // @match https://passthepopcorn.me/torrents.php?id=* // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // ==/UserScript== // ==OpenUserJS== // @author SB100 // ==/OpenUserJS== /* jshint esversion: 6 */ /** * ============================= * ADVANCED OPTIONS * ============================= */ // Select whether to use Similar or Recommendations algorithm from TMDb. // More info found here: https://www.themoviedb.org/talk/55635e59c3a368542c0026e6 const SETTING_USE_SIMILAR_OR_RECOMMENDATIONS = 'similar'; // 'similar' OR 'recommendations' /** * ============================= * END ADVANCED OPTIONS * DO NOT MODIFY BELOW THIS LINE * ============================= */ const BASE_TMDB_URL = 'https://api.themoviedb.org/3'; /** * Gets your TMDb API key. Asks the user for one if it hasn't been set yet */ function getTmdbApiKey() { const key = GM_getValue('tmdb_key', ''); if (!key) { const input = window.prompt(`Please input your TMDb API key. If you don't have one, please signup for one at https://themoviedb.org. Then go to Settings -> API -> API Key (v3 auth) Disable this userscript until you have one as this prompt will continue to show until one is provided`); const trimmed = input && input.trim(); if (/[a-f0-9]{32}/.test(trimmed)) { GM_setValue('tmdb_key', trimmed); return trimmed; } } return key; } /** * Turn a title into a url safe string */ function encodeTitle(title) { return encodeURIComponent(title.replace(/[^\w\d\s\-\.]+/gi, '')); } /** * Query the API for some results */ function queryApi(path, params = {}) { let resolver; let rejecter; const p = new Promise((resolveFn, rejectFn) => { resolver = resolveFn; rejecter = rejectFn; }); const paramStr = new URLSearchParams(params).toString(); const url = `${BASE_TMDB_URL}${path}${paramStr.length > 0 ? `?${paramStr}` : ''}` GM_xmlhttpRequest({ method: 'get', url: url, timeout: 10000, onloadstart: () => {}, onload: (result) => { if (result.status === 401) { const resp = JSON.parse(result.response); if (resp.status_code === 7) { GM_deleteValue('tmdb_key'); alert(`Invalid TMDb API key. It has now been removed. Please refresh the page and input a valid TMDb API key to continue using this userscript.`); rejecter(new Error('Invalid TMDb API Key')); return; } } if (result.status !== 200) { console.log('[TMDb Movie Recommendations]', result); rejecter(new Error('Not OK')); return; } resolver(JSON.parse(result.response)) }, onerror: (result) => { rejecter(result) }, ontimeout: (result) => { rejecter(result) } }); return p; } /** * Find the movies IMDb ID from the page */ function findImdbIdFromPage() { const elem = document.getElementById('imdb-title-link'); const href = elem && elem.href; return href && href.match(/tt\d+/)[0]; } /** * Find the TMBd movie info for the IMDb movie passed in */ async function findMovieInfoByIMDb(imdbId, key) { const movieInfo = await queryApi(`/find/${imdbId}`, { external_source: 'imdb_id', api_key: key }); if (movieInfo && Array.isArray(movieInfo.movie_results) && movieInfo.movie_results.length > 0) { return movieInfo.movie_results[0]; } return null; } /** * Get movie recommendations for a TMDb movie entry */ async function getSuggestions(tmdbId, key, type) { const movieRecommendations = await queryApi(`/movie/${tmdbId}/${type}`, { api_key: key }); if (movieRecommendations && Array.isArray(movieRecommendations.results) && movieRecommendations.results.length > 0) { return movieRecommendations .results .filter(r => typeof r.release_date !== 'undefined') .sort((a, b) => { if (a.vote_average > b.vote_average) return -1; if (b.vote_average > a.vote_average) return 1; return 0; }); } return null; } /** * Turn the results from the API into something the user can see in the sidebar */ function buildResults(results) { const list = document.createElement('ul'); list.classList.add('list', 'list--unstyled'); results.forEach(result => { const year = result.release_date.split('-')[0]; const li = document.createElement('li'); li.innerHTML = `<span style="font-size: 0.75em; vertical-align: middle;">(${result.vote_average})</span> <a href="/torrents.php?order_by=relevance&searchstr=${encodeTitle(`${result.title} ${year}`)}" style="vertical-align: middle;">${result.title}</a> <span style="vertical-align: middle;">[${year}]</span>`; li.title = `Popularity: ${result.popularity}; Vote count: ${result.vote_count}; Vote average: ${result.vote_average}`; list.appendChild(li); }); const panel = document.createElement('div'); panel.classList.add('panel'); panel.innerHTML = ` <div class='panel__heading'> <span class="panel__heading__title">TMDb ${SETTING_USE_SIMILAR_OR_RECOMMENDATIONS === 'similar' ? 'similar movies' : 'recommendations'}</span> <span style="float:right; font-size: 0.9em"></span> </div> <div class='panel__body'>${list.outerHTML}</div> `; const sidebar = document.querySelector('.sidebar'); sidebar && sidebar.appendChild(panel); } /** * Main script runner */ (async function () { 'use strict'; if (['similar', 'recommendations'].includes(SETTING_USE_SIMILAR_OR_RECOMMENDATIONS) === false) { alert('[TMDb Movie Recommendations] Configuration error. Allowed values are "similar" and "recommendations"'); return; } const key = getTmdbApiKey(); if (!key) { console.log(`[TMDb Movie Recommendations] No valid API key found, exiting userscript`); return; } const imdb = findImdbIdFromPage(); if (!imdb) { console.log(`[TMDb Movie Recommendations] Couldn't find IMDb ID`); return; } const movieInfo = await findMovieInfoByIMDb(imdb, key); if (!movieInfo) { console.log(`[TMDb Movie Recommendations] No movie info found from API`); return; } const recommendations = await getSuggestions(movieInfo.id, key, SETTING_USE_SIMILAR_OR_RECOMMENDATIONS); if (!recommendations) { console.log(`[TMDb Movie Recommendations] No recommendations found from API`); return; } buildResults(recommendations); })();