use crate::prelude::*;
use claxon::Error as ClaxonError;
use claxon::FlacReader;
use futures::StreamExt;
use futures::stream;
#[injectable]
pub(crate) struct DecodeVerifier {
verify_options: Ref<VerifyOptions>,
runner_options: Ref<RunnerOptions>,
}
impl DecodeVerifier {
pub(crate) async fn execute(&self, flacs: &[FlacFile]) -> Vec<SourceIssue> {
if self.verify_options.no_decode_check {
debug!("{} decode check due to settings", "Skipped".bold());
return Vec::new();
}
trace!("{} decode of {} FLACs", "Checking".bold(), flacs.len());
let cpus = self.runner_options.get_cpus();
let start = Instant::now();
let issues: Vec<SourceIssue> = stream::iter(flacs.iter().map(|flac| flac.path.clone()))
.map(|path| async move {
spawn_blocking(move || decode_flac(&path))
.await
.expect("decode task should not panic")
})
.buffered(cpus)
.filter_map(|issue| async move { issue })
.collect()
.await;
trace!(
"{} decode of {} FLACs in {:.3}s",
"Checked".bold(),
flacs.len(),
start.elapsed().as_secs_f64()
);
issues
}
}
pub(crate) fn decode_flac(path: &Path) -> Option<SourceIssue> {
decode(path).err().map(|error| SourceIssue::DecodeError {
path: path.to_path_buf(),
error: format!("{error}"),
})
}
fn decode(path: &Path) -> Result<(), ClaxonError> {
trace!("Decoding FLAC {}", path.display());
let mut reader = FlacReader::open(path)?;
let mut buffer = Vec::new();
let mut blocks = reader.blocks();
while let Some(block) = blocks.read_next_or_eof(buffer)? {
buffer = block.into_buffer();
}
Ok(())
}