summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakka <jakkadoujin@gmail.com>2025-06-22 14:42:27 +0300
committerjakka <jakkadoujin@gmail.com>2025-06-22 14:42:27 +0300
commit2a60a5f68f877cb05dd071703e456dbf050eb8c9 (patch)
tree71975120e98690d20d21de588246f7d65bae6187
parent1769f337c423cbe0248e0fd65f8c50e0bce0d845 (diff)
better reencoding logic
-rw-r--r--src/files.rs62
-rw-r--r--src/flac.rs198
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::<Vec<_>>();
+ });
+
+ #[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<Vec<u8>> {
+ let mut buffer: Vec<i32> = 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();
- 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)?,
- };
+ 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;
+ }
+
+ 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<u8>) -> 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<Vec<u8>> {
- let mut buffer: Vec<i32> = 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::<Vec<_>>();
- 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<Vec<u8>> {
- let mut buffer: Vec<i32> = 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::<Vec<_>>();
- 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<Vec<u8>> {
- let mut buffer: Vec<i32> = 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::<Vec<_>>();
- 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<Path>) -> 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<Path>) -> 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<Path>) -> Result<String> {