您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Import ratings for movies, TV series and episodes from a csv file.
当前为
// ==UserScript== // @name IMDB Ratings Importer // @namespace Neinei0k_imdb // @include https://www.imdb.com/user/ur*/ratings* // @version 1.01 // @license GNU General Public License v3.0 or later // @description Import ratings for movies, TV series and episodes from a csv file. // ==/UserScript== let elements = createHTMLForm(); function log(level, message) { console.log("(IMDB Ratings Importer) " + level + ": " + message); } function setStatus(message) { elements.status.textContent = message; } function createHTMLForm() { let elements = {}; try { let root = createRoot(); elements.text = createTextField(root); if (isFileAPISupported()) { elements.file = createFileInput(root); elements.isFromFile = createFromFileCheckbox(root); } else { createFileAPINotSupportedMessage(root); } elements.status = createStatusBar(root); createImportButton(root); } catch (message) { log("Error", message); } return elements; } function isFileAPISupported() { return window.File && window.FileReader && window.FileList && window.Blob; } function createRoot() { let container = document.querySelector('div.article.listo'); let nextChild = container.querySelector('div.lister'); if (container === null) { throw "div.article.listo element not found"; } let root = document.createElement('div'); root.setAttribute('class', 'aux-content-widget-2'); root.style.height = 'initial'; root.style.marginTop = '30px'; root.style.marginBottom = '30px'; container.insertBefore(root, nextChild); return root; } function createTextField(root) { let text = document.createElement('textarea'); text.style = "background-color: white; width: 100%; height: 100px; overflow: initial;"; root.appendChild(text); root.appendChild(document.createElement('br')); return text; } function createFileInput(root) { let file = document.createElement('input'); file.type = 'file'; file.disabled = true; file.style.marginBottom = '10px'; root.appendChild(file); root.appendChild(document.createElement('br')); return file; } function createFromFileCheckbox(root) { let isFromFile = createCheckbox("Import from file (otherwise import from text)"); root.appendChild(isFromFile.label); root.appendChild(document.createElement('br')); isFromFile.checkbox.addEventListener('change', fromFileOrTextChangeHandler, false); return isFromFile.checkbox; } function createCheckbox(textContent) { let checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.style = 'width: initial;'; let text = document.createElement('span'); text.style = 'font-weight: normal;'; text.textContent = textContent; let label = document.createElement('label'); label.appendChild(checkbox); label.appendChild(text); return {label: label, checkbox: checkbox}; } function fromFileOrTextChangeHandler(event) { let isChecked = event.target.checked; elements.text.disabled = isChecked; elements.file.disabled = !isChecked; } function createFileAPINotSupportedMessage(root) { let notSupported = document.createElement('div'); notSupported.style = 'font-weight: normal;'; notSupported.style.marginTop = '10px'; notSupported.style.marginBottom = '10px'; notSupported.textContent = "Your browser does not support File API for reading local files."; root.appendChild(notSupported); } function createStatusBar(root) { let status = document.createElement('div'); status.textContent = "Insert text or choose file. Press 'Import Ratings' button."; status.style.marginTop = '10px'; status.style.marginBottom = '10px'; root.appendChild(status); return status; } function createImportButton(root) { let importList = document.createElement('button'); importList.class = 'btn'; importList.textContent = "Import Ratings"; root.appendChild(importList); importList.addEventListener('click', importRatingsClickHandler, false); } function importRatingsClickHandler(event) { if (elements.hasOwnProperty('isFromFile') && elements.isFromFile.checked) { readFile(); } else { importRatings(extractItems(elements.text.value)); } } function readFile() { let file = elements.file.files[0]; if (file !== undefined) { log("Info", "Reading file " + file.name); setStatus("Reading file " + file.name); let fileReader = new FileReader(); fileReader.onload = fileOnloadHandler; fileReader.readAsText(file); } else { setStatus("Error: File is not selected"); } } function fileOnloadHandler(event) { if (event.target.error === null) { importRatings(extractItems(event.target.result)); } else { log("Error", e.target.error); setStatus("Error: " + e.target.error); } } function extractItems(text) { try { return extractItemsFromCSV(text); } catch (message) { log("Error", message); setStatus("Error: " + message); return []; } } function extractItemsFromCSV(text) { let table = parseCSV(text); let fields = findFieldNumbers(table); log("Info", "Found csv file fields Const(" + fields.const + ") and Your Rating(" + fields.rating + ")"); re = new RegExp("^tt[0-9]{7,8}$"); let items = []; // Add elements to the list for (let i = 1; i < table.length; i++) { let fconst = table[i][fields.const]; let frating = table[i][fields.rating]; if (re.exec(fconst) === null) { throw "Invalid 'const' field format on line " + (i+1); } if (frating === "") { continue; } frating = parseInt(frating); if (isNaN(frating)) { throw "Invalid 'your rating' field format on line " + (i+1); } items.push({const: fconst, rating: frating}); } return items; } function parseCSV(text) { let lines = text.split(/\r|\n/); let table = []; for (let i=0; i < lines.length; i++) { if (isEmpty(lines[i])) { continue; } let isInsideString = false; let row = [""]; for (let j=0; j < lines[i].length; j++) { if (!isInsideString && lines[i][j] === ',') { row.push(""); } else if (lines[i][j] === '"') { isInsideString = !isInsideString; } else { row[row.length-1] += lines[i][j]; } } table.push(row); if (isInsideString) { throw "Wrong number of \" on line " + (i+1); } if (row.length != table[0].length) { throw "Wrong number of fields on line " + (i+1) + ". Expected " + table[0].length + " but found " + row.length + "."; } } return table; } function isEmpty(str) { return str.trim().length === 0; } function findFieldNumbers(table) { let fieldNames = table[0]; let fieldNumbers = {'const': -1, 'rating': -1}; for (let i = 0; i < fieldNames.length; i++) { let fieldName = fieldNames[i].toLowerCase().trim(); if (fieldName === 'const') { fieldNumbers.const = i; } else if (fieldName === 'your rating') { fieldNumbers.rating = i; } } if (fieldNumbers.const === -1) { throw "Field 'const' not found."; } else if (fieldNumbers.rating === -1) { throw "Field 'your rating' not found."; } return fieldNumbers; } function importRatings(list) { if (list.length === 0) return; let l = {}; l.list = list; l.ready = 0; sendItem(l); } function sendItem(l) { log("Info", 'Set rating ' + l.list[l.ready].rating + " for " + l.list[l.ready].const); let url = 'https://api.graphql.imdb.com/'; let data = {"query": "mutation UpdateTitleRating($rating: Int!, $titleId: ID!) {\n rateTitle(input: {rating: $rating, titleId: $titleId}) {\n rating {\n value\n __typename\n }\n __typename\n }\n}", "operationName": "UpdateTitleRating", "variables": {"rating": l.list[l.ready].rating, "titleId": l.list[l.ready].const } }; sendRequest(sendItemHandler, l, url, JSON.stringify(data)); } function sendRequest(handler, l, url, data) { var x = new XMLHttpRequest(); x.onreadystatechange = function(event) { handler(l, event); } x.open('POST', url, true); x.setRequestHeader('Content-Type', 'application/json'); x.send(data); } function sendItemHandler(l, event) { let target = event.target; log("Info", 'Set rating ' + l.list[l.ready].rating + " for " + l.list[l.ready].const + " request: readyState(" + target.readyState + "), status(" + target.status + ")"); if (target.readyState == 4 && target.status == 200) { showReady(l); } } function showReady(l) { l.ready += 1; setStatus('Ready ' + l.ready + ' of ' + l.list.length + '.'); if (l.ready == l.list.length) { setStatus("Finished"); } else { sendItem(l); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址