summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock129
-rw-r--r--Cargo.toml8
-rw-r--r--src/files.rs4
-rw-r--r--src/flac.rs312
4 files changed, 138 insertions, 315 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1e2b0f3..2eb7f8e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 44cac70..54ff725 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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);
}
}