Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to custom wrapped folder name #121

Merged
merged 4 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ More field will be added in near future.

After preview & press button to export, User can defined export folder, and export in following options:

- `.cbz` with `komga` folder structure
- `.cbz` file ONLY
- `.cbz` with `komga` folder structure, using book name as folder name
- `.cbz` with `komga` folder structure, using custom folder name

<img src="screenshots/export.png" width="75%" height="75%">
| ![Export UI](screenshots/export.png) | ![Custom Export](screenshots/export_custom.png) |
| ------------------------------------ | ----------------------------------------------- |

### Quick Export (Komga Only)

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/controls/SessionData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export enum ExportMethod {
CBZ_ONLY,
/** Export cbz file with a folder wrapped. */
DEFAULT_WRAP_CBZ,
/** Export cbz file with a folder wrapped. */
CUSTOM_WRAP_CBZ,
}

export type SessionData = {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/pages/exportPanel.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.ms-1-5em {
margin-left: 1.5em;
}

.ps-9em {
padding-left: 9em;
}

.pe-3em {
padding-right: 3em;
}
50 changes: 45 additions & 5 deletions frontend/src/pages/exportPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import "./exportPanel.css";

// React
import { useEffect, useState } from "react";

// React Component
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";

// Project Component
import ColoredRadio from "../components/ColoredRadio";
import FolderSelector from "../components/FolderSelector";
import { ModalControl } from "../controls/ModalControl";

// Wails
import { ExportCbzOnly, ExportCbzWithDefaultWrap, GetDefaultOutputDirectory } from "../../wailsjs/go/application/App";
import {
ExportCbzOnly,
ExportCbzWithDefaultWrap,
ExportCbzWithWrap,
GetDefaultOutputDirectory,
} from "../../wailsjs/go/application/App";
import { comicinfo } from "../../wailsjs/go/models";
import { ExportMethod } from "../controls/SessionData";
import { basename } from "../filename";

/** Props Interface for FolderSelect */
type ExportProps = {
Expand Down Expand Up @@ -41,6 +50,7 @@ export default function ExportPanel({
}: Readonly<ExportProps>) {
// Since this is the final step, could ignore the interaction with App.tsx
const [exportDir, setExportDir] = useState<string>("");
const [customWrap, setCustomWrap] = useState<string>("");

// Set the export directory to input directory if it exists
useEffect(() => {
Expand All @@ -49,6 +59,9 @@ export default function ExportPanel({
GetDefaultOutputDirectory(originalDirectory).then((dir) => {
setExportDir(dir);
});

// Set custom wrap value
setCustomWrap(basename(originalDirectory));
}
}, []);

Expand Down Expand Up @@ -83,8 +96,18 @@ export default function ExportPanel({
promise = ExportCbzWithDefaultWrap(originalDirectory, exportDir, info);
break;

case ExportMethod.CUSTOM_WRAP_CBZ:
if (customWrap === "") {
modalControl.showErr("Custom Wrap folder cannot be empty");
return;
}

promise = ExportCbzWithWrap(originalDirectory, exportDir, customWrap, info);
break;

default:
throw new Error("Unhandled export method");
modalControl.showErr("Unhandled export method");
return;
}

// Start running
Expand Down Expand Up @@ -112,7 +135,7 @@ export default function ExportPanel({
/>

{/* Radio Buttons */}
<div className="w-50 mx-auto d-grid justify-content-center">
<div className="mx-auto ps-9em pe-3em">
<ColoredRadio
id="export-type-cbz"
name="export-type"
Expand All @@ -129,11 +152,28 @@ export default function ExportPanel({
checked={exportMethod === ExportMethod.DEFAULT_WRAP_CBZ}
onChange={() => setExportMethod(ExportMethod.DEFAULT_WRAP_CBZ)}
/>
<ColoredRadio
id="export-type-custom-wrapped"
name="export-type"
color="dark-blue"
label={"Export .cbz wrapped by custom folder"}
checked={exportMethod === ExportMethod.CUSTOM_WRAP_CBZ}
onChange={() => setExportMethod(ExportMethod.CUSTOM_WRAP_CBZ)}
/>

{exportMethod === ExportMethod.CUSTOM_WRAP_CBZ && (
<Form.Control
className="ms-1-5em"
type="text"
value={customWrap}
onChange={(e) => setCustomWrap(e.currentTarget.value)}
/>
)}
</div>

{/* Button to Export. Use d-grid to create block button, use w-25 to smaller size. */}
<div className="w-25 mx-auto d-grid gap-2 mt-4">
<Button variant="success" id="btn-export" onClick={() => handleExportCbz()}>
<div className="mt-4">
<Button variant="success" id="btn-export" className="w-25" onClick={() => handleExportCbz()}>
Export
</Button>
</div>
Expand Down
2 changes: 2 additions & 0 deletions frontend/wailsjs/go/application/App.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export function ExportCbzOnly(arg1:string,arg2:string,arg3:comicinfo.ComicInfo):

export function ExportCbzWithDefaultWrap(arg1:string,arg2:string,arg3:comicinfo.ComicInfo):Promise<string>;

export function ExportCbzWithWrap(arg1:string,arg2:string,arg3:string,arg4:comicinfo.ComicInfo):Promise<string>;

export function ExportXml(arg1:string,arg2:comicinfo.ComicInfo):Promise<string>;

export function GetAllGenreInput():Promise<application.HistoryResp>;
Expand Down
4 changes: 4 additions & 0 deletions frontend/wailsjs/go/application/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export function ExportCbzWithDefaultWrap(arg1, arg2, arg3) {
return window['go']['application']['App']['ExportCbzWithDefaultWrap'](arg1, arg2, arg3);
}

export function ExportCbzWithWrap(arg1, arg2, arg3, arg4) {
return window['go']['application']['App']['ExportCbzWithWrap'](arg1, arg2, arg3, arg4);
}

export function ExportXml(arg1, arg2) {
return window['go']['application']['App']['ExportXml'](arg1, arg2);
}
Expand Down
11 changes: 11 additions & 0 deletions internal/application/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ func (a *App) ExportCbzWithDefaultWrap(inputDir string, exportDir string, c *com
return a.exportCbz(inputDir, exportDir, c, archive.UseDefaultWrap())
}

// Export the .cbz (contains images & comicInfo) file to destination,
// wrapped with folder name specified by user.
//
// If the process success, then function will output empty string.
// Otherwise, function will return the reason for error.
//
// Both input directory and output directory MUST be absolute paths.
func (a *App) ExportCbzWithWrap(inputDir string, exportDir string, wrapFolder string, c *comicinfo.ComicInfo) (errMsg string) {
return a.exportCbz(inputDir, exportDir, c, archive.UseCustomWrap(wrapFolder))
}

// Core function to export a .cbz file with comicinfo file.
func (a *App) exportCbz(inputDir string, exportDir string, c *comicinfo.ComicInfo, opt archive.RenameOption) (errMsg string) {
// Check parameters first
Expand Down
27 changes: 21 additions & 6 deletions internal/archive/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ type RenameOption renameOption

// Unexported type, option for rename .cbz files.
type renameOption struct {
isWrap bool // Wrap .cbz file with a folder if true
isDefaultWrap bool // Use cbz filename as wrap folder name if true
isWrap bool // Wrap .cbz file with a folder if true
isDefaultWrap bool // Use cbz filename as wrap folder name if true
customWrapFolder string // custom wrap folder name
}

// Use default wrap option, which use .cbz filename as wrap folder.
Expand All @@ -25,16 +26,27 @@ type renameOption struct {
// {bookName}/{bookName}.cbz
func UseDefaultWrap() RenameOption {
return RenameOption{
isWrap: true,
isDefaultWrap: true,
isWrap: true,
isDefaultWrap: true,
customWrapFolder: "",
}
}

// Custom wrap folder name, which allow different with .cbz filename.
func UseCustomWrap(folder string) RenameOption {
return RenameOption{
isWrap: true,
isDefaultWrap: false,
customWrapFolder: folder,
}
}

// Not use any wrap method, only single .cbz file will be created.
func NoWrap() RenameOption {
return RenameOption{
isWrap: false,
isDefaultWrap: false,
isWrap: false,
isDefaultWrap: false,
customWrapFolder: "",
}
}

Expand All @@ -44,6 +56,7 @@ func NoWrap() RenameOption {
//
// RenameZip(absPath, NoWrap()) // No Wrap method
// RenameZip(absPath, UseDefaultWrap()) // Default wrap with .cbz filename
// RenameZip(absPath, UseCustomWrap("someFolder")) // Wrap with custom folder name
func RenameZip(absPath string, opt RenameOption) error {
originalDir := filepath.Dir(absPath)
originalFile := filepath.Base(absPath)
Expand All @@ -58,6 +71,8 @@ func RenameZip(absPath string, opt RenameOption) error {
var wrappedDir string
if opt.isDefaultWrap {
wrappedDir = filepath.Join(originalDir, name)
} else {
wrappedDir = filepath.Join(originalDir, opt.customWrapFolder)
}

err := os.Mkdir(wrappedDir, 0755)
Expand Down
29 changes: 29 additions & 0 deletions internal/archive/rename_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,35 @@ const (
_testCbz = "hello.cbz"
)

// Test Rename Zip archive to .cbz archive, with custom wrap option is enabled.
func TestRenameZipCustomWrap(t *testing.T) {
// Create a temp directory
tempDir := t.TempDir()

// Create a folder inside temp directory
os.MkdirAll(filepath.Join(tempDir, "tmp"), 0755)

// Prepare zip path
zipPath := filepath.Join(tempDir, "tmp", _testZip)

// Create a zip file
file1, _ := os.Create(zipPath)
file1.Close()

// Test Function
err := RenameZip(zipPath, UseCustomWrap("abc"))
if err != nil {
t.Error(err)
}

// Result Verify
dest, openErr := os.Open(filepath.Join(tempDir, "tmp", "abc", _testCbz))
if openErr != nil {
t.Error(openErr)
}
defer dest.Close()
}

// Test Rename Zip archive to .cbz archive, with default wrap option is enabled.
func TestRenameZipWrap(t *testing.T) {
// Create a temp directory
Expand Down
Binary file modified screenshots/export.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/export_custom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading