根据影片标题or原英文标题获取豆瓣评分 获取豆瓣评分(未使用豆瓣API)
// ==UserScript== // @name Netflix影片显示豆瓣评分 // @namespace http://tampermonkey.net/ // @include https://netflix.com/* // @include https://www.netflix.com/* // @connect douban.com // @connect zeptojs.com // @version 1.0.5 // @description 根据影片标题or原英文标题获取豆瓣评分 获取豆瓣评分(未使用豆瓣API) // @author naturezhm // @grant GM_xmlhttpRequest // @grant GM_addStyle // @require https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js // ==/UserScript== (function () { var logger = { debug: console.debug, log: console.log, info: console.info, warn: console.warn, error: console.error } function setStorage(key, value) { let storage = window.localStorage; storage.setItem(JSON.stringify(key), JSON.stringify(value)); } function getStorage(key) { let storage = window.localStorage; let rawDate = storage.getItem(JSON.stringify(key)); return JSON.parse(rawDate); } function addCss() { GM_addStyle(".douban{display:block;background-color: rgba(0,0,0,0.5)} .is-bob-open .douban { display: block; opacity: 0.3;}"); } function getMovieList(title, callback) { logger.debug("title: %s", title); if (null == title || undefined == title || null == callback || undefined == callback) { return null; } let subjectId = getStorage(title); let doubanInfo = getStorage(subjectId); if (null != doubanInfo && undefined != doubanInfo) { callback(doubanInfo); return; } GM_xmlhttpRequest({ method: "GET", // url: 'https://movie.douban.com/j/subject_suggest?q=' + title, url: 'https://m.douban.com/search/?query=' + title, "headers": { "user-agent": 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1' }, onload: function (response) { // logger.debug("response, %o", response); if (response.status == 200) { let subjectId = findElement(response.responseText, regex_search); logger.debug("subjectId: %s", subjectId); if (null == subjectId || undefined == subjectId || 0 == subjectId) { callback(null, title); } let doubanInfo = getStorage(subjectId); if (null == doubanInfo || undefined == doubanInfo) { getMovieInfo(title, subjectId, callback); } else { callback(doubanInfo); } } else { logger.warn(response.statusText); callback(null, title); } } }); } function getMovieInfo(title, subjectId, callback) { if (null == callback || undefined == callback) { return; } let url = "https://movie.douban.com/subject/" + subjectId; logger.debug("url, %o", url); GM_xmlhttpRequest({ method: "GET", url: url, onload: function (response) { if (response.status == 200) { let responseText = response.responseText; let doubanRate = findElement(responseText, regex_rate); let doubanVotes = findElement(responseText, regex_votes); logger.debug("doubanRate: %s, doubanVotes: %s, url: %s", doubanRate, doubanVotes, url); let doubenInfo = { "subjectId": subjectId, "doubanRate": doubanRate, "doubanVotes": doubanVotes, "doubanUrl": url }; setStorage(subjectId, doubenInfo); setStorage(title, subjectId); callback(doubenInfo); } else { logger.warn(response.statusText); callback(null, title); } } }); } function findElement(content, matchRegex) { let matchR###lt; let r###lt = 0; let times = 0; while ((matchR###lt = matchRegex.exec(content)) !== null && times < 10) { // This is necessary to avoid infinite loops with zero-width matches if (matchR###lt.index === matchRegex.lastIndex) { matchRegex.lastIndex++; } if ($.isArray(matchR###lt)) { matchR###lt.forEach((match, groupIndex) => { // logger.log(`Found match, group ${groupIndex}: ${match}`); // only get first match element. (follow Douban page rules.) r###lt = match; return; }); } else { r###lt = matchR###lt; } times++; } return r###lt; } function addDoubanElementInBox(element, doubanInfo, title) { try { if (null == element || undefined == element) { return; } if (hasDoubanTag(element)) { return; } if (null == doubanInfo || undefined == doubanInfo) { let errorMovieTimes = errorMovieMap[title]; if (errorMovieTimes > retryLimit) { $(element).append('<div class="douban"></div>'); return; } if (null == errorMovieTimes || undefined == errorMovieTimes) { errorMovieTimes = 1; errorMovieMap[title] = errorMovieTimes; } else { errorMovieTimes++; errorMovieMap[title] = errorMovieTimes; } return; } let html = '<div class="douban" style="position: absolute;top: 0;right: 0;z-index: 999;"><div class="douban-tag" style="display: inline-flex;margin: 5px;"><a href="' + doubanInfo.doubanUrl + '" target="_blank"><img src="https://img3.doubanio.com/favicon.ico" style="width: 16px;"></a><span style="margin: 0 2px;">' + (doubanInfo.doubanRate > 0 ? doubanInfo.doubanRate : 0) + '</span><span>|</span><span style="margin: 0 2px;">' + (doubanInfo.doubanVotes > 0 ? doubanInfo.doubanVotes : 0) + '</span></div></div>'; $(element).append(html); return; } catch (err) { logger.error(err); } } function hasDoubanTag(element) { return $(element).find(".douban").length > 0; } function trigerFetchDouban() { if (allMovieBoxlist.length <= 0) { return; } if (null != timer) { clearTimeout(timer); } timer = setTimeout(() => { let item = allMovieBoxlist.shift(); let innerTextWrapper = $(item).find(".fallback-text-container .fallback-text"); let movieTitle = null == innerTextWrapper ? null : innerTextWrapper[0].innerText; logger.debug("movieTitle: %o", movieTitle); // get rate vote and url from douban 豆瓣 getMovieList(movieTitle, (doubanInfo, title) => { // callback logger.debug("doubanInfo: %o", doubanInfo); // add new element in movie box addDoubanElementInBox(item, doubanInfo, title); trigerFetchDouban(); }); }, 50); } function mainFunc() { setInterval(() => { // execute every 3 second let movieBoxes = $(".title-card-container .title-card"); // logger.debug(movieBoxtitles.innerText); let times = 0; movieBoxes.each(function (index) { if (times > 500) { return; } // logger.debug('index: %d this: %o', index, this); logger.debug("current box length: %s", allMovieBoxlist.length); let item = this; if (hasDoubanTag(item) || allMovieBoxlist.indexOf(item) >= 0) { return; } allMovieBoxlist.push(item); times++; }); trigerFetchDouban(); }, 3000); } // match element like // <strong class="ll rating_num" property="v:average">2.2</strong> var regex_rate = /(?<=v:average.>)[^<]*/m; var regex_votes = /(?<=v:votes.>)[^<]*/m; var regex_search = /(?<=\/movie\/subject\/)[^\/]*/m; var allMovieBoxlist = []; var timer = null; var retryLimit = 3; var errorMovieMap = {}; logger.info('Hello douban rate for Netflix.'); // getMovieList("漫#杰西卡·琼斯"); mainFunc(); addCss(); })();