Compare commits
10 Commits
5169ccc171
...
971d7d41e0
Author | SHA1 | Date | |
---|---|---|---|
971d7d41e0 | |||
777d0766ca | |||
de95e6bebb | |||
2c12c2d6ea | |||
a0e2581f6b | |||
61b1dadb11 | |||
9af373fdab | |||
db2d236d17 | |||
7ef4853c40 | |||
9adeac0a37 |
6
arch.go
6
arch.go
@ -48,7 +48,8 @@ func Compress(archPath, contentDir string) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
log.Printf("compressing %s...", archPath)
|
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)
|
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
|
toolCmd.Dir = contentDir
|
||||||
|
|
||||||
log.Printf("decompressing %s...", archPath)
|
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)
|
return "", errors.Join(fmt.Errorf("decompression of %s failed", archPath), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
154
media.go
154
media.go
@ -2,19 +2,39 @@ package mex
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"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 {
|
func isImagePath(path string) bool {
|
||||||
switch strings.ToLower(filepath.Ext(path)) {
|
switch strings.ToLower(filepath.Ext(path)) {
|
||||||
case ".jpg", ".jpeg", ".png", ".webp", ".bmp", ".gif":
|
case ".jpg", ".jpeg", ".png", ".webp", ".bmp", ".gif":
|
||||||
@ -92,23 +112,13 @@ type Volume struct {
|
|||||||
Book *Book
|
Book *Book
|
||||||
Pages []*Page
|
Pages []*Page
|
||||||
Index int
|
Index int
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Volume) AveragePageSize() int {
|
avgSize int
|
||||||
if len(self.Pages) == 0 {
|
hash []byte
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalSize int
|
|
||||||
for _, page := range self.Pages {
|
|
||||||
totalSize += int(page.Node.Info.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalSize / len(self.Pages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Volume) export(path string, config ExportConfig, allocator *TempDirAllocator) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -145,24 +155,28 @@ func (self *Volume) export(path string, config ExportConfig, allocator *TempDirA
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Volume) supercedes(other *Volume) bool {
|
func (self *Volume) compare(other *Volume) int {
|
||||||
if len(self.Pages) > len(other.Pages) {
|
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 1
|
||||||
return true
|
} else if len(self.Pages) < len(other.Pages) {
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.AveragePageSize() > other.AveragePageSize() {
|
if self.avgSize > other.avgSize {
|
||||||
log.Printf("picking %s over %s because it has larger pages", self.Node.Name, other.Node.Name)
|
return 1
|
||||||
return true
|
} else if self.avgSize < other.avgSize {
|
||||||
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return bytes.Compare(self.hash, other.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Book struct {
|
type Book struct {
|
||||||
Node *Node
|
Node *Node
|
||||||
Volumes map[int]*Volume
|
Volumes map[int]*Volume
|
||||||
MaxVolume int
|
VolumeCount int
|
||||||
|
|
||||||
|
orphans []*Volume
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Book) Export(path string, config ExportConfig, allocator *TempDirAllocator) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Book) addVolume(volume *Volume) {
|
func (self *Book) addVolume(newVolume *Volume) {
|
||||||
currVolume, _ := self.Volumes[volume.Index]
|
insert := func(v *Volume) {
|
||||||
if currVolume == nil || volume.supercedes(currVolume) {
|
self.Volumes[v.Index] = v
|
||||||
self.Volumes[volume.Index] = volume
|
if v.Index >= self.VolumeCount {
|
||||||
|
self.VolumeCount = v.Index + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) parseVolumes(node *Node) {
|
func (self *Book) addOrphan(newVolume *Volume) {
|
||||||
if !node.Info.IsDir() {
|
for _, volume := range self.orphans {
|
||||||
|
if volume.compare(newVolume) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.orphans = append(self.orphans, newVolume)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Book) parseVolumes(node *Node) error {
|
||||||
|
if !node.Info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
volume := &Volume{
|
volume := &Volume{
|
||||||
Node: node,
|
Node: node,
|
||||||
@ -250,29 +289,55 @@ func (self *Book) parseVolumes(node *Node) {
|
|||||||
var pageIndex int
|
var pageIndex int
|
||||||
for _, child := range node.Children {
|
for _, child := range node.Children {
|
||||||
if child.Info.IsDir() {
|
if child.Info.IsDir() {
|
||||||
self.parseVolumes(child)
|
if err := self.parseVolumes(child); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else if isImagePath(child.Name) {
|
} else if isImagePath(child.Name) {
|
||||||
volume.Pages = append(volume.Pages, &Page{child, volume, pageIndex})
|
volume.Pages = append(volume.Pages, &Page{child, volume, pageIndex})
|
||||||
pageIndex++
|
pageIndex++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(volume.Pages) > 0 {
|
if len(volume.Pages) == 0 {
|
||||||
exp := regexp.MustCompile(`(\d+)\D*$`)
|
return nil
|
||||||
if matches := exp.FindStringSubmatch(node.Name); len(matches) >= 2 {
|
}
|
||||||
index, err := strconv.ParseInt(matches[1], 10, 32)
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
volume.Index = int(index)
|
size, err := io.Copy(hasher, fp)
|
||||||
if volume.Index > self.MaxVolume {
|
fp.Close()
|
||||||
self.MaxVolume = volume.Index
|
|
||||||
|
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)
|
self.addVolume(volume)
|
||||||
|
} else {
|
||||||
|
self.addOrphan(volume)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseBook(node *Node) (*Book, error) {
|
func ParseBook(node *Node) (*Book, error) {
|
||||||
@ -283,6 +348,19 @@ func ParseBook(node *Node) (*Book, error) {
|
|||||||
|
|
||||||
book.parseVolumes(node)
|
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 {
|
if len(book.Volumes) == 0 {
|
||||||
return nil, errors.New("no volumes found")
|
return nil, errors.New("no volumes found")
|
||||||
}
|
}
|
||||||
|
0
cmd/.gitignore → mex/.gitignore
vendored
0
cmd/.gitignore → mex/.gitignore
vendored
4
regexp.txt
Normal file
4
regexp.txt
Normal 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*$
|
Loading…
Reference in New Issue
Block a user