summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakka <jakka@jakka.su>2025-09-27 16:49:36 +0300
committerjakka <jakka@jakka.su>2025-09-27 16:49:36 +0300
commit275392373060afc6a895012e2064a4f4bbf37ea1 (patch)
tree04b9f18d2dc234e5eca6040d55173b5ef6245f11
parent82e4c55483c38569b577f2b1ba5641f504f2498e (diff)
major reencoding logic rewrite, code cleanup
-rw-r--r--CHANGELOG.md6
-rw-r--r--Cargo.lock125
-rw-r--r--Cargo.toml5
-rw-r--r--README.md1
-rw-r--r--src/db.rs73
-rw-r--r--src/files.rs35
-rw-r--r--src/flac.rs209
7 files changed, 273 insertions, 181 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9d1af8..0fd6aea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# v0.3.0
+* removed claxon dep in favor of flac_codec, same for metaflac
+* cleaned up code a bit
+
# v0.2.6-fix
* remembered about changelog.md
* fixed incorrect encoded_by tag match
@@ -7,4 +11,4 @@
* added better bar incremental logic by passing it to threads
* added graceful shutdown (albeit its a bit slow)
* checks file if it exists before reencoding
-* removes temporary file if it was left uncleaned from the previous session \ No newline at end of file
+* removes temporary file if it was left uncleaned from the previous session
diff --git a/Cargo.lock b/Cargo.lock
index 9257c5d..e52de08 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -59,22 +59,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
name = "bitflags"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
-name = "bumpalo"
-version = "3.19.0"
+name = "bitstream-io"
+version = "4.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
+dependencies = [
+ "core2",
+]
[[package]]
-name = "byteorder"
-version = "1.5.0"
+name = "bumpalo"
+version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "cc"
@@ -135,11 +144,6 @@ 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"
@@ -168,6 +172,40 @@ dependencies = [
]
[[package]]
+name = "core2"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
name = "ctrlc"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -206,6 +244,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -239,19 +283,30 @@ dependencies = [
]
[[package]]
+name = "flac-codec"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dba37ade3b9791d1a219ccb29826d06261538c523f38b5c5aadb44646b1d6a0"
+dependencies = [
+ "arrayvec",
+ "bitstream-io",
+ "md5",
+ "rayon",
+]
+
+[[package]]
name = "flac-reencoder"
-version = "0.2.7"
+version = "0.3.0"
dependencies = [
"anyhow",
"clap",
"clap_complete",
- "claxon",
"console",
"ctrlc",
"directories",
"flac-bound",
+ "flac-codec",
"indicatif",
- "metaflac",
"rusqlite",
"walkdir",
]
@@ -292,12 +347,6 @@ dependencies = [
]
[[package]]
-name = "hex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
-[[package]]
name = "indicatif"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -371,14 +420,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
-name = "metaflac"
-version = "0.2.8"
+name = "md5"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdf25a3451319c52a4a56d956475fbbb763bfb8420e2187d802485cb0fd8d965"
-dependencies = [
- "byteorder",
- "hex",
-]
+checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0"
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "nix"
@@ -441,6 +492,26 @@ dependencies = [
]
[[package]]
+name = "rayon"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
name = "redox_users"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 76abcde..f0003b0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "flac-reencoder"
-version = "0.2.7"
+version = "0.3.0"
edition = "2024"
repository = "https://github.com/justjakka/reencoder/"
license = "BSD-3-Clause"
@@ -18,12 +18,11 @@ flac-bound = { version = "0.5.0", default-features = false }
indicatif = { version = "0.18.0", features = ["improved_unicode"] }
walkdir = "2.5.0"
console = { version = "0.16.0", features = ["windows-console-colors"] }
-claxon = { git = "https://github.com/justjakka/claxon.git" }
-metaflac = "0.2.8"
rusqlite = { version = "0.36.0", default-features = false, features = [
"modern_sqlite",
] }
ctrlc = "3.4.7"
+flac-codec = { version = "1.2.0", features = ["rayon"] }
[features]
default = ["bundled"]
diff --git a/README.md b/README.md
index 1efe605..37cb744 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,6 @@
scans a specified folder and reencodes flacs if they needed to be reencoded
-currently this project uses [my own hacky fork of claxon](https://github.com/justjakka/claxon)
```
Usage: reencoder [OPTIONS] [path]
diff --git a/src/db.rs b/src/db.rs
index 7f0ccef..cdce946 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -22,21 +22,21 @@ const GET_MODTIME: &str = "SELECT modtime FROM flacs WHERE path = ?1";
pub trait Database {
type Conn;
- fn new(path: Option<impl AsRef<Path>>) -> Result<Self::Conn>;
- fn insert_file(&self, filename: impl AsRef<Path>) -> Result<()>;
- fn update_file(&self, filename: impl AsRef<Path>) -> Result<()>;
- fn check_file(&self, filename: impl AsRef<Path>) -> Result<bool>;
+ fn new(path: Option<&PathBuf>) -> Result<Self::Conn>;
+ fn insert_file(&self, filename: &Path) -> Result<()>;
+ fn update_file(&self, filename: &Path) -> Result<()>;
+ fn check_file(&self, filename: &Path) -> Result<bool>;
fn init_clean_files(&self) -> Result<Vec<PathBuf>, rusqlite::Error>;
- fn remove_file(&self, filename: impl AsRef<Path>) -> Result<()>;
+ fn remove_file(&self, filename: &Path) -> Result<()>;
fn get_toencode_files(&self) -> Result<Vec<PathBuf>, rusqlite::Error>;
fn get_toencode_number(&self) -> Result<u64, rusqlite::Error>;
- fn get_modtime(&self, file: impl AsRef<Path>) -> Result<u64>;
+ fn get_modtime(&self, file: &Path) -> Result<u64>;
fn vacuum(&self) -> Result<()>;
}
impl Database for Connection {
type Conn = Connection;
- fn new(path: Option<impl AsRef<Path>>) -> Result<Self> {
+ fn new(path: Option<&PathBuf>) -> Result<Self> {
let conn = if let Some(file) = path {
Connection::open(file)?
} else if let Some(base_dir) = BaseDirs::new() {
@@ -49,11 +49,10 @@ impl Database for Connection {
Ok(conn)
}
- fn insert_file(&self, filename: impl AsRef<Path>) -> Result<()> {
+ fn insert_file(&self, filename: &Path) -> Result<()> {
let toencode = !matches!(get_vendor(&filename)?.as_str(), CURRENT_VENDOR);
let modtime = filename
- .as_ref()
.metadata()?
.modified()?
.duration_since(UNIX_EPOCH)?
@@ -61,15 +60,14 @@ impl Database for Connection {
self.execute(
ADD_ITEM,
- params![filename.as_ref().to_str().unwrap(), toencode, modtime],
+ params![filename.to_str().unwrap(), toencode, modtime],
)?;
Ok(())
}
- fn update_file(&self, filename: impl AsRef<Path>) -> Result<()> {
+ fn update_file(&self, filename: &Path) -> Result<()> {
let modtime = filename
- .as_ref()
.metadata()?
.modified()?
.duration_since(UNIX_EPOCH)?
@@ -77,21 +75,17 @@ impl Database for Connection {
self.execute(
UPDATE_ITEM,
- params![filename.as_ref().to_str().unwrap(), false, modtime],
+ params![filename.to_str().unwrap(), false, modtime],
)?;
Ok(())
}
- fn check_file(&self, filename: impl AsRef<Path>) -> Result<bool> {
- if self.query_one(
- CHECK_FILE,
- params!(filename.as_ref().to_str().unwrap()),
- |row| {
- let num: bool = row.get(0)?;
- Ok(num)
- },
- )? {
+ fn check_file(&self, filename: &Path) -> Result<bool> {
+ if self.query_one(CHECK_FILE, params!(filename.to_str().unwrap()), |row| {
+ let num: bool = row.get(0)?;
+ Ok(num)
+ })? {
Ok(true)
} else {
Ok(false)
@@ -110,8 +104,8 @@ impl Database for Connection {
Ok(files)
}
- fn remove_file(&self, filename: impl AsRef<Path>) -> Result<()> {
- self.execute(REMOVE_FILE, params!(filename.as_ref().to_str().unwrap()))?;
+ fn remove_file(&self, filename: &Path) -> Result<()> {
+ self.execute(REMOVE_FILE, params!(filename.to_str().unwrap()))?;
Ok(())
}
@@ -133,15 +127,13 @@ impl Database for Connection {
})
}
- fn get_modtime(&self, file: impl AsRef<Path>) -> Result<u64> {
- Ok(self.query_one(
- GET_MODTIME,
- params![file.as_ref().to_str().unwrap()],
- |row| {
+ fn get_modtime(&self, file: &Path) -> Result<u64> {
+ Ok(
+ self.query_one(GET_MODTIME, params![file.to_str().unwrap()], |row| {
let modtime: u64 = row.get(0)?;
Ok(modtime)
- },
- )?)
+ })?,
+ )
}
fn vacuum(&self) -> Result<()> {
@@ -157,7 +149,7 @@ mod tests {
#[test]
fn check_localfiles() {
- let dbname = String::from("temp1.db");
+ let dbname = PathBuf::from("temp1.db");
let filenames = [
"./samples/16bit.flac",
"./samples/24bit.flac",
@@ -166,7 +158,8 @@ mod tests {
let mut counter = 0;
let conn = Connection::new(Some(&dbname)).unwrap();
for file in filenames {
- conn.insert_file(&file.to_string()).unwrap();
+ let filename = PathBuf::from(file);
+ conn.insert_file(&filename).unwrap();
}
let mut stmt = conn.prepare(TOENCODE_PATHS).unwrap();
let mut returned = stmt.query(()).unwrap();
@@ -180,7 +173,7 @@ mod tests {
#[test]
fn check_update() {
- let dbname = String::from("temp2.db");
+ let dbname = PathBuf::from("temp2.db");
let filenames = [
"./samples/16bit.flac",
"./samples/24bit.flac",
@@ -188,7 +181,7 @@ mod tests {
];
let conn = Connection::new(Some(&dbname)).unwrap();
for file in filenames {
- conn.insert_file(Path::new(file).canonicalize().unwrap())
+ conn.insert_file(&Path::new(file).canonicalize().unwrap())
.unwrap();
}
@@ -206,14 +199,8 @@ mod tests {
)
.unwrap();
- conn.update_file(
- Path::new("./samples/16bit.flac")
- .canonicalize()
- .unwrap()
- .to_str()
- .unwrap(),
- )
- .unwrap();
+ conn.update_file(&Path::new("./samples/16bit.flac").canonicalize().unwrap())
+ .unwrap();
let mut stmt = conn.prepare(TOENCODE_PATHS).unwrap();
let mut returned = stmt.query(()).unwrap();
diff --git a/src/files.rs b/src/files.rs
index 0407469..afb42d7 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -29,9 +29,9 @@ struct FileError {
}
impl FileError {
- fn new(file: impl AsRef<Path>, error: anyhow::Error) -> Self {
+ fn new(file: &Path, error: anyhow::Error) -> Self {
FileError {
- file: file.as_ref().to_path_buf(),
+ file: file.to_path_buf(),
error,
}
}
@@ -50,35 +50,34 @@ impl Display for FileError {
impl Error for FileError {}
-fn handle_file(file: impl AsRef<Path>, conn: &Connection) -> Result<()> {
- if conn.check_file(&file)? {
+fn handle_file(file: &Path, conn: &Connection) -> Result<()> {
+ if conn.check_file(file)? {
let modtime = file
- .as_ref()
.metadata()?
.modified()?
.duration_since(UNIX_EPOCH)?
.as_secs();
- let db_modtime = conn.get_modtime(&file)?;
+ let db_modtime = conn.get_modtime(file)?;
if modtime != db_modtime {
- conn.update_file(&file)?;
+ conn.update_file(file)?;
}
return Ok(());
}
- conn.insert_file(&file)?;
+ conn.insert_file(file)?;
Ok(())
}
pub fn index_files_recursively(
- path: impl AsRef<Path>,
+ path: &Path,
conn: &Connection,
handler: Arc<AtomicBool>,
) -> Result<()> {
- if !path.as_ref().is_dir() {
+ if !path.is_dir() {
return Err(anyhow!("Invalid root directory"));
}
- let abspath = path.as_ref().canonicalize()?;
+ let abspath = path.canonicalize()?;
#[cfg(not(test))]
let bar = ProgressBar::with_draw_target(Some(0), ProgressDrawTarget::stdout_with_hz(60))
@@ -108,7 +107,7 @@ pub fn index_files_recursively(
}
if path.extension().is_some_and(|x| x == "flac") {
if let Err(error) = handle_file(&path, conn) {
- eprintln!("{}", FileError::new(path, error));
+ eprintln!("{}", FileError::new(&path, error));
} else {
#[cfg(not(test))]
bar.inc(1);
@@ -172,7 +171,7 @@ pub fn reencode_files(conn: Connection, handler: Arc<AtomicBool>, threads: usize
Err(error) => eprintln!("{}", FileError::new(&file, error)),
Ok(false) => {
if let Err(error) = lock.lock().unwrap().update_file(&file) {
- eprintln!("{}", FileError::new(file, error));
+ eprintln!("{}", FileError::new(&file, error));
}
#[cfg(not(test))]
bar.inc(1)
@@ -205,6 +204,7 @@ pub fn clean_files(conn: &Connection, handler: Arc<AtomicBool>) -> Result<()> {
spinner.tick();
files.iter().for_each(|file| {
+ #[allow(clippy::collapsible_if)]
if handler.load(Ordering::SeqCst) && !file.exists() {
if let Err(error) = conn.remove_file(file) {
eprintln!("{}", FileError::new(file, error))
@@ -227,7 +227,7 @@ mod tests {
#[test]
fn test_index_lots_of_files() {
- let dbname = "temp3.db";
+ let dbname = PathBuf::from("temp3.db");
let handler = Arc::new(AtomicBool::new(true));
let conn = Connection::new(Some(&dbname)).unwrap();
index_files_recursively(Path::new("./testfiles"), &conn, handler).unwrap();
@@ -236,7 +236,7 @@ mod tests {
#[test]
fn test_clean_files() {
- let dbname = "temp4.db";
+ let dbname = PathBuf::from("temp4.db");
let handler = Arc::new(AtomicBool::new(true));
let conn = Connection::new(Some(&dbname)).unwrap();
let filenames = [
@@ -247,7 +247,8 @@ mod tests {
];
std::fs::copy("./samples/32bit.flac", "./samples/nonexisting.flac").unwrap();
for file in filenames {
- conn.insert_file(&file).unwrap();
+ let filename = PathBuf::from(file);
+ conn.insert_file(&filename).unwrap();
}
std::fs::remove_file("./samples/nonexisting.flac").unwrap();
@@ -260,7 +261,7 @@ mod tests {
#[test]
fn test_reencode_lots_of_files() {
- let dbname = "temp5.db";
+ let dbname = PathBuf::from("temp5.db");
let handler = Arc::new(AtomicBool::new(true));
let conn = Connection::new(Some(&dbname)).unwrap();
let temp = handler.clone();
diff --git a/src/flac.rs b/src/flac.rs
index 7b542ac..e882cd7 100644
--- a/src/flac.rs
+++ b/src/flac.rs
@@ -1,7 +1,9 @@
use anyhow::{Result, anyhow};
-use claxon::{FlacReader, FlacReaderOptions};
use flac_bound::FlacEncoder;
-use metaflac::{Block, Tag};
+use flac_codec::{
+ decode::{Metadata, verify},
+ *,
+};
use std::{
path::Path,
sync::{
@@ -10,52 +12,63 @@ use std::{
},
};
-pub const CURRENT_VENDOR: &str = "reference libFLAC 1.5.0 20250211";
-
+pub(crate) const CURRENT_VENDOR: &str = "reference libFLAC 1.5.0 20250211";
const BADTAGS: [&str; 3] = ["encoded_by", "encodedby", "encoder"];
-fn write_tags(filename: impl AsRef<Path>) -> 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)?;
-
- for block in tags.blocks() {
- match block {
- Block::VorbisComment(block) => {
- for (key, val) in block.comments.iter() {
- if !BADTAGS.contains(&key.to_lowercase().as_str()) {
- output.set_vorbis(key, val.to_owned());
- }
- }
- }
- Block::Padding(_) => {}
- _ => output.push_block(block.to_owned()),
- }
- }
-
- output.write_to_path(temp_name)?;
- Ok(())
-}
+fn encode_file(filename: &Path, handler: Arc<AtomicBool>) -> Result<bool> {
+ if verify(filename).is_err() {
+ return Err(anyhow!("corrupt file"));
+ };
-fn encode_file(filename: impl AsRef<Path>, handler: Arc<AtomicBool>) -> Result<bool> {
- let temp_name = filename.as_ref().with_extension("tmp");
+ let temp_name = filename.with_extension("tmp");
if temp_name.exists() {
std::fs::remove_file(&temp_name)?;
}
- let mut decoder = FlacReader::open(&filename)?;
- let streaminfo = decoder.streaminfo();
- let num_channels: usize = streaminfo.channels.try_into()?;
+ let mut reader = decode::FlacSampleReader::open(filename)?;
+
+ let blocklist = reader.metadata();
+
+ let streaminfo = blocklist.streaminfo();
+
+ let channels = streaminfo.channel_count() as u32;
+
+ let metadata = blocklist
+ .blocks()
+ .filter_map(|block| {
+ use metadata::Block;
+ use metadata::BlockRef::*;
+ match block {
+ SeekTable(table) => Some(Block::SeekTable(table.clone())),
+ Application(app) => Some(Block::Application(app.clone())),
+ Cuesheet(sheet) => Some(Block::Cuesheet(sheet.clone())),
+ Picture(picture) => Some(Block::Picture(picture.clone())),
+ VorbisComment(comments) => {
+ let mut cloned = comments.clone();
+ for tag in BADTAGS {
+ cloned.remove(tag);
+ }
+ cloned.vendor_string = CURRENT_VENDOR.to_string();
+ Some(Block::VorbisComment(cloned))
+ }
+ _ => None,
+ }
+ })
+ .collect::<Vec<metadata::Block>>();
let mut encoder = if let Some(encoder) = FlacEncoder::new() {
- if let Ok(encoder) = encoder
- .channels(streaminfo.channels)
- .bits_per_sample(streaminfo.bits_per_sample)
- .sample_rate(streaminfo.sample_rate)
- .compression_level(8)
- .verify(false)
- .init_file(&temp_name)
- {
+ if let Ok(encoder) = {
+ let mut encoder = encoder
+ .channels(streaminfo.channel_count() as u32)
+ .bits_per_sample(streaminfo.bits_per_sample())
+ .sample_rate(streaminfo.sample_rate())
+ .compression_level(8)
+ .verify(false);
+ if let Some(size) = reader.total_samples() {
+ encoder = encoder.total_samples_estimate(size)
+ }
+ encoder.init_file(&temp_name)
+ } {
encoder
} else {
return Err(anyhow!("failed to create encoder"));
@@ -64,28 +77,26 @@ fn encode_file(filename: impl AsRef<Path>, handler: Arc<AtomicBool>) -> Result<b
return Err(anyhow!("failed to create encoder"));
};
- let mut frame_reader = decoder.blocks();
- let mut buffer = Vec::new();
- let mut block_buffer = Vec::with_capacity(streaminfo.max_block_size as usize * num_channels);
-
while handler.load(Ordering::SeqCst) {
- match frame_reader.read_next_or_eof(block_buffer) {
- Ok(Some(block)) => {
- for ch in 0..block.channels() {
- buffer.push(block.channel(ch));
- }
+ match reader.fill_buf() {
+ Ok(buf) => {
+ if !buf.is_empty() {
+ let length = buf.len();
+ if encoder
+ .process_interleaved(buf, length as u32 / channels)
+ .is_err()
+ {
+ return Err(anyhow!(
+ "Error while processing samples:\t{:?}",
+ encoder.state()
+ ));
+ };
- if encoder.process(&buffer).is_err() {
- return Err(anyhow!(
- "Error while processing samples:\t{:?}",
- encoder.state()
- ));
- };
- buffer.clear();
- buffer = buffer.into_iter().map(|_| unreachable!()).collect();
- block_buffer = block.into_buffer();
+ reader.consume(length);
+ } else {
+ break;
+ }
}
- Ok(None) => break,
Err(error) => return Err(error.into()),
}
}
@@ -99,32 +110,51 @@ fn encode_file(filename: impl AsRef<Path>, handler: Arc<AtomicBool>) -> Result<b
if let Err(enc) = encoder.finish() {
return Err(anyhow!("Encoding failed:\t{:?}", enc.state()));
}
- write_tags(&filename)?;
- std::fs::rename(temp_name, filename)?;
+
+ metadata::update(&temp_name, |blocklist| {
+ for block in metadata {
+ use metadata::Block::*;
+ match block {
+ Application(b) => {
+ let _ = blocklist.insert(b);
+ }
+ Picture(b) => {
+ let _ = blocklist.insert(b);
+ }
+ VorbisComment(b) => {
+ let _ = blocklist.insert(b);
+ }
+ Cuesheet(b) => {
+ let _ = blocklist.insert(b);
+ }
+ SeekTable(b) => {
+ let _ = blocklist.insert(b);
+ }
+ _ => {}
+ }
+ }
+ Ok::<(), flac_codec::Error>(())
+ })?;
+
+ std::fs::rename(&temp_name, filename)?;
+
Ok(false)
}
-pub fn handle_encode(filename: impl AsRef<Path>, handler: Arc<AtomicBool>) -> Result<bool> {
- match encode_file(&filename, handler) {
+pub fn handle_encode(filename: &Path, handler: Arc<AtomicBool>) -> Result<bool> {
+ match encode_file(filename, handler) {
Err(error) => {
- let _ = std::fs::remove_file(filename.as_ref().with_extension("tmp"));
+ let _ = std::fs::remove_file(filename.with_extension("tmp"));
Err(error)
}
Ok(res) => Ok(res),
}
}
-pub fn get_vendor(file: impl AsRef<Path>) -> Result<String> {
- if let Some(vendor) = FlacReader::open_ext(
- file,
- FlacReaderOptions {
- metadata_only: true,
- read_vorbis_comment: true,
- },
- )?
- .vendor()
- {
- Ok(vendor.to_string())
+pub fn get_vendor(file: &Path) -> Result<String> {
+ let blocklist = metadata::BlockList::open(file)?;
+ if let Some(data) = blocklist.get::<metadata::VorbisComment>() {
+ Ok(data.vendor_string.to_owned())
} else {
Err(anyhow!("Vendor string not found"))
}
@@ -133,17 +163,18 @@ pub fn get_vendor(file: impl AsRef<Path>) -> Result<String> {
#[cfg(test)]
mod tests {
use super::*;
+ use std::path::PathBuf;
#[test]
fn bit16() {
- let name = "./samples/16bit.flac";
- let tempname = "./samples/16bit.flac.temp";
- std::fs::copy(name, tempname).unwrap();
+ let name = PathBuf::from("./samples/16bit.flac");
+ let tempname = PathBuf::from("./samples/16bit.flac.temp");
+ std::fs::copy(&name, &tempname).unwrap();
let handler = Arc::new(AtomicBool::new(true));
- encode_file(name, handler).unwrap();
+ encode_file(&name, handler).unwrap();
let output = std::process::Command::new("flac")
.arg("-wts")
- .arg(name)
+ .arg(&name)
.status();
std::fs::rename(tempname, name).unwrap();
assert!(output.unwrap().success());
@@ -151,14 +182,14 @@ mod tests {
#[test]
fn bit24() {
- let name = "./samples/24bit.flac";
- let tempname = "./samples/24bit.flac.temp";
- std::fs::copy(name, tempname).unwrap();
+ let name = PathBuf::from("./samples/24bit.flac");
+ let tempname = PathBuf::from("./samples/24bit.flac.temp");
+ std::fs::copy(&name, &tempname).unwrap();
let handler = Arc::new(AtomicBool::new(true));
- encode_file(name, handler).unwrap();
+ encode_file(&name, handler).unwrap();
let output = std::process::Command::new("flac")
.arg("-wts")
- .arg(name)
+ .arg(&name)
.status();
std::fs::rename(tempname, name).unwrap();
assert!(output.unwrap().success());
@@ -166,14 +197,14 @@ mod tests {
#[test]
fn bit32() {
- let name = "./samples/32bit.flac";
- let tempname = "./samples/32bit.flac.temp";
- std::fs::copy(name, tempname).unwrap();
+ let name = PathBuf::from("./samples/32bit.flac");
+ let tempname = PathBuf::from("./samples/32bit.flac.temp");
+ std::fs::copy(&name, &tempname).unwrap();
let handler = Arc::new(AtomicBool::new(true));
- encode_file(name, handler).unwrap();
+ encode_file(&name, handler).unwrap();
let output = std::process::Command::new("flac")
.arg("-wts")
- .arg(name)
+ .arg(&name)
.status();
std::fs::rename(tempname, name).unwrap();
assert!(output.unwrap().success());