extracted cover art handling from library package
This commit is contained in:
@@ -15,8 +15,8 @@ import (
|
||||
|
||||
"golang.org/x/image/draw"
|
||||
|
||||
"yellowjacket/backend/coverart"
|
||||
"yellowjacket/backend/metadata"
|
||||
"yellowjacket/backend/system"
|
||||
)
|
||||
|
||||
// thumbnailTier defines a single size tier for generated cover art thumbnails.
|
||||
@@ -80,16 +80,14 @@ func (l *Library) saveCoverArt(
|
||||
|
||||
saveStart := time.Now()
|
||||
|
||||
// Get the data directory for storing cover art.
|
||||
dataDir, err := system.GetUserDataDirPath()
|
||||
// Get the covers directory for storing cover art.
|
||||
coverDir, err := coverart.CoversDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(
|
||||
"could not get user data directory: %w", err,
|
||||
"could not resolve covers directory: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
coverDir := filepath.Join(dataDir, "covers")
|
||||
|
||||
// Ensure directory exists.
|
||||
if err := os.MkdirAll(coverDir, 0o755); err != nil {
|
||||
return "", fmt.Errorf(
|
||||
@@ -324,15 +322,13 @@ func encodeAndSaveImage(
|
||||
// _thumb files to _md, and generates any missing sized variants for each
|
||||
// original cover art file.
|
||||
func (l *Library) generateMissingSizedVariants() error {
|
||||
dataDir, err := system.GetUserDataDirPath()
|
||||
coverDir, err := coverart.CoversDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"could not get user data directory: %w", err,
|
||||
"could not resolve covers directory: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
coverDir := filepath.Join(dataDir, "covers")
|
||||
|
||||
entries, err := os.ReadDir(coverDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
@@ -483,16 +479,6 @@ func (l *Library) migrateLegacyThumbs(
|
||||
return migrated
|
||||
}
|
||||
|
||||
// SizedFilename derives a sized-variant filename from an original cover art
|
||||
// filename and a size suffix.
|
||||
// For example, SizedFilename("a1b2c3d4.jpg", "_sm") returns "a1b2c3d4_sm.jpg".
|
||||
func SizedFilename(originalFilename, suffix string) string {
|
||||
ext := filepath.Ext(originalFilename)
|
||||
name := strings.TrimSuffix(originalFilename, ext)
|
||||
|
||||
return name + suffix + ".jpg"
|
||||
}
|
||||
|
||||
// extensionFromMIME returns a file extension for common image MIME types.
|
||||
func extensionFromMIME(mimeType string) string {
|
||||
switch mimeType {
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"yellowjacket/backend/system"
|
||||
)
|
||||
|
||||
// CoverArtHandler serves cover art images via HTTP.
|
||||
type CoverArtHandler struct {
|
||||
coversDir string
|
||||
}
|
||||
|
||||
// NewCoverArtHandler creates a handler that serves cover art from the user data directory.
|
||||
func NewCoverArtHandler() (*CoverArtHandler, error) {
|
||||
dataDir, err := system.GetUserDataDirPath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get user data directory: %w", err)
|
||||
}
|
||||
|
||||
return &CoverArtHandler{
|
||||
coversDir: filepath.Join(dataDir, "covers"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ServeHTTP handles requests for cover art images.
|
||||
func (h *CoverArtHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Extract filename from path like "/covers/abc123.jpg"
|
||||
filename := filepath.Base(r.URL.Path)
|
||||
|
||||
// Prevent directory traversal
|
||||
if filename == "." || filename == ".." {
|
||||
http.NotFound(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Filenames are content-hashed (SHA-256), so they are immutable.
|
||||
// Set aggressive cache headers to avoid redundant re-fetches.
|
||||
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
|
||||
|
||||
filePath := filepath.Join(h.coversDir, filename)
|
||||
http.ServeFile(w, r, filePath)
|
||||
}
|
||||
+12
-17
@@ -4,9 +4,10 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yellowjacket/backend/coverart"
|
||||
)
|
||||
|
||||
// Sentinel errors for library queries.
|
||||
@@ -258,14 +259,11 @@ func (l *Library) GetAllAlbums() ([]Album, error) {
|
||||
|
||||
// Convert filesystem path to URL path for the asset handler.
|
||||
if row.CoverArtPath != "" {
|
||||
base := filepath.Base(row.CoverArtPath)
|
||||
album.CoverArtPath = "/covers/" + base
|
||||
album.CoverArtSmall = "/covers/" +
|
||||
SizedFilename(base, "_sm")
|
||||
album.CoverArtMedium = "/covers/" +
|
||||
SizedFilename(base, "_md")
|
||||
album.CoverArtLarge = "/covers/" +
|
||||
SizedFilename(base, "_lg")
|
||||
urls := coverart.ResolveURLs(row.CoverArtPath)
|
||||
album.CoverArtPath = urls.Original
|
||||
album.CoverArtSmall = urls.Small
|
||||
album.CoverArtMedium = urls.Medium
|
||||
album.CoverArtLarge = urls.Large
|
||||
}
|
||||
|
||||
albums = append(albums, album)
|
||||
@@ -345,14 +343,11 @@ func (l *Library) GetAlbumsByArtist(
|
||||
|
||||
// Convert filesystem path to URL path for the asset handler.
|
||||
if row.CoverArtPath != "" {
|
||||
base := filepath.Base(row.CoverArtPath)
|
||||
album.CoverArtPath = "/covers/" + base
|
||||
album.CoverArtSmall = "/covers/" +
|
||||
SizedFilename(base, "_sm")
|
||||
album.CoverArtMedium = "/covers/" +
|
||||
SizedFilename(base, "_md")
|
||||
album.CoverArtLarge = "/covers/" +
|
||||
SizedFilename(base, "_lg")
|
||||
urls := coverart.ResolveURLs(row.CoverArtPath)
|
||||
album.CoverArtPath = urls.Original
|
||||
album.CoverArtSmall = urls.Small
|
||||
album.CoverArtMedium = urls.Medium
|
||||
album.CoverArtLarge = urls.Large
|
||||
}
|
||||
|
||||
albums = append(albums, album)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"yellowjacket/backend/system"
|
||||
"yellowjacket/backend/coverart"
|
||||
)
|
||||
|
||||
// FullRescan clears the queue and player, wipes all library data
|
||||
@@ -182,15 +182,13 @@ func (l *Library) clearLibraryTables() error {
|
||||
|
||||
// clearCoverArtFiles removes all files from the covers directory.
|
||||
func (l *Library) clearCoverArtFiles() error {
|
||||
dataDir, err := system.GetUserDataDirPath()
|
||||
coverDir, err := coverart.CoversDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"could not get user data directory: %w", err,
|
||||
"could not resolve covers directory: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
coverDir := filepath.Join(dataDir, "covers")
|
||||
|
||||
entries, err := os.ReadDir(coverDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
||||
Reference in New Issue
Block a user