summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakka <jakkadoujin@gmail.com>2025-06-19 23:28:13 +0300
committerjakka <jakkadoujin@gmail.com>2025-06-19 23:28:13 +0300
commit2ae34c91a739c4963b52c22027a16a2aa34a9561 (patch)
treef4ae94f86dbe14fae06043358f326015fc488841
parent631a904769fb7859adfb7e0ca9fb1bb20f9c5c77 (diff)
added file checking when opening db from file. added colors to cleaning information and total indexed files. higher fps for progress bars
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml7
-rw-r--r--src/db.rs39
-rw-r--r--src/files.rs62
-rw-r--r--src/main.rs5
5 files changed, 66 insertions, 48 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7246b0b..164aa79 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -589,6 +589,7 @@ dependencies = [
"anyhow",
"clap",
"clap_complete",
+ "console",
"directories",
"flac-bound",
"futures-util",
diff --git a/Cargo.toml b/Cargo.toml
index 617c6fb..5481e8c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,11 +19,7 @@ flac-bound = { version = "0.5.0", features = [
], default-features = false }
futures-util = "0.3.31"
i24 = "2.1.0"
-indicatif = { version = "0.17.11", features = [
- "tokio",
- "improved_unicode",
- "futures",
-] }
+indicatif = { version = "0.17.11", features = ["tokio", "improved_unicode", "futures"] }
libsql = { version = "0.9.11" }
md-5 = "0.10.6"
metaflac = "0.2.8"
@@ -39,3 +35,4 @@ 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"] }
diff --git a/src/db.rs b/src/db.rs
index 79099b0..6622010 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -3,7 +3,7 @@ use directories::BaseDirs;
use futures_util::Stream;
use libsql::{Builder, Connection, params};
use std::{
- path::{Path, PathBuf},
+ path::Path,
time::{Duration, UNIX_EPOCH},
};
@@ -26,10 +26,14 @@ pub struct Database(pub Connection);
impl Database {
pub async fn new(path: impl AsRef<Path>) -> Result<Self> {
- let conn = Builder::new_local(path).build().await?.connect()?;
- conn.execute(TABLE_CREATE, ()).await?;
+ if path.as_ref().is_file() {
+ let conn = Builder::new_local(path).build().await?.connect()?;
+ conn.execute(TABLE_CREATE, ()).await?;
- Ok(Database(conn))
+ Ok(Database(conn))
+ } else {
+ Err(anyhow!("Not a file"))
+ }
}
pub async fn insert_file(&self, filename: impl AsRef<Path>) -> Result<()> {
@@ -102,26 +106,17 @@ impl Database {
}
}
- pub async fn clean_files(&self) -> Result<()> {
- let mut tasks = tokio::task::JoinSet::new();
+ pub async fn init_clean_files(
+ &self,
+ ) -> Result<impl Stream<Item = libsql::Result<libsql::Row>>> {
self.0.execute(DEDUPE_DB, ()).await?;
- let mut query_res = self.0.query(FETCH_FILES, ()).await?;
- while let Ok(Some(row)) = query_res.next().await {
- let path = PathBuf::from(row.get_str(0)?);
- let conn = self.0.clone();
- tasks.spawn(async move {
- if !path.exists() {
- let _ = conn
- .execute(REMOVE_FILE, params!(path.to_str().unwrap()))
- .await;
- }
- });
- }
-
- tasks.join_all().await;
-
- self.0.execute("VACUUM", ()).await?;
+ Ok(self.0.query(FETCH_FILES, ()).await?.into_stream())
+ }
+ pub async fn remove_file(&self, filename: impl AsRef<Path>) -> Result<()> {
+ self.0
+ .execute(REMOVE_FILE, params!(filename.as_ref().to_str().unwrap()))
+ .await?;
Ok(())
}
diff --git a/src/files.rs b/src/files.rs
index bbcc0c4..52b4058 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -1,7 +1,6 @@
use anyhow::{Result, anyhow};
use futures_util::StreamExt;
-#[cfg(not(test))]
-use indicatif::{ProgressBar, ProgressStyle};
+use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use pin_utils::pin_mut;
use std::{
error::Error,
@@ -15,8 +14,8 @@ use walkdir::WalkDir;
use crate::{db::Database, flac::encode_file};
-#[cfg(not(test))]
-const BAR_TEMPLATE: &str = "{msg} [{wide_bar:.green/cyan}] Elapsed: {elapsed} {pos:>7}/{len:7}";
+const BAR_TEMPLATE: &str = "{msg:<} [{wide_bar:.green/cyan}] Elapsed: {elapsed} {pos:>7}/{len:7}";
+const SPINNER_TEMPLATE: &str = "Removed from db: {pos:.green}";
#[derive(Debug)]
pub struct FileError {
@@ -86,8 +85,7 @@ pub async fn index_files_recursively(
let mut tasks: JoinSet<Result<(), anyhow::Error>> = JoinSet::new();
- #[cfg(not(test))]
- let bar = ProgressBar::new(0)
+ let bar = ProgressBar::with_draw_target(Some(0), ProgressDrawTarget::stdout_with_hz(60))
.with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-"))
.with_message("Indexing");
@@ -99,15 +97,13 @@ pub async fn index_files_recursively(
if let Some(ext) = path.extension() {
if ext == "flac" {
let newconn = conn.clone();
- #[cfg(not(test))]
+
let newbar = bar.clone();
tasks.spawn(async move {
handle_file(path, newconn).await?;
- #[cfg(not(test))]
newbar.inc(1);
Ok(())
});
- #[cfg(not(test))]
bar.inc_length(1);
}
}
@@ -127,7 +123,6 @@ pub async fn index_files_recursively(
}
}
- #[cfg(not(test))]
bar.abandon_with_message("Indexing aborted");
return Ok(())
},
@@ -140,7 +135,6 @@ pub async fn index_files_recursively(
}
}
- #[cfg(not(test))]
bar.finish_with_message("Finished indexing");
Ok(())
}
@@ -151,17 +145,18 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) ->
let mut tasks = JoinSet::new();
- #[cfg(not(test))]
- let bar = ProgressBar::new(conn.get_toencode_number().await?)
- .with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-"))
- .with_message("Reencoding");
+ let bar = ProgressBar::with_draw_target(
+ Some(conn.get_toencode_number().await?),
+ ProgressDrawTarget::stdout_with_hz(60),
+ )
+ .with_style(ProgressStyle::with_template(BAR_TEMPLATE)?.progress_chars("#>-"))
+ .with_message("Indexing");
while let Some(Ok(row)) = stream.next().await {
if let Some(file) = row.get_value(0)?.as_text() {
let filename = Path::new(file).canonicalize()?;
if filename.exists() {
let newconn = conn.clone();
- #[cfg(not(test))]
let newbar = bar.clone();
tasks.spawn(async move {
let file = filename.clone();
@@ -174,7 +169,6 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) ->
if let Err(error) = newconn.update_file(&filename).await {
return Err(anyhow!(FileError::new(&filename, error)));
};
- #[cfg(not(test))]
newbar.inc(1);
Ok(())
});
@@ -194,7 +188,6 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) ->
}
}
- #[cfg(not(test))]
bar.abandon_with_message("Reencoding aborted");
return Ok(())
},
@@ -207,12 +200,43 @@ pub async fn reencode_files(conn: &Database, canceltoken: CancellationToken) ->
}
}
- #[cfg(not(test))]
bar.finish_with_message("Finished encoding");
Ok(())
}
+pub async fn clean_files(conn: &Database) -> Result<()> {
+ let mut tasks: JoinSet<std::result::Result<(), anyhow::Error>> = JoinSet::new();
+
+ let query_res = conn.init_clean_files().await?;
+ pin_mut!(query_res);
+
+ let spinner = ProgressBar::with_draw_target(None, ProgressDrawTarget::stdout_with_hz(60))
+ .with_style(ProgressStyle::with_template(SPINNER_TEMPLATE)?);
+
+ while let Some(Ok(row)) = query_res.next().await {
+ let path = PathBuf::from(row.get_str(0)?);
+ let newconn = conn.clone();
+
+ let newspinner = spinner.clone();
+ tasks.spawn(async move {
+ if !path.exists() {
+ newconn.remove_file(path).await?;
+ newspinner.inc(1);
+ }
+ Ok(())
+ });
+ }
+
+ tasks.join_all().await;
+
+ spinner.finish();
+
+ conn.0.execute("VACUUM", ()).await?;
+
+ Ok(())
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/main.rs b/src/main.rs
index 1dd6f1b..9deb7c6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,6 +4,7 @@ mod flac;
use anyhow::Result;
use clap::{Arg, ArgAction, Command, ValueHint, command, value_parser};
use clap_complete::{Generator, Shell, generate};
+use console::style;
use std::path::PathBuf;
use tokio_util::sync::CancellationToken;
@@ -101,7 +102,7 @@ fn main() -> Result<()> {
if path.is_none() && !args.get_flag("clean") && !args.get_flag("doit") {
let count = conn.get_toencode_number().await?;
- println!("Files to reencode:\t{count}");
+ println!("Files to reencode:\t{}", style(count).green());
return Ok(());
}
@@ -115,7 +116,7 @@ fn main() -> Result<()> {
}
if args.get_flag("clean") {
- conn.clean_files().await?;
+ files::clean_files(&conn).await?;
}
if canceltoken.is_cancelled() {