suno_core/config/
effective.rs1use std::collections::BTreeMap;
4
5use crate::naming::CharacterSet;
6use crate::vocab::{AudioFormat, StemFormat, VideoCoverRetention, WebpEncodeSettings};
7
8use super::shape::{AreasConfig, Settings};
9
10#[derive(Debug, Default)]
17pub struct FlagOverrides {
18 pub token: Option<String>,
19 pub settings: Settings,
20}
21
22#[derive(Debug, Clone, PartialEq)]
24pub struct EffectiveSettings {
25 pub token: Option<String>,
27 pub stored_token: Option<String>,
29 pub token_command: Option<String>,
31 pub account_id: Option<String>,
33 pub format: AudioFormat,
34 pub concurrency: u32,
35 pub retries: u32,
36 pub min_newest: u32,
37 pub animated_covers: bool,
38 pub raw_animated_cover: bool,
41 pub video_cover_retention: VideoCoverRetention,
42 pub animated_cover_webp: WebpEncodeSettings,
43 pub details_sidecar: bool,
44 pub lyrics_sidecar: bool,
45 pub lrc_sidecar: bool,
46 pub video_mp4: bool,
47 pub download_stems: bool,
48 pub stem_format: StemFormat,
49 pub naming_template: String,
50 pub character_set: CharacterSet,
51 pub areas: Option<AreasConfig>,
53 pub album_overrides: BTreeMap<String, String>,
57 pub lead_tracks: Vec<String>,
62 pub number_singletons: bool,
65}
66
67impl EffectiveSettings {
68 pub fn requires_ffmpeg(&self) -> bool {
74 matches!(self.format, AudioFormat::Flac | AudioFormat::Alac) || self.animated_covers
75 }
76}
77
78pub fn animated_covers_flag_overridden(flag: Option<bool>, effective_animated: bool) -> bool {
89 flag == Some(true) && !effective_animated
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::config::Config;
96 use crate::config::fixtures::{no_env, no_flags};
97
98 #[test]
99 fn audio_format_display_roundtrip() {
100 for fmt in [
101 AudioFormat::Mp3,
102 AudioFormat::Flac,
103 AudioFormat::Wav,
104 AudioFormat::Alac,
105 ] {
106 let s = fmt.to_string();
107 assert_eq!(s.parse::<AudioFormat>().unwrap(), fmt);
108 }
109 }
110
111 #[test]
112 fn audio_format_ext() {
113 assert_eq!(AudioFormat::Mp3.ext(), "mp3");
114 assert_eq!(AudioFormat::Flac.ext(), "flac");
115 assert_eq!(AudioFormat::Wav.ext(), "wav");
116 assert_eq!(AudioFormat::Alac.ext(), "m4a");
117 }
118
119 fn base_settings(format: AudioFormat) -> EffectiveSettings {
120 let toml = "[accounts.a]\n";
121 let cfg = Config::from_toml(toml).unwrap();
122 let mut eff = cfg.resolve("a", None, &no_env(), &no_flags()).unwrap();
123 eff.format = format;
124 eff
125 }
126
127 #[test]
128 fn requires_ffmpeg_flac_always_needs_it() {
129 let mut eff = base_settings(AudioFormat::Flac);
130 eff.animated_covers = false;
131 assert!(eff.requires_ffmpeg());
132 eff.animated_covers = true;
133 assert!(eff.requires_ffmpeg());
134 }
135
136 #[test]
137 fn requires_ffmpeg_alac_always_needs_it() {
138 let mut eff = base_settings(AudioFormat::Alac);
139 eff.animated_covers = false;
140 assert!(eff.requires_ffmpeg(), "alac transcodes, so needs ffmpeg");
141 }
142
143 #[test]
144 fn requires_ffmpeg_mp3_needs_it_only_for_animated_webp() {
145 let mut eff = base_settings(AudioFormat::Mp3);
146 assert!(!eff.requires_ffmpeg(), "mp3 + no covers = no ffmpeg");
147 eff.animated_covers = true;
148 assert!(eff.requires_ffmpeg(), "mp3 + animated webp = needs ffmpeg");
149 eff.raw_animated_cover = true;
150 assert!(
151 eff.requires_ffmpeg(),
152 "mp3 + both (webp + raw mp4) = needs ffmpeg"
153 );
154 eff.animated_covers = false;
155 assert!(!eff.requires_ffmpeg(), "mp3 + raw mp4 only = no ffmpeg");
156 }
157
158 #[test]
159 fn flag_override_detected_for_neither_and_mp4() {
160 assert!(animated_covers_flag_overridden(Some(true), false));
164 }
165
166 #[test]
167 fn no_override_for_webp_or_both() {
168 assert!(!animated_covers_flag_overridden(Some(true), true));
171 }
172
173 #[test]
174 fn no_override_when_flag_absent() {
175 assert!(!animated_covers_flag_overridden(None, false));
178 assert!(!animated_covers_flag_overridden(None, true));
179 }
180}