1extern crate alloc;
7
8use alloc::collections::BTreeMap;
9use alloc::string::{String, ToString};
10use alloc::vec;
11use alloc::vec::Vec;
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
20#[serde(rename_all = "snake_case")]
21pub enum PictureType {
22 Other,
23 FileIcon, OtherFileIcon,
25 #[default]
26 CoverFront,
27 CoverBack,
28 LeafletPage,
29 Media, LeadArtist,
31 Artist,
32 Conductor,
33 Band,
34 Composer,
35 Lyricist,
36 RecordingLocation,
37 DuringRecording,
38 DuringPerformance,
39 VideoScreenCapture,
40 BrightColouredFish, Illustration,
42 BandLogo,
43 PublisherLogo,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct Picture {
49 pub mime_type: String,
51 pub picture_type: PictureType,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub description: Option<String>,
56 #[serde(with = "serde_bytes")]
58 pub data: Vec<u8>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct Comment {
68 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub language: Option<String>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub description: Option<String>,
74 pub text: String,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct Lyrics {
81 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pub language: Option<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
86 pub description: Option<String>,
87 pub text: String,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
93#[serde(rename_all = "snake_case")]
94pub enum SyncedLyricsContentType {
95 Other,
96 #[default]
97 Lyrics,
98 TextTranscription,
99 PartName, Events, Chord, Trivia, WebpageUrl,
104 ImageUrl,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct SyncedLyricsLine {
110 pub timestamp_ms: u64,
112 pub text: String,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct SyncedLyrics {
119 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub language: Option<String>,
122 #[serde(default)]
124 pub content_type: SyncedLyricsContentType,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub description: Option<String>,
128 pub lines: Vec<SyncedLyricsLine>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct UserText {
135 pub description: String,
137 pub value: String,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct UserUrl {
144 pub description: String,
146 pub url: String,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct Popularimeter {
153 #[serde(default, skip_serializing_if = "Option::is_none")]
155 pub email: Option<String>,
156 pub rating: u8,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
160 pub play_count: Option<u64>,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct WaveformData {
170 pub peaks_per_second: u32,
172 pub peaks: Vec<f32>,
174 #[serde(default = "default_one")]
176 pub channels: u8,
177}
178
179fn default_one() -> u8 {
180 1
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
185#[serde(rename_all = "snake_case")]
186pub enum SectionType {
187 Intro,
188 Verse,
189 PreChorus,
190 Chorus,
191 PostChorus,
192 Bridge,
193 Breakdown,
194 Drop,
195 Buildup,
196 Solo,
197 Instrumental,
198 Outro,
199 Silence,
200 Other,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct SectionMarker {
206 pub timestamp_ms: u64,
208 pub section_type: SectionType,
210 #[serde(default, skip_serializing_if = "Option::is_none")]
212 pub label: Option<String>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct BpmChange {
218 pub timestamp_ms: u64,
220 pub bpm: f32,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct KeyChange {
227 pub timestamp_ms: u64,
229 pub key: String,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct LoudnessPoint {
236 pub timestamp_ms: u64,
238 pub lufs: f32,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct CreatorNote {
245 #[serde(default, skip_serializing_if = "Option::is_none")]
247 pub timestamp_ms: Option<u64>,
248 pub text: String,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct CollaborationCredit {
255 pub role: String,
257 pub name: String,
259 #[serde(default, skip_serializing_if = "Option::is_none")]
261 pub timestamp_ms: Option<u64>,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct RemixChainEntry {
267 pub title: String,
269 pub artist: String,
271 #[serde(default, skip_serializing_if = "Option::is_none")]
273 pub year: Option<u32>,
274 #[serde(default, skip_serializing_if = "Option::is_none")]
276 pub isrc: Option<String>,
277 pub relationship: String,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct AnimatedCover {
284 pub mime_type: String,
286 #[serde(with = "serde_bytes")]
288 pub data: Vec<u8>,
289 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub duration_ms: Option<u32>,
292 #[serde(default, skip_serializing_if = "Option::is_none")]
294 pub loop_count: Option<u32>,
295}
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
299#[serde(rename_all = "snake_case")]
300pub enum CoverVariantType {
301 Standard,
302 Explicit,
303 Clean,
304 Remix,
305 Deluxe,
306 Limited,
307 Vinyl,
308 Cassette,
309 Digital,
310 Other,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct CoverVariant {
316 pub variant_type: CoverVariantType,
318 pub mime_type: String,
320 #[serde(with = "serde_bytes")]
322 pub data: Vec<u8>,
323 #[serde(default, skip_serializing_if = "Option::is_none")]
325 pub description: Option<String>,
326}
327
328#[derive(Debug, Clone, Default, Serialize, Deserialize)]
333pub struct FloMetadata {
334 #[serde(default, skip_serializing_if = "Option::is_none")]
337 pub title: Option<String>,
338
339 #[serde(default, skip_serializing_if = "Option::is_none")]
341 pub subtitle: Option<String>,
342
343 #[serde(default, skip_serializing_if = "Option::is_none")]
345 pub content_group: Option<String>,
346
347 #[serde(default, skip_serializing_if = "Option::is_none")]
349 pub album: Option<String>,
350
351 #[serde(default, skip_serializing_if = "Option::is_none")]
353 pub original_album: Option<String>,
354
355 #[serde(default, skip_serializing_if = "Option::is_none")]
357 pub set_subtitle: Option<String>,
358
359 #[serde(default, skip_serializing_if = "Option::is_none")]
361 pub track_number: Option<u32>,
362
363 #[serde(default, skip_serializing_if = "Option::is_none")]
365 pub track_total: Option<u32>,
366
367 #[serde(default, skip_serializing_if = "Option::is_none")]
369 pub disc_number: Option<u32>,
370
371 #[serde(default, skip_serializing_if = "Option::is_none")]
373 pub disc_total: Option<u32>,
374
375 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub isrc: Option<String>,
378
379 #[serde(default, skip_serializing_if = "Option::is_none")]
382 pub artist: Option<String>,
383
384 #[serde(default, skip_serializing_if = "Option::is_none")]
386 pub album_artist: Option<String>,
387
388 #[serde(default, skip_serializing_if = "Option::is_none")]
390 pub conductor: Option<String>,
391
392 #[serde(default, skip_serializing_if = "Option::is_none")]
394 pub remixer: Option<String>,
395
396 #[serde(default, skip_serializing_if = "Option::is_none")]
398 pub original_artist: Option<String>,
399
400 #[serde(default, skip_serializing_if = "Option::is_none")]
402 pub composer: Option<String>,
403
404 #[serde(default, skip_serializing_if = "Option::is_none")]
406 pub lyricist: Option<String>,
407
408 #[serde(default, skip_serializing_if = "Option::is_none")]
410 pub original_lyricist: Option<String>,
411
412 #[serde(default, skip_serializing_if = "Option::is_none")]
414 pub encoded_by: Option<String>,
415
416 #[serde(default, skip_serializing_if = "Option::is_none")]
418 pub involved_people: Option<Vec<(String, String)>>,
419
420 #[serde(default, skip_serializing_if = "Option::is_none")]
422 pub musician_credits: Option<Vec<(String, String)>>,
423
424 #[serde(default, skip_serializing_if = "Option::is_none")]
427 pub genre: Option<String>,
428
429 #[serde(default, skip_serializing_if = "Option::is_none")]
431 pub mood: Option<String>,
432
433 #[serde(default, skip_serializing_if = "Option::is_none")]
435 pub bpm: Option<u32>,
436
437 #[serde(default, skip_serializing_if = "Option::is_none")]
439 pub key: Option<String>,
440
441 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub language: Option<String>,
444
445 #[serde(default, skip_serializing_if = "Option::is_none")]
447 pub length_ms: Option<u64>,
448
449 #[serde(default, skip_serializing_if = "Option::is_none")]
452 pub year: Option<u32>,
453
454 #[serde(default, skip_serializing_if = "Option::is_none")]
456 pub recording_time: Option<String>,
457
458 #[serde(default, skip_serializing_if = "Option::is_none")]
460 pub release_time: Option<String>,
461
462 #[serde(default, skip_serializing_if = "Option::is_none")]
464 pub original_release_time: Option<String>,
465
466 #[serde(default, skip_serializing_if = "Option::is_none")]
468 pub encoding_time: Option<String>,
469
470 #[serde(default, skip_serializing_if = "Option::is_none")]
472 pub tagging_time: Option<String>,
473
474 #[serde(default, skip_serializing_if = "Option::is_none")]
477 pub copyright: Option<String>,
478
479 #[serde(default, skip_serializing_if = "Option::is_none")]
481 pub produced_notice: Option<String>,
482
483 #[serde(default, skip_serializing_if = "Option::is_none")]
485 pub publisher: Option<String>,
486
487 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub file_owner: Option<String>,
490
491 #[serde(default, skip_serializing_if = "Option::is_none")]
493 pub radio_station: Option<String>,
494
495 #[serde(default, skip_serializing_if = "Option::is_none")]
497 pub radio_station_owner: Option<String>,
498
499 #[serde(default, skip_serializing_if = "Option::is_none")]
502 pub album_sort: Option<String>,
503
504 #[serde(default, skip_serializing_if = "Option::is_none")]
506 pub artist_sort: Option<String>,
507
508 #[serde(default, skip_serializing_if = "Option::is_none")]
510 pub title_sort: Option<String>,
511
512 #[serde(default, skip_serializing_if = "Option::is_none")]
515 pub original_filename: Option<String>,
516
517 #[serde(default, skip_serializing_if = "Option::is_none")]
519 pub playlist_delay: Option<u32>,
520
521 #[serde(default, skip_serializing_if = "Option::is_none")]
523 pub encoder_settings: Option<String>,
524
525 #[serde(default, skip_serializing_if = "Option::is_none")]
528 pub url_commercial: Option<String>,
529
530 #[serde(default, skip_serializing_if = "Option::is_none")]
532 pub url_copyright: Option<String>,
533
534 #[serde(default, skip_serializing_if = "Option::is_none")]
536 pub url_audio_file: Option<String>,
537
538 #[serde(default, skip_serializing_if = "Option::is_none")]
540 pub url_artist: Option<String>,
541
542 #[serde(default, skip_serializing_if = "Option::is_none")]
544 pub url_audio_source: Option<String>,
545
546 #[serde(default, skip_serializing_if = "Option::is_none")]
548 pub url_radio_station: Option<String>,
549
550 #[serde(default, skip_serializing_if = "Option::is_none")]
552 pub url_payment: Option<String>,
553
554 #[serde(default, skip_serializing_if = "Option::is_none")]
556 pub url_publisher: Option<String>,
557
558 #[serde(default, skip_serializing_if = "Vec::is_empty")]
560 pub user_urls: Vec<UserUrl>,
561
562 #[serde(default, skip_serializing_if = "Vec::is_empty")]
565 pub comments: Vec<Comment>,
566
567 #[serde(default, skip_serializing_if = "Vec::is_empty")]
569 pub lyrics: Vec<Lyrics>,
570
571 #[serde(default, skip_serializing_if = "Vec::is_empty")]
573 pub synced_lyrics: Vec<SyncedLyrics>,
574
575 #[serde(default, skip_serializing_if = "Vec::is_empty")]
577 pub pictures: Vec<Picture>,
578
579 #[serde(default, skip_serializing_if = "Vec::is_empty")]
581 pub user_text: Vec<UserText>,
582
583 #[serde(default, skip_serializing_if = "Option::is_none")]
585 pub play_count: Option<u64>,
586
587 #[serde(default, skip_serializing_if = "Option::is_none")]
589 pub popularimeter: Option<Popularimeter>,
590
591 #[serde(default, skip_serializing_if = "Option::is_none")]
594 pub waveform_data: Option<WaveformData>,
595
596 #[serde(default, skip_serializing_if = "Option::is_none")]
598 #[serde(with = "serde_bytes_option")]
599 pub spectrum_fingerprint: Option<Vec<u8>>,
600
601 #[serde(default, skip_serializing_if = "Vec::is_empty")]
604 pub bpm_map: Vec<BpmChange>,
605
606 #[serde(default, skip_serializing_if = "Vec::is_empty")]
608 pub key_changes: Vec<KeyChange>,
609
610 #[serde(default, skip_serializing_if = "Vec::is_empty")]
612 pub loudness_profile: Vec<LoudnessPoint>,
613
614 #[serde(default, skip_serializing_if = "Option::is_none")]
616 pub integrated_loudness_lufs: Option<f32>,
617
618 #[serde(default, skip_serializing_if = "Option::is_none")]
620 pub loudness_range_lu: Option<f32>,
621
622 #[serde(default, skip_serializing_if = "Option::is_none")]
624 pub true_peak_dbtp: Option<f32>,
625
626 #[serde(default, skip_serializing_if = "Vec::is_empty")]
628 pub section_markers: Vec<SectionMarker>,
629
630 #[serde(default, skip_serializing_if = "Vec::is_empty")]
633 pub creator_notes: Vec<CreatorNote>,
634
635 #[serde(default, skip_serializing_if = "Vec::is_empty")]
637 pub collaboration_credits: Vec<CollaborationCredit>,
638
639 #[serde(default, skip_serializing_if = "Vec::is_empty")]
641 pub remix_chain: Vec<RemixChainEntry>,
642
643 #[serde(default, skip_serializing_if = "Option::is_none")]
646 pub animated_cover: Option<AnimatedCover>,
647
648 #[serde(default, skip_serializing_if = "Vec::is_empty")]
650 pub cover_variants: Vec<CoverVariant>,
651
652 #[serde(default, skip_serializing_if = "Option::is_none")]
654 pub artist_signature: Option<Picture>,
655
656 #[serde(default, skip_serializing_if = "Option::is_none")]
659 pub flo_encoder_version: Option<String>,
660
661 #[serde(default, skip_serializing_if = "Option::is_none")]
663 pub source_format: Option<String>,
664
665 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
667 pub custom: BTreeMap<String, String>,
668}
669
670mod serde_bytes_option {
672 use alloc::vec::Vec;
673 use serde::{Deserialize, Deserializer, Serialize, Serializer};
674
675 pub fn serialize<S>(data: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
676 where
677 S: Serializer,
678 {
679 match data {
680 Some(bytes) => serde_bytes::serialize(bytes, serializer),
681 None => Option::<&[u8]>::None.serialize(serializer),
682 }
683 }
684
685 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
686 where
687 D: Deserializer<'de>,
688 {
689 let opt: Option<serde_bytes::ByteBuf> = Option::deserialize(deserializer)?;
690 Ok(opt.map(|b| b.into_vec()))
691 }
692}
693
694impl FloMetadata {
695 pub fn new() -> Self {
697 Self::default()
698 }
699
700 pub fn with_basic(
702 title: Option<String>,
703 artist: Option<String>,
704 album: Option<String>,
705 ) -> Self {
706 Self {
707 title,
708 artist,
709 album,
710 ..Default::default()
711 }
712 }
713
714 pub fn to_msgpack(
716 &self,
717 ) -> Result<Vec<u8>, messagepack_serde::ser::Error<core::convert::Infallible>> {
718 messagepack_serde::to_vec(self)
719 }
720
721 pub fn from_msgpack(
723 data: &[u8],
724 ) -> Result<Self, messagepack_serde::de::Error<messagepack_serde::messagepack_core::io::RError>>
725 {
726 messagepack_serde::from_slice(data)
727 }
728
729 pub fn is_empty(&self) -> bool {
731 self.title.is_none()
732 && self.artist.is_none()
733 && self.album.is_none()
734 && self.pictures.is_empty()
735 && self.comments.is_empty()
736 && self.lyrics.is_empty()
737 && self.synced_lyrics.is_empty()
738 }
739
740 pub fn add_picture(&mut self, mime_type: &str, picture_type: PictureType, data: Vec<u8>) {
744 self.pictures.push(Picture {
745 mime_type: mime_type.to_string(),
746 picture_type,
747 description: None,
748 data,
749 });
750 }
751
752 pub fn front_cover(&self) -> Option<&Picture> {
754 self.pictures
755 .iter()
756 .find(|p| p.picture_type == PictureType::CoverFront)
757 }
758
759 pub fn any_picture(&self) -> Option<&Picture> {
761 self.pictures.first()
762 }
763
764 pub fn add_comment(&mut self, text: &str, language: Option<&str>) {
768 self.comments.push(Comment {
769 language: language.map(|s| s.to_string()),
770 description: None,
771 text: text.to_string(),
772 });
773 }
774
775 pub fn add_lyrics(&mut self, text: &str, language: Option<&str>) {
777 self.lyrics.push(Lyrics {
778 language: language.map(|s| s.to_string()),
779 description: None,
780 text: text.to_string(),
781 });
782 }
783
784 pub fn add_synced_lyrics_line(
786 &mut self,
787 timestamp_ms: u64,
788 text: &str,
789 language: Option<&str>,
790 ) {
791 let lang = language.map(|s| s.to_string());
792 if let Some(synced) = self.synced_lyrics.iter_mut().find(|s| s.language == lang) {
793 synced.lines.push(SyncedLyricsLine {
794 timestamp_ms,
795 text: text.to_string(),
796 });
797 } else {
798 self.synced_lyrics.push(SyncedLyrics {
799 language: lang,
800 content_type: SyncedLyricsContentType::Lyrics,
801 description: None,
802 lines: vec![SyncedLyricsLine {
803 timestamp_ms,
804 text: text.to_string(),
805 }],
806 });
807 }
808 }
809
810 pub fn set_custom(&mut self, key: &str, value: &str) {
814 self.custom.insert(key.to_string(), value.to_string());
815 }
816
817 pub fn get_custom(&self, key: &str) -> Option<&str> {
819 self.custom.get(key).map(|s| s.as_str())
820 }
821
822 pub fn add_section(
826 &mut self,
827 timestamp_ms: u64,
828 section_type: SectionType,
829 label: Option<&str>,
830 ) {
831 self.section_markers.push(SectionMarker {
832 timestamp_ms,
833 section_type,
834 label: label.map(|s| s.to_string()),
835 });
836 }
837
838 pub fn add_bpm_change(&mut self, timestamp_ms: u64, bpm: f32) {
840 self.bpm_map.push(BpmChange { timestamp_ms, bpm });
841 }
842
843 pub fn add_key_change(&mut self, timestamp_ms: u64, key: &str) {
845 self.key_changes.push(KeyChange {
846 timestamp_ms,
847 key: key.to_string(),
848 });
849 }
850
851 pub fn add_creator_note(&mut self, text: &str, timestamp_ms: Option<u64>) {
853 self.creator_notes.push(CreatorNote {
854 timestamp_ms,
855 text: text.to_string(),
856 });
857 }
858
859 pub fn add_collaboration(&mut self, role: &str, name: &str, timestamp_ms: Option<u64>) {
861 self.collaboration_credits.push(CollaborationCredit {
862 role: role.to_string(),
863 name: name.to_string(),
864 timestamp_ms,
865 });
866 }
867}