diff options
| author | jakka <jakkadoujin@gmail.com> | 2025-07-01 13:50:09 +0300 |
|---|---|---|
| committer | jakka <jakkadoujin@gmail.com> | 2025-07-01 13:50:09 +0300 |
| commit | 36b16f9b4847bdbef15cacc9e1223cc6e336f54f (patch) | |
| tree | 8bd5e7df9191543acc5c1d7c1b0c9174126bb134 | |
| parent | 2ac8079fc38202f04856debe5655635a4368d6df (diff) | |
moved to claxon
| -rw-r--r-- | Cargo.lock | 129 | ||||
| -rw-r--r-- | Cargo.toml | 8 | ||||
| -rw-r--r-- | src/files.rs | 4 | ||||
| -rw-r--r-- | src/flac.rs | 312 |
4 files changed, 138 insertions, 315 deletions
@@ -295,9 +295,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" @@ -425,6 +425,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] +name = "claxon" +version = "0.4.3" +source = "git+https://github.com/justjakka/claxon.git#919a0fe9902f2b4f2bd1152772cca746eb0df7d2" + +[[package]] name = "cmake" version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -453,6 +458,19 @@ dependencies = [ ] [[package]] +name = "console" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.60.2", +] + +[[package]] name = "core-foundation" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -589,7 +607,8 @@ dependencies = [ "anyhow", "clap", "clap_complete", - "console", + "claxon", + "console 0.15.11", "directories", "flac-bound", "futures-util", @@ -599,7 +618,6 @@ dependencies = [ "md-5", "metaflac", "pin-utils", - "symphonia", "tokio", "tokio-util", "walkdir", @@ -757,7 +775,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -950,9 +968,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown 0.15.4", @@ -960,17 +978,17 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.11" +version = "0.17.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +checksum = "4adb2ee6ad319a912210a36e56e3623555817bcc877a7e6e8802d1d69c4d8056" dependencies = [ - "console", + "console 0.16.0", "futures-core", - "number_prefix", "portable-atomic", "tokio", "unicode-segmentation", "unicode-width", + "unit-prefix", "web-time", ] @@ -1055,9 +1073,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" dependencies = [ "bitflags 2.9.1", "libc", @@ -1150,7 +1168,7 @@ dependencies = [ "bitflags 2.9.1", "cc", "fallible-iterator 0.3.0", - "indexmap 2.9.0", + "indexmap 2.10.0", "log", "memchr", "phf", @@ -1296,15 +1314,6 @@ dependencies = [ ] [[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1314,12 +1323,6 @@ dependencies = [ ] [[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1604,12 +1607,6 @@ dependencies = [ ] [[package]] -name = "regex-lite" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" - -[[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1857,62 +1854,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "symphonia" -version = "0.5.4" -source = "git+https://github.com/sscobici/Symphonia.git?rev=2213f274c3e7231fbd7b08aa9347049852915b29#2213f274c3e7231fbd7b08aa9347049852915b29" -dependencies = [ - "lazy_static", - "symphonia-bundle-flac", - "symphonia-core", - "symphonia-metadata", -] - -[[package]] -name = "symphonia-bundle-flac" -version = "0.5.4" -source = "git+https://github.com/sscobici/Symphonia.git?rev=2213f274c3e7231fbd7b08aa9347049852915b29#2213f274c3e7231fbd7b08aa9347049852915b29" -dependencies = [ - "log", - "symphonia-common", - "symphonia-core", - "symphonia-metadata", -] - -[[package]] -name = "symphonia-common" -version = "0.5.4" -source = "git+https://github.com/sscobici/Symphonia.git?rev=2213f274c3e7231fbd7b08aa9347049852915b29#2213f274c3e7231fbd7b08aa9347049852915b29" -dependencies = [ - "symphonia-core", - "symphonia-metadata", -] - -[[package]] -name = "symphonia-core" -version = "0.5.4" -source = "git+https://github.com/sscobici/Symphonia.git?rev=2213f274c3e7231fbd7b08aa9347049852915b29#2213f274c3e7231fbd7b08aa9347049852915b29" -dependencies = [ - "bitflags 2.9.1", - "bytemuck", - "lazy_static", - "log", - "num-complex", - "smallvec", -] - -[[package]] -name = "symphonia-metadata" -version = "0.5.4" -source = "git+https://github.com/sscobici/Symphonia.git?rev=2213f274c3e7231fbd7b08aa9347049852915b29#2213f274c3e7231fbd7b08aa9347049852915b29" -dependencies = [ - "lazy_static", - "log", - "regex-lite", - "smallvec", - "symphonia-core", -] - -[[package]] name = "syn" version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2214,6 +2155,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] +name = "unit-prefix" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" + +[[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -18,7 +18,7 @@ flac-bound = { version = "0.5.0", features = [ "libflac-noogg", ], default-features = false } futures-util = "0.3.31" -i24 = "2.1.0" +i24 = { version = "2.1.0", features = ["std"] } indicatif = { version = "0.17.11", features = [ "tokio", "improved_unicode", @@ -26,7 +26,6 @@ indicatif = { version = "0.17.11", features = [ ] } libsql = { version = "0.9.11" } md-5 = "0.10.6" -metaflac = "0.2.8" pin-utils = "0.1.0" tokio = { version = "1.45.1", features = [ "macros", @@ -36,7 +35,6 @@ tokio = { version = "1.45.1", features = [ ] } tokio-util = "0.7.15" walkdir = "2.5.0" -symphonia = { git = "https://github.com/sscobici/Symphonia.git", rev = "2213f274c3e7231fbd7b08aa9347049852915b29", default-features = false, features = [ - "flac", -] } console = { version = "0.15.11", features = ["windows-console-colors"] } +claxon = { git = "https://github.com/justjakka/claxon.git" } +metaflac = "0.2.8" diff --git a/src/files.rs b/src/files.rs index 8053da2..f9352d3 100644 --- a/src/files.rs +++ b/src/files.rs @@ -13,7 +13,7 @@ use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; use walkdir::WalkDir; -use crate::{db::Database, flac::encode_file}; +use crate::{db::Database, flac::handle_encode}; #[cfg(not(test))] const BAR_TEMPLATE: &str = "{msg:<} [{wide_bar:.green/cyan}] Elapsed: {elapsed} {pos:>7}/{len:7}"; @@ -169,7 +169,7 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) -> Ok(()) } res = async { - if let Err(error) = tokio::task::spawn_blocking(move || encode_file(file)).await? { + if let Err(error) = tokio::task::spawn_blocking(move || handle_encode(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))); diff --git a/src/flac.rs b/src/flac.rs index 05d915f..823f4e4 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -1,208 +1,114 @@ use anyhow::{Result, anyhow}; -use flac_bound::{FlacEncoder, WriteWrapper}; +use claxon::{FlacReader, FlacReaderOptions}; +use flac_bound::FlacEncoder; use md5::{Digest, Md5}; use metaflac::{Block, Tag}; -use std::{ - fs::File, - path::{Path, PathBuf}, -}; -use symphonia::core::{ - audio::{Audio, GenericAudioBufferRef}, - codecs::audio::AudioDecoder, - formats::{FormatOptions, FormatReader, TrackType, probe::Hint}, - io::MediaSourceStream, - meta::MetadataOptions, -}; +use std::path::Path; pub const CURRENT_VENDOR: &str = "reference libFLAC 1.5.0 20250211"; -type BoxedFormatReader = Box<dyn FormatReader>; -type BoxedAudioDecoder = Box<dyn AudioDecoder + 'static>; +fn write_tags(filename: impl AsRef<Path>, hash: Vec<u8>) -> Result<()> { + let tags = Tag::read_from_path(&filename)?; + let temp_name = filename.as_ref().with_extension("tmp"); + let mut output = Tag::read_from_path(&temp_name)?; -struct StreamConfig { - channels: u32, - bits_per_sample: Bps, - sample_rate: u32, -} - -enum Bps { - _16, - _24, - _32, -} + let mut streaminfo = tags.get_streaminfo().unwrap().clone(); -impl Bps { - fn new(num: u32) -> Result<Self> { - match num { - 16 => Ok(Bps::_16), - 24 => Ok(Bps::_24), - 32 => Ok(Bps::_32), - _ => Err(anyhow!("Invalid BPS")), - } - } + streaminfo.md5 = hash; + output.set_streaminfo(streaminfo); - fn value(&self) -> u32 { - match self { - Bps::_16 => 16, - Bps::_24 => 24, - Bps::_32 => 32, + for block in tags.blocks() { + match block { + Block::VorbisComment(comment) => { + for (key, val) in comment.comments.clone() { + if key != "ENCODER" { + output.set_vorbis(key, val); + } + } + } + Block::StreamInfo(_) | Block::Padding(_) => {} + _ => output.push_block(block.clone()), } } -} -struct FileEncoder { - filename: PathBuf, - streamdata: StreamConfig, - format: BoxedFormatReader, - decoder: BoxedAudioDecoder, + output.write_to_path(temp_name)?; + Ok(()) } -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_file(filename: impl AsRef<Path>) -> Result<()> { + let temp_name = filename.as_ref().with_extension("tmp"); + if temp_name.exists() { + std::fs::remove_file(&temp_name)?; } + let mut decoder = FlacReader::open(&filename)?; + let streaminfo = decoder.streaminfo(); - 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(); - - loop { - let packet = match self.format.next_packet() { - Ok(Some(packet)) => packet, - Ok(None) => break, - Err(error) => return Err(error.into()), - }; + let mut encoder = FlacEncoder::new() + .unwrap() + .channels(streaminfo.channels) + .bits_per_sample(streaminfo.bits_per_sample) + .sample_rate(streaminfo.sample_rate) + .compression_level(8) + .verify(false) + .init_file(&temp_name) + .unwrap(); - if packet.track_id() != track_id { - continue; - } + let mut hasher = Md5::new(); - 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); + let samples = decoder + .samples() + .map(|res| { + let sample = res.unwrap(); + match streaminfo.bits_per_sample { + 16 => { + hasher.update(i16::try_from(sample).unwrap().to_le_bytes()); } - 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 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() { - if key != "ENCODER" { - output.set_vorbis(key, val); - } - } + 24 => { + hasher.update(i24::i24::try_from_i32(sample).unwrap().to_le_bytes()); + } + 32 => { + hasher.update(sample.to_le_bytes()); } - Block::StreamInfo(_) | Block::Padding(_) => {} - _ => output.push_block(block.clone()), + _ => {} } - } - - output.write_to_path(self.temp_name())?; - 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(); + sample + }) + .collect::<Vec<i32>>(); - let decoder = symphonia::default::get_codecs() - .make_audio_decoder( - track.codec_params.as_ref().unwrap().audio().unwrap(), - &Default::default(), - ) + encoder + .process_interleaved(&samples, samples.len() as u32 / streaminfo.channels) .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(), - }; + if let Err(enc) = encoder.finish() { + return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); + } - Ok((format, decoder, config)) + let hash = hasher.finalize().to_vec(); + write_tags(&filename, hash)?; + std::fs::rename(temp_name, filename)?; + Ok(()) } -pub fn encode_file(filename: impl AsRef<Path>) -> Result<()> { - let mut filencoder = FileEncoder::new(filename)?; - let temp_name = filencoder.temp_name(); - - if temp_name.exists() { - std::fs::remove_file(&temp_name)?; +pub fn handle_encode(filename: impl AsRef<Path>) -> Result<()> { + if let Err(error) = encode_file(&filename) { + let _ = std::fs::remove_file(filename.as_ref().with_extension("tmp")); + Err(error) + } else { + Ok(()) } - - let mut outf = File::create(temp_name)?; - let mut outw = WriteWrapper(&mut outf); - let enc = FlacEncoder::new() - .unwrap() - .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 hash = filencoder.encode(enc)?; - filencoder.write_tags(hash)?; - std::fs::rename(filencoder.temp_name(), filencoder.filename)?; - Ok(()) } 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()) + if let Some(vendor) = FlacReader::open_ext( + file, + FlacReaderOptions { + metadata_only: true, + read_vorbis_comment: true, + }, + )? + .vendor() + { + Ok(vendor.to_string()) } else { Err(anyhow!("Vendor string not found")) } @@ -211,7 +117,6 @@ pub fn get_vendor(file: impl AsRef<Path>) -> Result<String> { #[cfg(test)] mod tests { use super::*; - use metaflac::Tag; #[test] fn bit16() { @@ -219,20 +124,11 @@ mod tests { let tempname = "16bit.flac.temp"; std::fs::copy(name, tempname).unwrap(); encode_file(name).unwrap(); - let target_md5 = Tag::read_from_path(tempname) - .unwrap() - .get_streaminfo() - .unwrap() - .md5 - .clone(); - let encoded_md5 = Tag::read_from_path(name) - .unwrap() - .get_streaminfo() - .unwrap() - .md5 - .clone(); - std::fs::remove_file(tempname).unwrap(); - assert_eq!(target_md5, encoded_md5); + let target_md5 = FlacReader::open(tempname).unwrap().streaminfo().md5sum; + let temp_md5 = FlacReader::open(name).unwrap().streaminfo().md5sum; + + std::fs::rename(tempname, name).unwrap(); + assert_eq!(target_md5, temp_md5); } #[test] @@ -241,20 +137,11 @@ mod tests { let tempname = "24bit.flac.temp"; std::fs::copy(name, tempname).unwrap(); encode_file(name).unwrap(); - let target_md5 = Tag::read_from_path(tempname) - .unwrap() - .get_streaminfo() - .unwrap() - .md5 - .clone(); - let encoded_md5 = Tag::read_from_path(name) - .unwrap() - .get_streaminfo() - .unwrap() - .md5 - .clone(); - std::fs::remove_file(tempname).unwrap(); - assert_eq!(target_md5, encoded_md5); + let target_md5 = FlacReader::open(tempname).unwrap().streaminfo().md5sum; + let temp_md5 = FlacReader::open(name).unwrap().streaminfo().md5sum; + + std::fs::rename(tempname, name).unwrap(); + assert_eq!(target_md5, temp_md5); } #[test] @@ -263,19 +150,10 @@ mod tests { let tempname = "32bit.flac.temp"; std::fs::copy(name, tempname).unwrap(); encode_file(name).unwrap(); - let target_md5 = Tag::read_from_path(tempname) - .unwrap() - .get_streaminfo() - .unwrap() - .md5 - .clone(); - let encoded_md5 = Tag::read_from_path(name) - .unwrap() - .get_streaminfo() - .unwrap() - .md5 - .clone(); - std::fs::remove_file(tempname).unwrap(); - assert_eq!(target_md5, encoded_md5); + let target_md5 = FlacReader::open(tempname).unwrap().streaminfo().md5sum; + let temp_md5 = FlacReader::open(name).unwrap().streaminfo().md5sum; + + std::fs::rename(tempname, name).unwrap(); + assert_eq!(target_md5, temp_md5); } } |
