Add torrents to Deluge via Web API

Add torrents to Deluge via Web API (requires patched deluge-web and ViolentMonkey)

当前为 2020-03-25 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @description   Add torrents to Deluge via Web API (requires patched deluge-web and ViolentMonkey)
// @grant         GM.xmlHttpRequest
// @homepageURL   https://github.com/lalbornoz/AddTorrentsDelugeTransmission
// @include       *
// @license       MIT
// @name          Add torrents to Deluge via Web API
// @namespace     https://greasyfork.org/users/467795
// @supportURL    https://github.com/lalbornoz/AddTorrentsDelugeTransmission
// @version       1.5
// ==/UserScript==

/*
 * Tunables
 */
let debug = false;
let delugeDownloadDir = {
  "": "/var/lib/deluge/downloads"
};
let delugeHostId = "";
let delugeHttpAuthPassword = ""; // (optional)
let delugeHttpAuthUsername = ""; // (optional)
let delugeTorrentDirectory = "/var/lib/deluge/torrents";
let delugeWebPassword = "";
let delugeWebUrl = "protocol://hostname[:port]/deluge";
let linkOpacity = 0.5;

// {{{ Module variables
let delugeRequestId = 0;
// }}}

// {{{ function basename(url)
function basename(url) {
  let url_ = url.split("/");
  return url_[url_.length - 1];
};
// }}}
// {{{ function delugeWebRequest(method, onLoadCb, params)
function delugeWebRequest(method, onLoadCb, params) {
  let headers = {"Content-type": "application/json"};
  let paramsJson = JSON.stringify(params);
  let xhrParams = {
    anonymous:    false,
    data:         '\{"method":"' + method + '","params":' + paramsJson + ',"id":' + (delugeRequestId++) + '\}',
    headers:      headers,
    method:       "POST",
    onload:       function (xhr) {
                    let response = null;
                    try {
                      response = JSON.parse(xhr.responseText);
                    }
                    catch (error) {
                      logError("Error parsing response from server as JSON: "
                               + xhr.responseText);
                    };
                    if (response.error === null) {
                      logDebug("Asynchronous `" + method
                               + "' Web API request succeeded w/ response="
                               + JSON.stringify(response));
                    } else {
                      logError("Asynchronous `" + method
                               + "' Web API request failed: " + response.error.message
                               + " (code " + response.error.code.toString() + ")");
                    };
                    onLoadCb(response, xhr);
                  },
    synchronous:  false,
    url:          delugeWebUrl + "/json"
  };
  if ((delugeHttpAuthPassword !== "")
  &&  (delugeHttpAuthUsername !== "")) {
    xhrParams["password"] = delugeHttpAuthPassword;
    xhrParams["user"] = delugeHttpAuthUsername;
  };
  logDebug("POSTing asynchronous `" + method + "' Web API request to " + xhrParams["url"]
           + " (JSON-encoded parameters: " + paramsJson + ")");
  GM.xmlHttpRequest(xhrParams);
};
// }}}
// {{{ function isMagnetLink(url)
function isMagnetLink(url) {
  if (url.match(/^magnet:/i)) {
    return true;
  } else {
    return false;
  };
};
// }}}
// {{{ function isTorrentLink(url)
function isTorrentLink(url) {
  if (url.match(/\.torrent(\?.*|)$/i)) {
    return true;
  } else {
    return false;
  };
};
// }}}
// {{{ function JavaScriptIsFuckingGarbage(SodOff)
function JavaScriptIsFuckingGarbage(SodOff) {
  return decodeURI(SodOff).replace(/\+/g, " ");
};
// }}}
// {{{ function JavaScriptIsFuckingWorthless(FuckYou)
function JavaScriptIsFuckingWorthless(FuckYou) {
  return btoa(new Uint8Array(FuckYou).reduce(
    function(data, byte) {
      return data + String.fromCharCode(byte);
    }, ""));
};
// }}}
// {{{ function logDebug(msg)
function logDebug(msg) {
  if (debug) {
    console.log("[Deluge] " + msg);
  };
};
// }}}
// {{{ function logError(msg)
function logError(msg) {
  logDebug(msg);
  alert("[Deluge] " + msg);
};
// }}}
// {{{ function logInfo(msg)
function logInfo(msg) {
  logDebug(msg);
  alert("[Deluge] " + msg);
};
// }}}
// {{{ function matchHostDict(dict, host)
function matchHostDict(dict, host) {
  let hostDomain = host.split(".").slice(-2);
  if (host in dict) {
    return dict[host];
  } else if (hostDomain in dict) {
    return dict[hostDomain];
  } else {
    return dict[""];
  };
};
// }}}

// {{{ function cbClickMagnet(e)
function cbClickMagnet(e) {
  let torrentUrl = this.href;
  if (e.ctrlKey) {
    logDebug("Ignoring " + torrentUrl + " due to <Ctrl> modifier.");
  } else {
    e.stopPropagation(); e.preventDefault();
    let torrentName = torrentUrl.match(/dn=([^&]+)/);
    if (torrentName === null) {
      logError("Invalid Magnet URI (missing Display Name)");
    } else {
      torrentName = JavaScriptIsFuckingGarbage(torrentName[1]);
      delugeWebRequest("auth.login",
                       function (response, xhr_) {
                         cbWebLoginResponse(response, null, delugeDownloadDir[""],
                                            torrentName, torrentUrl, null, xhr_);
                       }, [delugeWebPassword]);
    };
  };
};
// }}}
// {{{ function cbClickTorrent(e)
function cbClickTorrent(e) {
  let torrentUrl = this.href;
  if (e.ctrlKey) {
    logDebug("Ignoring " + torrentUrl + " due to <Ctrl> modifier.");
  } else {
    e.stopPropagation(); e.preventDefault();
    let torrentUrlHost = torrentUrl.match(new RegExp("^[^:]+://(?:[^:]+:[^@]+@)?([^/:]+)"));
    if (torrentUrlHost === null) {
      logDebug("Failed to obtain hostname from BitTorrent URL " + torrentUrl);
    } else {
      torrentUrlHost = torrentUrlHost[1];
      let torrentDownloadDir = "";
      if ((torrentDownloadDir = matchHostDict(delugeDownloadDir, torrentUrlHost)) === null) {
        torrentDownloadDir = delugeDownloadDir[""];
      };
      logDebug("Sending asynchronous GET request for " + torrentUrl);
      GM.xmlHttpRequest({
        method:             "GET",
        onreadystatechange: function (xhr) {
                              cbClickResponse(xhr.response, torrentDownloadDir,
                                              basename(torrentUrl), torrentUrl,
                                              torrentUrlHost, xhr);
                            },
        responseType:       "arraybuffer",
        synchronous:        false,
        url:                torrentUrl
      });
    };
  };
};
// }}}
// {{{ function cbClickResponse(torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr)
function cbClickResponse(torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr) {
  logDebug("Asynchronous GET request for " + torrentUrl
           + " readyState=" + xhr.readyState + " status=" + xhr.status);
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      delugeWebRequest("auth.login",
                       function (response, xhr_) {
                         cbWebLoginResponse(response, torrent, torrentDownloadDir,
                                            torrentName, torrentUrl, torrentUrlHost, xhr_);
                       }, [delugeWebPassword]);
    } else {
      logDebug("Asynchronous GET request for " + torrentUrl
               + " failed w/ status=" + xhr.status);
    };
  };
};
// }}}
// {{{ function cbWebLoginResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr)
function cbWebLoginResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr) {
  if (response.error === null) {
    delugeWebRequest("web.connect",
                     function (response_, xhr_) {
                       cbWebConnectResponse(response_, torrent, torrentDownloadDir,
                                            torrentName, torrentUrl, xhr_);
                     }, [delugeHostId]);
  };
};
// }}}
// {{{ function cbWebConnectResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr)
function cbWebConnectResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr) {
  if (response.error === null) {
    delugeWebRequest("web.get_config",
                     function (response_, xhr_) {
                       cbWebGetConfigResponse(response_, torrent, torrentDownloadDir,
                                              torrentName, torrentUrl, xhr_);
                     }, []);
  };
};
// }}}
// {{{ function cbWebGetConfigResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr)
function cbWebGetConfigResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr) {
  if (response.error === null) {
    let params = [{options: {"download_location": torrentDownloadDir}}];
    if (isMagnetLink(torrentUrl)) {
      params[0]["path"] = torrentUrl;
    } else {
      params[0]["data"] = JavaScriptIsFuckingWorthless(torrent);
      params[0]["path"] = delugeTorrentDirectory + "/" + torrentName;
    };
    delugeWebRequest("web.add_torrents",
                     function (response_, xhr_) {
                       cbWebAddTorrentsResponse(response_, torrent, torrentDownloadDir,
                                                torrentName, torrentUrl, torrentUrlHost, xhr_);
                     }, [params]);
  };
};
// }}}
// {{{ function cbWebAddTorrentsResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr)
function cbWebAddTorrentsResponse(response, torrent, torrentDownloadDir, torrentName, torrentUrl, torrentUrlHost, xhr) {
  if (response.error === null) {
    logInfo("Torrent `" + torrentName + "' added successfully.");
  };
};
// }}}

function main() {
  logDebug("Entry point");
  for (let link of document.links) {
    if (isMagnetLink(link.href)) {
      link.addEventListener("click", cbClickMagnet, true);
      link.style.opacity = linkOpacity;
      logDebug("Registered Magnet link " + link.href);
    } else if (isTorrentLink(link.href)) {
      link.addEventListener("click", cbClickTorrent, true);
      link.style.opacity = linkOpacity;
      logDebug("Registered BitTorrent link " + link.href);
    };
  };
};

main();

// vim:expandtab fileformat=dos sw=2 ts=2