From ad6f2ae6f6aa14926ef63a7fe124093dcceb024b Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 6 May 2026 08:09:30 +0300 Subject: fix(scanner, browse): fallback to original image when thumbnail generation fails for cover.jpg When scanning image files (e.g. cover.jpg in audiobook folders), thumbnailForImage can fail or produce a missing .thumbnails/cover.jpg. This caused broken thumbnails in the grid. - scanner.go buildThumbnailPath: after generating an image thumbnail, verify the output file exists via fs.Stat. If it does not exist, fall back to using the original image path as the thumbnail_path. - browse.go GetThumbnail: if os.Stat(ThumbnailPath) fails and the media type is image, fall back to serving the original AbsPath. All tests pass. --- internal/scanner/scanner.go | 11 ++++++++++- internal/service/browse.go | 26 +++++++++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) (limited to 'internal') diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go index 30b20d1..34e0ca5 100644 --- a/internal/scanner/scanner.go +++ b/internal/scanner/scanner.go @@ -202,7 +202,16 @@ func (s *FSScanner) buildThumbnailPath(ctx context.Context, path, setPath string if ext == ".svg" { return path, nil } - return s.thumbnailForImage(ctx, path, setPath) + thumbPath, err := s.thumbnailForImage(ctx, path, setPath) + if err != nil { + return "", err + } + if thumbPath != "" { + if _, statErr := s.fs.Stat(thumbPath); statErr == nil { + return thumbPath, nil + } + } + return path, nil } return "", nil } diff --git a/internal/service/browse.go b/internal/service/browse.go index b420d70..0df399b 100644 --- a/internal/service/browse.go +++ b/internal/service/browse.go @@ -183,14 +183,26 @@ func (s *browseService) GetThumbnail(ctx context.Context, mediaID, userID int64) return nil, errors.New("thumbnail not found") } info, err := os.Stat(media.ThumbnailPath) - if err != nil { - return nil, fmt.Errorf("stat thumbnail: %w", err) + if err == nil { + return &FileResult{ + Path: media.ThumbnailPath, + FileName: filepath.Base(media.ThumbnailPath), + FileSize: info.Size(), + }, nil } - return &FileResult{ - Path: media.ThumbnailPath, - FileName: filepath.Base(media.ThumbnailPath), - FileSize: info.Size(), - }, nil + // If the generated thumbnail is missing, fall back to the original file + // for image media so that cover.jpg and similar files still render. + if media.Type == model.MediaTypeImage { + info, err = os.Stat(media.AbsPath) + if err == nil { + return &FileResult{ + Path: media.AbsPath, + FileName: filepath.Base(media.AbsPath), + FileSize: info.Size(), + }, nil + } + } + return nil, fmt.Errorf("stat thumbnail: %w", err) } func (s *browseService) RegenerateThumbnail(ctx context.Context, mediaID, userID int64) error { -- cgit v1.2.3