From 133ca1fdaac8645aa375305a4309d54f4c704c83 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Wed, 22 Apr 2026 21:18:53 -0400 Subject: [PATCH] fix(scanner): extract metadata and covers for comic archives and kepub files Comic archive formats (.cbz, .cbr, .cb7, .cbt) and .kepub files were falling through to the default case in extractMetadata(), which only set the title from the filename. This meant ComicInfo.xml was never parsed and no cover images were extracted for comics without a Calibre metadata.opf sidecar file. The fix adds dedicated switch cases: - .cbz/.cbr/.cb7/.cbt: calls mergeMetadata() with nil, which triggers existing ComicInfo.xml parsing (title, series, issue number, writer, publisher, genre, reading direction, etc.) and cover image extraction from the archive. Falls back to sidecar cover if no image is found. - .kepub: treated the same as .epub since KEPUB is an EPUB variant, enabling full metadata and cover extraction. --- internal/services/media_scanner.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/services/media_scanner.go b/internal/services/media_scanner.go index 6755624..367bfdc 100644 --- a/internal/services/media_scanner.go +++ b/internal/services/media_scanner.go @@ -1077,7 +1077,7 @@ func (s *MediaScanner) extractMetadata(path string) (*MediaMetadata, error) { ext := strings.ToLower(filepath.Ext(path)) switch ext { - case ".epub": + case ".epub", ".kepub": metadata := &MediaMetadata{} result, err := s.extractEPUBMetadata(path) if err == nil { @@ -1113,6 +1113,24 @@ func (s *MediaScanner) extractMetadata(path string) (*MediaMetadata, error) { return metadata, nil case ".pdf": return s.extractPDFMetadata(path) + case ".cbz", ".cbr", ".cb7", ".cbt": + metadata, err := s.mergeMetadata(path, nil) + if err != nil { + return &MediaMetadata{ + Title: strings.TrimSuffix(filepath.Base(path), ext), + }, nil + } + if metadata.Title == "" { + metadata.Title = strings.TrimSuffix(filepath.Base(path), ext) + } + // If no cover from archive, try sidecar + if metadata.CoverPath == "" { + sidecarCover := findSidecarCover(path) + if sidecarCover != "" { + metadata.CoverPath = s.getRelativePath(sidecarCover) + } + } + return metadata, nil default: // For other formats, return basic metadata return &MediaMetadata{