118 lines
3.5 KiB
Rust
118 lines
3.5 KiB
Rust
use lofty::config::WriteOptions;
|
|
use lofty::tag::TagItem;
|
|
use lofty::tag::TagType::VorbisComments;
|
|
use lofty::tag::{ItemKey, ItemValue};
|
|
use lofty::tag::{Tag, TagExt};
|
|
use regex::Regex;
|
|
use std::collections::HashMap;
|
|
use std::env::var;
|
|
use std::io::Write;
|
|
use std::path::PathBuf;
|
|
use std::{fs::read_dir, fs::File, io::Read, process::Command};
|
|
|
|
pub fn edit(file_path: Vec<PathBuf>, extra_files: [String; 8]) -> std::io::Result<()> {
|
|
let tmp_file_path = PathBuf::from("/tmp/keks-sort-metadata");
|
|
let mut tmp_file = File::create(&tmp_file_path)?;
|
|
let editor = var("EDITOR").unwrap();
|
|
let mut files = Vec::new();
|
|
|
|
struct FileMetadata {
|
|
path: PathBuf,
|
|
tag: Tag,
|
|
}
|
|
|
|
for file in &file_path {
|
|
if file.is_dir() {
|
|
for music_file in read_dir(file)? {
|
|
let path = music_file?.path();
|
|
|
|
if path
|
|
.extension()
|
|
.and_then(|ext| ext.to_str())
|
|
.is_some_and(|ext| extra_files.iter().any(|x| x == ext))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
let tag = crate::get_metadata(&path);
|
|
files.push(FileMetadata { path, tag });
|
|
}
|
|
} else {
|
|
let tag = &crate::get_metadata(file);
|
|
files.push(FileMetadata {
|
|
path: file.to_path_buf(),
|
|
tag: tag.clone(),
|
|
});
|
|
}
|
|
}
|
|
|
|
for file in &files {
|
|
writeln!(
|
|
tmp_file.try_clone()?,
|
|
"------------ {:?} ------------",
|
|
file.path
|
|
)?;
|
|
for item in file.tag.items() {
|
|
writeln!(
|
|
tmp_file,
|
|
"{} = {}",
|
|
item.key().map_key(VorbisComments).unwrap(),
|
|
item.value().text().unwrap()
|
|
)?;
|
|
}
|
|
writeln!(tmp_file.try_clone()?)?; // writes empty line
|
|
}
|
|
|
|
Command::new(editor)
|
|
.arg(&tmp_file_path)
|
|
.status()
|
|
.expect("Something went wrong");
|
|
|
|
let mut editable = String::new();
|
|
File::open(&tmp_file_path)
|
|
.expect("Could not open file")
|
|
.read_to_string(&mut editable)?;
|
|
|
|
let parsed = parse(&editable);
|
|
for (mut file, parse) in files.into_iter().zip(parsed) {
|
|
for (key, value) in &parse {
|
|
let item_key =
|
|
ItemKey::from_key(VorbisComments, key).expect("ERROR: Failed to get item key");
|
|
let item = TagItem::new(item_key, ItemValue::Text(value.to_string()));
|
|
file.tag.insert(item);
|
|
}
|
|
file.tag
|
|
.save_to_path(&file.path, WriteOptions::default())
|
|
.expect("ERROR: Failed to write tag");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn parse(input: &str) -> Vec<HashMap<String, String>> {
|
|
let re = Regex::new(r#"(?m)^------------ ".*?" ------------\r?\n"#).unwrap();
|
|
let parts: Vec<&str> = re
|
|
.split(input)
|
|
.filter(|part| !part.trim().is_empty())
|
|
.collect();
|
|
let result: Vec<HashMap<String, String>> = parts
|
|
.into_iter()
|
|
.filter_map(|part| {
|
|
let part = part.trim();
|
|
|
|
if part.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
Some(
|
|
part.lines()
|
|
.filter_map(|line| {
|
|
let (key, value) = line.split_once('=')?;
|
|
Some((key.trim().to_string(), value.trim().to_string()))
|
|
})
|
|
.collect(),
|
|
)
|
|
})
|
|
.collect();
|
|
result
|
|
}
|