Add everything
This commit is contained in:
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module cbr2cbz
|
||||
|
||||
go 1.25.3
|
||||
|
||||
require github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
|
||||
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
257
main.go
Normal file
257
main.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/nwaples/rardecode"
|
||||
)
|
||||
|
||||
type ConversionStats struct {
|
||||
Successful int
|
||||
Skipped int
|
||||
Failed int
|
||||
Total int
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("CBR to CBZ Converter")
|
||||
fmt.Println(strings.Repeat("=", 60))
|
||||
|
||||
// Get current directory
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting current directory: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Find all .cbr files
|
||||
cbrFiles, err := filepath.Glob(filepath.Join(currentDir, "*.cbr"))
|
||||
if err != nil {
|
||||
fmt.Printf("Error searching for .cbr files: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(cbrFiles) == 0 {
|
||||
fmt.Println("No .cbr files found in current directory.")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d CBR file(s) to convert\n", len(cbrFiles))
|
||||
fmt.Println(strings.Repeat("=", 60))
|
||||
|
||||
// Process each file
|
||||
stats := ConversionStats{Total: len(cbrFiles)}
|
||||
|
||||
for _, cbrPath := range cbrFiles {
|
||||
result := convertCBRtoCBZ(cbrPath)
|
||||
switch result {
|
||||
case "success":
|
||||
stats.Successful++
|
||||
case "skipped":
|
||||
stats.Skipped++
|
||||
case "failed":
|
||||
stats.Failed++
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary
|
||||
fmt.Println()
|
||||
fmt.Println(strings.Repeat("=", 60))
|
||||
fmt.Println("Conversion Summary:")
|
||||
fmt.Printf(" ✓ Successful: %d\n", stats.Successful)
|
||||
fmt.Printf(" ⚠ Skipped: %d\n", stats.Skipped)
|
||||
fmt.Printf(" ✗ Failed: %d\n", stats.Failed)
|
||||
fmt.Printf(" Total: %d\n", stats.Total)
|
||||
|
||||
// Ask about deletion
|
||||
if stats.Successful > 0 {
|
||||
fmt.Println()
|
||||
fmt.Println(strings.Repeat("=", 60))
|
||||
fmt.Print("Delete original .cbr files? (yes/no): ")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
response, _ := reader.ReadString('\n')
|
||||
response = strings.TrimSpace(strings.ToLower(response))
|
||||
|
||||
if response == "yes" || response == "y" {
|
||||
fmt.Println("\nDeleting original files...")
|
||||
for _, cbrPath := range cbrFiles {
|
||||
cbzPath := strings.TrimSuffix(cbrPath, ".cbr") + ".cbz"
|
||||
if _, err := os.Stat(cbzPath); err == nil {
|
||||
if err := os.Remove(cbrPath); err != nil {
|
||||
fmt.Printf(" ✗ Failed to delete %s: %v\n", filepath.Base(cbrPath), err)
|
||||
} else {
|
||||
fmt.Printf(" ✓ Deleted: %s\n", filepath.Base(cbrPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("\nDone!")
|
||||
} else {
|
||||
fmt.Println("\nOriginal files kept.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func convertCBRtoCBZ(cbrPath string) string {
|
||||
cbzPath := strings.TrimSuffix(cbrPath, ".cbr") + ".cbz"
|
||||
baseName := filepath.Base(cbrPath)
|
||||
|
||||
fmt.Printf("\nProcessing: %s\n", baseName)
|
||||
|
||||
// Check if output file already exists
|
||||
if _, err := os.Stat(cbzPath); err == nil {
|
||||
fmt.Printf(" ⚠ Skipping: %s already exists\n", filepath.Base(cbzPath))
|
||||
return "skipped"
|
||||
}
|
||||
|
||||
// Create temporary directory
|
||||
tempDir, err := os.MkdirTemp("", "cbr2cbz-*")
|
||||
if err != nil {
|
||||
fmt.Printf(" ✗ Error creating temp directory: %v\n", err)
|
||||
return "failed"
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Extract RAR file
|
||||
fmt.Println(" → Extracting...")
|
||||
extractedFiles, err := extractRAR(cbrPath, tempDir)
|
||||
if err != nil {
|
||||
fmt.Printf(" ✗ Error extracting: %v\n", err)
|
||||
return "failed"
|
||||
}
|
||||
|
||||
if len(extractedFiles) == 0 {
|
||||
fmt.Println(" ✗ Error: No files found in archive")
|
||||
return "failed"
|
||||
}
|
||||
|
||||
fmt.Printf(" → Creating CBZ with %d files...\n", len(extractedFiles))
|
||||
|
||||
// Create CBZ file
|
||||
err = createCBZ(cbzPath, tempDir, extractedFiles)
|
||||
if err != nil {
|
||||
fmt.Printf(" ✗ Error creating CBZ: %v\n", err)
|
||||
os.Remove(cbzPath)
|
||||
return "failed"
|
||||
}
|
||||
|
||||
// Get file sizes
|
||||
originalInfo, _ := os.Stat(cbrPath)
|
||||
newInfo, _ := os.Stat(cbzPath)
|
||||
|
||||
originalSize := float64(originalInfo.Size()) / 1024 / 1024
|
||||
newSize := float64(newInfo.Size()) / 1024 / 1024
|
||||
ratio := (1 - float64(newInfo.Size())/float64(originalInfo.Size())) * 100
|
||||
|
||||
fmt.Printf(" ✓ Created: %s\n", filepath.Base(cbzPath))
|
||||
fmt.Printf(" Original: %.2f MB\n", originalSize)
|
||||
fmt.Printf(" New: %.2f MB\n", newSize)
|
||||
fmt.Printf(" Change: %+.1f%%\n", ratio)
|
||||
|
||||
return "success"
|
||||
}
|
||||
|
||||
func extractRAR(rarPath, destDir string) ([]string, error) {
|
||||
var extractedFiles []string
|
||||
|
||||
// Open RAR file
|
||||
r, err := rardecode.OpenReader(rarPath, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
// Extract each file
|
||||
for {
|
||||
header, err := r.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Skip directories and hidden files
|
||||
if header.IsDir || strings.HasPrefix(filepath.Base(header.Name), ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create destination path
|
||||
destPath := filepath.Join(destDir, header.Name)
|
||||
|
||||
// Create directories if needed
|
||||
if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create file
|
||||
outFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Copy content
|
||||
_, err = io.Copy(outFile, r)
|
||||
outFile.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extractedFiles = append(extractedFiles, header.Name)
|
||||
}
|
||||
|
||||
return extractedFiles, nil
|
||||
}
|
||||
|
||||
func createCBZ(cbzPath, sourceDir string, files []string) error {
|
||||
// Create ZIP file with best compression
|
||||
outFile, err := os.Create(cbzPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
zipWriter := zip.NewWriter(outFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// Sort files for consistent ordering
|
||||
sort.Strings(files)
|
||||
|
||||
// Add each file to ZIP
|
||||
for _, file := range files {
|
||||
// Create ZIP entry with best compression
|
||||
header := &zip.FileHeader{
|
||||
Name: file,
|
||||
Method: zip.Deflate,
|
||||
}
|
||||
header.SetMode(0644)
|
||||
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Open source file
|
||||
sourcePath := filepath.Join(sourceDir, file)
|
||||
sourceFile, err := os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy to ZIP
|
||||
_, err = io.Copy(writer, sourceFile)
|
||||
sourceFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user