mirror of
https://github.com/Radiquum/YAMPD.git
synced 2025-05-19 23:29:34 +05:00
feat: mod pack download
This commit is contained in:
parent
c01fc7bd5a
commit
56569917c1
8 changed files with 346 additions and 15 deletions
1
dev.py
1
dev.py
|
@ -7,6 +7,7 @@ if __name__ == "__main__":
|
|||
environment = os.environ.copy()
|
||||
environment["is_dev"] = "True"
|
||||
environment["NEXT_PUBLIC_API_URL"] = "http://127.0.0.1:5000/api"
|
||||
environment["NEXT_PUBLIC_SOCKET_URL"] = "http://127.0.0.1:5000"
|
||||
environment["MODRINTH_UA"] = "radiquum/YAMPD (kentai.waah@gmail.com)"
|
||||
environment["CURSEFORGE_API_KEY"] = "$2a$10$bL4bIL5pUWqfcO7KQtnMReakwtfHbNKh6v1uTpKlzhwoueEJQnPnm"
|
||||
|
||||
|
|
|
@ -73,3 +73,8 @@ export const MOD_ENDPOINT = (
|
|||
};
|
||||
return _endpoints[endpoint];
|
||||
};
|
||||
|
||||
export const DOWNLOAD_ENDPOINT = {
|
||||
downloadPack: `${API}/download/pack`,
|
||||
downloadMods: `${API}/download/mods`
|
||||
}
|
|
@ -1,8 +1,20 @@
|
|||
"use client";
|
||||
|
||||
import { MOD_ENDPOINT, PACK_ENDPOINT, PACKS_ENDPOINT } from "@/api/ENDPOINTS";
|
||||
import {
|
||||
DOWNLOAD_ENDPOINT,
|
||||
MOD_ENDPOINT,
|
||||
PACK_ENDPOINT,
|
||||
PACKS_ENDPOINT,
|
||||
} from "@/api/ENDPOINTS";
|
||||
import { Pack } from "@/types/pack";
|
||||
import { Button, Card, Label, Spinner, TextInput } from "flowbite-react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Label,
|
||||
Progress,
|
||||
Spinner,
|
||||
TextInput,
|
||||
} from "flowbite-react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
|
@ -12,15 +24,30 @@ import { HiDownload, HiPlusCircle, HiTrash } from "react-icons/hi";
|
|||
import { ModTable } from "../components/ModTable";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
import { io } from "socket.io-client";
|
||||
const socketURL = process.env.NEXT_PUBLIC_SOCKET_URL
|
||||
? process.env.NEXT_PUBLIC_SOCKET_URL
|
||||
: undefined;
|
||||
const socket = io(socketURL);
|
||||
|
||||
export default function PackPage() {
|
||||
const [packData, setPackData] = useState<Pack | null>(null);
|
||||
const [packDataLoading, setPackDataLoading] = useState(true);
|
||||
const router = useRouter();
|
||||
const id = useSearchParams().get("id") || "";
|
||||
|
||||
const [addModModalOpen, setAddModModalOpen] = useState(true);
|
||||
const [addModModalOpen, setAddModModalOpen] = useState(false);
|
||||
const [modUrl, setModUrl] = useState("");
|
||||
|
||||
const [downloadModalOpen, setdownloadModalOpen] = useState(false);
|
||||
const [downloadProgressFile, setDownloadProgressFile] = useState({
|
||||
filename: "filename",
|
||||
total: 1,
|
||||
current: 1,
|
||||
});
|
||||
const [downloadProgressTotal, setDownloadProgressTotal] = useState(100);
|
||||
const [downloadProgressCurrent, setDownloadProgressCurrent] = useState(100);
|
||||
|
||||
async function _getPacksData() {
|
||||
const res = await fetch(PACK_ENDPOINT("getPack", id));
|
||||
if (!res.ok) router.push("/404");
|
||||
|
@ -38,6 +65,55 @@ export default function PackPage() {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
function percentage(partialValue: number, totalValue: number) {
|
||||
const flo = (100 * partialValue) / totalValue;
|
||||
return Number(flo.toFixed(2));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function onDownloadCurrent(data: any) {
|
||||
switch (data.status) {
|
||||
case "pending":
|
||||
const progress = percentage(data.download_bytes, data.total_bytes);
|
||||
setDownloadProgressCurrent(progress);
|
||||
break;
|
||||
case "ok":
|
||||
setDownloadProgressCurrent(0);
|
||||
break;
|
||||
case "error":
|
||||
toast.error(data.message, {
|
||||
autoClose: 2500,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log(`GET UNKNOWN WS STATUS: ${data.status}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function onDownloadTotal(data: any) {
|
||||
const progress = percentage(data.current, data.total);
|
||||
setDownloadProgressTotal(progress);
|
||||
setDownloadProgressFile({
|
||||
filename: data.filename,
|
||||
total: data.total,
|
||||
current: data.current,
|
||||
});
|
||||
}
|
||||
|
||||
socket.on("download_current", (data) => onDownloadCurrent(data));
|
||||
socket.on("download_total", (data) => onDownloadTotal(data));
|
||||
|
||||
return () => {
|
||||
socket.off("download_current", (data) => onDownloadCurrent(data));
|
||||
socket.off("download_total", (data) => onDownloadTotal(data));
|
||||
};
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const handleImageUpdate = (e: any) => {
|
||||
if (!packData || !window) return;
|
||||
|
@ -188,6 +264,23 @@ export default function PackPage() {
|
|||
_getPacksData();
|
||||
}
|
||||
|
||||
async function downloadPack() {
|
||||
if (!packData) return;
|
||||
|
||||
fetch(`${DOWNLOAD_ENDPOINT["downloadPack"]}`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
pack_id: packData._id,
|
||||
}),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
accept: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
setdownloadModalOpen(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{packDataLoading && (
|
||||
|
@ -236,7 +329,7 @@ export default function PackPage() {
|
|||
<Button onClick={() => setAddModModalOpen(true)}>
|
||||
Add mod <HiPlusCircle className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
<Button>
|
||||
<Button onClick={() => downloadPack()}>
|
||||
Download <HiDownload className="ml-2 h-5 w-5" />
|
||||
</Button>
|
||||
<Button color={"red"} onClick={() => deletePack()}>
|
||||
|
@ -293,6 +386,56 @@ export default function PackPage() {
|
|||
<Button onClick={() => addMod()}>Save</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
<Modal show={downloadModalOpen}>
|
||||
<ModalHeader>Download progress</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="mb-4">
|
||||
<Progress
|
||||
progress={downloadProgressTotal}
|
||||
progressLabelPosition="inside"
|
||||
textLabel="Total"
|
||||
textLabelPosition="outside"
|
||||
size="lg"
|
||||
labelProgress
|
||||
labelText
|
||||
/>
|
||||
<div className="flex justify-end items-center mb-2">
|
||||
<p>
|
||||
{downloadProgressFile.current} / {downloadProgressFile.total}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Progress
|
||||
progress={downloadProgressCurrent}
|
||||
progressLabelPosition="inside"
|
||||
textLabel={downloadProgressFile.filename}
|
||||
textLabelPosition="outside"
|
||||
size="lg"
|
||||
labelProgress
|
||||
labelText
|
||||
/>
|
||||
</div>
|
||||
{downloadProgressFile.current == downloadProgressFile.total ? (
|
||||
<p>
|
||||
Mods downloaded, You can find them in path: packs/{packData?._id}
|
||||
/mods
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
disabled={
|
||||
downloadProgressFile.current != downloadProgressFile.total
|
||||
}
|
||||
onClick={() => setdownloadModalOpen(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
62
gui/bun.lock
62
gui/bun.lock
|
@ -10,6 +10,8 @@
|
|||
"react-dom": "^19.0.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-toastify": "^11.0.5",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
|
@ -147,6 +149,8 @@
|
|||
|
||||
"@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.11.0", "", {}, "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ=="],
|
||||
|
||||
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
|
||||
|
||||
"@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="],
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
||||
|
@ -183,6 +187,8 @@
|
|||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
"@types/cors": ["@types/cors@2.8.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
@ -245,7 +251,7 @@
|
|||
|
||||
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.7.2", "", { "os": "win32", "cpu": "x64" }, "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA=="],
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
||||
|
||||
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
|
||||
|
||||
|
@ -291,6 +297,8 @@
|
|||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
||||
|
||||
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
@ -383,6 +391,12 @@
|
|||
|
||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||
|
||||
"engine.io": ["engine.io@6.6.4", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1" } }, "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g=="],
|
||||
|
||||
"engine.io-client": ["engine.io-client@6.6.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w=="],
|
||||
|
||||
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
|
||||
|
||||
"es-abstract": ["es-abstract@1.23.9", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.3", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.0", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-regex": "^1.2.1", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.0", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.18" } }, "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA=="],
|
||||
|
@ -663,9 +677,9 @@
|
|||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
|
@ -679,7 +693,7 @@
|
|||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"next": ["next@15.3.1", "", { "dependencies": { "@next/env": "15.3.1", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1", "@next/swc-darwin-x64": "15.3.1", "@next/swc-linux-arm64-gnu": "15.3.1", "@next/swc-linux-arm64-musl": "15.3.1", "@next/swc-linux-x64-gnu": "15.3.1", "@next/swc-linux-x64-musl": "15.3.1", "@next/swc-win32-arm64-msvc": "15.3.1", "@next/swc-win32-x64-msvc": "15.3.1", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g=="],
|
||||
|
||||
|
@ -825,6 +839,14 @@
|
|||
|
||||
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
|
||||
|
||||
"socket.io": ["socket.io@4.8.1", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.3.2", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg=="],
|
||||
|
||||
"socket.io-adapter": ["socket.io-adapter@2.5.5", "", { "dependencies": { "debug": "~4.3.4", "ws": "~8.17.1" } }, "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg=="],
|
||||
|
||||
"socket.io-client": ["socket.io-client@4.8.1", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ=="],
|
||||
|
||||
"socket.io-parser": ["socket.io-parser@4.2.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
@ -921,6 +943,10 @@
|
|||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="],
|
||||
|
||||
"xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"zod": ["zod@3.24.3", "", {}, "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg=="],
|
||||
|
@ -961,6 +987,10 @@
|
|||
|
||||
"@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.31.1", "", { "dependencies": { "@typescript-eslint/types": "8.31.1", "@typescript-eslint/visitor-keys": "8.31.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag=="],
|
||||
|
||||
"engine.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"engine.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||
|
||||
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||
|
@ -969,6 +999,10 @@
|
|||
|
||||
"eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
|
||||
|
||||
"express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"express/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"is-bun-module/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
@ -977,8 +1011,20 @@
|
|||
|
||||
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
|
||||
"send/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"sharp/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"socket.io/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"socket.io-adapter/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"socket.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
|
||||
|
||||
"type-is/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
@ -1001,6 +1047,14 @@
|
|||
|
||||
"@typescript-eslint/utils/@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
"express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"express/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"send/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"type-is/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"@typescript-eslint/parser/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-toastify": "^11.0.5"
|
||||
"react-toastify": "^11.0.5",
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
|
|
|
@ -2,6 +2,8 @@ from flask import Blueprint
|
|||
|
||||
apiPack = Blueprint("pack", __name__, url_prefix="/api/pack")
|
||||
apiPacks = Blueprint("packs", __name__, url_prefix="/api/packs")
|
||||
apiDownload = Blueprint("download", __name__, url_prefix="/api/download")
|
||||
|
||||
from . import pack
|
||||
from . import packs
|
||||
from . import download
|
||||
|
|
110
src/api/download.py
Normal file
110
src/api/download.py
Normal file
|
@ -0,0 +1,110 @@
|
|||
import os
|
||||
from . import apiDownload
|
||||
from flask import request, jsonify, send_file, redirect, url_for, abort
|
||||
from config import PACKS_FOLDER
|
||||
import json
|
||||
from flask_socketio import emit
|
||||
import requests
|
||||
|
||||
|
||||
def download(path, url, name, total):
|
||||
r = requests.get(url, stream=True)
|
||||
if r.status_code != 200:
|
||||
emit(
|
||||
"download_current",
|
||||
{
|
||||
"status": "error",
|
||||
"message": f"Got a HTTP ERROR {r.status_code} while downloading {name}",
|
||||
},
|
||||
)
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Got a HTTP ERROR {r.status_code} while downloading {name}",
|
||||
}
|
||||
downloaded = 0
|
||||
if os.path.exists(f"{path}/{name}"):
|
||||
emit(
|
||||
"download_current",
|
||||
{"status": "ok", "message": f"{name} already downloaded"},
|
||||
namespace="/",
|
||||
broadcast=True,
|
||||
)
|
||||
return {"status": "ok", "message": f"{name} already downloaded"}
|
||||
with open(f"{path}/{name}", "wb") as fp:
|
||||
for data in r.iter_content(chunk_size=1024):
|
||||
size = fp.write(data)
|
||||
downloaded += size
|
||||
emit(
|
||||
"download_current",
|
||||
{
|
||||
"status": "pending",
|
||||
"total_bytes": total,
|
||||
"download_bytes": downloaded,
|
||||
},
|
||||
namespace="/",
|
||||
broadcast=True,
|
||||
)
|
||||
emit(
|
||||
"download_current",
|
||||
{"status": "ok", "message": f"{name} downloaded"},
|
||||
namespace="/",
|
||||
broadcast=True,
|
||||
)
|
||||
return {
|
||||
"status": "ok",
|
||||
"message": f"{name} downloaded",
|
||||
}
|
||||
|
||||
|
||||
@apiDownload.route("/pack", methods=["POST"])
|
||||
def downloadPack():
|
||||
pack = {}
|
||||
pack_id = request.json.get("pack_id")
|
||||
|
||||
with open(f"{PACKS_FOLDER}/{pack_id}/packfile.json") as fp:
|
||||
pack = json.load(fp)
|
||||
fp.close()
|
||||
|
||||
mods = pack.get("mods", [])
|
||||
total = len(mods)
|
||||
|
||||
os.makedirs(f"{PACKS_FOLDER}/{pack_id}/mods", exist_ok=True)
|
||||
|
||||
for i, mod in enumerate(mods):
|
||||
emit(
|
||||
"download_total",
|
||||
{
|
||||
"status": "ok",
|
||||
"total": total,
|
||||
"current": i,
|
||||
"title": mod.get("title"),
|
||||
"filename": mod.get("file").get("filename"),
|
||||
},
|
||||
namespace="/",
|
||||
broadcast=True,
|
||||
)
|
||||
download(
|
||||
f"{PACKS_FOLDER}/{pack_id}/mods",
|
||||
mod.get("file").get("url"),
|
||||
mod.get("file").get("filename"),
|
||||
mod.get("file").get("size"),
|
||||
)
|
||||
|
||||
emit(
|
||||
"download_total",
|
||||
{
|
||||
"status": "ok",
|
||||
"total": total,
|
||||
"current": total,
|
||||
"title": "",
|
||||
"filename": "",
|
||||
},
|
||||
namespace="/",
|
||||
broadcast=True,
|
||||
)
|
||||
return jsonify(
|
||||
{
|
||||
"status": "ok",
|
||||
"message": f"download of {pack_id} with {total} mods finished",
|
||||
},
|
||||
)
|
26
src/main.py
26
src/main.py
|
@ -3,10 +3,9 @@ from flask import render_template, send_file, abort
|
|||
from flaskwebgui import FlaskUI # import FlaskUI
|
||||
import os
|
||||
import sys
|
||||
from flask_socketio import SocketIO
|
||||
|
||||
|
||||
from api import apiPack
|
||||
from api import apiPacks
|
||||
from api import apiPack, apiPacks, apiDownload
|
||||
|
||||
|
||||
def resource_path(relative_path):
|
||||
|
@ -20,10 +19,11 @@ app = Flask(
|
|||
static_folder=resource_path("static"),
|
||||
template_folder=resource_path("templates"),
|
||||
)
|
||||
|
||||
socketio = SocketIO(app, cors_allowed_origins="*")
|
||||
|
||||
app.register_blueprint(apiPack)
|
||||
app.register_blueprint(apiPacks)
|
||||
app.register_blueprint(apiDownload)
|
||||
|
||||
|
||||
if os.getenv("is_dev") == "True":
|
||||
|
@ -51,9 +51,23 @@ def page_not_found(e):
|
|||
return render_template("404.html"), 404
|
||||
|
||||
|
||||
@socketio.on("connect")
|
||||
def handle_connect():
|
||||
print("Client connected")
|
||||
|
||||
|
||||
@socketio.on("disconnect")
|
||||
def handle_disconnect():
|
||||
print("Client disconnected")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if os.getenv("is_dev") == "True":
|
||||
app.run(host="0.0.0.0", debug=True, use_reloader=True)
|
||||
socketio.run(app, host="0.0.0.0", debug=True, use_reloader=True)
|
||||
# app.run(host="0.0.0.0", debug=True, use_reloader=True)
|
||||
else:
|
||||
FlaskUI(app=app, server="flask").run()
|
||||
# FlaskUI(app=app, server="flask").run()
|
||||
FlaskUI(
|
||||
app=app, socketio=socketio, server="flask_socketio", width=800, height=600
|
||||
).run()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue