// ==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(/^(?[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(/^#(?[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 }); } } });