refactor(metadata): move cover resolution to extensions

This commit is contained in:
zarzet
2026-08-19 13:12:29 +07:00
parent bdb10a2ae6
commit ea63291db8
49 changed files with 42 additions and 579 deletions
@@ -1393,10 +1393,9 @@ class MainActivity: FlutterFragmentActivity() {
"downloadCoverToFile" -> {
val coverUrl = call.argument<String>("cover_url") ?: ""
val outputPath = call.argument<String>("output_path") ?: ""
val maxQuality = call.argument<Boolean>("max_quality") ?: true
val response = withContext(Dispatchers.IO) {
try {
Gobackend.downloadCoverToFile(coverUrl, outputPath, maxQuality)
Gobackend.downloadCoverToFile(coverUrl, outputPath, false)
"""{"success":true}"""
} catch (e: Exception) {
"""{"success":false,"error":"${e.message?.replace("\"", "'")}"}"""
@@ -503,7 +503,7 @@ internal fun NativeDownloadFinalizer.downloadCoverForMetadata(context: Context,
Gobackend.downloadCoverToFile(
coverUrl,
output.absolutePath,
input.request.optBoolean("embed_max_quality_cover", true)
false
)
if (output.exists() && output.length() > 0L) {
output
+7 -202
View File
@@ -8,158 +8,28 @@ import (
_ "image/png"
"io"
"net/http"
"regexp"
"strings"
"sync"
"time"
)
const (
spotifySize300 = "ab67616d00001e02"
spotifySize640 = "ab67616d0000b273"
spotifySizeMax = "ab67616d000082c1"
)
// Square CDN covers using this path shape may return an image whose decoded
// dimensions differ from the dimensions advertised in the URL. Max-quality
// selection therefore probes both useful high-resolution variants and checks
// the image headers instead of trusting the filename.
var squareCoverSizeRegex = regexp.MustCompile(`/(\d+)x(\d+)-\d+-\d+-\d+-\d+\.jpg$`)
var tidalSizeRegex = regexp.MustCompile(`/\d+x\d+\.jpg$`)
var qobuzSizeRegex = regexp.MustCompile(`_\d+\.jpg$`)
func convertSmallToMedium(imageURL string) string {
if strings.Contains(imageURL, spotifySize300) {
return strings.Replace(imageURL, spotifySize300, spotifySize640, 1)
}
return imageURL
}
func downloadCoverToMemory(coverURL string, maxQuality bool) ([]byte, error) {
// downloadCoverToMemory downloads exactly the URL supplied by the metadata
// provider. Cover-resolution selection belongs to the provider extension;
// the app must not infer a provider from its CDN URL or rewrite that URL.
func downloadCoverToMemory(coverURL string) ([]byte, error) {
if coverURL == "" {
return nil, fmt.Errorf("no cover URL provided")
}
GoLog("[Cover] Original URL: %s", coverURL)
downloadURL := convertSmallToMedium(coverURL)
if downloadURL != coverURL {
GoLog("[Cover] Upgraded 300x300 → 640x640")
}
if !maxQuality {
GoLog("[Cover] Final URL: %s", downloadURL)
data, err := fetchCoverCached(downloadURL)
if err != nil {
return nil, err
}
return append([]byte(nil), data...), nil
}
candidates := maxQualityCoverCandidateURLs(downloadURL)
data, selectedURL, width, height, err := fetchBestCoverCandidate(candidates)
GoLog("[Cover] Provider URL: %s", coverURL)
data, err := fetchCoverCached(coverURL)
if err != nil {
// A CDN can reject an upgraded size while the provider-supplied URL is
// still valid. Preserve that URL as the final fallback.
if len(candidates) == 1 && candidates[0] == downloadURL {
return nil, err
}
data, err = fetchCoverCached(downloadURL)
if err != nil {
return nil, err
}
selectedURL = downloadURL
width, height = coverDimensions(data)
return nil, err
}
GoLog("[Cover] Selected URL: %s (%dx%d, %d KB)", selectedURL, width, height, len(data)/1024)
// Cached bytes are shared across goroutines and must never be mutated;
// hand callers their own copy.
return append([]byte(nil), data...), nil
}
type fetchedCoverCandidate struct {
url string
data []byte
width, height int
err error
}
func maxQualityCoverCandidateURLs(coverURL string) []string {
upgraded := upgradeToMaxQuality(coverURL)
candidates := []string{upgraded}
// This is deliberately based on the URL capability rather than a provider
// or extension ID. Any metadata source returning the same square-cover URL
// shape receives the same verified candidate selection.
if squareCoverSizeRegex.MatchString(coverURL) {
candidate1500 := squareCoverSizeRegex.ReplaceAllString(
coverURL,
"/1500x1500-000000-80-0-0.jpg",
)
if candidate1500 != upgraded {
candidates = append(candidates, candidate1500)
}
}
return uniqueNonEmptyStrings(candidates)
}
func uniqueNonEmptyStrings(values []string) []string {
seen := make(map[string]struct{}, len(values))
result := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, exists := seen[value]; exists {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
return result
}
func fetchBestCoverCandidate(urls []string) ([]byte, string, int, int, error) {
if len(urls) == 0 {
return nil, "", 0, 0, fmt.Errorf("no cover candidates available")
}
results := make(chan fetchedCoverCandidate, len(urls))
for _, candidateURL := range urls {
go func(url string) {
data, err := fetchCoverCached(url)
width, height := coverDimensions(data)
results <- fetchedCoverCandidate{
url: url, data: data, width: width, height: height, err: err,
}
}(candidateURL)
}
var best *fetchedCoverCandidate
var firstErr error
for range urls {
candidate := <-results
if candidate.err != nil || len(candidate.data) == 0 {
if firstErr == nil {
firstErr = candidate.err
}
continue
}
if best == nil || coverCandidateBetter(candidate, *best) {
copy := candidate
best = &copy
}
}
if best == nil {
if firstErr == nil {
firstErr = fmt.Errorf("cover candidates returned no image data")
}
return nil, "", 0, 0, firstErr
}
return best.data, best.url, best.width, best.height, nil
}
func coverDimensions(data []byte) (int, int) {
if len(data) == 0 {
return 0, 0
@@ -171,15 +41,6 @@ func coverDimensions(data []byte) (int, int) {
return config.Width, config.Height
}
func coverCandidateBetter(candidate, current fetchedCoverCandidate) bool {
candidatePixels := int64(candidate.width) * int64(candidate.height)
currentPixels := int64(current.width) * int64(current.height)
if candidatePixels != currentPixels {
return candidatePixels > currentPixels
}
return len(candidate.data) > len(current.data)
}
const (
coverCacheMaxBytes = 24 * 1024 * 1024
coverCacheTTL = 15 * time.Minute
@@ -307,59 +168,3 @@ func fetchCoverBytes(downloadURL string) ([]byte, error) {
return data, nil
}
func upgradeToMaxQuality(coverURL string) string {
if strings.Contains(coverURL, spotifySize640) {
return strings.Replace(coverURL, spotifySize640, spotifySizeMax, 1)
}
if squareCoverSizeRegex.MatchString(coverURL) {
return upgradeSquareCover(coverURL)
}
if strings.Contains(coverURL, "resources.tidal.com") {
return upgradeTidalCover(coverURL)
}
if strings.Contains(coverURL, "static.qobuz.com") {
return upgradeQobuzCover(coverURL)
}
return coverURL
}
func upgradeSquareCover(coverURL string) string {
if !squareCoverSizeRegex.MatchString(coverURL) {
return coverURL
}
upgraded := squareCoverSizeRegex.ReplaceAllString(coverURL, "/1900x1900-000000-80-0-0.jpg")
if upgraded != coverURL {
GoLog("[Cover] Square CDN: probing 1900x1900 and 1500x1500")
}
return upgraded
}
func upgradeTidalCover(coverURL string) string {
if !strings.Contains(coverURL, "resources.tidal.com") {
return coverURL
}
upgraded := tidalSizeRegex.ReplaceAllString(coverURL, "/origin.jpg")
if upgraded != coverURL {
GoLog("[Cover] Tidal: upgraded to origin resolution")
}
return upgraded
}
func upgradeQobuzCover(coverURL string) string {
if !strings.Contains(coverURL, "static.qobuz.com") {
return coverURL
}
upgraded := qobuzSizeRegex.ReplaceAllString(coverURL, "_max.jpg")
if upgraded != coverURL {
GoLog("[Cover] Qobuz: upgraded to max resolution")
}
return upgraded
}
+11 -78
View File
@@ -1,33 +1,12 @@
package gobackend
import (
"bytes"
"errors"
"image"
"image/color"
"image/png"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
)
func testPNG(t *testing.T, width, height int) []byte {
t.Helper()
img := image.NewRGBA(image.Rect(0, 0, width, height))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
img.Set(x, y, color.RGBA{R: uint8(x), G: uint8(y), B: 100, A: 255})
}
}
var buffer bytes.Buffer
if err := png.Encode(&buffer, img); err != nil {
t.Fatalf("encode test cover: %v", err)
}
return buffer.Bytes()
}
func resetCoverCache() {
coverMu.Lock()
coverCache = map[string]*coverCacheEntry{}
@@ -116,72 +95,26 @@ func TestFetchCoverCachedTTLExpiry(t *testing.T) {
}
}
func TestMaxQualityCoverCandidatesProbe1900And1500(t *testing.T) {
url := "https://cdn-images.dzcdn.net/images/cover/abc/1000x1000-000000-80-0-0.jpg"
candidates := maxQualityCoverCandidateURLs(url)
if len(candidates) != 2 {
t.Fatalf("expected two candidates, got %#v", candidates)
}
if !strings.Contains(candidates[0], "1900x1900") {
t.Fatalf("first candidate = %q", candidates[0])
}
if !strings.Contains(candidates[1], "1500x1500") {
t.Fatalf("second candidate = %q", candidates[1])
}
}
func TestDownloadCoverSelectsDecodedDimensionsBeforeByteSize(t *testing.T) {
func TestDownloadCoverUsesProviderURLUnchanged(t *testing.T) {
orig := coverFetch
defer func() { coverFetch = orig }()
resetCoverCache()
advertised1900ButSmaller := append(testPNG(t, 12, 12), bytes.Repeat([]byte{0}, 4096)...)
actualLargerDimensions := testPNG(t, 15, 15)
const providerURL = "https://i.scdn.co/image/ab67616d00001e02example"
var requestedURL string
coverFetch = func(url string) ([]byte, error) {
switch {
case strings.Contains(url, "1900x1900"):
return advertised1900ButSmaller, nil
case strings.Contains(url, "1500x1500"):
return actualLargerDimensions, nil
default:
return nil, errors.New("unexpected URL")
}
requestedURL = url
return []byte("provider-cover"), nil
}
url := "https://cdn-images.dzcdn.net/images/cover/abc/1000x1000-000000-80-0-0.jpg"
got, err := downloadCoverToMemory(url, true)
got, err := downloadCoverToMemory(providerURL)
if err != nil {
t.Fatalf("download max cover: %v", err)
t.Fatalf("download provider cover: %v", err)
}
if !bytes.Equal(got, actualLargerDimensions) {
t.Fatal("expected decoded 15x15 candidate instead of larger-byte 12x12 candidate")
if requestedURL != providerURL {
t.Fatalf("requested URL = %q, want provider URL %q", requestedURL, providerURL)
}
}
func TestBestCoverCandidateUsesByteSizeAsDimensionTiebreaker(t *testing.T) {
orig := coverFetch
defer func() { coverFetch = orig }()
resetCoverCache()
base := testPNG(t, 10, 10)
largerFile := append(append([]byte(nil), base...), bytes.Repeat([]byte{0}, 512)...)
coverFetch = func(url string) ([]byte, error) {
if strings.Contains(url, "large") {
return largerFile, nil
}
return base, nil
}
got, selected, width, height, err := fetchBestCoverCandidate(
[]string{"https://covers.test/small", "https://covers.test/large"},
)
if err != nil {
t.Fatalf("select cover: %v", err)
}
if selected != "https://covers.test/large" || width != 10 || height != 10 {
t.Fatalf("selected=%q dimensions=%dx%d", selected, width, height)
}
if !bytes.Equal(got, largerFile) {
t.Fatal("expected larger file when decoded dimensions are equal")
if string(got) != "provider-cover" {
t.Fatalf("downloaded cover = %q", got)
}
}
-1
View File
@@ -28,7 +28,6 @@ type DownloadRequest struct {
EmbedMetadata bool `json:"embed_metadata"`
ArtistTagMode string `json:"artist_tag_mode,omitempty"`
EmbedLyrics bool `json:"embed_lyrics"`
EmbedMaxQualityCover bool `json:"embed_max_quality_cover"`
EmbedReplayGain bool `json:"embed_replaygain,omitempty"`
PostProcessingEnabled bool `json:"post_processing_enabled,omitempty"`
TidalHighFormat string `json:"tidal_high_format,omitempty"`
+5 -2
View File
@@ -527,12 +527,15 @@ func RewriteSplitArtistTagsExport(filePath, artist, albumArtist string) (string,
return s, nil
}
func DownloadCoverToFile(coverURL string, outputPath string, maxQuality bool) error {
// The final bool is retained for gomobile ABI compatibility with existing
// native shells. Resolution selection is extension-owned and the value is
// intentionally ignored.
func DownloadCoverToFile(coverURL string, outputPath string, _ bool) error {
if coverURL == "" {
return fmt.Errorf("no cover URL provided")
}
data, err := downloadCoverToMemory(coverURL, maxQuality)
data, err := downloadCoverToMemory(coverURL)
if err != nil {
return fmt.Errorf("failed to download cover: %w", err)
}
+1 -2
View File
@@ -21,7 +21,6 @@ var fetchMusicBrainzAlbumArtistByISRC = FetchMusicBrainzAlbumArtistByISRC
type reEnrichRequest struct {
FilePath string `json:"file_path"`
CoverURL string `json:"cover_url"`
MaxQuality bool `json:"max_quality"`
EmbedLyrics bool `json:"embed_lyrics"`
LyricsMode string `json:"lyrics_mode,omitempty"`
ArtistTagMode string `json:"artist_tag_mode,omitempty"`
@@ -727,7 +726,7 @@ func ReEnrichFile(requestJSON string) (string, error) {
var coverTempPath string
var coverDataBytes []byte
if req.CoverURL != "" && req.shouldUpdateTag("cover", "cover") {
coverData, err := downloadCoverToMemory(req.CoverURL, req.MaxQuality)
coverData, err := downloadCoverToMemory(req.CoverURL)
if err != nil {
GoLog("[ReEnrich] Failed to download cover: %v\n", err)
} else {
+1 -1
View File
@@ -146,7 +146,7 @@ func embedExtensionDownloadMetadata(resp DownloadResponse, req DownloadRequest,
coverURL := firstNonEmptyTrimmed(resp.CoverURL, req.CoverURL)
var coverData []byte
if coverURL != "" {
data, err := downloadCoverToMemory(coverURL, req.EmbedMaxQualityCover)
data, err := downloadCoverToMemory(coverURL)
if err != nil {
GoLog("[DownloadWithExtensionFallback] Warning: failed to download cover for metadata embed: %v\n", err)
} else if len(data) > 0 {
@@ -72,16 +72,7 @@ func TestExtensionHealthClassificationAndValidation(t *testing.T) {
}
func TestCoverAndIDHSHelpers(t *testing.T) {
if got := upgradeToMaxQuality("https://cdn-images.dzcdn.net/images/cover/abc/500x500-000000-80-0-0.jpg"); !strings.Contains(got, "1900x1900") {
t.Fatalf("deezer cover = %q", got)
}
if got := upgradeToMaxQuality("https://resources.tidal.com/images/id/320x320.jpg"); !strings.Contains(got, "origin.jpg") {
t.Fatalf("tidal cover = %q", got)
}
if got := upgradeToMaxQuality("https://static.qobuz.com/images/covers/ab/cd/foo_600.jpg"); !strings.Contains(got, "_max.jpg") {
t.Fatalf("qobuz cover = %q", got)
}
if data, err := downloadCoverToMemory("", false); err == nil || data != nil {
if data, err := downloadCoverToMemory(""); err == nil || data != nil {
t.Fatalf("expected empty cover error")
}
+1 -2
View File
@@ -564,8 +564,7 @@ import Gobackend
let args = call.arguments as! [String: Any]
let coverURL = args["cover_url"] as! String
let outputPath = args["output_path"] as! String
let maxQuality = args["max_quality"] as? Bool ?? true
GobackendDownloadCoverToFile(coverURL, outputPath, maxQuality, &error)
GobackendDownloadCoverToFile(coverURL, outputPath, false, &error)
if let error = error { throw error }
return "{\"success\":true}"
-12
View File
@@ -382,18 +382,6 @@ abstract class AppLocalizations {
/// **'Save synced lyrics alongside your downloaded tracks'**
String get optionsEmbedLyricsSubtitle;
/// Download highest quality album art
///
/// In en, this message translates to:
/// **'Max Quality Cover'**
String get optionsMaxQualityCover;
/// Subtitle for max quality cover
///
/// In en, this message translates to:
/// **'Download highest resolution cover art'**
String get optionsMaxQualityCoverSubtitle;
/// Title for ReplayGain setting toggle
///
/// In en, this message translates to:
-7
View File
@@ -151,13 +151,6 @@ class AppLocalizationsDe extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Speichere synchronisierte Liedtexte zusammen mit heruntergeladenen Titeln';
@override
String get optionsMaxQualityCover => 'Maximale Cover-Qualität';
@override
String get optionsMaxQualityCoverSubtitle =>
'Cover in höchster Auflösung herunterladen';
@override
String get optionsReplayGain => 'ReplayGain';
-7
View File
@@ -148,13 +148,6 @@ class AppLocalizationsEn extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Save synced lyrics alongside your downloaded tracks';
@override
String get optionsMaxQualityCover => 'Max Quality Cover';
@override
String get optionsMaxQualityCoverSubtitle =>
'Download highest resolution cover art';
@override
String get optionsReplayGain => 'ReplayGain';
-14
View File
@@ -148,13 +148,6 @@ class AppLocalizationsEs extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Embed synced lyrics into FLAC files';
@override
String get optionsMaxQualityCover => 'Max Quality Cover';
@override
String get optionsMaxQualityCoverSubtitle =>
'Download highest resolution cover art';
@override
String get optionsReplayGain => 'ReplayGain';
@@ -5090,13 +5083,6 @@ class AppLocalizationsEsEs extends AppLocalizationsEs {
String get optionsEmbedLyricsSubtitle =>
'Guarda las letras sincronizadas junto a las pistas descargadas';
@override
String get optionsMaxQualityCover => 'Máxima calidad de portada';
@override
String get optionsMaxQualityCoverSubtitle =>
'Descargar la portada en la mayor resolución';
@override
String get optionsReplayGain => 'ReplayGain';
-7
View File
@@ -151,13 +151,6 @@ class AppLocalizationsFr extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Enregistrez les paroles synchronisées avec vos morceaux téléchargés';
@override
String get optionsMaxQualityCover => 'Pochette de qualité supérieure';
@override
String get optionsMaxQualityCoverSubtitle =>
'Télécharger la pochette en haute résolution';
@override
String get optionsReplayGain => 'ReplayGain';
-7
View File
@@ -151,13 +151,6 @@ class AppLocalizationsId extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Simpan lirik yang disinkronkan bersama dengan lagu yang Anda unduh';
@override
String get optionsMaxQualityCover => 'Cover Kualitas Maksimal';
@override
String get optionsMaxQualityCoverSubtitle =>
'Unduh cover art resolusi tertinggi';
@override
String get optionsReplayGain => 'ReplayGain';
-6
View File
@@ -148,12 +148,6 @@ class AppLocalizationsJa extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Save synced lyrics alongside your downloaded tracks';
@override
String get optionsMaxQualityCover => '最大品質のカバー';
@override
String get optionsMaxQualityCoverSubtitle => '最高解像度のカバーアートをダウンロード';
@override
String get optionsReplayGain => 'ReplayGain';
-6
View File
@@ -143,12 +143,6 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get optionsEmbedLyricsSubtitle => '다운로드된 트랙과 함께 동기화된 가사를 저장합니다';
@override
String get optionsMaxQualityCover => '고품질 표지 이미지';
@override
String get optionsMaxQualityCoverSubtitle => '최고 해상도의 표지 이미지를 다운로드';
@override
String get optionsReplayGain => '리플레이게인';
-14
View File
@@ -148,13 +148,6 @@ class AppLocalizationsPt extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Embed synced lyrics into FLAC files';
@override
String get optionsMaxQualityCover => 'Max Quality Cover';
@override
String get optionsMaxQualityCoverSubtitle =>
'Download highest resolution cover art';
@override
String get optionsReplayGain => 'ReplayGain';
@@ -5089,13 +5082,6 @@ class AppLocalizationsPtPt extends AppLocalizationsPt {
String get optionsEmbedLyricsSubtitle =>
'Salve letras sincronizadas ao lado das suas faixas baixadas';
@override
String get optionsMaxQualityCover => 'Capa de Qualidade Máxima';
@override
String get optionsMaxQualityCoverSubtitle =>
'Baixar capa do álbum com a mais alta resolução';
@override
String get optionsReplayGain => 'ReplayGain';
-7
View File
@@ -150,13 +150,6 @@ class AppLocalizationsRu extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Сохранять синхронизированный текст песни рядом с загруженным треком';
@override
String get optionsMaxQualityCover => 'Максимальное качество обложки';
@override
String get optionsMaxQualityCoverSubtitle =>
'Скачивать обложку в макс. разрешении';
@override
String get optionsReplayGain => 'ReplayGain';
-7
View File
@@ -151,13 +151,6 @@ class AppLocalizationsTr extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'İndirdiğiniz parçaların yanına senkronize edilmiş şarkı sözlerini kaydedin';
@override
String get optionsMaxQualityCover => 'En Yüksek Kapak Kalitesi';
@override
String get optionsMaxQualityCoverSubtitle =>
'En yüksek kalitedeki albüm kapaklarını indir';
@override
String get optionsReplayGain => 'ReplayGain';
-7
View File
@@ -151,13 +151,6 @@ class AppLocalizationsUk extends AppLocalizations {
String get optionsEmbedLyricsSubtitle =>
'Save synced lyrics alongside your downloaded tracks';
@override
String get optionsMaxQualityCover => 'Максимальна якість обкладинки';
@override
String get optionsMaxQualityCoverSubtitle =>
'Завантажити обкладинку з найвищою роздільною здатністю';
@override
String get optionsReplayGain => 'Нормалізація звуку';
-8
View File
@@ -922,9 +922,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3249,7 +3246,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "Bibliotheksordner",
"optionsMaxQualityCoverSubtitle": "Cover in höchster Auflösung herunterladen",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5476,9 +5472,6 @@
"nowPlayingDetails": "Details",
"collectionWishlistEmptyTitle": "Wunschliste ist leer",
"downloadAppleQqMultiPersonEnabled": "Sängerlabel für Duette und Gruppentitel enthalten",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5822,7 +5815,6 @@
"downloadFrom": "Herunterladen von",
"collectionWishlistEmptySubtitle": "Tippe auf das + bei den Titeln, um sie zum späteren Herunterladen zu speichern",
"cueSplitSplitting": "CUE-Sheet wird geteilt... ({current}/{total})",
"optionsMaxQualityCover": "Maximale Cover-Qualität",
"trackOptionAddToWishlist": "Zur Wunschliste hinzufügen",
"settingsBackup": "Backup & Restore",
"aboutPaxsenixSubtitle": "Lyrics proxy for Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou, and Genius",
-8
View File
@@ -182,14 +182,6 @@
"@optionsEmbedLyricsSubtitle": {
"description": "Subtitle for embed lyrics"
},
"optionsMaxQualityCover": "Max Quality Cover",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"optionsMaxQualityCoverSubtitle": "Download highest resolution cover art",
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"optionsReplayGain": "ReplayGain",
"@optionsReplayGain": {
"description": "Title for ReplayGain setting toggle"
-8
View File
@@ -150,14 +150,6 @@
"@optionsEmbedLyricsSubtitle": {
"description": "Subtitle for embed lyrics"
},
"optionsMaxQualityCover": "Max Quality Cover",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"optionsMaxQualityCoverSubtitle": "Download highest resolution cover art",
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"optionsExtensionStore": "Extension Repo",
"@optionsExtensionStore": {
"description": "Show/hide store tab"
-8
View File
@@ -182,14 +182,6 @@
"@optionsEmbedLyricsSubtitle": {
"description": "Subtitle for embed lyrics"
},
"optionsMaxQualityCover": "Máxima calidad de portada",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"optionsMaxQualityCoverSubtitle": "Descargar la portada en la mayor resolución",
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"optionsReplayGain": "ReplayGain",
"@optionsReplayGain": {
"description": "Title for ReplayGain setting toggle"
-8
View File
@@ -922,9 +922,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3249,7 +3246,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "Dossier de bibliothèque",
"optionsMaxQualityCoverSubtitle": "Télécharger la pochette en haute résolution",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5476,9 +5472,6 @@
"nowPlayingDetails": "Détails",
"collectionWishlistEmptyTitle": "La liste de souhaits est vide",
"downloadAppleQqMultiPersonEnabled": "Étiquettes d'intervenants incluses pour les duos et les morceaux en groupe",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5822,7 +5815,6 @@
"downloadFrom": "Télécharger depuis",
"collectionWishlistEmptySubtitle": "Appuyez sur le signe « + » à côté des morceaux pour enregistrer ceux que vous souhaitez télécharger plus tard",
"cueSplitSplitting": "Fractionnement de la liste CUE... ({current}/{total})",
"optionsMaxQualityCover": "Pochette de qualité supérieure",
"trackOptionAddToWishlist": "Ajouter à la liste de souhaits",
"settingsBackup": "Sauvegarde & Restauration",
"aboutPaxsenixSubtitle": "Proxy de paroles pour Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou et Genius",
-8
View File
@@ -941,9 +941,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3312,7 +3309,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "Library Folder",
"optionsMaxQualityCoverSubtitle": "Unduh cover art resolusi tertinggi",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5587,9 +5583,6 @@
"nowPlayingDetails": "Details",
"collectionWishlistEmptyTitle": "Wishlist is empty",
"downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5933,7 +5926,6 @@
"downloadFrom": "Unduh Dari",
"collectionWishlistEmptySubtitle": "Tap + on tracks to save what you want to download later",
"cueSplitSplitting": "Splitting CUE sheet... ({current}/{total})",
"optionsMaxQualityCover": "Cover Kualitas Maksimal",
"trackOptionAddToWishlist": "Add to Wishlist",
"settingsBackup": "Backup & Restore",
"aboutPaxsenixSubtitle": "Lyrics proxy for Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou, and Genius",
-8
View File
@@ -922,9 +922,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3249,7 +3246,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "ライブラリのフォルダ",
"optionsMaxQualityCoverSubtitle": "最高解像度のカバーアートをダウンロード",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5476,9 +5472,6 @@
"nowPlayingDetails": "Details",
"collectionWishlistEmptyTitle": "Wishlist is empty",
"downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5822,7 +5815,6 @@
"downloadFrom": "ダウンロード元",
"collectionWishlistEmptySubtitle": "Tap + on tracks to save what you want to download later",
"cueSplitSplitting": "Splitting CUE sheet... ({current}/{total})",
"optionsMaxQualityCover": "最大品質のカバー",
"trackOptionAddToWishlist": "ウィッシュリストに追加",
"settingsBackup": "Backup & Restore",
"aboutPaxsenixSubtitle": "Lyrics proxy for Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou, and Genius",
-8
View File
@@ -182,14 +182,6 @@
"@optionsEmbedLyricsSubtitle": {
"description": "Subtitle for embed lyrics"
},
"optionsMaxQualityCover": "고품질 표지 이미지",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"optionsMaxQualityCoverSubtitle": "최고 해상도의 표지 이미지를 다운로드",
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"optionsReplayGain": "리플레이게인",
"@optionsReplayGain": {
"description": "Title for ReplayGain setting toggle"
-8
View File
@@ -150,14 +150,6 @@
"@optionsEmbedLyricsSubtitle": {
"description": "Subtitle for embed lyrics"
},
"optionsMaxQualityCover": "Max Quality Cover",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"optionsMaxQualityCoverSubtitle": "Download highest resolution cover art",
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"optionsExtensionStore": "Extension Repo",
"@optionsExtensionStore": {
"description": "Show/hide store tab"
-8
View File
@@ -922,9 +922,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3249,7 +3246,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "Library Folder",
"optionsMaxQualityCoverSubtitle": "Baixar capa do álbum com a mais alta resolução",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5476,9 +5472,6 @@
"nowPlayingDetails": "Details",
"collectionWishlistEmptyTitle": "Wishlist is empty",
"downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5822,7 +5815,6 @@
"downloadFrom": "Baixar De",
"collectionWishlistEmptySubtitle": "Tap + on tracks to save what you want to download later",
"cueSplitSplitting": "Splitting CUE sheet... ({current}/{total})",
"optionsMaxQualityCover": "Capa de Qualidade Máxima",
"trackOptionAddToWishlist": "Add to Wishlist",
"settingsBackup": "Backup & Restore",
"aboutPaxsenixSubtitle": "Lyrics proxy for Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou, and Genius",
-8
View File
@@ -922,9 +922,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3249,7 +3246,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "Папка библиотеки",
"optionsMaxQualityCoverSubtitle": "Скачивать обложку в макс. разрешении",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5476,9 +5472,6 @@
"nowPlayingDetails": "Details",
"collectionWishlistEmptyTitle": "Список желаний пуст",
"downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5822,7 +5815,6 @@
"downloadFrom": "Скачивать из",
"collectionWishlistEmptySubtitle": "Нажмите + на треках, чтобы сохранить то, что вы хотите скачать позже",
"cueSplitSplitting": "Разделение CUE sheet... ({current}/{total})",
"optionsMaxQualityCover": "Максимальное качество обложки",
"trackOptionAddToWishlist": "Добавить в список желаний",
"settingsBackup": "Backup & Restore",
"aboutPaxsenixSubtitle": "Lyrics proxy for Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou, and Genius",
-8
View File
@@ -922,9 +922,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3249,7 +3246,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "Kitaplık Klasörü",
"optionsMaxQualityCoverSubtitle": "En yüksek kalitedeki albüm kapaklarını indir",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5476,9 +5472,6 @@
"nowPlayingDetails": "Details",
"collectionWishlistEmptyTitle": "İstek listesi boş",
"downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5822,7 +5815,6 @@
"downloadFrom": "İndirme Kaynağı",
"collectionWishlistEmptySubtitle": "Daha sonra indirmek istediğiniz parçaları kaydetmek için parçaların üzerine + işaretiyle dokunun",
"cueSplitSplitting": "CUE sayfası bölünüyor... ({current}/{total})",
"optionsMaxQualityCover": "En Yüksek Kapak Kalitesi",
"trackOptionAddToWishlist": "Add to Wishlist",
"settingsBackup": "Backup & Restore",
"aboutPaxsenixSubtitle": "Lyrics proxy for Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou, and Genius",
-8
View File
@@ -922,9 +922,6 @@
"@audioAnalysisMono": {
"description": "Audio channel layout label - mono"
},
"@optionsMaxQualityCoverSubtitle": {
"description": "Subtitle for max quality cover"
},
"@audioAnalysisNyquist": {
"description": "Nyquist frequency metric label"
},
@@ -3249,7 +3246,6 @@
"description": "Tutorial extensions tip 2"
},
"libraryFolder": "Папка бібліотеки",
"optionsMaxQualityCoverSubtitle": "Завантажити обкладинку з найвищою роздільною здатністю",
"@metadataProviderPriorityTitle": {
"description": "Metadata priority page title"
},
@@ -5476,9 +5472,6 @@
"nowPlayingDetails": "Details",
"collectionWishlistEmptyTitle": "Список бажань порожній",
"downloadAppleQqMultiPersonEnabled": "Speaker labels included for duets and group tracks",
"@optionsMaxQualityCover": {
"description": "Download highest quality album art"
},
"@snackbarExtensionUpdatedVersion": {
"description": "Snackbar after updating an extension from the repo tab",
"placeholders": {
@@ -5822,7 +5815,6 @@
"downloadFrom": "Завантажити з",
"collectionWishlistEmptySubtitle": "Натисніть + на треках, щоб зберегти те, що ви хочете завантажити пізніше",
"cueSplitSplitting": "Розділення аркуша CUE... ({current}/{total})",
"optionsMaxQualityCover": "Максимальна якість обкладинки",
"trackOptionAddToWishlist": "Додати до списку бажань",
"settingsBackup": "Backup & Restore",
"aboutPaxsenixSubtitle": "Lyrics proxy for Musixmatch, Netease, Apple Music, QQ Music, Spotify, Deezer, YouTube, Kugou, and Genius",
-4
View File
@@ -25,7 +25,6 @@ class AppSettings {
final bool embedReplayGain;
// Apply ReplayGain/R128 tags as volume normalization in the built-in player.
final bool playbackNormalization;
final bool maxQualityCover;
final bool isFirstLaunch;
final bool checkForUpdates;
final String updateChannel;
@@ -134,7 +133,6 @@ class AppSettings {
this.embedLyrics = true,
this.embedReplayGain = false,
this.playbackNormalization = false,
this.maxQualityCover = true,
this.isFirstLaunch = true,
this.checkForUpdates = true,
this.updateChannel = 'stable',
@@ -209,7 +207,6 @@ class AppSettings {
bool? embedLyrics,
bool? embedReplayGain,
bool? playbackNormalization,
bool? maxQualityCover,
bool? isFirstLaunch,
bool? checkForUpdates,
String? updateChannel,
@@ -288,7 +285,6 @@ class AppSettings {
embedReplayGain: embedReplayGain ?? this.embedReplayGain,
playbackNormalization:
playbackNormalization ?? this.playbackNormalization,
maxQualityCover: maxQualityCover ?? this.maxQualityCover,
isFirstLaunch: isFirstLaunch ?? this.isFirstLaunch,
checkForUpdates: checkForUpdates ?? this.checkForUpdates,
updateChannel: updateChannel ?? this.updateChannel,
-2
View File
@@ -20,7 +20,6 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
embedLyrics: json['embedLyrics'] as bool? ?? true,
embedReplayGain: json['embedReplayGain'] as bool? ?? false,
playbackNormalization: json['playbackNormalization'] as bool? ?? false,
maxQualityCover: json['maxQualityCover'] as bool? ?? true,
isFirstLaunch: json['isFirstLaunch'] as bool? ?? true,
checkForUpdates: json['checkForUpdates'] as bool? ?? true,
updateChannel: json['updateChannel'] as String? ?? 'stable',
@@ -116,7 +115,6 @@ Map<String, dynamic> _$AppSettingsToJson(
'embedLyrics': instance.embedLyrics,
'embedReplayGain': instance.embedReplayGain,
'playbackNormalization': instance.playbackNormalization,
'maxQualityCover': instance.maxQualityCover,
'isFirstLaunch': instance.isFirstLaunch,
'checkForUpdates': instance.checkForUpdates,
'updateChannel': instance.updateChannel,
@@ -705,7 +705,6 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
settings.embedMetadata &&
settings.embedLyrics &&
!_shouldSkipLyrics(extensionState, track.source, item.service),
embedMaxQualityCover: settings.embedMetadata && settings.maxQualityCover,
embedReplayGain: settings.embedReplayGain,
postProcessingEnabled: postProcessingEnabled,
tidalHighFormat: settings.tidalHighFormat,
@@ -657,10 +657,7 @@ extension _DownloadQueueEmbedding on DownloadQueueNotifier {
// Started here, awaited only after the lyrics fetch below so the two
// network round trips overlap. Errors are handled inside the fetch
// (it resolves to null), never as an unhandled rejection.
coverFuture = _sharedEmbedCover(
coverUrl,
maxQuality: settings.maxQualityCover,
);
coverFuture = _sharedEmbedCover(coverUrl);
}
String? lrcContent;
@@ -1021,33 +1018,24 @@ extension _DownloadQueueEmbedding on DownloadQueueNotifier {
static const _embedCoverCacheMax = 8;
/// One cover fetch per URL, shared by every track in the batch.
Future<String?> _sharedEmbedCover(
String coverUrl, {
required bool maxQuality,
}) {
final cacheKey = '${maxQuality ? 'max' : 'original'}|$coverUrl';
final existing = _embedCoverCache.remove(cacheKey);
Future<String?> _sharedEmbedCover(String coverUrl) {
final existing = _embedCoverCache.remove(coverUrl);
if (existing != null) {
_embedCoverCache[cacheKey] = existing; // LRU touch
_embedCoverCache[coverUrl] = existing; // LRU touch
return existing;
}
final fetch = _downloadEmbedCover(coverUrl, maxQuality: maxQuality).then((
path,
) {
if (path == null) _embedCoverCache.remove(cacheKey); // allow retry
final fetch = _downloadEmbedCover(coverUrl).then((path) {
if (path == null) _embedCoverCache.remove(coverUrl); // allow retry
return path;
});
_embedCoverCache[cacheKey] = fetch;
_embedCoverCache[coverUrl] = fetch;
while (_embedCoverCache.length > _embedCoverCacheMax) {
_evictEmbedCover(_embedCoverCache.keys.first);
}
return fetch;
}
Future<String?> _downloadEmbedCover(
String coverUrl, {
required bool maxQuality,
}) async {
Future<String?> _downloadEmbedCover(String coverUrl) async {
try {
final tempDir = await getTemporaryDirectory();
final uniqueId =
@@ -1058,7 +1046,6 @@ extension _DownloadQueueEmbedding on DownloadQueueNotifier {
final result = await PlatformBridge.downloadCoverToFile(
coverUrl,
coverPath,
maxQuality: maxQuality,
);
if (result['error'] != null) {
_log.w('Failed to download cover: ${result['error']}');
-5
View File
@@ -576,11 +576,6 @@ class SettingsNotifier extends Notifier<AppSettings> {
_syncLyricsSettingsToBackend();
}
void setMaxQualityCover(bool enabled) {
state = state.copyWith(maxQualityCover: enabled);
_saveSettings();
}
void setFirstLaunchComplete() {
state = state.copyWith(isFirstLaunch: false);
_saveSettings();
@@ -52,15 +52,6 @@ class MetadataSettingsPage extends ConsumerWidget {
settings.artistTagMode,
),
),
SettingsSwitchItem(
icon: Icons.image,
title: context.l10n.optionsMaxQualityCover,
subtitle: context.l10n.optionsMaxQualityCoverSubtitle,
value: settings.maxQualityCover,
onChanged: (v) => ref
.read(settingsProvider.notifier)
.setMaxQualityCover(v),
),
SettingsSwitchItem(
icon: Icons.graphic_eq,
title: context.l10n.optionsReplayGain,
@@ -233,12 +233,6 @@ class SettingsSearchCatalog {
title: l10n.optionsArtistTagMode,
keywords: const ['artist separator', 'multiple artists'],
),
SettingsSearchEntry(
icon: Icons.image,
title: l10n.optionsMaxQualityCover,
subtitle: l10n.optionsMaxQualityCoverSubtitle,
keywords: const ['artwork', 'album art', 'cover size'],
),
SettingsSearchEntry(
icon: Icons.graphic_eq,
title: l10n.optionsReplayGain,
+3 -8
View File
@@ -37,11 +37,11 @@ class _MetadataCandidateArtworkState extends State<_MetadataCandidateArtwork> {
void initState() {
super.initState();
if (widget.coverUrl?.isNotEmpty == true) {
unawaited(_loadMaxQualityCover());
unawaited(_loadCover());
}
}
Future<void> _loadMaxQualityCover() async {
Future<void> _loadCover() async {
final tempDir = await Directory.systemTemp.createTemp(
'metadata_candidate_cover_',
);
@@ -50,7 +50,6 @@ class _MetadataCandidateArtworkState extends State<_MetadataCandidateArtwork> {
final result = await PlatformBridge.downloadCoverToFile(
widget.coverUrl!,
path,
maxQuality: true,
);
if (result['error'] != null || !await File(path).exists()) {
await tempDir.delete(recursive: true);
@@ -588,11 +587,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> {
);
final coverPath = '${tempDir.path}${Platform.pathSeparator}cover.jpg';
try {
await PlatformBridge.downloadCoverToFile(
coverUrl,
coverPath,
maxQuality: true,
);
await PlatformBridge.downloadCoverToFile(coverUrl, coverPath);
final file = File(coverPath);
if (!await file.exists() || await file.length() <= 0) {
await tempDir.delete(recursive: true);
-5
View File
@@ -685,14 +685,12 @@ extension _TrackMetadataLyricsAndSaving on _TrackMetadataScreenState {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
tempOutput,
maxQuality: true,
);
}
} else if (_coverUrl != null && _coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
tempOutput,
maxQuality: true,
);
} else {
if (mounted) {
@@ -781,14 +779,12 @@ extension _TrackMetadataLyricsAndSaving on _TrackMetadataScreenState {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
maxQuality: true,
);
}
} else if (_coverUrl != null && _coverUrl!.isNotEmpty) {
result = await PlatformBridge.downloadCoverToFile(
_coverUrl!,
outputPath,
maxQuality: true,
);
} else {
if (mounted) {
@@ -996,7 +992,6 @@ extension _TrackMetadataLyricsAndSaving on _TrackMetadataScreenState {
final request = <String, dynamic>{
'file_path': cleanFilePath,
'cover_url': _coverUrl ?? '',
'max_quality': true,
'embed_lyrics': settings.embedLyrics,
'lyrics_mode': settings.lyricsMode,
'artist_tag_mode': artistTagMode,
@@ -81,7 +81,6 @@ Map<String, dynamic> buildBatchReEnrichRequest({
final request = <String, dynamic>{
'file_path': item.filePath,
'cover_url': '',
'max_quality': true,
'embed_lyrics': settings.embedLyrics,
'lyrics_mode': settings.lyricsMode,
'artist_tag_mode': settings.artistTagMode,
-1
View File
@@ -37,7 +37,6 @@ class CoverDownloadService {
final download = await PlatformBridge.downloadCoverToFile(
normalizedUrl,
tempPath,
maxQuality: true,
);
final error = download['error']?.toString().trim() ?? '';
if (error.isNotEmpty) throw StateError(error);
@@ -18,7 +18,6 @@ class DownloadRequestPayload {
final bool embedMetadata;
final String artistTagMode;
final bool embedLyrics;
final bool embedMaxQualityCover;
final bool embedReplayGain;
final bool postProcessingEnabled;
final String tidalHighFormat;
@@ -80,7 +79,6 @@ class DownloadRequestPayload {
this.embedMetadata = true,
this.artistTagMode = 'joined',
this.embedLyrics = true,
this.embedMaxQualityCover = true,
this.embedReplayGain = false,
this.postProcessingEnabled = false,
this.tidalHighFormat = 'mp3_320',
@@ -144,7 +142,6 @@ class DownloadRequestPayload {
'embed_metadata': embedMetadata,
'artist_tag_mode': artistTagMode,
'embed_lyrics': embedLyrics,
'embed_max_quality_cover': embedMaxQualityCover,
'embed_replaygain': embedReplayGain,
'post_processing_enabled': postProcessingEnabled,
'tidal_high_format': tidalHighFormat,
@@ -212,7 +209,6 @@ class DownloadRequestPayload {
embedMetadata: embedMetadata,
artistTagMode: artistTagMode,
embedLyrics: embedLyrics,
embedMaxQualityCover: embedMaxQualityCover,
embedReplayGain: embedReplayGain,
postProcessingEnabled: postProcessingEnabled,
tidalHighFormat: tidalHighFormat,
+2 -4
View File
@@ -899,13 +899,11 @@ class PlatformBridge {
static Future<Map<String, dynamic>> downloadCoverToFile(
String coverUrl,
String outputPath, {
bool maxQuality = true,
}) {
String outputPath,
) {
return _invokeMap('downloadCoverToFile', {
'cover_url': coverUrl,
'output_path': outputPath,
'max_quality': maxQuality,
});
}
-2
View File
@@ -864,7 +864,6 @@ void main() {
embedMetadata: false,
artistTagMode: artistTagModeSplitVorbis,
embedLyrics: false,
embedMaxQualityCover: false,
embedReplayGain: true,
postProcessingEnabled: true,
tidalHighFormat: 'opus_256',
@@ -924,7 +923,6 @@ void main() {
'embed_metadata': false,
'artist_tag_mode': artistTagModeSplitVorbis,
'embed_lyrics': false,
'embed_max_quality_cover': false,
'embed_replaygain': true,
'post_processing_enabled': true,
'tidal_high_format': 'opus_256',