PTP Ratings Exporter for IMDb

Automatically add and rate movies to your IMDb watchlist

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         PTP Ratings Exporter for IMDb
// @namespace    PTP Ratings Exporter for IMDb
// @version      1.1.0
// @description  Automatically add and rate movies to your IMDb watchlist
// @author       Delgan
// @match        https://passthepopcorn.me/forums.php?*&threadid=37787&*
// @match        https://passthepopcorn.me/forums.php?*&threadid=37787
// @match        https://passthepopcorn.me/forums.php?threadid=37787&*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

function main() {
    let placeholder = `Fill in this text box with an JSON array looking like this:
    [
        {"title": "Sonatine", "year": 1993, "imdb": {"id": "tt0108188", "url": "https://www.imdb.com/title/tt0108188/", "my_rating": 9}},
        {"title": "Se7en", "year": 1995, "imdb": {"id": "tt0114369", "url": "https://www.imdb.com/title/tt0114369/", "my_rating": 6}}
    ]`

    let textarea = `<textarea id='ptp-ratings-exporter-textarea' style='width: 100%; height: 7rem; padding: 10px;' placeholder='${placeholder}'></textarea>`;
    let button = `<button id='ptp-ratings-exporter-button' style='display:block; margin: auto; margin-top: 10px;'>Rate my movies</button>`;
    let checkbox = `<label style='cursor:pointer;display:block;text-align:center;margin-top:5px;'><input id='ptp-ratings-exporter-checkbox-watchlist' type="checkbox" /> Add movies to watchlist</label>`;
    let progression = `<div id='ptp-ratings-exporter-progression' data-count="0" data-progress="0" style='text-align:center; margin-top: 5px;'></div>`;

    document.getElementById("content").insertAdjacentHTML("afterbegin", textarea + checkbox + button + progression);
    document.getElementById("ptp-ratings-exporter-button").addEventListener("click", rateMovies);
    renderProgression();
}

function getAuthToken(imdbId, onSuccess) {
    GM_xmlhttpRequest({
        url: `https://www.imdb.com/title/${imdbId}/`,
        method: "PUT",
        timeout: 30000,
        onload: function (response) {
            let auth;
            try {
                let html = document.createElement("html");
                html.innerHTML = response.responseText;
                auth = html.querySelector("#star-rating-widget").getAttribute("data-auth");
            } catch (err) {
                alert(`An error occurred while fetching IMDb auth token: '${err.message}'`);
                return;
            }
            onSuccess(auth);
        },
        onerror: function (_) {
            alert(`The HTTP request for auth token  failed`);
        },
        ontimeout: function (_) {
            alert(`The HTTP request for auth token timed out`);
        },
    });
}

function addMovieToWatchlist(imdbId) {
    GM_xmlhttpRequest({
        url: `https://www.imdb.com/watchlist/${imdbId}`,
        method: "PUT",
        timeout: 30000,
        onload: function (_) {
            console.log(`Successfully added movie '${imdbId}' to watchlist`);
        },
        onerror: function (_) {
            alert(`The HTTP request for movie '${imdbId}' failed`);
        },
        ontimeout: function (_) {
            alert(`The HTTP request for movie '${imdbId}' timed out`);
        },
    });
}

function rateMovie(imdbId, rating, auth) {
    let data = {
        "tconst": imdbId,
        "rating": Math.min(Math.max(rating, 1), 10).toFixed(),
        "auth": auth,
        "tracking_tag": "title-maindetails",
        "pageId": imdbId,
        "pageType": "title",
        "subpageType": "main",
    };

    let payload = "";
    for (let field in data) {
        payload += `${field}=${encodeURIComponent(data[field])}&`;
    }

    let headers = {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0',
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'X-Requested-With': 'XMLHttpRequest',
    };

    GM_xmlhttpRequest({
        url: `https://www.imdb.com/ratings/_ajax/title`,
        method: "POST",
        headers: headers,
        timeout: 30000,
        data: payload,
        onload: function (_) {
            console.log(`Successfully rated movie '${imdbId}'`);
        },
        onerror: function (_) {
            alert(`The HTTP request for movie '${imdbId}' failed`);
        },
        ontimeout: function (_) {
            alert(`The HTTP request for movie '${imdbId}' timed out`);
        },
    });
}

function disableForm() {
    document.getElementById("ptp-ratings-exporter-textarea").setAttribute("disabled", "disabled");
    document.getElementById("ptp-ratings-exporter-button").setAttribute("disabled", "disabled");
}

function enableForm() {
    document.getElementById("ptp-ratings-exporter-textarea").removeAttribute("disabled");
    document.getElementById("ptp-ratings-exporter-button").removeAttribute("disabled");
}


function resetProgression(progress, count) {
    let progression = document.getElementById("ptp-ratings-exporter-progression");
    progression.setAttribute("data-progress", progress);
    progression.setAttribute("data-count", count);
    renderProgression();
}

function updateProgression() {
    let progression = document.getElementById("ptp-ratings-exporter-progression");
    let progress = parseInt(progression.getAttribute("data-progress"));
    progression.setAttribute("data-progress", progress + 1);
    renderProgression();

    if (progress + 1 === parseInt(progression.getAttribute("data-count"))) {
        enableForm();
    }
}

function renderProgression() {
    let progression = document.getElementById("ptp-ratings-exporter-progression");
    let progress = progression.getAttribute("data-progress")
    let count = progression.getAttribute("data-count");
    if (count === "0") {
        progression.innerHTML = "Progression: ---";
    } else {
        progression.innerHTML = `Progression: ${progress} / ${count}`;
    }
}

function rateMovies() {
    disableForm();

    resetProgression(0, 0);

    let text = document.getElementById("ptp-ratings-exporter-textarea").value;

    if (text === "") {
        alert("No data to parse");
        enableForm();
        return;
    }

    let movies;

    try {
        movies = JSON.parse(text);
    } catch (err) {
        alert(`Unable to parse the JSON array: "${err.message}"`);
        enableForm();
        return;
    }

    if (!Array.isArray(movies)) {
        alert("Invalid parsed JSON, it should be an array");
        enableForm();
        return;
    }

    if (movies.length === 0) {
        enableForm();
        return;
    }

    resetProgression(0, movies.length);

    let addMoviesToWatchList = document.getElementById("ptp-ratings-exporter-checkbox-watchlist");

    let delay = 1000;

    for (let movie of movies) {
        if (!movie.hasOwnProperty("imdb")) {
            alert("Missing 'imdb' property in one data entry");
            updateProgression();
            continue;
        }
        if (!movie.imdb.hasOwnProperty("id")) {
            alert("Missing 'id' property in one data entry");
            updateProgression();
            continue;
        }
        if (!movie.imdb.hasOwnProperty("my_rating")) {
            alert("Missing 'my_rating' property in one data entry");
            updateProgression();
            continue;
        }

        let imdbId = movie.imdb.id;
        let myRating = parseFloat(movie.imdb.my_rating);

        if (!imdbId.match(/tt[0-9]+/)) {
            alert(`The imdb id format is incorrect: '${imdbId}'`);
            updateProgression();
            continue;
        }
        if (Number.isNaN(myRating) || myRating < 0 || myRating > 10) {
            alert(`The rating value is incorrect: '${myRating}'`);
            updateProgression();
            continue;
        }

        setTimeout(function () {addMoviesToWatchList
            updateProgression();
            getAuthToken(imdbId, function (auth) {
                if (addMoviesToWatchList) {
                    addMovieToWatchlist(imdbId);
                }
                rateMovie(imdbId, myRating, auth);
            });
        }, delay);

        delay += 1000;
    }
}

main();