fix(artwork): validate ffmpeg pipe before returning in cover art fallback
ffmpeg.ExtractImage returns a pipe-based reader immediately, before ffmpeg finishes processing. When the audio file has no embedded image stream (e.g. a plain MP3), ffmpeg exits with an error that closes the pipe asynchronously. The selectImageReader function saw the non-nil reader as a success and returned it instead of falling through to the next source in the chain (album art). This caused getCoverArt to return an error response for tracks on albums where the disc artwork reader was invoked but no embedded art existed. Fixed by reading one byte from the pipe to validate the stream delivers data before returning it. If the read fails, the reader is closed and nil is returned, allowing the fallback chain to continue to album artwork. Closes #5265
This commit is contained in:
+16
-1
@@ -130,10 +130,25 @@ func fromFFmpegTag(ctx context.Context, ffmpeg ffmpeg.FFmpeg, path string) sourc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
return r, path, nil
|
// Validate that the stream actually contains image data by reading the first byte.
|
||||||
|
// ffmpeg.ExtractImage returns a pipe reader that may fail asynchronously if the
|
||||||
|
// file has no video/image stream (e.g., an MP3 without embedded art).
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
n, err := r.Read(buf)
|
||||||
|
if n == 0 || err != nil {
|
||||||
|
r.Close()
|
||||||
|
return nil, "", fmt.Errorf("ffmpeg produced no image data for %s: %w", path, err)
|
||||||
|
}
|
||||||
|
return readCloser{Reader: io.MultiReader(bytes.NewReader(buf[:n]), r), Closer: r}, path, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readCloser combines a Reader and a Closer into an io.ReadCloser.
|
||||||
|
type readCloser struct {
|
||||||
|
io.Reader
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
func fromAlbum(ctx context.Context, a *artwork, id model.ArtworkID) sourceFunc {
|
func fromAlbum(ctx context.Context, a *artwork, id model.ArtworkID) sourceFunc {
|
||||||
return func() (io.ReadCloser, string, error) {
|
return func() (io.ReadCloser, string, error) {
|
||||||
r, _, err := a.Get(ctx, id, 0, false)
|
r, _, err := a.Get(ctx, id, 0, false)
|
||||||
|
|||||||
Reference in New Issue
Block a user