2023-09-05 02:58:33 -03:00
|
|
|
use anyhow::Result;
|
2023-09-19 15:42:46 -03:00
|
|
|
use camino::{Utf8Path, Utf8PathBuf};
|
2023-09-05 02:58:33 -03:00
|
|
|
use clap::{Parser, Subcommand};
|
|
|
|
|
use m3u::Entry;
|
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
|
|
|
|
|
/// Check if files in a playlist actually exist.
|
|
|
|
|
#[derive(Parser, Debug)]
|
|
|
|
|
#[command(author, version, about, long_about = None)]
|
|
|
|
|
struct Args {
|
|
|
|
|
/// Check mode.
|
|
|
|
|
#[command(subcommand)]
|
|
|
|
|
mode: Mode,
|
|
|
|
|
/// Playlist file name.
|
|
|
|
|
input: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Subcommand, Clone, Debug)]
|
|
|
|
|
enum Mode {
|
|
|
|
|
#[command()]
|
|
|
|
|
Files,
|
|
|
|
|
#[command()]
|
|
|
|
|
Directories {
|
|
|
|
|
#[arg(long, short)]
|
|
|
|
|
base_directory: String,
|
|
|
|
|
#[arg(long, short, default_value_t = false)]
|
|
|
|
|
relative: bool,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_directories(base_directory: &str, is_relative: bool) -> Result<HashSet<String>> {
|
|
|
|
|
let mut result = HashSet::<String>::new();
|
2023-09-19 15:42:46 -03:00
|
|
|
for entry in Utf8Path::new(base_directory).read_dir_utf8()? {
|
2023-09-05 02:58:33 -03:00
|
|
|
let entry = entry?;
|
|
|
|
|
if entry.metadata()?.is_dir() {
|
|
|
|
|
let p = if is_relative {
|
2023-09-19 15:42:46 -03:00
|
|
|
entry.file_name().to_string()
|
2023-09-05 02:58:33 -03:00
|
|
|
} else {
|
2023-09-19 15:42:46 -03:00
|
|
|
entry.path().to_string()
|
2023-09-05 02:58:33 -03:00
|
|
|
};
|
|
|
|
|
result.insert(p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
let args = Args::parse();
|
|
|
|
|
let mut reader = m3u::Reader::open_ext(&args.input)?;
|
|
|
|
|
match args.mode {
|
|
|
|
|
Mode::Files => {
|
|
|
|
|
for entry in reader.entry_exts() {
|
|
|
|
|
if let Entry::Path(p) = entry?.entry {
|
2023-09-19 15:42:46 -03:00
|
|
|
let p = Utf8PathBuf::try_from(p)?;
|
2023-09-05 02:58:33 -03:00
|
|
|
if std::fs::metadata(&p).is_err() {
|
2023-09-19 15:42:46 -03:00
|
|
|
println!("{p}");
|
2023-09-05 02:58:33 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Mode::Directories {
|
|
|
|
|
base_directory,
|
|
|
|
|
relative,
|
|
|
|
|
} => {
|
|
|
|
|
let mut playlist_directories = HashSet::<String>::new();
|
|
|
|
|
for entry in reader.entry_exts() {
|
|
|
|
|
if let Entry::Path(p) = entry?.entry {
|
2023-09-19 15:42:46 -03:00
|
|
|
let p = Utf8PathBuf::try_from(p)?;
|
2023-09-05 02:58:33 -03:00
|
|
|
if let Some(dir) = p.parent() {
|
2023-09-19 15:42:46 -03:00
|
|
|
playlist_directories.insert(dir.to_string());
|
2023-09-05 02:58:33 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let actual_directories = get_directories(&base_directory, relative)?;
|
|
|
|
|
let mut missing_directories = actual_directories
|
|
|
|
|
.difference(&playlist_directories)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
missing_directories.sort_unstable();
|
|
|
|
|
for d in missing_directories {
|
|
|
|
|
println!("{}", d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|