unify folder naming with resolved audio profiles across providers

This commit is contained in:
2026-04-24 00:35:38 +02:00
parent d5b336ca4e
commit 232901f3eb
11 changed files with 550 additions and 33 deletions

View File

@@ -531,7 +531,11 @@ func (c *Client) getDownloadableFromTrackManifestForFormats(ctx context.Context,
}
}
return &provider.Downloadable{URL: uri, Extension: ext, Source: "tidal"}, nil
profile := tidalAudioProfileFromFormats(attrFormats)
if profile.Container == "" {
profile = tidalAudioProfileFromExtension(ext)
}
return &provider.Downloadable{URL: uri, Extension: ext, Source: "tidal", Audio: profile}, nil
}
func formatsForQuality(quality int, preferAtmos bool) []string {
@@ -638,7 +642,121 @@ func downloadableFromPlaybackManifest(resp map[string]any) *provider.Downloadabl
} else if strings.Contains(codec, "ec-3") || strings.Contains(codec, "eac3") || strings.Contains(codec, "joc") || strings.Contains(codec, "atmos") {
ext = "mka"
}
return &provider.Downloadable{URL: streamURL, Extension: ext, Source: "tidal"}
profile := tidalAudioProfileFromCodec(codec)
if profile.Container == "" {
profile = tidalAudioProfileFromExtension(ext)
}
audioQuality := strings.ToUpper(strings.TrimSpace(stringify(resp["audioQuality"])))
if audioQuality == "" {
audioQuality = strings.ToUpper(strings.TrimSpace(stringify(manifest["audioQuality"])))
}
if audioQuality != "" {
profile = applyTidalAudioQuality(profile, audioQuality)
}
if strings.Contains(strings.ToUpper(stringify(resp["audioMode"])), "ATMOS") {
profile = tidalAtmosAudioProfile()
}
return &provider.Downloadable{URL: streamURL, Extension: ext, Source: "tidal", Audio: profile}
}
func tidalAudioProfileFromFormats(formats []any) provider.AudioProfile {
best := provider.AudioProfile{}
for _, raw := range formats {
f := strings.ToUpper(strings.TrimSpace(stringify(raw)))
switch {
case strings.Contains(f, "EAC3") || strings.Contains(f, "JOC") || strings.Contains(f, "ATMOS"):
return tidalAtmosAudioProfile()
case strings.Contains(f, "FLAC_HIRES"):
best = provider.AudioProfile{Container: "FLAC", Codec: "FLAC", Quality: "HI_RES_LOSSLESS", BitDepth: 24}
case strings.Contains(f, "FLAC"):
if best.Container == "" {
best = provider.AudioProfile{Container: "FLAC", Codec: "FLAC", Quality: "LOSSLESS", BitDepth: 16, SamplingRate: "44.1"}
}
case strings.Contains(f, "AACLC"):
if best.Container == "" {
best = provider.AudioProfile{Container: "M4A", Codec: "AACLC", Quality: "HIGH", BitDepth: 16, SamplingRate: "44.1", BitrateKbps: 320}
}
case strings.Contains(f, "HEAAC"):
if best.Container == "" {
best = provider.AudioProfile{Container: "M4A", Codec: "HEAACV1", Quality: "LOW", BitDepth: 16, SamplingRate: "44.1", BitrateKbps: 96}
}
}
}
return best
}
func tidalAudioProfileFromCodec(codec string) provider.AudioProfile {
c := strings.ToLower(strings.TrimSpace(codec))
switch {
case strings.Contains(c, "ec-3") || strings.Contains(c, "eac3") || strings.Contains(c, "joc") || strings.Contains(c, "atmos"):
return tidalAtmosAudioProfile()
case strings.Contains(c, "flac"):
return provider.AudioProfile{Container: "FLAC", Codec: "FLAC", Quality: "LOSSLESS", BitDepth: 16, SamplingRate: "44.1"}
case strings.Contains(c, "mp4a.40.5") || strings.Contains(c, "mp4a.40.29"):
return provider.AudioProfile{Container: "M4A", Codec: "HEAACV1", Quality: "LOW", BitDepth: 16, SamplingRate: "44.1", BitrateKbps: 96}
case strings.Contains(c, "mp4a") || strings.Contains(c, "aac"):
return provider.AudioProfile{Container: "M4A", Codec: "AACLC", Quality: "HIGH", BitDepth: 16, SamplingRate: "44.1", BitrateKbps: 320}
default:
return provider.AudioProfile{}
}
}
func tidalAtmosAudioProfile() provider.AudioProfile {
return provider.AudioProfile{Container: "MKA", Codec: "EAC3_JOC", Quality: "ATMOS", BitDepth: 24, SamplingRate: "48"}
}
func tidalAudioProfileFromExtension(ext string) provider.AudioProfile {
switch strings.ToLower(strings.TrimSpace(ext)) {
case "flac":
return provider.AudioProfile{Container: "FLAC", Codec: "FLAC", Quality: "LOSSLESS", BitDepth: 16, SamplingRate: "44.1"}
case "mka":
return tidalAtmosAudioProfile()
case "m4a":
return provider.AudioProfile{Container: "M4A", Codec: "AACLC", Quality: "HIGH", BitDepth: 16, SamplingRate: "44.1", BitrateKbps: 320}
default:
container := strings.ToUpper(strings.TrimSpace(ext))
if container == "" {
container = "M4A"
}
return provider.AudioProfile{Container: container, Codec: container}
}
}
func applyTidalAudioQuality(profile provider.AudioProfile, audioQuality string) provider.AudioProfile {
aq := strings.ToUpper(strings.TrimSpace(audioQuality))
if aq == "" {
return profile
}
profile.Quality = aq
switch aq {
case "HI_RES", "HI_RES_LOSSLESS":
if strings.EqualFold(profile.Container, "FLAC") {
if profile.BitDepth < 24 {
profile.BitDepth = 24
}
}
case "LOSSLESS":
if strings.EqualFold(profile.Container, "FLAC") {
if profile.BitDepth == 0 {
profile.BitDepth = 16
}
if profile.SamplingRate == "" {
profile.SamplingRate = "44.1"
}
}
case "HIGH":
if strings.EqualFold(profile.Container, "M4A") && profile.BitrateKbps == 0 {
profile.BitrateKbps = 320
}
case "LOW":
if strings.EqualFold(profile.Container, "M4A") {
profile.Codec = "HEAACV1"
if profile.BitrateKbps == 0 {
profile.BitrateKbps = 96
}
}
}
return profile
}
func bestHLSVariantURL(masterURL, playlist string) string {