use std::{
collections::HashMap,
fmt::Debug,
ops::{Deref, DerefMut},
};
use librespot_core::FileId;
use crate::util::impl_deref_wrapped;
use librespot_protocol as protocol;
use protocol::metadata::AudioFile as AudioFileMessage;
use librespot_protocol::metadata::audio_file::Format;
use protobuf::Enum;
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum AudioFileFormat {
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, }
impl TryFrom<i32> for AudioFileFormat {
type Error = i32;
fn try_from(value: i32) -> Result<Self, Self::Error> {
Ok(match value {
10 => AudioFileFormat::AAC_160,
11 => AudioFileFormat::AAC_320,
12 => AudioFileFormat::MP4_128,
13 => AudioFileFormat::OTHER5,
_ => Format::from_i32(value).ok_or(value)?.into(),
})
}
}
impl From<Format> for AudioFileFormat {
fn from(value: Format) -> Self {
match value {
Format::OGG_VORBIS_96 => AudioFileFormat::OGG_VORBIS_96,
Format::OGG_VORBIS_160 => AudioFileFormat::OGG_VORBIS_160,
Format::OGG_VORBIS_320 => AudioFileFormat::OGG_VORBIS_320,
Format::MP3_256 => AudioFileFormat::MP3_256,
Format::MP3_320 => AudioFileFormat::MP3_320,
Format::MP3_160 => AudioFileFormat::MP3_160,
Format::MP3_96 => AudioFileFormat::MP3_96,
Format::MP3_160_ENC => AudioFileFormat::MP3_160_ENC,
Format::AAC_24 => AudioFileFormat::AAC_24,
Format::AAC_48 => AudioFileFormat::AAC_48,
Format::FLAC_FLAC => AudioFileFormat::FLAC_FLAC,
Format::XHE_AAC_24 => AudioFileFormat::XHE_AAC_24,
Format::XHE_AAC_16 => AudioFileFormat::XHE_AAC_16,
Format::XHE_AAC_12 => AudioFileFormat::XHE_AAC_12,
Format::FLAC_FLAC_24BIT => AudioFileFormat::FLAC_FLAC_24BIT,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct AudioFiles(pub HashMap<AudioFileFormat, FileId>);
impl_deref_wrapped!(AudioFiles, HashMap<AudioFileFormat, FileId>);
impl AudioFiles {
pub fn is_ogg_vorbis(format: AudioFileFormat) -> bool {
matches!(
format,
AudioFileFormat::OGG_VORBIS_320
| AudioFileFormat::OGG_VORBIS_160
| AudioFileFormat::OGG_VORBIS_96
)
}
pub fn is_mp3(format: AudioFileFormat) -> bool {
matches!(
format,
AudioFileFormat::MP3_320
| AudioFileFormat::MP3_256
| AudioFileFormat::MP3_160
| AudioFileFormat::MP3_96
| AudioFileFormat::MP3_160_ENC
)
}
pub fn is_flac(format: AudioFileFormat) -> bool {
matches!(format, AudioFileFormat::FLAC_FLAC)
}
pub fn mime_type(format: AudioFileFormat) -> Option<&'static str> {
if Self::is_ogg_vorbis(format) {
Some("audio/ogg")
} else if Self::is_mp3(format) {
Some("audio/mpeg")
} else if Self::is_flac(format) {
Some("audio/flac")
} else {
None
}
}
}
impl From<&[AudioFileMessage]> for AudioFiles {
fn from(files: &[AudioFileMessage]) -> Self {
let audio_files: HashMap<AudioFileFormat, FileId> = files
.iter()
.filter_map(|file| {
let file_id = FileId::from(file.file_id());
let format = file
.format
.ok_or(format!("Ignoring file <{file_id}> with unspecified format",))
.and_then(|format| match format.enum_value() {
Ok(f) => Ok((f.into(), file_id)),
Err(unknown) => Err(format!(
"Ignoring file <{file_id}> with unknown format {unknown}",
)),
});
if let Err(ref why) = format {
trace!("{why}");
}
format.ok()
})
.collect();
AudioFiles(audio_files)
}
}