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