您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add Rotten Tomatoes ratings to IMDb movie pages
当前为
// ==UserScript== // @name IMDb Tomatoes // @description Add Rotten Tomatoes ratings to IMDb movie pages // @author chocolateboy // @copyright chocolateboy // @namespace https://github.com/chocolateboy/userscripts // @version 1.1.1 // @license GPL: http://www.gnu.org/copyleft/gpl.html // @include http://*.imdb.tld/title/tt* // @include http://*.imdb.tld/*/title/tt* // @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.js // @grant GM_addStyle // @grant GM_deleteValue // @grant GM_getValue // @grant GM_listValues // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_xmlhttpRequest // ==/UserScript== /* * OK: * * http://www.imdb.com/title/tt0309698/ - 4 widgets * http://www.imdb.com/title/tt0086312/ - 3 widgets * http://www.imdb.com/title/tt0037638/ - 2 widgets * * Fixed: * * Layout: * * http://www.imdb.com/title/tt0162346/ - 4 widgets * http://www.imdb.com/title/tt0159097/ - 4 widgets * * Fallback (RT doesn't have an alias, or has the wrong alias): * * http://www.imdb.com/awards-central/title/tt2402927/ - Carol * * http://www.imdb.com/title/tt0066921/ - A Clockwork Orange * http://www.imdb.com/title/tt0104070/ - Death Becomes Her * http://www.imdb.com/title/tt0363163/ - Downfall * http://www.imdb.com/title/tt0057115/ - The Great Escape * http://www.imdb.com/title/tt0120755/ - Mission: Impossible II * http://www.imdb.com/title/tt0120768/ - The Negotiator * http://www.imdb.com/title/tt0910936/ - Pineapple Express * http://www.imdb.com/title/tt0145487/ - Spider Man * http://www.imdb.com/title/tt0120915/ - Star Wars: Episode I - The Phantom Menace * http://www.imdb.com/title/tt0448134/ - Sunshine * http://www.imdb.com/title/tt0129387/ - There's Something About Mary * http://www.imdb.com/title/tt0418279/ - Transformers * * Diacritics: * * http://www.imdb.com/title/tt0211915/ - Amélie * * Double quotes in the consensus: * * http://www.imdb.com/title/tt3181822/ - The Boy Next Door * * Broken: * * http://www.imdb.com/title/tt0451279/ - Wonder Woman (2017) */ // XXX unaliased and incorrectly aliased titles are common: // http://developer.rottentomatoes.com/forum/read/110751/2 'use strict'; const COMMAND_NAME = GM_info.script.name + ': clear cache' const COMPACT_LAYOUT = '.plot_summary_wrapper .minPlotHeightWithPoster' const CURRENT_YEAR = new Date().getFullYear() const NOW = Date.now() const ONE_DAY = 1000 * 60 * 60 * 24 const STATUS_TO_STYLE = { 'N/A': 'tbd', Fresh: 'favorable', Rotten: 'unfavorable' } // promisified cross-origin HTTP requests function get (url) { return new Promise ((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url, onload: function (res) { resolve(res.responseText) }, onerror: function (res) { reject(`error loading ${url}:`, res) }, }) }) } // purge expired entries function purgeCached (date) { for (let key of GM_listValues()) { let entry = JSON.parse(GM_getValue(key)) if (date === -1 || date > entry.expires) GM_deleteValue(key) } } // prepend a widget to the review bar or append a link to the star box function render ($target, { consensus, score, url }) { let status if (score === -1) { status = 'N/A' } else if (score < 60) { status = 'Rotten' } else { status = 'Fresh' } let altText = (consensus && consensus !== 'N/A') ? consensus.replace(/--/g, '—').replace(/"/g, '"') : status let style = STATUS_TO_STYLE[status] if ($target.hasClass('titleReviewBar')) { // reduce the amount of space taken up by the Metacritic widget // and make it consistent with our style (i.e. site name rather // than domain name) $target.find('a[href="http://www.metacritic.com"]').text('Metacritic') // 4 review widgets is too many for the "compact" layout (e.g. // a poster but no trailer). it's designed for a maximum of 3. // to work around this, we hoist the review bar out of the // movie-info block (plot_summary_wrapper) and float it left // beneath the poster e.g.: // // before: // // [ [ ] [ ] ] // [ [ ] [ ] ] // [ [ Poster ] [ Info ] ] // [ [ ] [ ] ] // [ [ ] [ [MC] [IMDb] [&c.] ] ] // // after: // // [ [ ] [ ] ] // [ [ ] [ ] ] // [ [ Poster ] [ Info ] ] // [ [ ] [ ] ] // [ [ ] [ ] ] // [ ] // [ [RT] [MC] [IMDb] [&c.] ] if ($(COMPACT_LAYOUT).length && $target.find('.titleReviewBarItem').length > 2) { let $clear = $('<div class="clear"> </div>') $('.plot_summary_wrapper').after($target.remove()) $target.before($clear).after($clear).css({ 'float': 'left', 'padding-top': '11px', 'padding-bottom': '0px' }) } let rating = score === -1 ? 'N/A' : score let html = ` <div class="titleReviewBarItem"> <a href="${url}" title="${altText}"><div class="metacriticScore score_${style} titleReviewBarSubItem"><span>${rating}</span></div></a> <div class="titleReviewBarSubItem"> <div> <a href="${url}">Tomatometer</a> </div> <div> <span class="subText"> From <a href="http://www.rottentomatoes.com" target="_blank">Rotten Tomatoes</a> </span> </div> </div> </div> <div class="divider"></div> ` $target.prepend(html) } else { let rating = score === -1 ? 'N/A' : `${score}%` let html = ` <span class="ghost">|</span> Rotten Tomatoes: <a href="${url}" title="${altText}">${rating}</a> ` $target.append(html) } } // register this first so data can be cleared even if there's an error GM_registerMenuCommand(COMMAND_NAME, function () { purgeCached(-1) }) // make the background color more legible (darker) if the score is N/A GM_addStyle('.score_tbd { background-color: #D9D9D9 }') let $type = $('meta[property="og:type"') let $titleReviewBar = $('.titleReviewBar') let $starBox = $('.star-box-details') let $target = ($titleReviewBar.length && $titleReviewBar) || ($starBox.length && $starBox) if ($target && $type.attr('content') === 'video.movie') { let $url = $('link[rel=canonical]') if ($url.length) { purgeCached(NOW) let imdbId = $url.attr('href').match(/\/title\/tt(\d{7})\//)[1] let cached = JSON.parse(GM_getValue(imdbId, 'null')) if (cached) { if (!cached.error) render($target, cached.data) } else { let title = $('meta[property="og:title"]').attr('content').match(/^(.+?)\s+\(\d{4}\)$/)[1] let imdb = { id: imdbId, title } let url = `http://www.omdbapi.com/?i=tt${imdbId}&r=json&tomatoes=true` get(url) .then(json => processJSON(json, imdb)) .then(data => { let json = JSON.stringify({ expires: NOW + ONE_DAY, data }) GM_setValue(imdbId, json) render($target, data) }) .catch(error => { console.error(error) let json = JSON.stringify({ expires: NOW + ONE_DAY, error }) GM_setValue(imdbId, json) }) } } } // compare two film titles; return true if they're "equal" (disregarding case, // punctuation and diacritics); otherwise return false function matchTitle ($t1, $t2) { let t1 = $t1.trim() let t2 = $t2.trim() let compare = t1.localeCompare(t2, [], { sensitivity: 'base', ignorePunctuation: true }) return compare === 0 } // process the OMDb API's JSON response. returning data rather than HTML allows the // same (cached) data to be rendered in the two targets (one of which — the // review bar — is only visible to logged-in users) function processJSON (json, imdb) { let rt = JSON.parse(json) let error if (rt.Error) { error = `can't retrieve JSON from the OMDb API: ${rt.Error}` } else if (!matchTitle(rt.Title, imdb.title)) { let imdbTitle = JSON.stringify(imdb.title) let rtTitle = JSON.stringify(rt.Title) error = `title mismatch: imdb: ${imdbTitle}, rt: ${rtTitle}` } else if (!rt.tomatoURL || rt.tomatoURL === 'N/A') { error = 'no Rotten Tomatoes URL defined' } if (error) { error = `error querying data for tt${imdb.id}: ${error}` return tryExternalReviews(error, imdb) } let score = rt.tomatoMeter === 'N/A' ? -1 : Number(rt.tomatoMeter) return { consensus: rt.tomatoConsensus, score, url: rt.tomatoURL } } // fall back to querying the film's "External Reviews" page for // a Rotten Tomatoes link. if this doesn't work (no link, broken link) // raise the original error function tryExternalReviews (error, imdb) { console.warn(`falling back on external reviews because: ${error}`) // don't scrape the external-reviews link from IMDb's "Reviews" widget: IMDb is unreliable e.g. // // http://www.imdb.com/awards-central/title/tt2402927/ // // links to: // // http://www.imdb.com/awards-central/title/tt2402927/externalreviews // // which doesn't exist... let url = `/title/tt${imdb.id}/externalreviews` return get(url) .then(html => { let $rtLink = $(html).find('a[href^="/offsite/?page-action=offsite-rottentomatoes&"]') if ($rtLink.length) { let url = $rtLink.attr('href') console.log(`found RT link: ${url}`) return get(url) } else { console.warn(`can't find RT link in ${url}`) throw error } }) .then(html => { try { // XXX the use of `parseHTML` and `filter` here is difficult to // explain beyond "because jQuery". // use this instead? https://gist.github.com/cowboy/742952 let $html = $($.parseHTML(html)) let $meta = $html.filter('meta[name="twitter:data1"]') let score = Number($meta.attr('content').match(/^(\d+)/)[1]) let consensus = $html.filter('meta[name=description]').attr('content') let url = $html.filter('link[rel=canonical]').attr('href') return { consensus, score, url } } catch (e) { throw `invalid RT link: ${e.message}` } }) }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址