Fixed up the indexing a bit

This commit is contained in:
Connor Johnstone
2026-03-05 12:44:53 -05:00
parent 2ffdce4fbc
commit 0c45d8957a
3 changed files with 91 additions and 12 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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"
);
}
}