Minehut folder download

Download folders from Minehut's file manager without having to pay

目前為 2023-10-10 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Minehut folder download
// @namespace   Violentmonkey Scripts
// @match       *://app.minehut.com/*
// @grant       none
// @version     1.0.1
// @author      -
// @require     https://greasyfork.org/scripts/441873-zip-js/code/zipjs.js?version=1030820
// @description Download folders from Minehut's file manager without having to pay
// @license     MIT
// ==/UserScript==


(() => {
    let downloadFolder = (async (progressFn) => {
        function getCookieValue(cookieName) {
            let name = cookieName + "=";
            let decodedCookie = decodeURIComponent(document.cookie);
            let cookieArray = decodedCookie.split(';');
            for (let i = 0; i < cookieArray.length; i++) {
                let cookie = cookieArray[i];
                while (cookie.charAt(0) === ' ') {
                    cookie = cookie.substring(1);
                }
                if (cookie.indexOf(name) === 0) {
                    return cookie.substring(name.length, cookie.length);
                }
            }
            return "";
        }

        let accessToken = getCookieValue('access_token_prd');
        let slgToken = localStorage.getItem('slg_user_token');
        let minehutSession = localStorage.getItem('minehut_session_id');
        let activeServerData = localStorage.getItem('activeServer');

        if (!accessToken || !slgToken || !minehutSession || !activeServerData) {
            alert('You must select a server to use this script°');
            return;
        }

        let activeServer = JSON.parse(activeServerData);
        let sideCarBase = `https://${activeServer._id}.manager.minehut.com`;

        let headers = {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${accessToken}`,
            'x-profile-id': slgToken,
            'x-session-id': minehutSession
        };

        async function apiRequest(url, method = 'GET', body = undefined) {
            return await fetch('https://api.minehut.com' + url, {
                method: method,
                headers: headers,
                body: body
            });
        }

        async function sideCarRequest(url, method = 'GET', body = undefined) {
            return await fetch(sideCarBase + url, {
                method: method,
                headers: headers,
                body: body
            });
        }

        async function list(path) {
            let response = await apiRequest(`/file/${activeServer._id}/list/${path}`);
            return await response.json();
        }

        async function download(path) {
            let response = await sideCarRequest(`/file/download?files=${JSON.stringify([path])}`);
            return await response.blob();
        }

        async function listRecursive(path) {
            let files = await list(path);
            let fileList = [];
            for (let file of files.files) {
                if (file.directory) {
                    fileList = fileList.concat(await listRecursive(`${path}/${file.name}`));
                } else if (!file.blocked) {
                    fileList.push(`${path}/${file.name}`);
                }
            }
            return fileList;
        }

        function downloadBlob(blob, fileName) {
            let a = document.createElement('a');
            a.href = URL.createObjectURL(blob);
            a.download = fileName;
            a.click();
            URL.revokeObjectURL(a.href);
        }

        async function createZip(files, progressFn) {
            const zipFileWriter = new zip.BlobWriter();
            const zipWriter = new zip.ZipWriter(zipFileWriter);

            let i = 1;
            for (let path of files) {
                progressFn(`Downloading (${i}/${files.length}) ...`);
                let data = await download(path);
                await zipWriter.add(path.replace(/^\/+/, ''), new zip.BlobReader(data));
                i++;
            }
            await zipWriter.close();
            return await zipFileWriter.getData();
        }

        let folderName = prompt('Enter the full path of the folder to download (e.g. world or plugins/WorldEdit)');
        if (!folderName) {
            alert('You must enter a folder name');
            return;
        }

        if (!folderName.startsWith('/')) {
            folderName = '/' + folderName;
        }
        if (folderName.endsWith('/')) {
            folderName = folderName.slice(0, -1);
        }

        progressFn('Scanning folder...');
        let contents = await listRecursive(folderName);
        let result = await createZip(contents, progressFn);
        let name = folderName.split('/').pop();
        if (name === '') {
            name = 'root';
        }
        downloadBlob(result, name + '.zip');
    });

    let activeDownload = false;
    let button = document.createElement('button');
    button.className = 'themed___PBHap primary-theme___q1d2u no-border___6w-uL themed-control___KmjCR no-underline___h2l-L page-control___rxMPx';
    button.textContent = 'Download Folder';
    button.style.position = 'fixed';
    button.style.zIndex = '10000';
    button.style.top = '5px';
    button.style.right = '5px';
    button.style.display = 'none';
    button.onclick = async () => {
        if (activeDownload) {
            return;
        }
        activeDownload = true;
        button.textContent = 'Starting...';
        try {
            await downloadFolder(progress => {
                button.textContent = progress;
            });
        } catch (e) {
            alert('An error occurred while downloading the folder');
        }
        activeDownload = false;
        button.textContent = 'Download Folder';
    };

    document.body.appendChild(button);

    setInterval(() => {
        if (window.location.pathname.startsWith('/dashboard/files')) {
            button.style.display = 'block';
        } else {
            button.style.display = 'none';
        }
    }, 250);
})();