use bytes::{Buf, Bytes}; use std::{fs::File, io::Read, path::Path}; use thiserror::Error; #[derive(Error, Debug)] pub enum FormatDetectionError { #[error("could not read file")] FileReadError, #[error("file format unrecognised")] UnrecognisedFormat, } #[derive(Clone, Copy, Debug, PartialEq)] pub enum FileFormat { FLAC, MP3, OggVorbis, OggOpus, OggFLAC, OggSpeex, OggTheora, AIFF, Wav, WavPack, } impl ToString for FileFormat { fn to_string(&self) -> String { match self { FileFormat::FLAC => "FLAC".to_string(), FileFormat::MP3 => "MP3".to_string(), FileFormat::OggVorbis => "Ogg (Vorbis)".to_string(), FileFormat::OggOpus => "Ogg (Opus)".to_string(), FileFormat::OggFLAC => "Ogg (FLAC)".to_string(), FileFormat::OggSpeex => "Ogg (Speex)".to_string(), FileFormat::OggTheora => "Ogg (Theora)".to_string(), FileFormat::AIFF => "AIFF".to_string(), FileFormat::Wav => "Wav".to_string(), FileFormat::WavPack => "WavPack".to_string(), } } } pub fn detect_ogg_subformat(path: &Path) -> Result { let file = File::open(path); if let Err(_error) = file { return Err(FormatDetectionError::FileReadError); } let file = file.unwrap(); let limit = file .metadata() .map(|m| std::cmp::min(m.len(), 128) as usize + 1) .unwrap_or(0); let mut bytes = Vec::with_capacity(limit); if let Err(_err) = file.take(128).read_to_end(&mut bytes) { return Err(FormatDetectionError::FileReadError); } let mut mem = Bytes::from(bytes); mem.advance(28); let vorbis_type = mem.get_u8(); match vorbis_type { 0x01 => return Ok(FileFormat::OggVorbis), 0x7f => return Ok(FileFormat::OggFLAC), 0x80 => return Ok(FileFormat::OggTheora), // S for speex 0x53 => return Ok(FileFormat::OggSpeex), _ => {} } Err(FormatDetectionError::UnrecognisedFormat) } fn wavpack_matcher(buf: &[u8]) -> bool { // 77 76 70 6b buf.len() >= 4 && buf[0] == 0x77 && buf[1] == 0x76 && buf[2] == 0x70 && buf[3] == 0x6b } pub fn detect_format(path: &Path) -> Result { let mut info = infer::Infer::new(); info.add("custom/wavpack", "wv", wavpack_matcher); let kind = info.get_from_path(path); if let Err(_error) = kind { return Err(FormatDetectionError::FileReadError); } let kind = kind.unwrap(); if kind.is_none() { return Err(FormatDetectionError::UnrecognisedFormat); } let kind = kind.unwrap(); match kind.mime_type() { "audio/mpeg" => { return Ok(FileFormat::MP3); } "audio/x-wav" => { return Ok(FileFormat::Wav); } "custom/wavpack" => { return Ok(FileFormat::WavPack); } "audio/ogg" => { return detect_ogg_subformat(path); } "audio/x-flac" => { return Ok(FileFormat::FLAC); } "audio/x-aiff" => { return Ok(FileFormat::AIFF); } "audio/opus" => { return Ok(FileFormat::OggOpus); } _ => {} } Err(FormatDetectionError::UnrecognisedFormat) }