From 0af90441ff7fb86dd8531ec2102474b04e0eca62 Mon Sep 17 00:00:00 2001 From: cvzi Date: Fri, 22 Sep 2023 14:04:41 +0200 Subject: [PATCH] First try to fix search #23 --- Show_metacritic_ratings.user.js | 587 ++++++++++++++++++++++---------- 1 file changed, 405 insertions(+), 182 deletions(-) diff --git a/Show_metacritic_ratings.user.js b/Show_metacritic_ratings.user.js index 334bc68..41ec7e9 100644 --- a/Show_metacritic_ratings.user.js +++ b/Show_metacritic_ratings.user.js @@ -15,10 +15,11 @@ // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js // @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0.txt // @antifeature tracking When a metacritic rating is displayed, we may store the url of the current website and the metacritic url in our database. Log files are temporarily retained by our database hoster Cloudflare Workers® and contain your IP address and browser configuration. -// @version 93 +// @version 94 // @connect metacritic.com // @connect met.acritic.workers.dev // @connect imdb.com +// @connect fandom-prod.apigee.net // @match https://*.bandcamp.com/* // @match https://play.google.com/store/music/album/* // @match https://play.google.com/store/movies/details/* @@ -105,8 +106,7 @@ const baseURLps4 = 'https://www.metacritic.com/game/playstation-4/' const baseURLxone = 'https://www.metacritic.com/game/xbox-one/' const baseURLtv = 'https://www.metacritic.com/tv/' -const baseURLsearch = 'https://www.metacritic.com/search/{query}?page=1&category={type}' -const baseURLautosearch = 'https://www.metacritic.com/autosearch' +const baseURLsearch = 'https://fandom-prod.apigee.net/v1/xapi/finder/metacritic/search/{query}/web?apiKey={apiKey}&componentName=search-tabs&componentDisplayName=Search+Page+Tab+Filters&componentType=FilterConfig&mcoTypeId={type}&offset=0&limit=30' const baseURLdatabase = 'https://met.acritic.workers.dev/r.php' const baseURLwhitelist = 'https://met.acritic.workers.dev/whitelist.php' @@ -133,9 +133,6 @@ const windowPositions = [ } ] -// http://www.designcouch.com/home/why/2013/05/23/dead-simple-pure-css-loading-spinner/ -const CSS = '#mcdiv123 .grespinner{height:16px;width:16px;margin:0 auto;position:relative;animation:rotation .6s infinite linear;border-left:6px solid rgba(0,174,239,.15);border-right:6px solid rgba(0,174,239,.15);border-bottom:6px solid rgba(0,174,239,.15);border-top:6px solid rgba(0,174,239,.8);border-radius:100%}@keyframes rotation{from{transform:rotate(0)}to{transform:rotate(359deg)}}#mcdiv123searchresults .result{font:12px arial,helvetica,serif;border-top-width:1px;border-top-color:#ccc;border-top-style:solid;padding:5px}#mcdiv123searchresults .result .result_type{display:inline}#mcdiv123searchresults .result .result_wrap{float:left;width:100%}#mcdiv123searchresults .result .has_score{padding-left:42px}#mcdiv123searchresults .result .basic_stats{height:1%;overflow:hidden}#mcdiv123searchresults .result h3{font-size:14px;font-weight:700}#mcdiv123searchresults .result a{color:#09f;font-weight:700;text-decoration:none}#mcdiv123searchresults .metascore_w.game.seventyfive,#mcdiv123searchresults .metascore_w.positive,#mcdiv123searchresults .metascore_w.score_favorable,#mcdiv123searchresults .metascore_w.score_outstanding,#mcdiv123searchresults .metascore_w.sixtyone{background-color:#6c3}#mcdiv123searchresults .metascore_w.forty,#mcdiv123searchresults .metascore_w.game.fifty,#mcdiv123searchresults .metascore_w.mixed,#mcdiv123searchresults .metascore_w.score_mixed{background-color:#fc3}#mcdiv123searchresults .metascore_w.negative,#mcdiv123searchresults .metascore_w.score_terrible,#mcdiv123searchresults .metascore_w.score_unfavorable{background-color:red}#mcdiv123searchresults a.metascore_w,#mcdiv123searchresults span.metascore_w{display:inline-block}#mcdiv123searchresults .result .metascore_w{color:#fff!important;font-family:Arial,Helvetica,sans-serif;font-size:17px;font-style:normal!important;font-weight:700!important;height:2em;line-height:2em;text-align:center;vertical-align:middle;width:2em;float:left;margin:0 0 0 -42px}#mcdiv123searchresults .result .more_stats{font-size:10px;color:#444}#mcdiv123searchresults .result .release_date .data{font-weight:700;color:#000}#mcdiv123searchresults ol,#mcdiv123searchresults ul{list-style:none}#mcdiv123searchresults .result li.stat{background:0 0;display:inline;float:left;margin:0;padding:0 6px 0 0;white-space:nowrap}#mcdiv123searchresults .result .deck{margin:3px 0 0}#mcdiv123searchresults .result .basic_stat{display:inline;float:right;overflow:hidden;width:100%}' - let myDOMParser = null function domParser () { if (myDOMParser === null) { @@ -146,21 +143,180 @@ function domParser () { async function versionUpdate () { const version = parseInt(await GM.getValue('version', 0)) - if (version <= 91) { + if (version <= 93) { // Reset database await GM.setValue('map', '{}') await GM.setValue('black', '[]') await GM.setValue('hovercache', '{}') await GM.setValue('requestcache', '{}') - await GM.setValue('searchcache', '{}') - await GM.setValue('autosearchcache', '{}') await GM.setValue('temporaryblack', '{}') + await GM.setValue('searchcache', false) // Unused + await GM.setValue('autosearchcache', false) // Unused } - if (version < 92) { - await GM.setValue('version', 92) + if (version < 94) { + await GM.setValue('version', 94) } } +const BOX_CSS = ` + #mcdiv123 { + position: fixed; + background-color: #fff; + border: 2px solid #bbb; + border-radius: 6px; + box-shadow: 0 0 3px 3px rgba(100, 100, 100, 0.2); + color: #000; + min-width: 150; + max-height: 80%; + max-width: 640; + overflow: auto; + padding: 3px; + z-index: 2147483601; + } + + #mcisearchquery { + background: white; + color: black; + width: 450px; + display: inline; + } + + #mcisearchbutton { + background: silver; + color: black; + border: 2px solid black; + padding: 3px; + display: inline; + margin: 0px 5px; + cursor: pointer; + } + + /* http://www.designcouch.com/home/why/2013/05/23/dead-simple-pure-css-loading-spinner/ */ + #mcdiv123 .grespinner { + display: inline-block; + height: 20px; + width: 20px; + margin: 0 auto; + position: relative; + animation: rotation .6s infinite linear; + border-left: 6px solid rgba(0,174,239,.15); + border-right: 6px solid rgba(0,174,239,.15); + border-bottom: 6px solid rgba(0,174,239,.15); + border-top: 6px solid rgba(0,174,239,.8); + border-radius: 100% + } + + @keyframes rotation { + from { + transform: rotate(0) + } + + to { + transform: rotate(359deg) + } + } + + #mcdiv123searchresults { + font-size: 12px; + max-width: 95% + } + + .mcdiv123_correct_entry { + cursor: pointer; + color: green; + font-size: 25px; + margin-top: 10px; + } + .mcdiv123_correct_entry:hover { + color: #41fd41; + } + + .mcdiv123_incorrect { + cursor: pointer; + float: right; + color: crimson; + font-size: 11px; + } + .mcdiv123_incorrect { + cursor: pointer; + float: right; + color: crimson; + font-size: 15px; + margin-right: 10px; + } + .mcdiv123_incorrect:hover { + cursor: pointer; + float: right; + color: crimson; + font-size: 15px; + margin-right: 10px; + border:2px solid white; + } + .mcdiv123_incorrect:hover { + border-color: crimson; + } + + #mcdiv123searchresults .result { + font: 12px arial,helvetica,serif; + border-top-width: 1px; + border-top-color: #ccc; + border-top-style: solid; + padding: 5px + } + + .mcdiv123_cover { + max-width: 200px; + max-height: 140px; + } + + #mcdiv123searchresults .result .mcdiv123_score_badge { + display: inline-block; + margin: 3px; + font-weight: 600; + border-radius: 6px; + color: black; + padding: 5px; + } + + #mcdiv123searchresults .result .floatleft { + float: left; + } + + #mcdiv123searchresults .result .clearleft { + clear: left; + } + + #mcdiv123searchresults .result .resultcontent { + max-width: 360px; + margin-left: 10px; + } + + #mcdiv123searchresults .result .mcdiv_release_date { + color: silver + } + + .mcdiv123_image_placeholder { + width: 82px; + height: 82px; + background: rgb(64, 64, 64); + border-radius: 8px; + } + + #mcdiv123searchresults .result a { + color: #09f; + font-weight: 700; + text-decoration: none + } + + #mcdiv123searchresults .mcdiv_desc { + max-height:120px; + overflow-y: auto; + scrollbar-color: #d9d9d9 #eee; + scrollbar-width: thin; + } + +` + async function acceptGDPR (showDialog) { if (showDialog === true) { await GM.setValue('gdpr', null) @@ -212,11 +368,6 @@ function delay (ms) { }) } -function getHostname (url) { - const a = document.createElement('a') - a.href = url - return a.hostname -} function absoluteMetaURL (url) { if (url.startsWith('https://')) { return url @@ -323,29 +474,72 @@ function randomStringId () { function fixMetacriticURLs (html) { return html.replace(/ 89) { + return colors.universalAcclaim + } + if (score > 74) { + return colors.generallyFavorable + } + if (score > 49) { + return colors.mixedOrAverage + } + if (score > 19) { + return colors.generallyUnfavorable + } + if (score > 0) { + return colors.overwhelmingDislike + } + return colors.tbd + } else { + if (score > 80) { + return colors.universalAcclaim + } + if (score > 60) { + return colors.generallyFavorable + } + if (score > 39) { + return colors.mixedOrAverage + } + if (score > 19) { + return colors.generallyUnfavorable + } + if (score > 0) { + return colors.overwhelmingDislike + } + return colors.tbd + } +} + function replaceBrackets (str) { str = str.replace(/\([^(]*\)/g, '') str = str.replace(/\[[^\]]*\]/g, '') @@ -472,16 +666,6 @@ async function addToMap (url, metaurl) { return [url, metaurl] } -async function removeFromMap (url) { - const data = JSON.parse(await GM.getValue('map', '{}')) - - url = filterUniversalUrl(url) - if (url in data) { - delete data[url] - await GM.setValue('map', JSON.stringify(data)) - } -} - async function addToTemporaryBlacklist (metaurl) { const data = JSON.parse(await GM.getValue('temporaryblack', '{}')) @@ -689,7 +873,6 @@ function asyncRequest (data) { } const defaultHeaders = { Referer: data.url, - // Host: getHostname(data.url), 'User-Agent': navigator.userAgent } const defaultData = { @@ -969,7 +1152,7 @@ async function loadMetacriticUrl (fromSearch) { } } - if (fromSearch) { + if (!fromSearch) { startSearch() } }) @@ -989,97 +1172,62 @@ async function loadMetacriticUrl (fromSearch) { async function startSearch () { waitForHotkeysMETA() - const cache = JSON.parse(await GM.getValue('autosearchcache', '{}')) - const now = (new Date()).getTime() - const timeout = 2 * 60 * 60 * 1000 - for (const prop in cache) { - // Delete cached values, that are older than 2 hours - if (now - (new Date(cache[prop].time)).getTime() > timeout) { - delete cache[prop] - } - } - if (current.type === 'music') { current.searchTerm = current.data[0] } else { current.searchTerm = current.data.join(' ') } - let response - if (current.searchTerm in cache) { - response = cache[current.searchTerm] - } else { - response = await asyncRequest({ - method: 'POST', - url: baseURLautosearch, - data: 'search_term=' + encodeURIComponent(current.searchTerm) + '&image_size=98&search_each=1&sort_type=popular', - headers: { - Referer: current.metaurl, - 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', - // Host: 'www.metacritic.com', - 'User-Agent': 'MetacriticUserscript Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0', - 'X-Requested-With': 'XMLHttpRequest' - } - }) - response = { - time: (new Date()).toJSON(), - json: JSON.parse(response.responseText) - } - cache[current.searchTerm] = response - await GM.setValue('autosearchcache', JSON.stringify(cache)) - } + const items = await fandomProdApigeeSearch(current.searchTerm, current.type) - if (!response || !('json' in response)) { - alert('ShowMetacriticRatings: Error 05') + if (!items) { + alert('ShowMetacriticRatings: Error 05 item=', items) } - const data = response.json + let multiple = false - if (data && data.autoComplete && data.autoComplete.results && data.autoComplete.results.length) { - // Remove data with wrong type - data.autoComplete = data.autoComplete.results - - const newdata = [] - data.autoComplete.forEach(function (result) { - if (metacritic2searchType(result.refType) === current.type) { - newdata.push(result) + if (items.length === 0) { + // No results + console.debug('ShowMetacriticRatings: No results for searchTerm=' + current.searchTerm) + } else if (items.length === 1) { + // One result, let's show it + const itemURL = absoluteMetaURL(items[0].metacriticUrl) + if (!await isBlacklistedUrl(document.location.href, itemURL)) { + current.metaurl = itemURL + loadMetacriticUrl(true) + return + } + } else { + // More than one result + multiple = true + console.debug('ShowMetacriticRatings: Multiple results for searchTerm=' + current.searchTerm) + const exactMatches = [] + items.forEach(function (result, i) { // Try to find the correct result by matching the search term to exactly one movie title + if (current.searchTerm.toLowerCase() === result.title.toLowerCase()) { + exactMatches.push(result) } }) - data.autoComplete = newdata - if (data.autoComplete.length === 0) { - // No results - console.debug('ShowMetacriticRatings: No results (after filtering by type) for searchTerm=' + current.searchTerm) - } else if (data.autoComplete.length === 1) { - // One result, let's show it - if (!await isBlacklistedUrl(document.location.href, absoluteMetaURL(data.autoComplete[0].url))) { - current.metaurl = absoluteMetaURL(data.autoComplete[0].url) - loadMetacriticUrl(true) - return - } - } else { - // More than one result - multiple = true - console.debug('ShowMetacriticRatings: Multiple results for searchTerm=' + current.searchTerm) - const exactMatches = [] - data.autoComplete.forEach(function (result, i) { // Try to find the correct result by matching the search term to exactly one movie title - if (current.searchTerm === result.name) { + if (exactMatches.length === 0) { + // Try to be a bit more fuzzy + items.forEach(function (result, i) { + if (removeSymbols(current.searchTerm.toLowerCase()) === removeSymbols(result.title.toLowerCase())) { exactMatches.push(result) } }) - if (exactMatches.length === 1) { - // Only one exact match, let's show it - console.debug('ShowMetacriticRatings: Only one exact match for searchTerm=' + current.searchTerm) - if (!await isBlacklistedUrl(document.location.href, absoluteMetaURL(exactMatches[0].url))) { - current.metaurl = absoluteMetaURL(exactMatches[0].url) - loadMetacriticUrl(true) - return - } + } + if (exactMatches.length === 1) { + // Only one exact match, let's show it + console.debug('ShowMetacriticRatings: Only one exact match for searchTerm=' + current.searchTerm) + const itemURL = absoluteMetaURL(exactMatches[0].metacriticUrl) + if (!await isBlacklistedUrl(document.location.href, itemURL)) { + current.metaurl = itemURL + loadMetacriticUrl(true) + return } } - } else { - console.debug('ShowMetacriticRatings: No results (at all) for searchTerm=' + current.searchTerm) } + // HERE: multiple results or no result. The user may type "meta" now if (multiple) { - balloonAlert('Multiple metacritic results. Type "meta" for manual search.', 10000, false, { bottom: 5, top: 'auto', maxWidth: 400, paddingRight: 5 }, () => openSearchBox(true)) + balloonAlert('Multiple metacritic results. Type "meta" for manual search.', 10000, false, { bottom: 5, top: 'auto', maxWidth: 400, paddingRight: 5, cursor: 'pointer' }, () => openSearchBox(true)) } } @@ -1091,22 +1239,12 @@ function openSearchBox (search) { query = current.data.join(' ') } $('#mcdiv123').remove() + const div = $('
').appendTo(document.body) div.css({ - position: 'fixed', - bottom: 0, - left: 0, minWidth: 300, - maxHeight: '80%', - maxWidth: 640, - overflow: 'auto', - backgroundColor: '#fff', - border: '2px solid #bbb', - borderRadius: ' 6px', - boxShadow: '0 0 3px 3px rgba(100, 100, 100, 0.2)', - color: '#000', - padding: ' 3px', - zIndex: '2147483601' + bottom: 0, + left: 0 }) GM.getValue('position', false).then(function (s) { @@ -1121,62 +1259,146 @@ function openSearchBox (search) { } }) - $('').appendTo(div).focus().val(query).on('keypress', function (e) { + $('').appendTo(div).focus().val(query).on('keypress', function (e) { const code = e.keyCode || e.which if (code === 13) { // Enter key searchBoxSearch(e, $('#mcisearchquery').val()) } }) - $('