Compare commits

...

10 Commits

Author SHA1 Message Date
971d7d41e0 Print 7za/unrar output on failure 2023-11-04 19:11:34 -07:00
777d0766ca Update regex 2023-11-03 19:58:39 -07:00
de95e6bebb Bugfixes 2023-11-03 19:25:26 -07:00
2c12c2d6ea Hash pages 2023-11-03 19:03:08 -07:00
a0e2581f6b Cleanup 2023-11-03 18:28:56 -07:00
61b1dadb11 Equality checking 2023-11-03 17:39:33 -07:00
9af373fdab Add additional regexes 2023-11-03 16:58:36 -07:00
db2d236d17 Cleanup 2023-11-02 20:07:16 -07:00
7ef4853c40 Do not discard orphan volumes 2023-11-02 18:03:10 -07:00
9adeac0a37 Rename cmd to mex 2023-11-02 17:14:30 -07:00
6 changed files with 132 additions and 48 deletions

View File

@ -48,7 +48,8 @@ func Compress(archPath, contentDir string) error {
)
log.Printf("compressing %s...", archPath)
if err := toolCmd.Run(); err != nil {
if output, err := toolCmd.CombinedOutput(); err != nil {
fmt.Println(string(output))
return errors.Join(fmt.Errorf("compression of %s failed", archPath), err)
}
@ -91,7 +92,8 @@ func Decompress(archPath string, allocator *TempDirAllocator) (string, error) {
toolCmd.Dir = contentDir
log.Printf("decompressing %s...", archPath)
if err := toolCmd.Run(); err != nil {
if output, err := toolCmd.CombinedOutput(); err != nil {
fmt.Println(string(output))
return "", errors.Join(fmt.Errorf("decompression of %s failed", archPath), err)
}

154
media.go
View File

@ -2,19 +2,39 @@ package mex
import (
"bytes"
"crypto/sha256"
_ "embed"
"errors"
"fmt"
"html/template"
"log"
"io"
"math"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"sync"
)
//go:embed regexp.txt
var volumeExpStrs string
func parseVolumeIndex(path string) *int {
for _, expStr := range strings.Split(volumeExpStrs, "\n") {
exp := regexp.MustCompile(expStr)
if matches := exp.FindStringSubmatch(filepath.Base(path)); len(matches) >= 2 {
if index, err := strconv.ParseInt(matches[1], 10, 32); err == nil {
indexInt := int(index)
return &indexInt
}
}
}
return nil
}
func isImagePath(path string) bool {
switch strings.ToLower(filepath.Ext(path)) {
case ".jpg", ".jpeg", ".png", ".webp", ".bmp", ".gif":
@ -92,23 +112,13 @@ type Volume struct {
Book *Book
Pages []*Page
Index int
}
func (self *Volume) AveragePageSize() int {
if len(self.Pages) == 0 {
return 0
}
var totalSize int
for _, page := range self.Pages {
totalSize += int(page.Node.Info.Size())
}
return totalSize / len(self.Pages)
avgSize int
hash []byte
}
func (self *Volume) export(path string, config ExportConfig, allocator *TempDirAllocator) error {
name, err := buildTemplatedName(config.VolumeTemplate, stripExt(self.Node.Name), self.Index, self.Book.MaxVolume)
name, err := buildTemplatedName(config.VolumeTemplate, stripExt(self.Node.Name), self.Index, self.Book.VolumeCount)
if err != nil {
return err
}
@ -145,24 +155,28 @@ func (self *Volume) export(path string, config ExportConfig, allocator *TempDirA
return nil
}
func (self *Volume) supercedes(other *Volume) bool {
func (self *Volume) compare(other *Volume) int {
if len(self.Pages) > len(other.Pages) {
log.Printf("picking %s over %s because it has more pages", self.Node.Name, other.Node.Name)
return true
return 1
} else if len(self.Pages) < len(other.Pages) {
return -1
}
if self.AveragePageSize() > other.AveragePageSize() {
log.Printf("picking %s over %s because it has larger pages", self.Node.Name, other.Node.Name)
return true
if self.avgSize > other.avgSize {
return 1
} else if self.avgSize < other.avgSize {
return -1
}
return false
return bytes.Compare(self.hash, other.hash)
}
type Book struct {
Node *Node
Volumes map[int]*Volume
MaxVolume int
VolumeCount int
orphans []*Volume
}
func (self *Book) Export(path string, config ExportConfig, allocator *TempDirAllocator) error {
@ -230,17 +244,42 @@ func (self *Book) Export(path string, config ExportConfig, allocator *TempDirAll
return nil
}
func (self *Book) addVolume(volume *Volume) {
currVolume, _ := self.Volumes[volume.Index]
if currVolume == nil || volume.supercedes(currVolume) {
self.Volumes[volume.Index] = volume
func (self *Book) addVolume(newVolume *Volume) {
insert := func(v *Volume) {
self.Volumes[v.Index] = v
if v.Index >= self.VolumeCount {
self.VolumeCount = v.Index + 1
}
}
func (self *Book) parseVolumes(node *Node) {
if !node.Info.IsDir() {
currVolume, _ := self.Volumes[newVolume.Index]
if currVolume == nil {
insert(newVolume)
} else {
switch currVolume.compare(newVolume) {
case 1:
self.addOrphan(newVolume)
case -1:
self.addOrphan(currVolume)
insert(newVolume)
}
}
}
func (self *Book) addOrphan(newVolume *Volume) {
for _, volume := range self.orphans {
if volume.compare(newVolume) == 0 {
return
}
}
self.orphans = append(self.orphans, newVolume)
}
func (self *Book) parseVolumes(node *Node) error {
if !node.Info.IsDir() {
return nil
}
volume := &Volume{
Node: node,
@ -250,29 +289,55 @@ func (self *Book) parseVolumes(node *Node) {
var pageIndex int
for _, child := range node.Children {
if child.Info.IsDir() {
self.parseVolumes(child)
if err := self.parseVolumes(child); err != nil {
return err
}
} else if isImagePath(child.Name) {
volume.Pages = append(volume.Pages, &Page{child, volume, pageIndex})
pageIndex++
}
}
if len(volume.Pages) > 0 {
exp := regexp.MustCompile(`(\d+)\D*$`)
if matches := exp.FindStringSubmatch(node.Name); len(matches) >= 2 {
index, err := strconv.ParseInt(matches[1], 10, 32)
if len(volume.Pages) == 0 {
return nil
}
sort.Slice(volume.Pages, func(i, j int) bool {
return strings.Compare(volume.Pages[i].Node.Name, volume.Pages[j].Node.Name) < 0
})
var (
hasher = sha256.New()
totalSize = 0
)
for _, page := range volume.Pages {
fp, err := os.Open(page.Node.Path)
if err != nil {
panic(err)
return err
}
volume.Index = int(index)
if volume.Index > self.MaxVolume {
self.MaxVolume = volume.Index
size, err := io.Copy(hasher, fp)
fp.Close()
if err != nil {
return err
}
totalSize += int(size)
}
volume.avgSize = totalSize / len(volume.Pages)
volume.hash = hasher.Sum(nil)
if index := parseVolumeIndex(node.Name); index != nil {
volume.Index = *index
self.addVolume(volume)
} else {
self.addOrphan(volume)
}
}
return nil
}
func ParseBook(node *Node) (*Book, error) {
@ -283,6 +348,19 @@ func ParseBook(node *Node) (*Book, error) {
book.parseVolumes(node)
if len(book.orphans) > 0 {
sort.Slice(book.orphans, func(i, j int) bool {
return strings.Compare(book.orphans[i].Node.Name, book.orphans[j].Node.Name) < 0
})
for _, volume := range book.orphans {
volume.Index = book.VolumeCount
book.addVolume(volume)
}
book.orphans = nil
}
if len(book.Volumes) == 0 {
return nil, errors.New("no volumes found")
}

View File

4
regexp.txt Normal file
View File

@ -0,0 +1,4 @@
第\s*(\d+)\s*巻
(?i)vo?l?u?m?e?[.\s]*(\d+)
(?i)cha?p?t?e?r?[.\s]*(\d+)
(\d+)\D*$