Fixed up the indexing a bit
This commit is contained in:
@@ -28,8 +28,12 @@ drift index /path/to/music
|
||||
|
||||
Scans for tagged files, fetches similar artists and top tracks from Last.fm, and stores everything in a local SQLite database (`~/.local/share/drift/drift.db`).
|
||||
|
||||
Stale tracks (files previously indexed but no longer on disk) are automatically removed.
|
||||
|
||||
Flags:
|
||||
- `-v` — print progress
|
||||
- `-v` — print new artists indexed + summary
|
||||
- `-vv` — also print each track added/removed
|
||||
- `-vvv` — also print skipped artists
|
||||
- `-f` — re-index artists that were already indexed
|
||||
|
||||
### Build a playlist
|
||||
|
||||
17
src/db.rs
17
src/db.rs
@@ -145,6 +145,23 @@ pub fn insert_track(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_tracks_in_directory(conn: &Connection, dir_prefix: &str) -> Result<Vec<String>, rusqlite::Error> {
|
||||
let pattern = format!("{dir_prefix}%");
|
||||
let mut stmt = conn.prepare("SELECT path FROM tracks WHERE path LIKE ?1")?;
|
||||
let rows = stmt.query_map([pattern], |row| row.get(0))?;
|
||||
rows.collect()
|
||||
}
|
||||
|
||||
pub fn delete_tracks(conn: &Connection, paths: &[String]) -> Result<usize, rusqlite::Error> {
|
||||
let tx = conn.unchecked_transaction()?;
|
||||
let mut deleted = 0;
|
||||
for path in paths {
|
||||
deleted += tx.execute("DELETE FROM tracks WHERE path = ?1", [path])?;
|
||||
}
|
||||
tx.commit()?;
|
||||
Ok(deleted)
|
||||
}
|
||||
|
||||
pub fn insert_artist_with_similar(
|
||||
conn: &Connection,
|
||||
mbid: &str,
|
||||
|
||||
78
src/main.rs
78
src/main.rs
@@ -7,8 +7,9 @@ mod mpd;
|
||||
mod playlist;
|
||||
mod tui;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use rand::prelude::*;
|
||||
@@ -26,9 +27,9 @@ struct Cli {
|
||||
enum Command {
|
||||
/// Index a music directory
|
||||
Index {
|
||||
/// Print progress
|
||||
#[arg(short)]
|
||||
verbose: bool,
|
||||
/// Verbosity level (-v, -vv, -vvv)
|
||||
#[arg(short, action = clap::ArgAction::Count)]
|
||||
verbose: u8,
|
||||
/// Re-index already indexed artists
|
||||
#[arg(short)]
|
||||
force: bool,
|
||||
@@ -109,7 +110,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_index(verbose: bool, force: bool, directory: &str) {
|
||||
fn cmd_index(verbose: u8, force: bool, directory: &str) {
|
||||
dotenvy::dotenv().ok();
|
||||
|
||||
let api_key = env::var("LASTFM_API_KEY").unwrap_or_default();
|
||||
@@ -120,9 +121,30 @@ fn cmd_index(verbose: bool, force: bool, directory: &str) {
|
||||
|
||||
let conn = db::open(&db_path()).expect("failed to open database");
|
||||
let lastfm = lastfm::LastfmClient::new(api_key);
|
||||
let dir = Path::new(directory);
|
||||
|
||||
for path in filesystem::walk_music_files(dir) {
|
||||
let dir = std::fs::canonicalize(directory).unwrap_or_else(|e| {
|
||||
eprintln!("Error: cannot resolve directory {directory}: {e}");
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
let dir_prefix = format!("{}/", dir.display());
|
||||
|
||||
let mut stale_paths: HashSet<String> = match db::get_tracks_in_directory(&conn, &dir_prefix) {
|
||||
Ok(paths) => paths.into_iter().collect(),
|
||||
Err(e) => {
|
||||
eprintln!("DB error loading existing tracks: {e}");
|
||||
HashSet::new()
|
||||
}
|
||||
};
|
||||
|
||||
let mut artists_indexed: usize = 0;
|
||||
let mut artists_skipped: usize = 0;
|
||||
let mut tracks_added: usize = 0;
|
||||
|
||||
for path in filesystem::walk_music_files(&dir) {
|
||||
let path_str = path.to_string_lossy().into_owned();
|
||||
stale_paths.remove(&path_str);
|
||||
|
||||
let tags = match metadata::read_tags(&path, &[
|
||||
metadata::Tag::ArtistMbid,
|
||||
metadata::Tag::TrackMbid,
|
||||
@@ -152,7 +174,7 @@ fn cmd_index(verbose: bool, force: bool, directory: &str) {
|
||||
let display_name = artist_name.as_deref().unwrap_or(&artist_mbid);
|
||||
|
||||
if !already_indexed || force {
|
||||
if verbose {
|
||||
if verbose >= 1 {
|
||||
println!("Indexing {display_name}...");
|
||||
}
|
||||
|
||||
@@ -184,14 +206,50 @@ fn cmd_index(verbose: bool, force: bool, directory: &str) {
|
||||
eprintln!("Last.fm top tracks error for {display_name}: {e}");
|
||||
}
|
||||
}
|
||||
} else if verbose {
|
||||
|
||||
artists_indexed += 1;
|
||||
} else {
|
||||
if verbose >= 3 {
|
||||
println!("Skipping {display_name} (already indexed)");
|
||||
}
|
||||
artists_skipped += 1;
|
||||
}
|
||||
|
||||
let path_str = path.to_string_lossy();
|
||||
if let Err(e) = db::insert_track(&conn, &path_str, &artist_mbid, recording_mbid.as_deref(), track_title.as_deref()) {
|
||||
eprintln!("DB error inserting track {}: {e}", path.display());
|
||||
continue;
|
||||
}
|
||||
tracks_added += 1;
|
||||
|
||||
if verbose >= 2 {
|
||||
let label = track_title.as_deref().unwrap_or(&path_str);
|
||||
println!(" + {label}");
|
||||
}
|
||||
}
|
||||
|
||||
// Sweep stale tracks
|
||||
let stale: Vec<String> = stale_paths.into_iter().collect();
|
||||
let tracks_stale = if !stale.is_empty() {
|
||||
if verbose >= 2 {
|
||||
for p in &stale {
|
||||
println!(" - {p}");
|
||||
}
|
||||
}
|
||||
match db::delete_tracks(&conn, &stale) {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
eprintln!("DB error removing stale tracks: {e}");
|
||||
0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if verbose >= 1 {
|
||||
println!(
|
||||
"Done: {artists_indexed} artists indexed, {artists_skipped} skipped, {tracks_added} tracks added, {tracks_stale} stale tracks removed"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user