Simple script that downlaod terabox file without app
// ==UserScript==
// @name tera-download
// @version 0.4
// @description Simple script that downlaod terabox file without app
// @namespace https://github.com/tttt369/tera-download
// @run-at document-start
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @match *://*.1024-terabox.com/*
// @match *://*.1024terabox.com/*
// @match *://*.1024tera.com/*
// @match *://*.bestclouddrive.com/*
// @match *://*.dubox.com/*
// @match *://*.freeterabox.com/*
// @match *://*.gibibox.com/*
// @match *://*.mirrobox.com/*
// @match *://*.nephobox.com/*
// @match *://*.pebibox.com/*
// @match *://*.tera1024box.com/*
// @match *://*.terabox1024.com/*
// @match *://*.terabox.app/*
// @match *://*.terabox.com/*
// @match *://*.teraboxfree.com/*
// @match *://*.terastream.fun/*
// @match *://*.terabox.link/*
// @match *://*.teraboxlink.com/*
// @match *://*.terabox-share.com/*
// @match *://*.teraboxshare.com/*
// @match *://*.terabox.space/*
// @match *://*.teraboxurl.com/*
// @match *://*.terabox.zone/*
// @match *://*.terafileshare.com/*
// @match *://*.teralinkshare.com/*
// @match *://*.terareferral.com/*
// @match *://*.tera-share.com/*
// @match *://*.terasharefile.com/*
// @match *://*.terasharelink.com/*
// @match *://*.terashareus.com/*
// @match *://*.terabox.best/*
// @match *://*.teraboxapp.com/*
// @license MIT
// ==/UserScript==
// from: https://global-staticplat.cdn.bcebos.com/general-conf/domain.json
(function() {
'use strict';
const UTIL = {
update_menu() {
function register(label, handler) {
const id = GM_registerMenuCommand(label, handler);
STATE.registeredMenuIds.push(id);
};
STATE.registeredMenuIds.forEach(GM_unregisterMenuCommand);
STATE.registeredMenuIds = [];
const data = Object.values(STATE.data);
const [filteredData] = UTIL.clean_data(data)
if (!filteredData.length) return;
filteredData.slice(0, 9).forEach(dict => {
register(dict.name, () => UTIL.batch_download([dict]));
});
register("download all", () => {
UTIL.batch_download(data);
});
},
clean_data(data) {
const dirData = []
const filteredData = data.filter(dict => {
if (dict.isdir) {
dirData.push(dict.name)
return false
}
return true
})
const log = dirData.length ? `${dirData.join(", ")} is folder, open folder to get download link` : ""
return [filteredData, log]
},
async batch_download(data) {
if (!UTIL.check()) return;
const [filteredData, log] = UTIL.clean_data(data)
if (log.length) alert(log)
for (const [idx, dict] of filteredData.entries()) {
if (!dict || !dict.url) continue;
if (idx > 0) {
await new Promise(r => setTimeout(r, 2000));
}
UTIL.download_file(dict.url, dict.name);
}
},
download_file(url, filename) {
if (!url.length) return
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
},
parse_response(data) {
if (data.error_msg && data.error_msg !== TARGET.succMsg) {
STATE.islogin = false;
}
if (!data.list) return;
data.list.forEach(dict => {
const fsId = String(dict.fs_id);
if (dict.isdir) {
STATE.data[fsId] = {
name: dict.server_filename,
url: "",
isdir: 1,
};
}
else if (dict.dlink) {
STATE.data[fsId] = {
name: dict.server_filename,
url: dict.dlink,
isdir: 0,
};
}
});
UTIL.update_menu();
},
check() {
if (!STATE.islogin) {
alert("you need to login to download")
return 0
}
return 1
},
get_query(url, param) {
const params = new URL(url).searchParams;
const fid = params.get(param);
return fid
}
}
const INTERCEPT = {
xhr() {
function handle_xhr_response(xhr) {
const url = xhr._url;
if (url.includes(TARGET.membership) || url.includes(TARGET.list)) {
const responseData = JSON.parse(xhr.responseText);
UTIL.parse_response(responseData);
}
}
const xhrProto = STATE.win.XMLHttpRequest.prototype;
const originalOpen = xhrProto.open;
const originalSend = xhrProto.send;
xhrProto.open = function(method, url) {
this._url = url;
return originalOpen.apply(this, arguments);
};
xhrProto.send = function(body) {
this.addEventListener("load", function() {
handle_xhr_response(this);
});
return originalSend.apply(this, arguments);
};
},
default_download() {
document.addEventListener('click', async function(e) {
const downloadBtn = e.target.closest(TARGET.downloadClass);
if (!downloadBtn) return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
const selectedElem = document.querySelectorAll(TARGET.selectedCss);
if (selectedElem.length === 0) {
alert('please select file');
return;
}
const dataToDownload = [];
for (const elem of selectedElem) {
let data = null;
const nameElem = elem.querySelector(TARGET.fileNameClass);
const name = nameElem ? nameElem.textContent.trim() : "";
const srcElem = elem.querySelector(TARGET.fileIdclass);
const fsId = srcElem ? UTIL.get_query(srcElem.src, "fid") : null;
if (fsId && STATE.data[fsId]) {
data = STATE.data[fsId];
} else {
data = Object.values(STATE.data).find(f => f.name === name);
}
if (data) dataToDownload.push(data);
}
UTIL.batch_download(dataToDownload);
}, true);
},
directory() {
function start_body_observer() {
if (document.body) {
bodyObserver.observe(document.body, {
childList: true,
subtree: true,
characterData: true
});
}
}
function debounced_update_menu() {
if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
UTIL.update_menu();
}, 500);
}
let currentTarget = null;
let timeoutId = null;
const bodyObserver = new MutationObserver(() => {
const newTarget = document.querySelector(TARGET.directoryClass);
if (!newTarget) return;
if (newTarget !== currentTarget) {
currentTarget = newTarget;
debounced_update_menu();
}
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', start_body_observer);
} else {
start_body_observer();
}
}
}
const UNBLOCK = {
constructor() {
function spoof_window_size() {
Object.defineProperty(STATE.win, "outerWidth", {
get: function() {
return STATE.win.innerWidth;
}
});
Object.defineProperty(STATE.win, "outerHeight", {
get: function() {
return STATE.win.innerHeight;
}
});
}
function block_anti_debug_intervals() {
const originalSetInterval = STATE.win.setInterval;
STATE.win.setInterval = function(fn, delay) {
if (typeof fn === 'function') {
const fnStr = fn.toString();
if (delay === 2000 && (fnStr.includes('outerWidth') || fnStr.includes('debugger'))) {
console.log("Anti-debug loop blocked.");
return null;
}
}
return originalSetInterval.apply(this, arguments);
};
}
const originalConstructor = STATE.win.Function.prototype.constructor;
STATE.win.Function.prototype.constructor = function(str) {
if (str && str.includes('debugger')) {
return function() {};
}
return originalConstructor.apply(this, arguments);
};
spoof_window_size()
block_anti_debug_intervals()
},
keyboard() {
STATE.win.addEventListener('keydown', function(e) {
if (e.keyCode === 123 || (e.ctrlKey && e.shiftKey && e.keyCode === 73)) {
e.stopImmediatePropagation();
}
}, true);
},
click() {
STATE.win.addEventListener('contextmenu', function(e) {
e.stopImmediatePropagation();
}, true);
},
}
const STATE = {
// you need to use unsafeWindow to unblock debugger detection.
win: typeof unsafeWindow !== 'undefined' ? unsafeWindow : window,
registeredMenuIds: [GM_registerMenuCommand("Initializing...", () => {})],
data: {},
islogin: true,
}
const TARGET = {
membership: "/membership/proxy/user?",
list: "/share/list?",
succMsg: "succ",
downloadClass: ".download-btn",
selectedCss: ".file-item-listmode.checked",
fileNameClass: ".file-item-name",
fileIdclass: ".file-icon-img",
directoryClass: '.breadcrumb-item.is-current'
}
INTERCEPT.xhr()
UNBLOCK.constructor()
UNBLOCK.click()
UNBLOCK.keyboard()
INTERCEPT.default_download()
INTERCEPT.directory()
})();