userscripts/AnimeBytes/ab-filelist.user.js
2024-02-27 13:19:10 +01:00

208 lines
8.3 KiB
JavaScript

// ==UserScript==
// @name AnimeBytes Filelist Improvements
// @author WeebDataHoarder
// @version 1.2.1
// @downloadURL https://git.gammaspectra.live/WeebDataHoarder/userscripts/raw/branch/master/AnimeBytes/ab-filelist.user.js
// @updateURL https://git.gammaspectra.live/WeebDataHoarder/userscripts/raw/branch/master/AnimeBytes/ab-filelist.user.js
// @description AnimeBytes Filelist Improvements. Adds full folder path, exact file sizes, fixes path names eating spaces, adds piece information. MIT license
// @match https://animebytes.tv/torrents.php?*id=*
// @match https://animebytes.tv/torrents2.php?*id=*
// @exclude https://animebytes.tv/torrents*groupid=*
// @icon https://mei.kuudere.pw/jEBHMictkaa.png
// @grant GM_addStyle
// @require deps/compat.js?1
// @require deps/bdecode.js?2
// @run-at document-end
// ==/UserScript==
let handledTorrentIds = {
};
const eventHandler = (e) => {
const tabs = e.target.closest("div.tabs");
if(tabs === null){ //?????
return;
}
const fileListElement = tabs.querySelector("div[id$='_filelist']");
if(fileListElement === null){ //?????
return;
}
try{
const torrentId = parseInt(fileListElement.id.match(/^(?<torrentId>[0-9]+)_filelist$/).groups.torrentId);
if(torrentId in handledTorrentIds){
return;
}
handledTorrentIds[torrentId] = true;
let folderElement = document.getElementById("filelist_" + torrentId + "_foldername");
if(folderElement !== null){
return;
}
const downloadLink = document.querySelector("tr.group_torrent a[href^='/torrent/"+torrentId+"/download/']");
fetch((new URL(downloadLink.getAttribute("href"), window.location.href)).toString(), {
method: "GET",
mode: "same-origin",
credentials: "omit"
}).then((response) => response.arrayBuffer().then((buffer) => {
if(!response.ok){
return;
}
try{
const value = bdecode(new Uint8Array(buffer), undefined, undefined);
if("info" in value && "name" in value.info){
let fileList = {};
const baseName = value.info.name;
const pieceSize = "piece length" in value.info ? value.info["piece length"] : 0;
const pieceCount = "pieces" in value.info ? value.info.pieces.length / 20 : 0; //SHA1 byte size (160 bits)
if("files" in value.info){
value.info.files.forEach((entry, index) => {
const path = [baseName].concat(entry.path.slice(0, -1)).join("/");
const name = entry.path.slice(-1)[0];
const matchPath = entry.path.slice(0, -1).concat([name]).join("/").replace(/[ \t\r\n]/g, "");
fileList[path + "/" + name] = {
index: index,
path: path,
name: name,
match: matchPath,
length: entry.length,
};
});
}else{
if(!("length" in value.info)){ // ???
fileList[baseName] = {
index: 0,
path: "",
name: baseName,
match: baseName.replace(/[ \t\r\n]/g, ""),
length: 0,
};
}else{
fileList[baseName] = {
index: 0,
path: "",
name: baseName,
match: baseName.replace(/[ \t\r\n]/g, ""),
length: value.info.length,
};
}
}
const fileCount = Object.values(fileList).length;
const fileListTable = document.getElementById("filelist_" + torrentId);
const pieceInformation = document.createElement("p");
pieceInformation.append("Piece Count: " + pieceCount.toLocaleString());
pieceInformation.append(document.createElement("br"));
pieceInformation.append("Piece Size: " + ((b) => {
let i = ~~(Math.log2(b)/10);
return (b/Math.pow(1024,i)).toFixed(2) + " " + ("KMGTPEZY"[i-1]||"") + "iB"
})(pieceSize));
if(fileCount > 1){
pieceInformation.append(document.createElement("br"));
pieceInformation.append("File Count: " + fileCount.toLocaleString());
}
if("__hash" in value.info){
value.info.__hash.then((result) => {
pieceInformation.append(document.createElement("br"));
pieceInformation.append("Info Hash: " + Array.prototype.map.call(
new Uint8Array(result),
n => n.toString(16).padStart(2, "0")
).join(""))
});
}
fileListTable.insertAdjacentElement("beforebegin", pieceInformation);
fileListTable.querySelectorAll("tr").forEach((tableRow, index) => {
const pathElement = tableRow.querySelector("td");
if(tableRow.querySelector("td > strong") !== null){
//Skip header
return;
}
const formattedSizeElement = tableRow.querySelector("td:last-of-type");
const matchPath = pathElement.textContent.replace(/[ \t\r\n]/g, "");
const formattedSize = formattedSizeElement.textContent; //TODO
let size = "";
let path = pathElement.textContent.split("/").splice(0, -1).join("/");
let name = pathElement.textContent.split("/").splice(-1)[0];
for(let [key, entry] of Object.entries(fileList)){
if(entry.match === matchPath){
path = entry.path;
name = entry.name;
size = entry.length.toLocaleString() + " bytes";
delete fileList[key];
break;
}
}
while (pathElement.firstChild) {
pathElement.firstChild.remove();
}
let pre = document.createElement("code");
pre.textContent = (path !== "" ? path + "/" : "") + name;
pathElement.append(pre);
if(size !== ""){
formattedSizeElement.setAttribute("title", size);
}
});
for(let [key, entry] of Object.entries(fileList)){
console.log("Unmatched file list for #"+torrentId+" element: " + key);
console.log(entry);
}
}
}catch (e){
console.log(e);
}
})).catch((e) => {
});
}catch (e){
console.log(e);
}
}
GM_addStyle('div.tabs code { font-family: monospace; }');
document.querySelectorAll("a[href$='filelist']").forEach((e) => {
const match = e.getAttribute("href").match(/^#(?<torrentId>[0-9]+)[_\/]filelist$/);
if(match !== null){
const torrentId = parseInt(match.groups.torrentId);
if(window.location.hash === ("#" + torrentId + "/filelist")){
eventHandler({target: e});
}else{
e.addEventListener("click", eventHandler, {
once: true,
capture: true
});
e.addEventListener("mouseover", eventHandler, {
once: true,
capture: true
});
}
}
});