refactor(scanner): merge add/update into ingest, use shared utilities

- add/update consolidated into ingest(isNew) — persist branch differs,
  error handling and cover path fully shared (Task 24)
- zipIndex/cover use bookfile.OpenReaderAt instead of hand-rolled
  open+stat pairs
- root containment check uses bookfile.Contains (local inside removed)
- cover write goes through media.WriteAtomic (now exported, log-free —
  callers own context); media no longer logs inside the atomic helper
This commit is contained in:
2026-09-14 22:12:30 +08:00
parent fbd5cd243d
commit b9b7022212
2 changed files with 38 additions and 86 deletions
+6 -7
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
@@ -54,7 +53,7 @@ func (m *M) EnsureCover(ctx context.Context, bookID int64, format string, size,
return err
}
dir := bookfile.CoverDir(m.cfg.CacheDir, bookfile.DirKey(bookID, bookfile.Hash(size, modTS)))
return writeAtomic(dir, "cover"+ext, img)
return WriteAtomic(dir, "cover"+ext, img)
}
// PageIndex returns the page list for a CBZ, using redis cache when available.
@@ -141,26 +140,26 @@ func (m *M) EnsurePage(bookID int64, size, modTS int64, root, rel string, n int,
if err != nil {
return "", err
}
if err := writeAtomic(dir, strconv.Itoa(n)+ext, data); err != nil {
if err := WriteAtomic(dir, strconv.Itoa(n)+ext, data); err != nil {
return "", err
}
return dst, nil
}
// writeAtomic writes data to dir/name via tmp+rename. B11: cleans tmp only on failure.
func writeAtomic(dir, name string, data []byte) error {
// WriteAtomic writes data to dir/name via tmp+rename. B11: cleans tmp only on
// failure, so a crash mid-write never leaves a readable half-written file.
// It does not log — callers own the context (which book, which page).
func WriteAtomic(dir, name string, data []byte) error {
if err := os.MkdirAll(dir, 0o755); err != nil {
return err
}
tmp := filepath.Join(dir, fmt.Sprintf("%s.tmp-%d", name, time.Now().UnixNano()))
if err := os.WriteFile(tmp, data, 0o644); err != nil {
log.Printf("media: write %s: %v", name, err)
os.Remove(tmp)
return err
}
dst := filepath.Join(dir, name)
if err := os.Rename(tmp, dst); err != nil {
log.Printf("media: rename %s: %v", name, err)
os.Remove(tmp)
return err
}