librespot_metadata/audio/
file.rs

1use std::{
2    collections::HashMap,
3    fmt::Debug,
4    ops::{Deref, DerefMut},
5};
6
7use librespot_core::FileId;
8
9use crate::util::impl_deref_wrapped;
10use librespot_protocol as protocol;
11use protocol::metadata::AudioFile as AudioFileMessage;
12
13use librespot_protocol::metadata::audio_file::Format;
14use protobuf::Enum;
15
16#[allow(non_camel_case_types)]
17#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
18pub enum AudioFileFormat {
19    OGG_VORBIS_96,   // 0
20    OGG_VORBIS_160,  // 1
21    OGG_VORBIS_320,  // 2
22    MP3_256,         // 3
23    MP3_320,         // 4
24    MP3_160,         // 5
25    MP3_96,          // 6
26    MP3_160_ENC,     // 7
27    AAC_24,          // 8
28    AAC_48,          // 9
29    FLAC_FLAC,       // 16
30    XHE_AAC_24,      // 18
31    XHE_AAC_16,      // 19
32    XHE_AAC_12,      // 20
33    FLAC_FLAC_24BIT, // 22
34    // not defined in protobuf, but sometimes send
35    AAC_160, // 10
36    AAC_320, // 11
37    MP4_128, // 12
38    OTHER5,  // 13
39}
40
41impl TryFrom<i32> for AudioFileFormat {
42    type Error = i32;
43
44    fn try_from(value: i32) -> Result<Self, Self::Error> {
45        Ok(match value {
46            10 => AudioFileFormat::AAC_160,
47            11 => AudioFileFormat::AAC_320,
48            12 => AudioFileFormat::MP4_128,
49            13 => AudioFileFormat::OTHER5,
50            _ => Format::from_i32(value).ok_or(value)?.into(),
51        })
52    }
53}
54
55impl From<Format> for AudioFileFormat {
56    fn from(value: Format) -> Self {
57        match value {
58            Format::OGG_VORBIS_96 => AudioFileFormat::OGG_VORBIS_96,
59            Format::OGG_VORBIS_160 => AudioFileFormat::OGG_VORBIS_160,
60            Format::OGG_VORBIS_320 => AudioFileFormat::OGG_VORBIS_320,
61            Format::MP3_256 => AudioFileFormat::MP3_256,
62            Format::MP3_320 => AudioFileFormat::MP3_320,
63            Format::MP3_160 => AudioFileFormat::MP3_160,
64            Format::MP3_96 => AudioFileFormat::MP3_96,
65            Format::MP3_160_ENC => AudioFileFormat::MP3_160_ENC,
66            Format::AAC_24 => AudioFileFormat::AAC_24,
67            Format::AAC_48 => AudioFileFormat::AAC_48,
68            Format::FLAC_FLAC => AudioFileFormat::FLAC_FLAC,
69            Format::XHE_AAC_24 => AudioFileFormat::XHE_AAC_24,
70            Format::XHE_AAC_16 => AudioFileFormat::XHE_AAC_16,
71            Format::XHE_AAC_12 => AudioFileFormat::XHE_AAC_12,
72            Format::FLAC_FLAC_24BIT => AudioFileFormat::FLAC_FLAC_24BIT,
73        }
74    }
75}
76
77#[derive(Debug, Clone, Default)]
78pub struct AudioFiles(pub HashMap<AudioFileFormat, FileId>);
79
80impl_deref_wrapped!(AudioFiles, HashMap<AudioFileFormat, FileId>);
81
82impl AudioFiles {
83    pub fn is_ogg_vorbis(format: AudioFileFormat) -> bool {
84        matches!(
85            format,
86            AudioFileFormat::OGG_VORBIS_320
87                | AudioFileFormat::OGG_VORBIS_160
88                | AudioFileFormat::OGG_VORBIS_96
89        )
90    }
91
92    pub fn is_mp3(format: AudioFileFormat) -> bool {
93        matches!(
94            format,
95            AudioFileFormat::MP3_320
96                | AudioFileFormat::MP3_256
97                | AudioFileFormat::MP3_160
98                | AudioFileFormat::MP3_96
99                | AudioFileFormat::MP3_160_ENC
100        )
101    }
102
103    pub fn is_flac(format: AudioFileFormat) -> bool {
104        matches!(format, AudioFileFormat::FLAC_FLAC)
105    }
106}
107
108impl From<&[AudioFileMessage]> for AudioFiles {
109    fn from(files: &[AudioFileMessage]) -> Self {
110        let audio_files: HashMap<AudioFileFormat, FileId> = files
111            .iter()
112            .filter_map(|file| {
113                let file_id = FileId::from(file.file_id());
114                let format = file
115                    .format
116                    .ok_or(format!("Ignoring file <{file_id}> with unspecified format",))
117                    .and_then(|format| match format.enum_value() {
118                        Ok(f) => Ok((f.into(), file_id)),
119                        Err(unknown) => Err(format!(
120                            "Ignoring file <{file_id}> with unknown format {unknown}",
121                        )),
122                    });
123
124                if let Err(ref why) = format {
125                    trace!("{why}");
126                }
127
128                format.ok()
129            })
130            .collect();
131
132        AudioFiles(audio_files)
133    }
134}