From 2a60a5f68f877cb05dd071703e456dbf050eb8c9 Mon Sep 17 00:00:00 2001 From: jakka Date: Sun, 22 Jun 2025 14:42:27 +0300 Subject: better reencoding logic --- src/files.rs | 62 +++++++++---------- src/flac.rs | 198 ++++++++++++++--------------------------------------------- 2 files changed, 78 insertions(+), 182 deletions(-) diff --git a/src/files.rs b/src/files.rs index 764de56..8053da2 100644 --- a/src/files.rs +++ b/src/files.rs @@ -92,35 +92,34 @@ pub async fn index_files_recursively( .with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-")) .with_message("Indexing"); - let _ = WalkDir::new(abspath) - .into_iter() - .map(|file| { - let path = file.unwrap().into_path(); - if path.is_file() { - if let Some(ext) = path.extension() { - if ext == "flac" { - let newconn = conn.clone(); - let newtoken = canceltoken.clone(); - #[cfg(not(test))] - let newbar = bar.clone(); - tasks.spawn(async move { - tokio::select! { - _ = newtoken.cancelled() => Ok(()), - res = async { - handle_file(path, newconn).await?; - #[cfg(not(test))] - newbar.inc(1); - Ok(()) - } => res - } - }); + for entry in WalkDir::new(abspath) { + let path = entry?.into_path(); + if !path.is_file() { + continue; + } + + if path.extension().is_some_and(|x| x == "flac") { + let newconn = conn.clone(); + let newtoken = canceltoken.clone(); + #[cfg(not(test))] + let newbar = bar.clone(); + + tasks.spawn(async move { + tokio::select! { + _ = newtoken.cancelled() => Ok(()), + res = async { + handle_file(path, newconn).await?; #[cfg(not(test))] - bar.inc_length(1); - } + newbar.inc(1); + Ok(()) + } => res } - } - }) - .collect::>(); + }); + + #[cfg(not(test))] + bar.inc_length(1); + } + } while let Some(task) = tasks.join_next().await { match task { @@ -132,9 +131,9 @@ pub async fn index_files_recursively( #[cfg(not(test))] { if canceltoken.is_cancelled() { - bar.abandon_with_message("Indexing abandoned"); + bar.abandon_with_message("Indexing aborted"); } else { - bar.finish_with_message("Finished encoding"); + bar.finish_with_message("Finished indexing"); } } Ok(()) @@ -171,9 +170,10 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) -> } res = async { if let Err(error) = tokio::task::spawn_blocking(move || encode_file(file)).await? { + let _ = std::fs::remove_file(filename.with_extension("tmp.metadata_edit")); + let _ = std::fs::remove_file(filename.with_extension("tmp")); return Err(anyhow!(FileError::new(&filename, error))); }; - if let Err(error) = newconn.update_file(&filename).await { return Err(anyhow!(FileError::new(&filename, error))); }; @@ -197,7 +197,7 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) -> #[cfg(not(test))] { if canceltoken.is_cancelled() { - bar.abandon_with_message("Reencoding abandoned"); + bar.abandon_with_message("Reencoding aborted"); } else { bar.finish_with_message("Finished encoding"); } diff --git a/src/flac.rs b/src/flac.rs index 4283aad..85a75f1 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -1,6 +1,5 @@ use anyhow::{Result, anyhow}; use flac_bound::{FlacEncoder, WriteWrapper}; -use i24::i24; use md5::{Digest, Md5}; use metaflac::{Block, Tag}; use std::{ @@ -73,18 +72,49 @@ impl FileEncoder { self.filename.clone().with_extension("tmp") } - fn encode(self, encoder: FlacEncoder) -> Result<()> { - let filename = self.filename.clone(); - let tempname = self.temp_name(); + fn encode(&mut self, mut encoder: FlacEncoder) -> Result> { + let mut buffer: Vec = Vec::new(); + let mut hasher = Md5::new(); + let track_id = self.format.default_track(TrackType::Audio).unwrap().id; + let offset = self.streamdata.bits_per_sample.value(); + + loop { + let packet = match self.format.next_packet() { + Ok(Some(packet)) => packet, + Ok(None) => break, + Err(error) => return Err(error.into()), + }; + + if packet.track_id() != track_id { + continue; + } - let hash = match self.streamdata.bits_per_sample { - Bps::_16 => encode_cycle_16(self.format, self.decoder, encoder)?, - Bps::_24 => encode_cycle_24(self.format, self.decoder, encoder)?, - Bps::_32 => encode_cycle_32(self.format, self.decoder, encoder)?, - }; + if let GenericAudioBufferRef::S32(buf) = self.decoder.decode(&packet)? { + for sample in buf.iter_interleaved() { + let real_sample = sample >> (32 - offset); + hasher.update(real_sample.to_le_bytes()); + buffer.push(real_sample); + } + encoder + .process_interleaved(&buffer, buf.samples_planar() as u32) + .unwrap(); + buffer.clear(); + } else { + return Err(anyhow!("unsupported codec")); + } + } + + if let Err(enc) = encoder.finish() { + return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); + } + + Ok(hasher.finalize().to_vec()) + } + + fn write_tags(&self, hash: Vec) -> Result<()> { + let tags = Tag::read_from_path(&self.filename)?; + let mut output = Tag::read_from_path(self.temp_name())?; - let tags = Tag::read_from_path(&filename)?; - let mut output = Tag::read_from_path(&tempname)?; let mut streaminfo = tags.get_streaminfo().unwrap().clone(); streaminfo.md5 = hash; @@ -104,10 +134,7 @@ impl FileEncoder { } } - output.write_to_path(&tempname)?; - - std::fs::rename(tempname, filename)?; - + output.write_to_path(self.temp_name())?; Ok(()) } } @@ -147,142 +174,8 @@ fn init_decoder( Ok((format, decoder, config)) } -fn encode_cycle_16( - mut format: BoxedFormatReader, - mut decoder: BoxedAudioDecoder, - mut encoder: FlacEncoder, -) -> Result> { - let mut buffer: Vec = Vec::new(); - let mut hasher = Md5::new(); - let track_id = format.default_track(TrackType::Audio).unwrap().id; - - loop { - let packet = match format.next_packet() { - Ok(Some(packet)) => packet, - Ok(None) => break, - Err(error) => return Err(error.into()), - }; - - if packet.track_id() != track_id { - continue; - } - - if let GenericAudioBufferRef::S32(buf) = decoder.decode(&packet)? { - let _ = buf - .iter_interleaved() - .map(|sample| { - let real_sample = sample >> 16; - hasher.update(i16::try_from(real_sample).unwrap().to_le_bytes()); - buffer.push(real_sample); - }) - .collect::>(); - encoder - .process_interleaved(&buffer, u32::try_from(buf.samples_planar()).unwrap()) - .unwrap(); - buffer.clear(); - } else { - return Err(anyhow!("unsupported codec")); - } - } - - if let Err(enc) = encoder.finish() { - return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); - } - - Ok(hasher.finalize().to_vec()) -} - -fn encode_cycle_24( - mut format: BoxedFormatReader, - mut decoder: BoxedAudioDecoder, - mut encoder: FlacEncoder, -) -> Result> { - let mut buffer: Vec = Vec::new(); - let mut hasher = Md5::new(); - let track_id = format.default_track(TrackType::Audio).unwrap().id; - - loop { - let packet = match format.next_packet() { - Ok(Some(packet)) => packet, - Ok(None) => break, - Err(error) => return Err(error.into()), - }; - - if packet.track_id() != track_id { - continue; - } - - if let GenericAudioBufferRef::S32(buf) = decoder.decode(&packet)? { - let _ = buf - .iter_interleaved() - .map(|sample| { - let real_sample = sample >> 8; - hasher.update(i24::try_from(real_sample).unwrap().to_le_bytes()); - buffer.push(real_sample); - }) - .collect::>(); - encoder - .process_interleaved(&buffer, u32::try_from(buf.samples_planar()).unwrap()) - .unwrap(); - buffer.clear(); - } else { - return Err(anyhow!("unsupported codec")); - } - } - - if let Err(enc) = encoder.finish() { - return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); - } - - Ok(hasher.finalize().to_vec()) -} - -fn encode_cycle_32( - mut format: BoxedFormatReader, - mut decoder: BoxedAudioDecoder, - mut encoder: FlacEncoder, -) -> Result> { - let mut buffer: Vec = Vec::new(); - let mut hasher = Md5::new(); - let track_id = format.default_track(TrackType::Audio).unwrap().id; - - loop { - let packet = match format.next_packet() { - Ok(Some(packet)) => packet, - Ok(None) => break, - Err(error) => return Err(error.into()), - }; - - if packet.track_id() != track_id { - continue; - } - - if let GenericAudioBufferRef::S32(buf) = decoder.decode(&packet)? { - let _ = buf - .iter_interleaved() - .map(|sample| { - hasher.update(sample.to_le_bytes()); - buffer.push(sample); - }) - .collect::>(); - encoder - .process_interleaved(&buffer, u32::try_from(buf.samples_planar()).unwrap()) - .unwrap(); - buffer.clear(); - } else { - return Err(anyhow!("unsupported codec")); - } - } - - if let Err(enc) = encoder.finish() { - return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); - } - - Ok(hasher.finalize().to_vec()) -} - pub fn encode_file(filename: impl AsRef) -> Result<()> { - let filencoder = FileEncoder::new(filename)?; + let mut filencoder = FileEncoder::new(filename)?; let temp_name = filencoder.temp_name(); if temp_name.exists() { @@ -301,7 +194,10 @@ pub fn encode_file(filename: impl AsRef) -> Result<()> { .init_write(&mut outw) .unwrap(); - filencoder.encode(enc) + let hash = filencoder.encode(enc)?; + filencoder.write_tags(hash)?; + std::fs::remove_file(filencoder.temp_name())?; + Ok(()) } pub fn get_vendor(file: impl AsRef) -> Result { -- cgit v1.3.1