mirror of
https://github.com/Belphemur/CBZOptimizer.git
synced 2025-10-14 12:38:50 +02:00
feat: init with converting cbz files
This commit is contained in:
61
cbz/cbz_creator.go
Normal file
61
cbz/cbz_creator.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package cbz
|
||||
|
||||
import (
|
||||
"CBZOptimizer/packer"
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func WriteChapterToCBZ(chapter *packer.Chapter, outputFilePath string) error {
|
||||
// Create a new ZIP file
|
||||
zipFile, err := os.Create(outputFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create .cbz file: %w", err)
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
// Create a new ZIP writer
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// Write each page to the ZIP archive
|
||||
for _, page := range chapter.Pages {
|
||||
// Construct the file name for the page
|
||||
var fileName string
|
||||
if page.IsSplitted {
|
||||
// Use the format page%03d-%02d for split pages
|
||||
fileName = fmt.Sprintf("page_%03d-%02d%s", page.Index, page.SplitPartIndex, page.Extension)
|
||||
} else {
|
||||
// Use the format page%03d for non-split pages
|
||||
fileName = fmt.Sprintf("page_%03d%s", page.Index, page.Extension)
|
||||
}
|
||||
|
||||
// Create a new file in the ZIP archive
|
||||
fileWriter, err := zipWriter.Create(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file in .cbz: %w", err)
|
||||
}
|
||||
|
||||
// Write the page contents to the file
|
||||
_, err = fileWriter.Write(page.Contents.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write page contents: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally, write the ComicInfo.xml file if present
|
||||
if chapter.ComicInfoXml != "" {
|
||||
comicInfoWriter, err := zipWriter.Create("ComicInfo.xml")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create ComicInfo.xml in .cbz: %w", err)
|
||||
}
|
||||
|
||||
_, err = comicInfoWriter.Write([]byte(chapter.ComicInfoXml))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write ComicInfo.xml contents: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
115
cbz/cbz_creator_test.go
Normal file
115
cbz/cbz_creator_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package cbz
|
||||
|
||||
import (
|
||||
"CBZOptimizer/packer"
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWriteChapterToCBZ(t *testing.T) {
|
||||
// Define test cases
|
||||
testCases := []struct {
|
||||
name string
|
||||
chapter *packer.Chapter
|
||||
expectedFiles []string
|
||||
}{
|
||||
{
|
||||
name: "Single page, no ComicInfo",
|
||||
chapter: &packer.Chapter{
|
||||
Pages: []*packer.Page{
|
||||
{
|
||||
Index: 0,
|
||||
Extension: ".jpg",
|
||||
Contents: bytes.NewBuffer([]byte("image data")),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedFiles: []string{"page_000.jpg"},
|
||||
},
|
||||
{
|
||||
name: "Multiple pages with ComicInfo",
|
||||
chapter: &packer.Chapter{
|
||||
Pages: []*packer.Page{
|
||||
{
|
||||
Index: 0,
|
||||
Extension: ".jpg",
|
||||
Contents: bytes.NewBuffer([]byte("image data 1")),
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Extension: ".jpg",
|
||||
Contents: bytes.NewBuffer([]byte("image data 2")),
|
||||
},
|
||||
},
|
||||
ComicInfoXml: "<Series>Boundless Necromancer</Series>",
|
||||
},
|
||||
expectedFiles: []string{"page_000.jpg", "page_001.jpg", "ComicInfo.xml"},
|
||||
},
|
||||
{
|
||||
name: "Split page",
|
||||
chapter: &packer.Chapter{
|
||||
Pages: []*packer.Page{
|
||||
{
|
||||
Index: 0,
|
||||
Extension: ".jpg",
|
||||
Contents: bytes.NewBuffer([]byte("split image data")),
|
||||
IsSplitted: true,
|
||||
SplitPartIndex: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedFiles: []string{"page_000-01.jpg"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create a temporary file for the .cbz output
|
||||
tempFile, err := os.CreateTemp("", "*.cbz")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temporary file: %v", err)
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
// Write the chapter to the .cbz file
|
||||
err = WriteChapterToCBZ(tc.chapter, tempFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write chapter to CBZ: %v", err)
|
||||
}
|
||||
|
||||
// Open the .cbz file as a zip archive
|
||||
r, err := zip.OpenReader(tempFile.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open CBZ file: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// Collect the names of the files in the archive
|
||||
var filesInArchive []string
|
||||
for _, f := range r.File {
|
||||
filesInArchive = append(filesInArchive, f.Name)
|
||||
}
|
||||
|
||||
// Check if all expected files are present
|
||||
for _, expectedFile := range tc.expectedFiles {
|
||||
found := false
|
||||
for _, actualFile := range filesInArchive {
|
||||
if actualFile == expectedFile {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file %s not found in archive", expectedFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are no unexpected files
|
||||
if len(filesInArchive) != len(tc.expectedFiles) {
|
||||
t.Errorf("Expected %d files, but found %d", len(tc.expectedFiles), len(filesInArchive))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
71
cbz/cbz_loader.go
Normal file
71
cbz/cbz_loader.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package cbz
|
||||
|
||||
import (
|
||||
"CBZOptimizer/packer"
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func LoadChapter(filePath string) (*packer.Chapter, error) {
|
||||
// Open the .cbz file
|
||||
r, err := zip.OpenReader(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open .cbz file: %w", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
chapter := &packer.Chapter{
|
||||
FilePath: filePath,
|
||||
}
|
||||
|
||||
for _, f := range r.File {
|
||||
if !f.FileInfo().IsDir() {
|
||||
// Open the file inside the zip
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open file inside .cbz: %w", err)
|
||||
}
|
||||
|
||||
// Determine the file extension
|
||||
ext := strings.ToLower(filepath.Ext(f.Name))
|
||||
|
||||
if ext == ".xml" && strings.ToLower(filepath.Base(f.Name)) == "comicinfo.xml" {
|
||||
// Read the ComicInfo.xml file content
|
||||
xmlContent, err := ioutil.ReadAll(rc)
|
||||
if err != nil {
|
||||
rc.Close()
|
||||
return nil, fmt.Errorf("failed to read ComicInfo.xml content: %w", err)
|
||||
}
|
||||
chapter.ComicInfoXml = string(xmlContent)
|
||||
} else {
|
||||
// Read the file contents for page
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.Copy(buf, rc)
|
||||
if err != nil {
|
||||
rc.Close()
|
||||
return nil, fmt.Errorf("failed to read file contents: %w", err)
|
||||
}
|
||||
|
||||
// Create a new Page object
|
||||
page := &packer.Page{
|
||||
Index: uint16(len(chapter.Pages)), // Simple index based on order
|
||||
Extension: ext,
|
||||
Size: uint64(buf.Len()),
|
||||
Contents: buf,
|
||||
IsSplitted: false,
|
||||
}
|
||||
|
||||
// Add the page to the chapter
|
||||
chapter.Pages = append(chapter.Pages, page)
|
||||
}
|
||||
rc.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return chapter, nil
|
||||
}
|
30
cbz/cbz_loader_test.go
Normal file
30
cbz/cbz_loader_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package cbz
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadChapter(t *testing.T) {
|
||||
// Define the path to the .cbz file
|
||||
chapterFilePath := "../testdata/Chapter 1.cbz"
|
||||
|
||||
// Load the chapter
|
||||
chapter, err := LoadChapter(chapterFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load chapter: %v", err)
|
||||
}
|
||||
|
||||
// Check the number of pages
|
||||
expectedPages := 16
|
||||
actualPages := len(chapter.Pages)
|
||||
if actualPages != expectedPages {
|
||||
t.Errorf("Expected %d pages, but got %d", expectedPages, actualPages)
|
||||
}
|
||||
|
||||
// Check if ComicInfoXml contains the expected series name
|
||||
expectedSeries := "<Series>Boundless Necromancer</Series>"
|
||||
if !strings.Contains(chapter.ComicInfoXml, expectedSeries) {
|
||||
t.Errorf("ComicInfoXml does not contain the expected series: %s", expectedSeries)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user