summaryrefslogtreecommitdiff
path: root/internal/service/write.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-09 11:11:49 +0300
committerPaul Buetow <paul@buetow.org>2026-05-09 11:11:49 +0300
commit8c32cd117abe79f4ee6cff3a950ffc35c95faeff (patch)
tree5b63a4789351da4f2d9c1cd2e7007f67d43b3371 /internal/service/write.go
parent59c8870061465142306e7a6e5d3fcb344686cfe9 (diff)
Refine media browsing and set covers
Diffstat (limited to 'internal/service/write.go')
-rw-r--r--internal/service/write.go83
1 files changed, 65 insertions, 18 deletions
diff --git a/internal/service/write.go b/internal/service/write.go
index b5cd181..b23355a 100644
--- a/internal/service/write.go
+++ b/internal/service/write.go
@@ -149,7 +149,10 @@ func (s *writeService) RegenerateSetCover(ctx context.Context, setID int64, fold
}
prefix := filepath.ToSlash(strings.Trim(folder, "/"))
- var candidates []model.Media
+ var artworkCandidates []model.Media
+ var videoCandidates []model.Media
+ var imageCandidates []model.Media
+ var thumbnailCandidates []model.Media
for _, m := range media {
if m.DeletedAt != nil {
continue
@@ -163,37 +166,81 @@ func (s *writeService) RegenerateSetCover(ctx context.Context, setID int64, fold
if strings.Contains(suffix, "/") {
continue
}
- } else if strings.Contains(rel, "/") {
- continue
}
- if m.Type == model.MediaTypeVideo {
- candidates = append(candidates, m)
+ switch {
+ case isFolderArtworkMedia(m):
+ artworkCandidates = append(artworkCandidates, m)
+ case m.Type == model.MediaTypeVideo:
+ videoCandidates = append(videoCandidates, m)
+ case m.Type == model.MediaTypeImage:
+ imageCandidates = append(imageCandidates, m)
+ case m.ThumbnailPath != "":
+ thumbnailCandidates = append(thumbnailCandidates, m)
}
}
- if len(candidates) == 0 {
- return errors.New("no video files available for cover")
- }
-
- candidate := candidates[0]
- if len(candidates) > 1 {
- candidate = candidates[mrand.Intn(len(candidates))]
- }
baseDir := filepath.Join(s.mediaRoot, filepath.FromSlash(set.RootPath))
if prefix != "" {
baseDir = filepath.Join(baseDir, filepath.FromSlash(prefix))
}
coverPath := filepath.Join(filepath.Clean(baseDir), ".cover.jpg")
- meta, err := s.prober.Probe(ctx, candidate.AbsPath)
+
+ switch {
+ case len(artworkCandidates) > 0:
+ candidate := randomMedia(artworkCandidates)
+ if err := s.thumbGen.Generate(ctx, candidate.AbsPath, coverPath, 0); err != nil {
+ return fmt.Errorf("generate cover: %w", err)
+ }
+ case len(videoCandidates) > 0:
+ candidate := randomMedia(videoCandidates)
+ meta, err := s.prober.Probe(ctx, candidate.AbsPath)
+ if err != nil {
+ return fmt.Errorf("probe cover candidate: %w", err)
+ }
+ if err := s.thumbGen.Generate(ctx, candidate.AbsPath, coverPath, meta.Duration); err != nil {
+ return fmt.Errorf("generate cover: %w", err)
+ }
+ case len(imageCandidates) > 0:
+ candidate := randomMedia(imageCandidates)
+ if err := s.thumbGen.Generate(ctx, candidate.AbsPath, coverPath, 0); err != nil {
+ return fmt.Errorf("generate cover: %w", err)
+ }
+ case len(thumbnailCandidates) > 0:
+ candidate := randomMedia(thumbnailCandidates)
+ if err := copyFile(candidate.ThumbnailPath, coverPath); err != nil {
+ return fmt.Errorf("copy thumbnail cover: %w", err)
+ }
+ default:
+ return errors.New("no media files available for cover")
+ }
+
+ return nil
+}
+
+func randomMedia(media []model.Media) model.Media {
+ if len(media) == 1 {
+ return media[0]
+ }
+ return media[mrand.Intn(len(media))]
+}
+
+func copyFile(src, dst string) error {
+ in, err := os.Open(src)
if err != nil {
- return fmt.Errorf("probe cover candidate: %w", err)
+ return err
}
+ defer in.Close()
- if err := s.thumbGen.Generate(ctx, candidate.AbsPath, coverPath, meta.Duration); err != nil {
- return fmt.Errorf("generate cover: %w", err)
+ out, err := os.Create(dst)
+ if err != nil {
+ return err
}
- return nil
+ if _, err := io.Copy(out, in); err != nil {
+ _ = out.Close()
+ return err
+ }
+ return out.Close()
}
func (s *writeService) saveUploadedMedia(ctx context.Context, setID int64, path string, data io.Reader, size int64) (*model.Media, error) {