fix: various improvements and fixes

This commit is contained in:
zarzet
2026-02-11 00:22:48 +07:00
parent fe1c96ea12
commit bd42655c0e
20 changed files with 634 additions and 108 deletions
+55 -12
View File
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
@@ -782,6 +783,7 @@ func EditFileMetadata(filePath, metadataJSON string) (string, error) {
lower := strings.ToLower(filePath)
isFlac := strings.HasSuffix(lower, ".flac")
coverPath := strings.TrimSpace(fields["cover_path"])
if isFlac {
trackNum := 0
@@ -809,7 +811,7 @@ func EditFileMetadata(filePath, metadataJSON string) (string, error) {
Comment: fields["comment"],
}
if err := EmbedMetadata(filePath, meta, ""); err != nil {
if err := EmbedMetadata(filePath, meta, coverPath); err != nil {
return "", fmt.Errorf("failed to write FLAC metadata: %w", err)
}
@@ -1692,19 +1694,47 @@ func ReEnrichFile(requestJSON string) (string, error) {
GoLog("[ReEnrich] track=%d, disc=%d, date=%s, isrc=%s, genre=%s, label=%s\n",
req.TrackNumber, req.DiscNumber, req.ReleaseDate, req.ISRC, req.Genre, req.Label)
lower := strings.ToLower(req.FilePath)
isFlac := strings.HasSuffix(lower, ".flac")
// Download cover art to temp file
var coverTempPath string
var coverDataBytes []byte
if req.CoverURL != "" {
coverData, err := downloadCoverToMemory(req.CoverURL, req.MaxQuality)
if err != nil {
GoLog("[ReEnrich] Failed to download cover: %v\n", err)
} else {
tmpFile, err := os.CreateTemp("", "reenrich_cover_*.jpg")
if err == nil {
coverTempPath = tmpFile.Name()
tmpFile.Write(coverData)
tmpFile.Close()
GoLog("[ReEnrich] Cover downloaded: %d KB\n", len(coverData)/1024)
coverDataBytes = coverData
GoLog("[ReEnrich] Cover downloaded: %d KB\n", len(coverData)/1024)
// MP3/Opus requires a real image file path for Dart FFmpeg.
// FLAC uses in-memory embed and does not require temp files.
if !isFlac {
tmpFile, err := os.CreateTemp("", "reenrich_cover_*.jpg")
if err != nil {
fallbackDir := filepath.Dir(req.FilePath)
if fallbackDir == "" || fallbackDir == "." {
GoLog("[ReEnrich] Failed to create cover temp file: %v\n", err)
} else {
tmpFile, err = os.CreateTemp(fallbackDir, "reenrich_cover_*.jpg")
if err != nil {
GoLog("[ReEnrich] Failed to create cover temp file (fallback dir %s): %v\n", fallbackDir, err)
}
}
}
if err == nil && tmpFile != nil {
coverTempPath = tmpFile.Name()
if _, writeErr := tmpFile.Write(coverData); writeErr != nil {
GoLog("[ReEnrich] Failed writing cover temp file: %v\n", writeErr)
tmpFile.Close()
os.Remove(coverTempPath)
coverTempPath = ""
} else if closeErr := tmpFile.Close(); closeErr != nil {
GoLog("[ReEnrich] Failed closing cover temp file: %v\n", closeErr)
os.Remove(coverTempPath)
coverTempPath = ""
}
}
}
}
}
@@ -1734,9 +1764,6 @@ func ReEnrichFile(requestJSON string) (string, error) {
}
}
lower := strings.ToLower(req.FilePath)
isFlac := strings.HasSuffix(lower, ".flac")
// Build enriched metadata response for Dart (includes online search results)
enrichedMeta := map[string]interface{}{
"track_name": req.TrackName,
@@ -1772,8 +1799,24 @@ func ReEnrichFile(requestJSON string) (string, error) {
Lyrics: lyricsLRC,
}
if err := EmbedMetadata(req.FilePath, metadata, coverTempPath); err != nil {
return "", fmt.Errorf("failed to embed metadata: %w", err)
if len(coverDataBytes) > 0 {
if err := EmbedMetadataWithCoverData(req.FilePath, metadata, coverDataBytes); err != nil {
return "", fmt.Errorf("failed to embed metadata with cover: %w", err)
}
} else {
if err := EmbedMetadata(req.FilePath, metadata, ""); err != nil {
return "", fmt.Errorf("failed to embed metadata: %w", err)
}
}
if len(coverDataBytes) > 0 {
embeddedCover, err := ExtractCoverArt(req.FilePath)
if err != nil || len(embeddedCover) == 0 {
if err != nil {
return "", fmt.Errorf("metadata embedded but cover verification failed: %w", err)
}
return "", fmt.Errorf("metadata embedded but cover verification failed: empty embedded cover")
}
GoLog("[ReEnrich] Cover verified after embed (%d bytes)\n", len(embeddedCover))
}
GoLog("[ReEnrich] FLAC metadata embedded successfully\n")
+89 -22
View File
@@ -4,8 +4,13 @@ import (
"bytes"
"encoding/binary"
"fmt"
stdimage "image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"io"
"os"
"path/filepath"
"strconv"
"strings"
@@ -14,6 +19,82 @@ import (
"github.com/go-flac/go-flac/v2"
)
func detectCoverMIME(coverPath string, coverData []byte) string {
// Prefer magic-byte detection over file extension.
// Some providers return non-JPEG data behind .jpg URLs.
if len(coverData) >= 8 &&
coverData[0] == 0x89 &&
coverData[1] == 0x50 &&
coverData[2] == 0x4E &&
coverData[3] == 0x47 &&
coverData[4] == 0x0D &&
coverData[5] == 0x0A &&
coverData[6] == 0x1A &&
coverData[7] == 0x0A {
return "image/png"
}
if len(coverData) >= 3 &&
coverData[0] == 0xFF &&
coverData[1] == 0xD8 &&
coverData[2] == 0xFF {
return "image/jpeg"
}
if len(coverData) >= 6 {
header := string(coverData[:6])
if header == "GIF87a" || header == "GIF89a" {
return "image/gif"
}
}
if len(coverData) >= 12 &&
string(coverData[:4]) == "RIFF" &&
string(coverData[8:12]) == "WEBP" {
return "image/webp"
}
switch strings.ToLower(filepath.Ext(strings.TrimSpace(coverPath))) {
case ".png":
return "image/png"
case ".jpg", ".jpeg":
return "image/jpeg"
case ".webp":
return "image/webp"
case ".gif":
return "image/gif"
}
return "image/jpeg"
}
func buildPictureBlock(coverPath string, coverData []byte) (flac.MetaDataBlock, error) {
if len(coverData) == 0 {
return flac.MetaDataBlock{}, fmt.Errorf("empty cover data")
}
mime := detectCoverMIME(coverPath, coverData)
picture := &flacpicture.MetadataBlockPicture{
PictureType: flacpicture.PictureTypeFrontCover,
MIME: mime,
Description: "Front Cover",
ImageData: coverData,
}
// Width/height/depth are optional in practice; keep zero when decode fails.
if cfg, format, err := stdimage.DecodeConfig(bytes.NewReader(coverData)); err == nil {
picture.Width = uint32(cfg.Width)
picture.Height = uint32(cfg.Height)
switch format {
case "png":
picture.ColorDepth = 32
case "jpeg":
picture.ColorDepth = 24
default:
picture.ColorDepth = 0
}
}
return picture.Marshal(), nil
}
type Metadata struct {
Title string
Artist string
@@ -127,19 +208,12 @@ func EmbedMetadata(filePath string, metadata Metadata, coverPath string) error {
}
}
picture, err := flacpicture.NewFromImageData(
flacpicture.PictureTypeFrontCover,
"Front Cover",
coverData,
"image/jpeg",
)
picBlock, err := buildPictureBlock(coverPath, coverData)
if err != nil {
fmt.Printf("[Metadata] Warning: Failed to create picture block: %v\n", err)
} else {
picBlock := picture.Marshal()
f.Meta = append(f.Meta, &picBlock)
fmt.Printf("[Metadata] Cover art embedded successfully (%d bytes)\n", len(coverData))
return fmt.Errorf("failed to create picture block: %w", err)
}
f.Meta = append(f.Meta, &picBlock)
fmt.Printf("[Metadata] Cover art embedded successfully (%d bytes)\n", len(coverData))
}
} else {
fmt.Printf("[Metadata] Warning: Cover file does not exist: %s\n", coverPath)
@@ -238,19 +312,12 @@ func EmbedMetadataWithCoverData(filePath string, metadata Metadata, coverData []
}
}
picture, err := flacpicture.NewFromImageData(
flacpicture.PictureTypeFrontCover,
"Front Cover",
coverData,
"image/jpeg",
)
picBlock, err := buildPictureBlock("", coverData)
if err != nil {
fmt.Printf("[Metadata] Warning: Failed to create picture block: %v\n", err)
} else {
picBlock := picture.Marshal()
f.Meta = append(f.Meta, &picBlock)
fmt.Printf("[Metadata] Cover art embedded successfully (%d bytes)\n", len(coverData))
return fmt.Errorf("failed to create picture block: %w", err)
}
f.Meta = append(f.Meta, &picBlock)
fmt.Printf("[Metadata] Cover art embedded successfully (%d bytes)\n", len(coverData))
}
return f.Save(filePath)