Greasy Fork

来自缓存

Greasy Fork is available in English.

IMDB Ratings Importer

Import ratings for movies, TV series and episodes from a csv file.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name            IMDB Ratings Importer
// @namespace       Neinei0k_imdb
// @include         https://www.imdb.com/user/ur*/ratings*
// @grant           none
// @version         2.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 request_data_add_rating = {
    "query": "mutation UpdateTitleRating($rating: Int!, $titleId: ID!) {\n  rateTitle(input: {rating: $rating, titleId: $titleId}) {\n    rating {\n      value\n    }\n  }\n}",
    "operationName": "UpdateTitleRating",
    "variables": {
        "rating": 0,
        "titleId": ""
    }
}

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('.ipc-page-section--base');
	if (container === null) {
		throw ".ipc-page-section--base element not found";
	}
  let nextChild = container.children[0];
	let root = document.createElement('div');
	root.setAttribute('class', 'aux-content-widget-2 ipc-list-card--base ipc-list-card--border-line');
	root.style.height = 'initial';
  root.style.marginTop = '30px';
  root.style.marginBottom = '30px';
  root.style.padding = '10px';
	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;
}
 
async function importRatings(list) {
	if (list.length === 0)
		return;
 
	let l = {};
	l.list = list;
	l.ready = 0;
  
  for (let i = 0; i < list.length; ++i) {
    log("Info", `Setting rating ${list[i].rating} for ${list[i].const}...`);
    request_data_add_rating.variables.rating = list[i].rating;
    request_data_add_rating.variables.titleId = list[i].const;
    await sendRequest(request_data_add_rating);
    setStatus(`Ready ${i+1} of ${list.length}.`);
  }
}
 
function sendRequest(data) {
  return fetch("https://api.graphql.imdb.com/", {
    "credentials": "include",
    "headers": {
        "Accept": "application/graphql+json, application/json",
        "content-type": "application/json",
    },
    "referrer": "https://www.imdb.com/",
    "body": JSON.stringify(data),
    "method": "POST",
    "mode": "cors"
	}).then((response) => {
  	if (!response.ok) {
      throw new Error(`Request failed with status code ${response.status}`);
    }

    return response.json();
  });
}