From eb838afe24b3f33d8dff88d570574aeea8060f15 Mon Sep 17 00:00:00 2001 From: jakka Date: Thu, 19 Jun 2025 00:33:52 +0300 Subject: rewrote flac logic. added (non-working) batch reencoding test. added reencode status update on reencoding --- src/flac.rs | 238 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 128 insertions(+), 110 deletions(-) (limited to 'src/flac.rs') diff --git a/src/flac.rs b/src/flac.rs index 64ac6a9..42f9d21 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -1,10 +1,9 @@ use anyhow::{Result, anyhow}; use flac_bound::{FlacEncoder, WriteWrapper}; use i24::i24; -use md5::{Digest, Md5, Md5Core, digest::core_api::CoreWrapper}; +use md5::{Digest, Md5}; use metaflac::{Block, Tag}; use std::{ - ffi::OsStr, fs::File, path::{Path, PathBuf}, }; @@ -54,13 +53,107 @@ impl Bps { } } +struct FileEncoder { + filename: PathBuf, + streamdata: StreamConfig, + format: BoxedFormatReader, + decoder: BoxedAudioDecoder, +} + +impl FileEncoder { + fn new(file: impl AsRef) -> Result { + let (format, decoder, config) = init_decoder(&file)?; + Ok(FileEncoder { + filename: file.as_ref().to_path_buf(), + streamdata: config, + format, + decoder, + }) + } + + fn temp_name(&self) -> PathBuf { + self.filename.clone().with_extension("tmp") + } + + fn encode(self, encoder: FlacEncoder) -> Result<()> { + let filename = self.filename.clone(); + let tempname = self.temp_name(); + + 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)?, + }; + + 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; + output.set_streaminfo(streaminfo); + + for block in tags.blocks() { + match block { + Block::VorbisComment(comment) => { + for (key, val) in comment.comments.clone() { + output.set_vorbis(key, val); + } + } + Block::StreamInfo(_) => {} + _ => output.push_block(block.clone()), + } + } + + output.write_to_path(&tempname)?; + + std::fs::rename(tempname, filename)?; + + Ok(()) + } +} + +fn init_decoder( + filename: impl AsRef, +) -> Result<(BoxedFormatReader, BoxedAudioDecoder, StreamConfig)> { + let src = std::fs::File::open(filename)?; + let mss = MediaSourceStream::new(Box::new(src), Default::default()); + let mut hint = Hint::new(); + hint.with_extension("flac"); + + let format_opts: FormatOptions = Default::default(); + let metadata_opts: MetadataOptions = Default::default(); + + let format = symphonia::default::get_probe() + .probe(&hint, mss, format_opts, metadata_opts) + .unwrap(); + + let track = format.default_track(TrackType::Audio).unwrap(); + + let decoder = symphonia::default::get_codecs() + .make_audio_decoder( + track.codec_params.as_ref().unwrap().audio().unwrap(), + &Default::default(), + ) + .unwrap(); + + let params = track.codec_params.as_ref().unwrap().audio().unwrap(); + + let config = StreamConfig { + channels: u32::try_from(params.channels.as_ref().unwrap().count()).unwrap(), + bits_per_sample: Bps::new(params.bits_per_sample.unwrap())?, + sample_rate: params.sample_rate.unwrap(), + }; + + Ok((format, decoder, config)) +} + fn encode_cycle_16( - mut format: Box, - mut decoder: Box, + mut format: BoxedFormatReader, + mut decoder: BoxedAudioDecoder, mut encoder: FlacEncoder, - hasher: &mut CoreWrapper, -) -> Result<()> { +) -> Result> { let mut buffer: Vec = Vec::new(); + let mut hasher = Md5::new(); let track_id = format.default_track(TrackType::Audio).unwrap().id; loop { @@ -96,16 +189,16 @@ fn encode_cycle_16( return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); } - Ok(()) + Ok(hasher.finalize().to_vec()) } fn encode_cycle_24( - mut format: Box, - mut decoder: Box, + mut format: BoxedFormatReader, + mut decoder: BoxedAudioDecoder, mut encoder: FlacEncoder, - hasher: &mut CoreWrapper, -) -> Result<()> { +) -> Result> { let mut buffer: Vec = Vec::new(); + let mut hasher = Md5::new(); let track_id = format.default_track(TrackType::Audio).unwrap().id; loop { @@ -141,16 +234,16 @@ fn encode_cycle_24( return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); } - Ok(()) + Ok(hasher.finalize().to_vec()) } fn encode_cycle_32( - mut format: Box, - mut decoder: Box, + mut format: BoxedFormatReader, + mut decoder: BoxedAudioDecoder, mut encoder: FlacEncoder, - hasher: &mut CoreWrapper, -) -> Result<()> { +) -> Result> { let mut buffer: Vec = Vec::new(); + let mut hasher = Md5::new(); let track_id = format.default_track(TrackType::Audio).unwrap().id; loop { @@ -185,116 +278,41 @@ fn encode_cycle_32( return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); } - Ok(()) + Ok(hasher.finalize().to_vec()) } -fn init_decoder( - filename: impl AsRef, -) -> Result<(BoxedFormatReader, BoxedAudioDecoder, StreamConfig)> { - let src = std::fs::File::open(filename)?; - let mss = MediaSourceStream::new(Box::new(src), Default::default()); - let mut hint = Hint::new(); - hint.with_extension("flac"); - - let format_opts: FormatOptions = Default::default(); - let metadata_opts: MetadataOptions = Default::default(); - - let format = symphonia::default::get_probe() - .probe(&hint, mss, format_opts, metadata_opts) - .unwrap(); - - let track = format.default_track(TrackType::Audio).unwrap(); - - let decoder = symphonia::default::get_codecs() - .make_audio_decoder( - track.codec_params.as_ref().unwrap().audio().unwrap(), - &Default::default(), - ) - .unwrap(); +fn encode_file(filename: impl AsRef) -> Result<()> { + let filencoder = FileEncoder::new(filename)?; - let params = track.codec_params.as_ref().unwrap().audio().unwrap(); - - let config = StreamConfig { - channels: u32::try_from(params.channels.as_ref().unwrap().count()).unwrap(), - bits_per_sample: Bps::new(params.bits_per_sample.unwrap())?, - sample_rate: params.sample_rate.unwrap(), - }; - - Ok((format, decoder, config)) -} - -fn write_tags( - file: impl AsRef, - tempname: impl AsRef, - hasher: impl Digest, -) -> Result<()> { - let tags = Tag::read_from_path(file)?; - let mut output = Tag::read_from_path(&tempname)?; - let mut streaminfo = tags.get_streaminfo().unwrap().clone(); - - streaminfo.md5 = hasher.finalize()[..].to_vec(); - output.set_streaminfo(streaminfo); - - for block in tags.blocks() { - match block { - Block::VorbisComment(comment) => { - for (key, val) in comment.comments.clone() { - output.set_vorbis(key, val); - } - } - Block::StreamInfo(_) => {} - _ => output.push_block(block.clone()), - } - } - - output.write_to_path(&tempname)?; - Ok(()) -} - -fn encode_file(filename: impl AsRef) -> Result<()> { - let file = Path::new(&filename); - let tempname = &format!("{}.tmp", file.to_str().unwrap()); - - let (format, decoder, config) = init_decoder(file)?; - - let mut outf = File::create(tempname)?; + let mut outf = File::create(filencoder.temp_name())?; let mut outw = WriteWrapper(&mut outf); let enc = FlacEncoder::new() .unwrap() - .channels(config.channels) - .bits_per_sample(config.bits_per_sample.value()) - .sample_rate(config.sample_rate) + .channels(filencoder.streamdata.channels) + .bits_per_sample(filencoder.streamdata.bits_per_sample.value()) + .sample_rate(filencoder.streamdata.sample_rate) .compression_level(8) .verify(false) .init_write(&mut outw) .unwrap(); - let mut hasher = Md5::new(); - - match config.bits_per_sample { - Bps::_16 => encode_cycle_16(format, decoder, enc, &mut hasher), - Bps::_24 => encode_cycle_24(format, decoder, enc, &mut hasher), - Bps::_32 => encode_cycle_32(format, decoder, enc, &mut hasher), - }?; - - write_tags(file, tempname, hasher)?; - - std::fs::rename(tempname, file)?; - - Ok(()) + filencoder.encode(enc) } -pub fn handle_encode(file: PathBuf) -> Result<()> { +pub fn handle_encode(file: impl AsRef) -> Result> { if let Err(error) = encode_file(&file) { Err(anyhow!(files::FileError::new(file, error))) } else { - Ok(()) + Ok(file) } } -pub fn get_vendor(file: &Path) -> Result { - let tag = Tag::read_from_path(file)?; - Ok(tag.vorbis_comments().unwrap().vendor_string.clone()) +pub fn get_vendor(file: impl AsRef) -> Result { + if let Some(vorbis) = Tag::read_from_path(file)?.vorbis_comments() { + Ok(vorbis.vendor_string.to_owned()) + } else { + Err(anyhow!("Vendor string not found")) + } } #[cfg(test)] @@ -307,7 +325,7 @@ mod tests { let name = "16bit.flac"; let tempname = "16bit.flac.temp"; std::fs::copy(name, tempname).unwrap(); - encode_file(std::path::Path::new(name)).unwrap(); + encode_file(name).unwrap(); let target_md5 = Tag::read_from_path(tempname) .unwrap() .get_streaminfo() @@ -329,7 +347,7 @@ mod tests { let name = "24bit.flac"; let tempname = "24bit.flac.temp"; std::fs::copy(name, tempname).unwrap(); - encode_file(std::path::Path::new(name)).unwrap(); + encode_file(name).unwrap(); let target_md5 = Tag::read_from_path(tempname) .unwrap() .get_streaminfo() @@ -351,7 +369,7 @@ mod tests { let name = "32bit.flac"; let tempname = "32bit.flac.temp"; std::fs::copy(name, tempname).unwrap(); - encode_file(std::path::Path::new(name)).unwrap(); + encode_file(name).unwrap(); let target_md5 = Tag::read_from_path(tempname) .unwrap() .get_streaminfo() -- cgit v1.3.1