summaryrefslogtreecommitdiff
path: root/src/flac.rs
diff options
context:
space:
mode:
authorjakka <jakkadoujin@gmail.com>2025-06-19 00:33:52 +0300
committerjakka <jakkadoujin@gmail.com>2025-06-19 00:33:52 +0300
commiteb838afe24b3f33d8dff88d570574aeea8060f15 (patch)
tree144e2d61e16eb02b3b314fdd376c6afabfb467b7 /src/flac.rs
parent6d9e1300a2769fe4c0f4b290122143f52fc7a3ef (diff)
rewrote flac logic. added (non-working) batch reencoding test. added reencode status update on reencoding
Diffstat (limited to 'src/flac.rs')
-rw-r--r--src/flac.rs238
1 files changed, 128 insertions, 110 deletions
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<Path>) -> Result<Self> {
+ 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<Path>,
+) -> 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<dyn FormatReader>,
- mut decoder: Box<dyn AudioDecoder + 'static>,
+ mut format: BoxedFormatReader,
+ mut decoder: BoxedAudioDecoder,
mut encoder: FlacEncoder,
- hasher: &mut CoreWrapper<Md5Core>,
-) -> Result<()> {
+) -> 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 {
@@ -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<dyn FormatReader>,
- mut decoder: Box<dyn AudioDecoder + 'static>,
+ mut format: BoxedFormatReader,
+ mut decoder: BoxedAudioDecoder,
mut encoder: FlacEncoder,
- hasher: &mut CoreWrapper<Md5Core>,
-) -> Result<()> {
+) -> 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 {
@@ -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<dyn FormatReader>,
- mut decoder: Box<dyn AudioDecoder + 'static>,
+ mut format: BoxedFormatReader,
+ mut decoder: BoxedAudioDecoder,
mut encoder: FlacEncoder,
- hasher: &mut CoreWrapper<Md5Core>,
-) -> Result<()> {
+) -> 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 {
@@ -185,116 +278,41 @@ fn encode_cycle_32(
return Err(anyhow!("Encoding failed:\t{:?}", enc.state()));
}
- Ok(())
-}
-
-fn init_decoder(
- filename: impl AsRef<Path>,
-) -> 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 write_tags(
- file: impl AsRef<Path>,
- tempname: impl AsRef<Path>,
- 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(())
+ Ok(hasher.finalize().to_vec())
}
-fn encode_file(filename: impl AsRef<OsStr>) -> Result<()> {
- let file = Path::new(&filename);
- let tempname = &format!("{}.tmp", file.to_str().unwrap());
-
- let (format, decoder, config) = init_decoder(file)?;
+fn encode_file(filename: impl AsRef<Path>) -> Result<()> {
+ let filencoder = FileEncoder::new(filename)?;
- 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<Path>) -> Result<impl AsRef<Path>> {
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<String> {
- let tag = Tag::read_from_path(file)?;
- Ok(tag.vorbis_comments().unwrap().vendor_string.clone())
+pub fn get_vendor(file: impl AsRef<Path>) -> Result<String> {
+ 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()