dirtycajunrice / PTP To Radarr

// ==UserScript==
// @name         PTP To Radarr
// @version      0.8.4
// @author       DirtyCajunRice
// @description  Easily add movies to radarr straight from PTP
// @homepage     https://passthepopcorn.me/forums.php?action=viewthread&threadid=35294
// @icon         https://ptpimg.me/19kcyv.png
// @updateURL    https://openuserjs.org/meta/dirtycajunrice/PTP_To_Radarr.meta.js
// @supportURL   https://passthepopcorn.me/forums.php?action=viewthread&threadid=35294
// @match        https://passthepopcorn.me/*
// @require      https://openuserjs.org/src/libs/sizzle/GM_config.js
// @copyright    2019, dirtycajunrice (https://openuserjs.org//users/dirtycajunrice)
// @license      MIT
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_openInTab
// @grant        GM_log
// @grant        GM_notification
// @grant        GM_registerMenuCommand
// ==/UserScript==

// Changelog 0.8.4 - Fixed href bug for covers
                  // Fixed update logic
// Changelog 0.8.3 - Major internal code refactor
                  // Found lingering URL sanitization missed
// Changelog 0.8.2 - Fixed autoupdate trying to update non-existant buttons
// Changelog 0.8.1 - Fixed "Huge view" themeChecker for Raise
                  // Fixed "Huge view" in collections
                  // Added AutoUpdate for "Huge view"
// Changelog 0.8.0 - Added "Huge view"
// Changelog 0.7.1 - Fixed bug with GM_config iframe document access logic
                  // Fixed bug with themeChecker
// Changelog 0.7.0 - Added AutoSync to settings
                  // Update buttons after a sync (manual and auto)
// Changelog 0.6.1 - Added Stylesheet checking to fix cover buttons. Confirmed working on all official and supported custom stylesheets + Raise
// Changelog 0.6.0 - Added error checking to help debug issues
                  // Added Radarr URL sanitization

var current_page_type = "";

(function () {
    "use strict";
    GM_config.init({
        "id": "PTPToRadarr",
        "title": "PTP To Radarr Settings",
        "css": `#PTPToRadarr {background: #333333;}
                #PTPToRadarr .field_label {color: #fff;}
                #PTPToRadarr .config_header {color: #fff; padding-bottom: 10px;}
                #PTPToRadarr .reset {color: #f00; text-align: center;}
                #PTPToRadarr .config_var {text-align: center;}
                #PTPToRadarr_radarr_syncbutton_var {float: left;}
                #PTPToRadarr .reset_holder {text-align: center;}
                #PTPToRadarr_radarr_minimumavailability_var {padding-right: 95;}
                #PTPToRadarr_radarr_apikey_var {padding-left: 49;}
                #PTPToRadarr_radarr_url_var {padding-left: 69;}
                #PTPToRadarr .saveclose_buttons {margin: 16px 10px 10px; padding: 2px 12px; background-color: #e7e7e7; color: black; text-decoration: none; border: none; border-radius: 6px;}
                #PTPToRadarr_field_radarr_syncbutton {background-color: #e7e7e7; color: black; text-decoration: none; border: none; border-radius: 6px; margin-left: 10px; margin-top: 16px; height: 20px;}`,
        "events": {
            "open": function(doc) {
                let style = this.frame.style;
                style.width = "400px";
                style.height = "295px";
                style.inset = "";
                style.top = "6%";
                style.right = "6%";
                style.borderRadius = "25px";
                doc.getElementById("PTPToRadarr_buttons_holder").prepend(doc.getElementById("PTPToRadarr_radarr_syncbutton_var"));
            }
        },
        "fields": {
            "radarr_url": {
                "label": "Radarr URL",
                "type": "text",
                "default": "https://domain.tld/radarr"
            },
            "radarr_apikey": {
                "label": "Radarr API Key",
                "type": "text",
                "default": ""
            },
            "radarr_profileid": {
                "label": "Radarr Quality Profile ID",
                "type": "text",
                "default": "1"
            },
            "radarr_rootfolderpath": {
                "label": "Radarr Root Folder Path",
                "type": "text",
                "default": "/mnt/movies"
            },
            "radarr_minimumavailability":
            {
                "label": "Minimum Availability",
                "type": "select",
                "options": ["preDB", "announced", "inCinemas", "released"],
                "default": "released"
            },
            "radarr_searchformovie": {
                "label": "Search for movie on request",
                "type": "checkbox",
                "default": false
            },
            "radarr_sync_interval":
            {
                "label": "AutoSync Interval (Minutes)",
                "type": "select",
                "options": ["15", "30", "60", "120", "360", "1440", "Never"],
                "default": "Never"
            },
            "radarr_syncbutton": {
                "label": "Sync Radarr Movies",
                "type": "button",
                "click": get_radarr_movies
            }
        }
    });
    new GM_registerMenuCommand("PTP To Radarr Settings",() => GM_config.open());
    let url = window.location.href;
    let radarr_url = GM_config.get("radarr_url").replace(/\/$/, "");
    if (document.getElementById("torrent-table")) {
        current_page_type = "singletorrent";
    }
    else {
        current_page_type = "multi";
    }
    if (current_page_type) {
        set_html(false);
    }
    let interval = GM_config.get("radarr_sync_interval");
    if (interval != "Never") {
        let millisecondInterval = Number(interval) * 60000;
        window.setTimeout(() => autoSync(millisecondInterval));
        window.setInterval(() => autoSync(millisecondInterval), millisecondInterval);
    }
})();

function themeChecker () {
    let typeAThemes = ["marcel.css", "ptp-raise"];
    typeAThemes.forEach((theme) => {
        if (document.head.querySelector("[href*=\"" + theme + "\"]")) {
            return true;
        }
    });
    return false;
}

function clickswap (imdbid, titleSlug) {
    let radarr_url = GM_config.get("radarr_url").replace(/\/$/, "");
    let button = document.getElementById("ptptoradarr-" + imdbid)
    button.firstChild.src = "https://ptpimg.me/19kcyv.png";
    button.removeEventListener("click", new_movie_lookup, false);
    button.addEventListener("click", function () {
        GM_openInTab(radarr_url.concat("/movies/", titleSlug), "active");
    }, false);
}

function set_html (update) {
    let theme = themeChecker();
    let radarr_url = GM_config.get("radarr_url").replace(/\/$/, "");

    let coverView = document.querySelector(".cover-movie-list__container");
    let coverViewMovies = Array.from(document.getElementsByClassName("cover-movie-list__movie__rating-and-tags"));
    let listView = document.querySelector(".js-basic-movie-list");
    let listViewMovies = Array.from(document.getElementsByClassName("basic-movie-list__movie__ratings-and-tags"));
    let hugeView = document.querySelector(".huge-movie-list__movie");
    let hugeViewMovies = Array.from(document.getElementsByClassName("huge-movie-list__movie__ratings"));
    if (update) {
        let buttons = document.querySelectorAll("[id^=\"ptptoradarr-tt\"]");
        if (buttons.length === 0) {
            return;
        }
        buttons.forEach((button) => {
            window.setTimeout(function () {
                button.parentNode.removeChild(button);
            });
        });
    }
    if (current_page_type == "singletorrent") {
        let a = document.querySelector("[href^=\"http://www.imdb.com/title/tt\"]");
        let movie = document.querySelector(".panel__body");
        if (a) {
            buttonBuilder(movie, a.href, "single", theme);
        }
    }
    else if (current_page_type == "multi") {
        if (coverView && !coverView.classList.contains("hidden")) {
            coverViewMovies.forEach((movie) => {
                window.setTimeout(() => {
                    let a = movie.querySelector("[href^=\"http://www.imdb.com/title/tt\"]");
                    if (a) {
                        buttonBuilder(movie, a.href, "medium", theme);
                    }
                });
            });
        }
        else if (listView && !listView.classList.contains("hidden")) {
            listViewMovies.forEach((movie) => {
                window.setTimeout(() => {
                    let a = movie.querySelector("[href^=\"http://www.imdb.com/title/tt\"]");
                    if (a) {
                        buttonBuilder(movie, a.href, "small", theme);
                    }
                });
            });
        }
        else if (hugeView) {
            hugeViewMovies.forEach((movie) => {
                window.setTimeout(() => {
                    let a = movie.querySelector("[href^=\"http://www.imdb.com/title/tt\"]");
                    if (a) {
                        buttonBuilder(movie, a.href, "large", theme);
                    }
                });
            });
        }
    }
}

function buttonBuilder (movie, href, type, theme) {
    let radarr_url = GM_config.get("radarr_url").replace(/\/$/, "");
    let imdbid = href.substr(26, 9);
    let exists = check_exists(imdbid);
    let button = document.createElement("button");
    let img = document.createElement("img");
    button.id = "ptptoradarr-" + imdbid;
    button.type = type;
    Object.assign(button.style, {border: "none", backgroundColor: "transparent"});
    Object.assign(img.style, {border: "none", backgroundColor: "transparent"});
    Object.assign(img, {height: 32, width: 32});
    button.appendChild(img);
    img.imdbid = imdbid;
    if (type == "single") {
        Object.assign(button.style, {position: "absolute", top: "6%", zIndex: 10});
        movie.style.position = "relative";
        movie.prepend(button);
    }
    else if (type == "small") {
        Object.assign(img, {height: 16, width: 16});
        movie.appendChild(button);
    }
    else if (type == "medium") {
        let posterContainer = movie.closest(".cover-movie-list__movie");
        posterContainer.prepend(button);
        Object.assign(button.style, {position: "absolute", top: "3%", zIndex: 10});
        if (theme) {
            posterContainer.style.position = "relative";
        }
    }
    else if (type == "large") {
        let posterContainer = movie.closest(".huge-movie-list__movie").firstChild;
        posterContainer.prepend(button);
        Object.assign(button.style, {position: "absolute", top: "3%", zIndex: 10});
        if (theme) {
            button.style.left = "3%";
            posterContainer.style.position = "relative";
        }
    }
    if (exists) {
        img.src = "https://ptpimg.me/19kcyv.png";
        button.addEventListener("click", function () {
            GM_openInTab(radarr_url.concat("/movies/", exists[0].titleSlug), "active");
        }, false);
    }
    else {
        img.src = "https://ptpimg.me/0q4vvz.png";
        button.addEventListener("click", new_movie_lookup, false);
    }
}
function errorNotificationHandler (error, expected, errormsg) {
    let prestring = "PTPToRadar::";
    if (expected) {
        GM_log(prestring + "Error: " + errormsg + " Actual Error: " + error);
        GM_notification("Error: " + errormsg);
    }
    else {
        GM_log(prestring + "Unexpected Error: Please report this error in the forum. Actual Error: " + error);
        GM_notification("Unexpected Error: Please report this error in the forum. Actual Error: " + error);
    }
}
function get_radarr_movies() {
    let radarr_url = GM_config.get("radarr_url").replace(/\/$/, "");
    let radarr_apikey = GM_config.get("radarr_apikey");
    GM_xmlhttpRequest({
        method: "GET",
        url: radarr_url.concat("/api/movie"),
        headers: {
            "X-Api-Key": radarr_apikey,
            "Accept": "text/json"
        },
        onload: function(response) {
            let responseJSON = null;
            if (!response.responseJSON) {
                try {
                    responseJSON = JSON.parse(response.responseText);
                    if (responseJSON.error == "Unauthorized") {
                        throw "creds";
                    }
                    GM_setValue("existing_movies", JSON.stringify(responseJSON));
                    let timestamp = + new Date()
                    GM_setValue("last_sync_timestamp", timestamp)
                    GM_log("PTPToRadarr::Sync: Setting last sync timestamp to " + timestamp)
                    GM_notification("Radarr Sync Complete!", "PTP To Radarr");
                    set_html(true);
                }
                catch (e) {
                    if (e instanceof SyntaxError) {
                        errorNotificationHandler(e, true, "No JSON in response. Please check your Radarr URL.")
                    }
                    else if (e == "creds") {
                        errorNotificationHandler(e, true, "Invalid API Key. Please check your Radarr API Key")
                    }
                    else {
                        errorNotificationHandler(e, false)
                    }
                }
            }
        }
    });
};

function check_exists (imdbid) {
    let movieliststr = GM_getValue("existing_movies", "{}");
    let movie_list = JSON.parse(movieliststr);
    let filter = null
    try {
        filter = movie_list.filter(movie => movie.imdbId == imdbid);
    }
    catch (e) {
        if (e instanceof TypeError) {
            return false
        }
        else {
            errorNotificationHandler(e, false)
            return false
        }
    }
    if (!filter.length) {
        return false
    }
    else {
        return filter
    };
}

function autoSync (interval) {
    let currentTimestamp = + new Date();
    let lastSyncTimestamp = GM_getValue("last_sync_timestamp", 0);
    if (currentTimestamp - lastSyncTimestamp >= interval) {
        let notification = "It has been " + ((currentTimestamp - lastSyncTimestamp) / 60000).toFixed(1) +
            " minutes since your last sync which exceeds your threshold of " +
            (interval / 60000) + " minutes. AutoSyncing...";
        GM_log(notification);
        GM_notification(notification);
        get_radarr_movies();
    }
}

function new_movie_lookup (link) {
    let imdbid = ""
    if (link.originalTarget) {
        imdbid = link.originalTarget.imdbid;
    }
    else {
        imdbid = link.target.imdbid;
    }
    let radarr_url = GM_config.get("radarr_url").replace(/\/$/, "");
    let radarr_apikey = GM_config.get("radarr_apikey");
    let movie = "";
    GM_xmlhttpRequest({
        method: "GET",
        url: radarr_url.concat("/api/movie/lookup/imdb?imdbId=", imdbid),
        headers: {
            "X-Api-Key": radarr_apikey,
            "Accept": "text/json"
        },
        onload: function(response) {
            let responseJSON = null;
            if (!response.responseJSON) {
                if (!response.responseText) {
                    GM_notification("No results found");
                    return;
                }
                responseJSON = JSON.parse(response.responseText);
                add_movie(responseJSON, imdbid);
            }
        }
    })
}

function add_movie (movie, imdbid) {
    let radarr_url = GM_config.get("radarr_url").replace(/\/$/, "");
    let radarr_apikey = GM_config.get("radarr_apikey");
    movie.ProfileId = GM_config.get("radarr_profileid");
    movie.RootFolderPath = GM_config.get("radarr_rootfolderpath");
    movie.monitored = true;
    movie.minimumAvailability = GM_config.get("radarr_minimumavailability");;
    if (GM_config.get("radarr_searchformovie")) {
        movie.addOptions = {searchForMovie: true}
    }
    GM_xmlhttpRequest({
        method: "POST",
        url: radarr_url.concat("/api/movie"),
        headers: {
            "X-Api-Key": radarr_apikey,
            "Accept": "text/json",
            "Content-Type": "application/json"
        },
        data: JSON.stringify(movie),
        onload: function(response) {
            let responseJSON = null;
            if (!response.responseJSON) {
                responseJSON = JSON.parse(response.responseText);
                try {
                    if (!responseJSON.title && responseJSON.firstChild.errorMessage == "This movie has already been added") throw "exists";
                    clickswap(imdbid, responseJSON.titleSlug);
                    GM_notification(responseJSON.title + " Sucessfully sent to Radarr", "PTP To Radarr")
                }
                catch (e) {
                    if (e == "exists") {
                        errorNotificationHandler(e, true, "Movie already exists in Radarr. Please resync your movies.");
                    }
                    else {
                        errorNotificationHandler(e, false);
                    }
                }
            }
        }
    });
}