您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Opens a modal to add magnet links to Transmission with different destination directories.
// ==UserScript== // @name add to remote transmission // @namespace https://github.com/DoWhileGeek/add-to-remote-transmission // @description Opens a modal to add magnet links to Transmission with different destination directories. // @author DoWhileGeek // @license MIT // @version 1.0.0 // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @match https://webtorrent.io/free-torrents // @supportURL https://github.com/DoWhileGeek/add-to-remote-transmission/issues // @include https://webtorrent.io/* // ==/UserScript== // Default values (can be set by the user) var host = GM_getValue("host", "http://127.0.0.1"); var port = GM_getValue("port", "9091"); var username = GM_getValue("username", ""); var password = GM_getValue("password", ""); var subDir = "/transmission/rpc"; var webHost = host + ":" + port + subDir; // Retrieve options from storage or use default var options = GM_getValue("options", []); var currentMagnetLink = null; function postTorrentByURI(uri, destination, sessionID = null) { var req = JSON.stringify({ method: "torrent-add", arguments: { filename: uri, "download-dir": destination, }, }); var headers = { "User-Agent": "Mozilla/5.0", Accept: "text/xml", Authorization: "Basic " + btoa(username + ":" + password), }; if (sessionID) { headers["X-Transmission-Session-Id"] = sessionID; } GM_xmlhttpRequest({ method: "POST", url: webHost, headers: headers, data: req, onload: function (response) { if (response.status === 200 && response.responseText.match(/success/)) { alert("Torrent successfully added!"); } else if (response.responseText.match(/duplicate torrent/)) { alert("Torrent already exists"); } else if (response.status === 409) { var newSessID = response.responseHeaders.match( /x-transmission-session-id: ([^\r\n]+)/i )[1]; postTorrentByURI(uri, destination, newSessID); } else { console.error("Failed to add torrent:", response.responseText); alert("Failed to add torrent. Check console for details."); } }, onerror: function (response) { console.error("Request error:", response); alert("Request error. Check console for details."); }, ontimeout: function (response) { console.error("Request timeout:", response); alert("Request timeout. Check console for details."); }, }); } function addSelectedTorrent(destination) { if (currentMagnetLink && currentMagnetLink.match(/magnet:/)) { postTorrentByURI(currentMagnetLink, destination); } else { alert("No torrent link selected or invalid link."); } } // Helper function to traverse upwards to find a parent <a> tag function findParentMagnetLink(element) { while (element && element.tagName !== "A") { element = element.parentElement; } return element && element.href && element.href.match(/magnet:/) ? element.href : null; } // Function to create and show the modal function showModal(event) { event.preventDefault(); // Modal container let modal = document.createElement("div"); modal.style.position = "fixed"; modal.style.top = "0"; modal.style.left = "0"; modal.style.width = "100%"; modal.style.height = "100%"; modal.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; modal.style.zIndex = "1000"; modal.style.display = "flex"; modal.style.justifyContent = "center"; modal.style.alignItems = "center"; // Modal content let modalContent = document.createElement("div"); modalContent.style.backgroundColor = "white"; modalContent.style.padding = "20px"; modalContent.style.borderRadius = "5px"; modalContent.style.width = "300px"; modalContent.style.boxShadow = "0 2px 10px rgba(0, 0, 0, 0.1)"; modalContent.style.display = "flex"; modalContent.style.flexDirection = "column"; modalContent.style.gap = "8px"; // Message let message = document.createElement("span"); message.innerText = "Choose an option for the magnet link:"; //message.style. modalContent.appendChild(message); // Options options .filter((option) => option.label !== "" && option.directory !== "") .forEach((option) => { let button = document.createElement("button"); button.innerText = option.label; button.style.margin = "5px 0"; button.onclick = function () { addSelectedTorrent(option.directory); document.body.removeChild(modal); }; modalContent.appendChild(button); }); // Settings button let settingsButton = document.createElement("button"); settingsButton.innerText = "Settings"; settingsButton.style.margin = "5px 0"; settingsButton.onclick = function () { document.body.removeChild(modal); openSettings(); }; modalContent.appendChild(settingsButton); // Close modal when clicking outside of it modal.addEventListener("click", function (event) { if (event.target === modal) { document.body.removeChild(modal); } }); // Append content to modal modal.appendChild(modalContent); // Append modal to body document.body.appendChild(modal); } // Attach event listener to all anchor tags document.querySelectorAll("a").forEach((anchor) => { if (anchor.href.match(/magnet:/)) { anchor.addEventListener("click", function (event) { currentMagnetLink = anchor.href; showModal(event); }); } }); // Settings menu function openSettings() { let settingsModal = document.createElement("div"); settingsModal.style.position = "fixed"; settingsModal.style.top = "0"; settingsModal.style.left = "0"; settingsModal.style.width = "100%"; settingsModal.style.height = "100%"; settingsModal.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; settingsModal.style.zIndex = "1000"; settingsModal.style.display = "flex"; settingsModal.style.justifyContent = "center"; settingsModal.style.alignItems = "center"; let settingsContent = document.createElement("div"); settingsContent.style.backgroundColor = "white"; settingsContent.style.padding = "20px"; settingsContent.style.borderRadius = "5px"; settingsContent.style.width = "350px"; settingsContent.style.boxShadow = "0 2px 10px rgba(0, 0, 0, 0.1)"; settingsContent.style.display = "flex"; settingsContent.style.flexDirection = "column"; settingsContent.style.gap = "8px"; settingsContent.style.overflow = "hidden"; let settingsHeader = document.createElement("h4"); settingsHeader.innerText = "Regular Settings"; settingsContent.appendChild(settingsHeader); // Host setting let hostLabel = document.createElement("label"); hostLabel.innerText = "Host:"; hostLabel.htmlFor = "hostInput"; settingsContent.appendChild(hostLabel); let hostInput = document.createElement("input"); hostInput.id = "hostInput"; hostInput.type = "text"; hostInput.placeholder = "Host"; hostInput.value = GM_getValue("host", ""); settingsContent.appendChild(hostInput); // Port setting let portLabel = document.createElement("label"); portLabel.innerText = "Port:"; portLabel.htmlFor = "portInput"; settingsContent.appendChild(portLabel); let portInput = document.createElement("input"); portInput.id = "portInput"; portInput.type = "text"; portInput.placeholder = "Port"; portInput.value = GM_getValue("port", ""); settingsContent.appendChild(portInput); // Username setting let usernameLabel = document.createElement("label"); usernameLabel.innerText = "Username:"; usernameLabel.htmlFor = "usernameInput"; settingsContent.appendChild(usernameLabel); let usernameInput = document.createElement("input"); usernameInput.id = "usernameInput"; usernameInput.type = "text"; usernameInput.placeholder = "Username"; usernameInput.value = GM_getValue("username", ""); settingsContent.appendChild(usernameInput); // Password setting let passwordLabel = document.createElement("label"); passwordLabel.innerText = "Password:"; passwordLabel.htmlFor = "passwordInput"; settingsContent.appendChild(passwordLabel); let passwordInput = document.createElement("input"); passwordInput.id = "passwordInput"; passwordInput.type = "password"; passwordInput.placeholder = "Password"; passwordInput.value = GM_getValue("password", ""); settingsContent.appendChild(passwordInput); let optionsHeader = document.createElement("h4"); optionsHeader.innerText = "Directory Preset Options"; settingsContent.appendChild(optionsHeader); let optionsContainer = document.createElement("div"); optionsContainer.style.display = "flex"; optionsContainer.style.flexDirection = "column"; optionsContainer.style.gap = "16px"; // Larger gap between option groups settingsContent.appendChild(optionsContainer); // Ensure there is at least one option if (options.length === 0) { options.push({ label: "", directory: "" }); } function updateOptionsUI() { optionsContainer.innerHTML = ""; options.forEach((option, index) => { let optionDiv = document.createElement("div"); optionDiv.style.display = "flex"; optionDiv.style.gap = "5px"; optionDiv.style.flexWrap = "wrap"; optionDiv.style.width = "100%"; let labelInput = document.createElement("input"); labelInput.type = "text"; labelInput.placeholder = "Label"; labelInput.value = option.label; labelInput.style.flex = "1"; optionDiv.appendChild(labelInput); let directoryInput = document.createElement("input"); directoryInput.type = "text"; directoryInput.placeholder = "Directory"; directoryInput.value = option.directory; directoryInput.style.flex = "2"; optionDiv.appendChild(directoryInput); let removeButton = document.createElement("button"); removeButton.innerText = "Remove"; removeButton.disabled = options.length === 1; // Disable if only one option removeButton.onclick = function () { options.splice(index, 1); updateOptionsUI(); }; optionDiv.appendChild(removeButton); optionsContainer.appendChild(optionDiv); // Update options array on input change labelInput.addEventListener("input", () => { option.label = labelInput.value; }); directoryInput.addEventListener("input", () => { option.directory = directoryInput.value; }); }); let addButton = document.createElement("button"); addButton.innerText = "Add Option"; addButton.onclick = function () { options.push({ label: "", directory: "" }); updateOptionsUI(); }; optionsContainer.appendChild(addButton); } updateOptionsUI(); let saveButton = document.createElement("button"); saveButton.innerText = "Save"; saveButton.style.marginTop = "20px"; saveButton.onclick = function () { GM_setValue("host", hostInput.value); GM_setValue("port", portInput.value); GM_setValue("username", usernameInput.value); GM_setValue("password", passwordInput.value); GM_setValue("options", options); document.body.removeChild(settingsModal); }; settingsContent.appendChild(saveButton); settingsModal.appendChild(settingsContent); settingsModal.addEventListener("click", function (event) { if (event.target === settingsModal) { document.body.removeChild(settingsModal); } }); document.body.appendChild(settingsModal); }