Skip to content
This repository was archived by the owner on Jul 19, 2024. It is now read-only.

Commit

Permalink
Code Cleanup and Fixes (#16)
Browse files Browse the repository at this point in the history
* Adjusted endpoints for media conversion
Adjusted MediaResponse type format
Support for log files (partial)

* code cleanup, fixes

* more error checking

* fix up message a bit

* update documentation, errors

* add min texture size check

* Fix multiple upload of media files for conversion (Issue #15)

---------

Co-authored-by: Nawias <[email protected]>
  • Loading branch information
TurtleP and Nawias authored Apr 21, 2024
1 parent e042537 commit ea1af41
Show file tree
Hide file tree
Showing 11 changed files with 561 additions and 374 deletions.
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"eslint-plugin-react-refresh": "^0.4.3",
"postcss": "^8.4.30",
"tailwind-styled-components": "^2.2.0",
"tailwindcss": "^3.3.3",
"tailwindcss": "^3.4.1",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"vite-tsconfig-paths": "^4.2.1"
Expand Down
65 changes: 31 additions & 34 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Flask from "@components/Flask";
import Footer from "@components/Footer";
import Banner from "@components/Banner";

import { prepareContent, BundlerResponse } from "./services/bundler";
import Bundler, { BundlerResponse } from "./services/Bundler";

Check failure on line 5 in src/App.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

Cannot find module './services/Bundler' or its corresponding type declarations.
import { Toaster, toast } from "react-hot-toast";

import successSfx from "@assets/sound/success.ogg";
Expand All @@ -13,7 +13,7 @@ import errorSfx from "@assets/sound/error.ogg";
import useSound from "use-sound";
import JSZip from "jszip";

import { MediaFile } from "./services/converters/MediaConverter";
import { MediaFile } from "./services/MediaConverter";
import { isZipFile, convertFiles, isValidFile } from "./services/utilities";

const downloadBlob = (blob: Blob) => {
Expand Down Expand Up @@ -50,61 +50,58 @@ function App() {
const handleUploadError = (error: BundlerResponse | string) => {
playError();

const message = (typeof error === "string") ? error : error.message;
const message = typeof error === "string" ? error : error.message;
return `Error: ${message}`;
};

const handleZipUpload = async (archive: File) => {
toast.promise(prepareContent(archive), {
const bundler = new Bundler(archive);

toast.promise(bundler.prepareContent(), {
loading: "Uploading..",
success: handleUploadSuccess,
error: handleUploadError,
});
}
};

const handleConversions = async (files: File[]) => {
toast.promise(
convertFiles(files),
{
loading: "Uploading..",
success: (files: MediaFile[]) => {
playSuccess();
const zip = new JSZip();

for (const file of files) {
zip.file(file.filepath, file.data);
}

zip.generateAsync({type: "blob"}).then((blob: Blob) => downloadBlob(blob));
return "Downloaded.";
},
error: handleUploadError
}
);
}
toast.promise(convertFiles(files), {
loading: "Uploading..",
success: (files: MediaFile[]) => {
playSuccess();
const zip = new JSZip();

for (const file of files) {
zip.file(file.filepath, file.data);
}

zip
.generateAsync({ type: "blob" })
.then((blob: Blob) => downloadBlob(blob));
return "Downloaded.";
},
error: handleUploadError,
});
};

const handleUpload = async (files: File[]) => {
try {
for (const file of files) {
if (file.size == 0) throw Error("Invalid file.");

if (!isValidFile(file))
throw Error("Invalid file type.");
if (!isValidFile(file)) throw Error("Invalid file type.");

if (isZipFile(file))
handleZipUpload(file);
else
handleConversions(files);
if (isZipFile(file)) handleZipUpload(file);
}
handleConversions(files);
} catch (exception) {
toast.error(handleUploadError((exception as Error).message));
}
}
};

return (
<>
<Banner />
<Banner />
<Toaster
toastOptions={{
className: `
Expand All @@ -115,13 +112,13 @@ function App() {
className: `
bg-green-600
text-white
`
`,
},
error: {
className: `
bg-red-600
text-white
`
`,
},
}}
/>
Expand Down
129 changes: 129 additions & 0 deletions src/services/Bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import JSZip, { JSZipObject } from "jszip";
import Config, { ConfigMetadata, parseConfig } from "./Config";

export type BundleIcons = {
ctr?: Blob;
cafe?: Blob;
hac?: Blob;
};

/*
** Bundler class
** Represents a bundle of files and configuration.
*/
export default class Bundle {
private file: File;

private zip: JSZip | undefined;
private config: Config | undefined;

readonly ConfigName = "lovebrew.toml";

constructor(zip: File) {
this.file = zip;
}

/**
* Validates the bundle
* @returns {Promise<boolean>} - Whether the file is a valid bundle.
*/
public async validate(): Promise<boolean> {
this.zip = await JSZip.loadAsync(this.file);

const data = await this.zip.file(this.ConfigName)?.async("string");

if (data === undefined) {
throw Error("Missing configuration file.");
}

this.config = parseConfig(data);

const source = this.config.build.source;
if (this.zip.file(new RegExp(`^${source}/.+`)).length === 0) {
throw Error(`Source folder '${source}' not found.`);
}

return true;
}

/**
* Finds all defined icons in the bundle.
* @returns {Promise<BundleIcon>} - A map of icon names to their respective blobs.
*/
public async findDefinedIcons(): Promise<BundleIcons> {
if (this.zip === undefined) {
throw Error("Zip file not loaded.");
}

if (this.config === undefined) {
throw Error("Configuration file not loaded.");
}

const result: BundleIcons = {};
const icons = this.config.getIcons();

for (const [key, value] of Object.entries(icons)) {
const file = this.zip.file(value);

if (file === null) continue;

const blob = await file.async("blob");
result[key as keyof BundleIcons] = blob;
}

return result;
}

/**
* Fetches all files within the defined source directory.
* @returns {Promise<Array<File>>} - An array of files within the source directory.
*/
public async getSourceFiles(): Promise<Array<File>> {
if (this.zip === undefined) {
throw Error("Zip file not loaded.");
}

if (this.config === undefined) {
throw Error("Configuration file not loaded.");
}

const source = this.config.build.source;

const files = await Promise.all(
this.zip
.file(new RegExp(`^${source}/.+`))
.map(async (file: JSZipObject) => {
const blob = await file.async("blob");
const length = source.length + 1;

return new File([blob], file.name.slice(length));
})
);

return files;
}

public getMetadata(): ConfigMetadata {
if (this.config === undefined) {
throw Error("Configuration file not loaded.");
}

return this.config.metadata;
}

public getTargets(): Array<string> {
if (this.config === undefined) {
throw Error("Configuration file not loaded.");
}

return this.config.build.targets;
}

public isPackaged(): boolean {
if (this.config === undefined) {
throw Error("Configuration file not loaded.");
}

return this.config.isPackaged();
}
}
46 changes: 46 additions & 0 deletions src/services/Config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import toml from "toml";

export type ConfigIcons = {
ctr?: string;
cafe?: string;
hac?: string;
};

export type ConfigMetadata = {
title: string;
author: string;
description: string;
version: string;
icons: ConfigIcons;
};

export type ConfigBuild = {
targets: Array<string>;
source: string;
packaged?: boolean;
};

export default class Config {
metadata!: ConfigMetadata;
build!: ConfigBuild;

public getIcons(): ConfigIcons {
return this.metadata.icons;
}

public getTargets(): Array<string> {
return this.build.targets;
}

public isPackaged(): boolean {
return this.build.packaged ?? false;
}
}

export function parseConfig(content: string): Config {
const configData = toml.parse(content);
const config = new Config();

Object.assign(config, configData);
return config;
}
Loading

0 comments on commit ea1af41

Please sign in to comment.