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

Allow custom default export directory by config #113

Merged
merged 4 commits into from
Dec 1, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ go.work
build/bin
node_modules
frontend/dist

# Configuration file
config.yaml
2 changes: 2 additions & 0 deletions .vscode/.cspell/package-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Equalf
Infof
innerxml
iofs
knadh
koanf
Komga
lazydb
logrus
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,31 @@ Already contains a `.cbz` archive and `ComicInfo.xml` at `{selected-folder}/{com

User can directly copy exported folder to `komga` comic directory.

## Configuration

This program support some customizations by `.yaml` file configuration.

Your configuration file should like:

```
your-folder/
├─ ComicInfo-Parser.exe
├─ config.yaml
```

If no configuration is found, program will NOT create for yourself. Instead, it will use its default behavior.

You may found a sample of configuration file in `config-example.yaml`.

### Configuration Options

You should use absolute paths as possible. If folder is missing, then program will try to create for all folders.

| Field | Type | Usage |
| ---------------- | ------ | ------------------------------------------------------------------------------- |
| `default` | struct | storing default values for program |
| `default.export` | string | default export folder path, if empty string, then create inside input directory |

## Data

All data will be stored in sqlite3 database, which located at `{Home Directory}/comicInfo-parser/storage.db`.
Expand Down
5 changes: 5 additions & 0 deletions config-example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
default:
export: ./my-export
export:
- ./folder1
- ./folder2
12 changes: 10 additions & 2 deletions frontend/src/pages/exportPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import InputGroup from "react-bootstrap/InputGroup";
import { ModalControl } from "../controls/ModalControl";

// Wails
import { ExportCbz, GetDirectory, GetDirectoryWithDefault } from "../../wailsjs/go/application/App";
import {
ExportCbz,
GetDefaultOutputDirectory,
GetDirectory,
GetDirectoryWithDefault,
} from "../../wailsjs/go/application/App";
import { comicinfo } from "../../wailsjs/go/models";

/** Props Interface for FolderSelect */
Expand All @@ -34,7 +39,10 @@ export default function ExportPanel({ comicInfo: info, originalDirectory, modalC
// Set the export directory to input directory if it exists
useEffect(() => {
if (originalDirectory !== undefined) {
setExportDir(originalDirectory);
// Load config from file
GetDefaultOutputDirectory(originalDirectory).then((dir) => {
setExportDir(dir);
});
}
}, []);

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 @@ -15,6 +15,8 @@ export function GetAllTagInput():Promise<application.HistoryResp>;

export function GetComicInfo(arg1:string):Promise<application.ComicInfoResponse>;

export function GetDefaultOutputDirectory(arg1:string):Promise<string>;

export function GetDirectory():Promise<string>;

export function GetDirectoryWithDefault(arg1:string):Promise<string>;
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 @@ -26,6 +26,10 @@ export function GetComicInfo(arg1) {
return window['go']['application']['App']['GetComicInfo'](arg1);
}

export function GetDefaultOutputDirectory(arg1) {
return window['go']['application']['App']['GetDefaultOutputDirectory'](arg1);
}

export function GetDirectory() {
return window['go']['application']['App']['GetDirectory']();
}
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ go 1.23.1

require (
github.com/dark-person/lazydb v0.1.6
github.com/knadh/koanf/parsers/yaml v0.1.0
github.com/knadh/koanf/providers/file v1.1.2
github.com/knadh/koanf/v2 v2.1.2
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/wailsapp/wails/v2 v2.9.2
Expand All @@ -12,13 +15,16 @@ require (
require (
github.com/bep/debounce v1.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang-migrate/migrate/v4 v4.18.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/labstack/echo/v4 v4.11.4 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
Expand All @@ -28,6 +34,8 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ github.com/dark-person/lazydb v0.1.6/go.mod h1:5YakMfYNfg78SRqT9nW+QqpBvE7Q4VOHi
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y=
Expand All @@ -20,6 +24,14 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
Expand All @@ -46,6 +58,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down
5 changes: 4 additions & 1 deletion internal/application/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"os"

"github.com/dark-person/comicinfo-parser/internal/assets"
"github.com/dark-person/comicinfo-parser/internal/config"
"github.com/dark-person/lazydb"
"github.com/wailsapp/wails/v2/pkg/runtime"
)
Expand All @@ -13,11 +15,12 @@ import (
type App struct {
DB *lazydb.LazyDB
ctx context.Context
cfg *config.ProgramConfig
}

// NewApp creates a new App application struct
func NewApp(db *lazydb.LazyDB) *App {
return &App{DB: db}
return &App{DB: db, cfg: assets.Config()}
}

// startup is called when the app starts. The context is saved
Expand Down
10 changes: 10 additions & 0 deletions internal/application/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,13 @@ func (a *App) GetDirectoryWithDefault(defaultDirectory string) string {
}
return directory
}

// Attempt to load default output directory.
// If no default directory is set, then return input directory instead.
func (a *App) GetDefaultOutputDirectory(inputDir string) string {
if a.cfg.DefaultExport == "" {
return inputDir
}

return a.cfg.DefaultExport
}
52 changes: 52 additions & 0 deletions internal/application/directory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package application

import (
"path/filepath"
"testing"

"github.com/dark-person/comicinfo-parser/internal/config"
"github.com/stretchr/testify/assert"
)

func TestGetDefaultOutputDirectory(t *testing.T) {
type testCase struct {
configExportDir string // Default export directory for config files
inputDir string // Parameter for function
want string // Expected result
}

absPath1, err := filepath.Abs("/absPath/")
if err != nil {
t.Errorf("Failed to get absolute path")
}

absPath2, err := filepath.Abs("relatedPath")
if err != nil {
t.Errorf("Failed to get absolute path")
}

// Create a dummy APP
a := &App{}

// Start test
tests := []testCase{
{"/absPath/", "inputDir", absPath1},
{"relatedPath", "inputDir", absPath2},
{"", "inputDir", "inputDir"},
}

for idx, tt := range tests {
// Reset config
a.cfg = config.Default()

// Plug exported directory to dummy app
if tt.configExportDir != "" {
a.cfg.DefaultExport, _ = filepath.Abs(tt.configExportDir)
}

// Run function
result := a.GetDefaultOutputDirectory(tt.inputDir)

assert.EqualValuesf(t, tt.want, result, "Unexpected directory in case %d", idx)
}
}
28 changes: 28 additions & 0 deletions internal/assets/configs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package assets

import (
"fmt"

"github.com/dark-person/comicinfo-parser/internal/config"
)

var cfg *config.ProgramConfig

// Load config from yaml file.
// If any error occur in loading, then a default config will be returned, and no error return.
func Config() *config.ProgramConfig {
// Return parsed config if any
if cfg != nil {
return cfg
}

// Load Config from filesystem
c, err := config.LoadYaml("config.yaml")
if err != nil {
fmt.Println(err)
}

// Set loaded config to package
cfg = c
return cfg
}
55 changes: 55 additions & 0 deletions internal/config/load.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package config

import (
"fmt"
"os"
"path/filepath"

"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/v2"
)

// Load yaml config from given path,
// while no koanf instance will preserved (i.e. every call overwrite previous call).
//
// If failed to load config, then a default config will be returned.
func LoadYaml(path string) (*ProgramConfig, error) {
var k = koanf.New(".")

// Check if file exist
if _, err := os.Stat(path); err != nil {
return Default(), fmt.Errorf("path %s does not exist", path)
}

// Start Load file
err := k.Load(file.Provider(path), yaml.Parser())
if err != nil {
return Default(), err
}

// Unmarshal to struct
var out ProgramConfig
err = k.UnmarshalWithConf("", &out, koanf.UnmarshalConf{Tag: "koanf", FlatPaths: true})
if err != nil {
return Default(), err
}

// Parse path due to relative path issue
out.DefaultExport, err = parsePath(out.DefaultExport)
if err != nil {
return Default(), err
}

return &out, nil
}

// Convert relative path to absolute path.
// If path passed is empty string, then it perform nothing.
func parsePath(relativePath string) (absPath string, err error) {
if relativePath == "" {
return "", nil
}

return filepath.Abs(relativePath)
}
46 changes: 46 additions & 0 deletions internal/config/load_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package config

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
)

func TestLoadYaml(t *testing.T) {
type testCase struct {
path string
want *ProgramConfig
wantErr bool
}

exPath, err := os.Getwd()
if err != nil {
panic(err)
}
t.Log(exPath)

tests := []testCase{
{"mock/case-normal.yaml", &ProgramConfig{DefaultExport: filepath.Join(exPath, "./my-export")}, false},
{"mock/case-typo1.yaml", &ProgramConfig{DefaultExport: ""}, false},
{"mock/case-typo2.yaml", &ProgramConfig{DefaultExport: ""}, false},
{"mock/not-exist.yaml", nil, true},
}

for idx, tt := range tests {
// Load YAML file and check result
c, err := LoadYaml(tt.path)

if tt.wantErr {
assert.NotNilf(t, err, "Error should be returned in case %d, but return nil", idx)

// Ensure default is returned
assert.EqualValuesf(t, Default(), c, "Incorrect values in case %d, should be default config", idx)

} else {
assert.EqualValuesf(t, tt.want, c, "Incorrect values in case %d", idx)
assert.Nilf(t, err, "Unexpected error in case %d", idx)
}
}
}
Loading