diff options
| author | jakka <jakkadoujin@gmail.com> | 2025-06-14 00:22:23 +0300 |
|---|---|---|
| committer | jakka <jakkadoujin@gmail.com> | 2025-06-14 00:22:23 +0300 |
| commit | 11d428265b3f7f5541779ef2df24bfb17d7250aa (patch) | |
| tree | fca76dc45e78325d5b4dc2597002d430eec888c2 /src | |
| parent | cddc489acdd2f0ed93632093f8b86a45fc3601aa (diff) | |
added more file processing logic
Diffstat (limited to 'src')
| -rw-r--r-- | src/db.rs | 57 | ||||
| -rw-r--r-- | src/files.rs | 46 | ||||
| -rw-r--r-- | src/flac.rs | 18 |
3 files changed, 90 insertions, 31 deletions
@@ -1,7 +1,8 @@ use anyhow::{Result, anyhow}; use directories::BaseDirs; -use futures_util::StreamExt; +use futures_util::Stream; use libsql::{Builder, Connection, params}; +use pin_utils::pin_mut; use std::{ ffi::OsStr, fmt::Display, @@ -83,21 +84,6 @@ impl Database { Ok(()) } - pub async fn get_files_toencode(&self) -> Result<Vec<String>> { - let rows = self.0.query(TOENCODE_QUERY, ()).await?; - if rows.column_count() == 0 { - return Err(anyhow!(Errors::EmptyQuery)); - }; - - let filenames = rows - .into_stream() - .map(|row| row.unwrap().get_str(0).unwrap().to_string()) - .collect::<Vec<String>>() - .await; - - Ok(filenames) - } - pub async fn check_file(&self, filename: &impl AsRef<OsStr>) -> Result<bool> { let abs_filename = absolute(Path::new(filename))?; @@ -152,6 +138,12 @@ impl Database { Ok(()) } + + pub async fn get_toencode_stream( + &self, + ) -> Result<impl Stream<Item = libsql::Result<libsql::Row>>> { + Ok(self.0.query(TOENCODE_QUERY, ()).await?.into_stream()) + } } pub async fn open_default_db() -> Result<Database> { @@ -165,6 +157,8 @@ pub async fn open_default_db() -> Result<Database> { #[cfg(test)] mod tests { + use futures_util::StreamExt; + use super::*; #[tokio::test] @@ -175,9 +169,22 @@ mod tests { for file in filenames { let _ = conn.insert_file(&file.to_string()).await; } - let returned = conn.get_files_toencode().await.unwrap(); + let returned = conn + .0 + .query(TOENCODE_QUERY, ()) + .await + .unwrap() + .into_stream(); + pin_mut!(returned); + + let mut counter = 0; + + while let Some(Ok(_)) = returned.next().await { + counter += 1 + } + std::fs::remove_file(dbname).unwrap(); - assert!(returned.is_empty()) + assert!(counter == 0) } #[tokio::test] @@ -203,8 +210,18 @@ mod tests { conn.update_file(&"16bit.flac".to_string()).await.unwrap(); - let returned = conn.get_files_toencode().await.unwrap(); + let returned = conn + .0 + .query(TOENCODE_QUERY, ()) + .await + .unwrap() + .into_stream(); + pin_mut!(returned); + let mut counter = 0; + while let Some(Ok(_)) = returned.next().await { + counter += 1 + } std::fs::remove_file(dbname).unwrap(); - assert!(returned.is_empty()) + assert!(counter == 0) } } diff --git a/src/files.rs b/src/files.rs index e0d9757..dc96650 100644 --- a/src/files.rs +++ b/src/files.rs @@ -1,5 +1,6 @@ use anyhow::{Result, anyhow}; -use libsql::Connection; +use futures_util::StreamExt; +use pin_utils::pin_mut; use std::{ fmt::Display, path::{Path, PathBuf, absolute}, @@ -7,14 +8,20 @@ use std::{ }; use tokio::{fs::read_dir, task::JoinSet}; -use crate::db::Database; +use crate::{db::Database, flac::handle_encode}; #[derive(Debug)] -struct FileError { +pub struct FileError { file: PathBuf, error: anyhow::Error, } +impl FileError { + pub fn new(file: PathBuf, error: anyhow::Error) -> Self { + FileError { file, error } + } +} + impl Display for FileError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -37,17 +44,17 @@ async fn handle_file(file: PathBuf, conn: Database) -> Result<()> { let db_time = conn.get_modtime(&file).await?; if modtime != db_time { if let Err(error) = conn.update_file(&file).await { - return Err(anyhow!(FileError { file, error })); + return Err(anyhow!(FileError::new(file, error))); }; } return Ok(()); } - Err(error) => return Err(anyhow!(FileError { file, error })), + Err(error) => return Err(anyhow!(FileError::new(file, error))), _ => {} } if let Err(error) = conn.insert_file(&file).await { - return Err(anyhow!(FileError { file, error })); + return Err(anyhow!(FileError::new(file, error))); } Ok(()) @@ -59,7 +66,6 @@ pub async fn index_files_recursively(path: &Path, conn: &Database) -> Result<()> } let abspath = absolute(path)?; let mut tasks = JoinSet::new(); - let mut counter: i64 = 0; let mut dirs = vec![abspath]; @@ -74,12 +80,34 @@ pub async fn index_files_recursively(path: &Path, conn: &Database) -> Result<()> if let Some(ext) = path.extension() { if ext == "flac" { let newconn = conn.clone(); - counter += 1; tasks.spawn(async move { handle_file(path, newconn).await }); } } } - print!("\rFiles found:\t{counter}") + } + } + + while let Some(task) = tasks.join_next().await { + match task { + Ok(Err(error)) => eprintln!("{error}"), + Err(error) => eprintln!("Error encountered:\t{}", error), + _ => {} + } + } + + Ok(()) +} + +pub async fn reencode_files(conn: &Database) -> Result<()> { + let stream = conn.get_toencode_stream().await?; + pin_mut!(stream); + + let mut tasks = JoinSet::new(); + + while let Some(Ok(row)) = stream.next().await { + if let Some(file) = row.get_value(0)?.as_text() { + let filename = PathBuf::from(file.clone()); + tasks.spawn_blocking(move || handle_encode(filename)); } } diff --git a/src/flac.rs b/src/flac.rs index 16627e2..940e816 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -3,7 +3,11 @@ use flac_bound::{FlacEncoder, WriteWrapper}; use i24::i24; use md5::{Digest, Md5, Md5Core, digest::core_api::CoreWrapper}; use metaflac::{Block, Tag}; -use std::{ffi::OsStr, fs::File, path::Path}; +use std::{ + ffi::OsStr, + fs::File, + path::{Path, PathBuf}, +}; use symphonia::core::{ audio::{Audio, GenericAudioBufferRef}, codecs::audio::AudioDecoder, @@ -12,6 +16,8 @@ use symphonia::core::{ meta::MetadataOptions, }; +use crate::files; + pub const CURRENT_VENDOR: &str = "reference libFLAC 1.5.0 20250211"; struct StreamConfig { @@ -246,7 +252,7 @@ fn write_tags( Ok(()) } -pub fn encode_file(filename: impl AsRef<OsStr>) -> Result<()> { +fn encode_file(filename: impl AsRef<OsStr>) -> Result<()> { let file = Path::new(&filename); let tempname = &format!("{}.tmp", file.to_str().unwrap()); @@ -279,6 +285,14 @@ pub fn encode_file(filename: impl AsRef<OsStr>) -> Result<()> { Ok(()) } +pub fn handle_encode(file: PathBuf) -> Result<()> { + if let Err(error) = encode_file(&file) { + Err(anyhow!(files::FileError::new(file, error))) + } else { + Ok(()) + } +} + pub fn get_vendor(file: &Path) -> Result<String> { let tag = Tag::read_from_path(file)?; Ok(tag.vorbis_comments().unwrap().vendor_string.clone()) |
