From 81737024f1d39c022d7901de380a5d87df70d4b4 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 4 May 2026 08:26:08 +0300 Subject: task 9: Centralize file extension and media type mappings in internal/mediatype Create a new internal/mediatype package with a single source of truth for: - Extension-to-media-type mappings (TypeForExt) - Extension-to-MIME mappings (MIMETypeForExt) - Supported extension checks (IsSupportedExt, IsImageExt, IsCoverImageExt) Replace duplicated logic across: - internal/service/service.go (supportedExtensions, guessMediaType) - internal/scanner/scanner.go (mediaExtensions, imageExtensions, mediaTypeFromExt) - internal/api/handlers.go (probe.MimeTypeForFilename) - internal/probe/probe.go (imageExtensions, isImagePath) - internal/probe/remux.go (MimeTypeForFilename) Divergent defaults unified: both scanner and service now default unknown extensions to video (model.MediaTypeVideo) via mediatype.TypeForExt. Update tests to use mediatype package and remove obsolete scanner tests. Update AGENTS.md to reflect the new package. --- internal/probe/image_test.go | 9 ++++---- internal/probe/probe.go | 15 ++---------- internal/probe/remux.go | 54 ++------------------------------------------ 3 files changed, 9 insertions(+), 69 deletions(-) (limited to 'internal/probe') diff --git a/internal/probe/image_test.go b/internal/probe/image_test.go index 0e55263..770d6e2 100644 --- a/internal/probe/image_test.go +++ b/internal/probe/image_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "testing" + "codeberg.org/snonux/player/internal/mediatype" "codeberg.org/snonux/player/internal/model" ) @@ -72,18 +73,18 @@ func TestIsImagePath(t *testing.T) { } for _, c := range cases { t.Run(c.path, func(t *testing.T) { - if got := isImagePath(c.path); got != c.want { - t.Errorf("isImagePath(%q) = %v, want %v", c.path, got, c.want) + if got := mediatype.IsImageExt(c.path); got != c.want { + t.Errorf("IsImageExt(%q) = %v, want %v", c.path, got, c.want) } }) } } func TestIsImagePath_CaseInsensitive(t *testing.T) { - if !isImagePath("UPPER.JPG") { + if !mediatype.IsImageExt("UPPER.JPG") { t.Error("expected true for uppercase extension") } - if !isImagePath("Mixed.PnG") { + if !mediatype.IsImageExt("Mixed.PnG") { t.Error("expected true for mixed-case extension") } } diff --git a/internal/probe/probe.go b/internal/probe/probe.go index 4257516..e94d2e5 100644 --- a/internal/probe/probe.go +++ b/internal/probe/probe.go @@ -7,10 +7,9 @@ import ( "fmt" "os" "os/exec" - "path/filepath" "strconv" - "strings" + "codeberg.org/snonux/player/internal/mediatype" "codeberg.org/snonux/player/internal/model" "github.com/rwcarlsen/goexif/exif" ) @@ -49,7 +48,7 @@ func (f *FFProber) Probe(ctx context.Context, path string) (*model.Metadata, err return nil, err } // For images, also extract EXIF data. - if isImagePath(path) { + if mediatype.IsImageExt(path) { extractEXIF(path, meta) } return meta, nil @@ -108,16 +107,6 @@ func parseFFprobeOutput(data []byte) (*model.Metadata, error) { return meta, nil } -var imageExtensions = map[string]struct{}{ - ".jpg": {}, ".jpeg": {}, ".png": {}, ".gif": {}, ".webp": {}, ".bmp": {}, ".avif": {}, ".svg": {}, -} - -func isImagePath(path string) bool { - ext := strings.ToLower(filepath.Ext(path)) - _, ok := imageExtensions[ext] - return ok -} - func extractEXIF(path string, meta *model.Metadata) { f, err := os.Open(path) if err != nil { diff --git a/internal/probe/remux.go b/internal/probe/remux.go index 594e1e0..8c285cb 100644 --- a/internal/probe/remux.go +++ b/internal/probe/remux.go @@ -5,11 +5,8 @@ import ( "fmt" "io" "log/slog" - "mime" "os" "os/exec" - "path/filepath" - "strings" ) // Remuxer remuxes media on-the-fly to a browser-friendly container. @@ -94,54 +91,7 @@ func hasMPEGTSsync(buf []byte, packetSize int) bool { return true } } - } - return false + } + return false } -// MimeTypeForFilename returns an HTTP Content-Type based on the file extension. -func MimeTypeForFilename(name string) string { - ext := strings.ToLower(filepath.Ext(name)) - t := mime.TypeByExtension(ext) - if t != "" { - return t - } - switch ext { - case ".mp4", ".m4v": - return "video/mp4" - case ".mkv": - return "video/x-matroska" - case ".avi": - return "video/x-msvideo" - case ".mov": - return "video/quicktime" - case ".webm": - return "video/webm" - case ".mp3": - return "audio/mpeg" - case ".flac": - return "audio/flac" - case ".wav": - return "audio/wav" - case ".aac", ".m4a": - return "audio/mp4" - case ".ogg", ".opus": - return "audio/ogg" - case ".m4b": - return "audio/x-m4b" - case ".jpg", ".jpeg": - return "image/jpeg" - case ".png": - return "image/png" - case ".gif": - return "image/gif" - case ".webp": - return "image/webp" - case ".bmp": - return "image/bmp" - case ".avif": - return "image/avif" - case ".svg": - return "image/svg+xml" - } - return "application/octet-stream" -} -- cgit v1.2.3