librespot_metadata/audio/
file.rs1use 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, OGG_VORBIS_160, OGG_VORBIS_320, MP3_256, MP3_320, MP3_160, MP3_96, MP3_160_ENC, AAC_24, AAC_48, FLAC_FLAC, XHE_AAC_24, XHE_AAC_16, XHE_AAC_12, FLAC_FLAC_24BIT, AAC_160, AAC_320, MP4_128, OTHER5, }
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}