fix: properly handle editing multiple files
fixed wrong parsing causing to overwrite all files with wrong metadata fixed wrong file header in editor buffer
This commit is contained in:
Generated
+45
@@ -8,6 +8,15 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -191,6 +200,12 @@ version = "0.4.33"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -208,6 +223,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"glob",
|
"glob",
|
||||||
"lofty",
|
"lofty",
|
||||||
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -249,6 +265,35 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ edition = "2024"
|
|||||||
clap = { version = "4.6.6", features = ["derive"] }
|
clap = { version = "4.6.6", features = ["derive"] }
|
||||||
glob = "0.3.4"
|
glob = "0.3.4"
|
||||||
lofty = "0.25.1"
|
lofty = "0.25.1"
|
||||||
|
regex = "1.13.1"
|
||||||
|
|||||||
+58
-28
@@ -1,25 +1,30 @@
|
|||||||
use lofty::config::WriteOptions;
|
use lofty::config::WriteOptions;
|
||||||
|
use lofty::tag::TagItem;
|
||||||
use lofty::tag::TagType::VorbisComments;
|
use lofty::tag::TagType::VorbisComments;
|
||||||
use lofty::tag::{Accessor, TagItem};
|
|
||||||
use lofty::tag::{ItemKey, ItemValue};
|
use lofty::tag::{ItemKey, ItemValue};
|
||||||
use lofty::tag::{Tag, TagExt};
|
use lofty::tag::{Tag, TagExt};
|
||||||
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env::var;
|
use std::env::var;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{fs::File, fs::read_dir, io::Read, process::Command};
|
use std::{fs::read_dir, fs::File, io::Read, process::Command};
|
||||||
|
|
||||||
pub fn edit(file_path: PathBuf, extra_files: [String; 8]) -> std::io::Result<()> {
|
pub fn edit(file_path: PathBuf, extra_files: [String; 8]) -> std::io::Result<()> {
|
||||||
let tmp_file = PathBuf::from("/tmp/keks-sort-metadata");
|
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 editor = var("EDITOR").unwrap();
|
||||||
let mut file = File::create(&tmp_file)?;
|
|
||||||
let mut tags: Vec<Tag> = Vec::new();
|
|
||||||
let mut path: PathBuf = file_path.clone();
|
let mut path: PathBuf = file_path.clone();
|
||||||
|
let mut files = Vec::new();
|
||||||
|
|
||||||
|
struct FileMetadata {
|
||||||
|
path: PathBuf,
|
||||||
|
tag: Tag,
|
||||||
|
}
|
||||||
|
|
||||||
if file_path.is_dir() {
|
if file_path.is_dir() {
|
||||||
for music_file in read_dir(&file_path)? {
|
for music_file in read_dir(&file_path)? {
|
||||||
path = music_file?.path();
|
path = music_file?.path();
|
||||||
println!("{:?}", path);
|
|
||||||
|
|
||||||
if path
|
if path
|
||||||
.extension()
|
.extension()
|
||||||
@@ -30,57 +35,82 @@ pub fn edit(file_path: PathBuf, extra_files: [String; 8]) -> std::io::Result<()>
|
|||||||
}
|
}
|
||||||
|
|
||||||
let tag = crate::get_metadata(&path);
|
let tag = crate::get_metadata(&path);
|
||||||
tags.push(tag);
|
files.push(FileMetadata { path, tag });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let tag = &crate::get_metadata(&file_path);
|
let tag = &crate::get_metadata(file_path);
|
||||||
tags.push(tag.clone());
|
files.push(FileMetadata {
|
||||||
|
path,
|
||||||
|
tag: tag.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for tag in &tags {
|
for file in &files {
|
||||||
println!("{:?}", tag.title());
|
writeln!(
|
||||||
writeln!(file.try_clone()?, "------------ {:?} ------------", path)?;
|
tmp_file.try_clone()?,
|
||||||
for item in tag.items() {
|
"------------ {:?} ------------",
|
||||||
|
file.path
|
||||||
|
)?;
|
||||||
|
for item in file.tag.items() {
|
||||||
writeln!(
|
writeln!(
|
||||||
file,
|
tmp_file,
|
||||||
"{} = {}",
|
"{} = {}",
|
||||||
item.key().map_key(VorbisComments).unwrap(),
|
item.key().map_key(VorbisComments).unwrap(),
|
||||||
item.value().text().unwrap()
|
item.value().text().unwrap()
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
writeln!(file.try_clone()?)?; // writes empty line
|
writeln!(tmp_file.try_clone()?)?; // writes empty line
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::new(editor)
|
Command::new(editor)
|
||||||
.arg(&tmp_file)
|
.arg(&tmp_file_path)
|
||||||
.status()
|
.status()
|
||||||
.expect("Something went wrong");
|
.expect("Something went wrong");
|
||||||
|
|
||||||
let mut editable = String::new();
|
let mut editable = String::new();
|
||||||
File::open(&tmp_file)
|
File::open(&tmp_file_path)
|
||||||
.expect("Could not open file")
|
.expect("Could not open file")
|
||||||
.read_to_string(&mut editable)?;
|
.read_to_string(&mut editable)?;
|
||||||
|
|
||||||
let parsed = parse(&editable);
|
let parsed = parse(&editable);
|
||||||
for mut tag in tags {
|
for (mut file, parse) in files.into_iter().zip(parsed) {
|
||||||
for (key, value) in &parsed {
|
for (key, value) in &parse {
|
||||||
let item_key =
|
let item_key =
|
||||||
ItemKey::from_key(VorbisComments, key).expect("ERROR: Failed to get 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()));
|
let item = TagItem::new(item_key, ItemValue::Text(value.to_string()));
|
||||||
tag.insert(item.clone());
|
file.tag.insert(item);
|
||||||
}
|
}
|
||||||
tag.save_to_path(&file_path, WriteOptions::default())
|
file.tag
|
||||||
|
.save_to_path(&file.path, WriteOptions::default())
|
||||||
.expect("ERROR: Failed to write tag");
|
.expect("ERROR: Failed to write tag");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> HashMap<String, String> {
|
fn parse(input: &str) -> Vec<HashMap<String, String>> {
|
||||||
input
|
let re = Regex::new(r#"(?m)^------------ ".*?" ------------\r?\n"#).unwrap();
|
||||||
.lines()
|
let parts: Vec<&str> = re
|
||||||
.filter_map(|line| {
|
.split(input)
|
||||||
let (key, value) = line.split_once('=')?;
|
.filter(|part| !part.trim().is_empty())
|
||||||
Some((key.trim().to_string(), value.trim().to_string()))
|
.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()
|
.collect();
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -12,7 +12,7 @@ pub fn move_files(
|
|||||||
should_copy: bool,
|
should_copy: bool,
|
||||||
has_parent: bool,
|
has_parent: bool,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let target_dir = PathBuf::from("/home/user/Music/Tracks");
|
let target_dir = PathBuf::from("/home/user/Desktop/coding/keks-sort");
|
||||||
|
|
||||||
let artist = tag.artist();
|
let artist = tag.artist();
|
||||||
let album = tag.album();
|
let album = tag.album();
|
||||||
@@ -37,9 +37,10 @@ pub fn move_files(
|
|||||||
let target = target_dir.join(artist_dir).join(album_dir.as_ref());
|
let target = target_dir.join(artist_dir).join(album_dir.as_ref());
|
||||||
|
|
||||||
let file_name = if let Some(track_number) = track_number {
|
let file_name = if let Some(track_number) = track_number {
|
||||||
|
let number: u32 = track_number.parse()?;
|
||||||
PathBuf::from(format!(
|
PathBuf::from(format!(
|
||||||
"{:02}. {} - {}.flac",
|
"{:02}. {} - {}.flac",
|
||||||
track_number,
|
number,
|
||||||
artist.unwrap(),
|
artist.unwrap(),
|
||||||
title.unwrap(),
|
title.unwrap(),
|
||||||
))
|
))
|
||||||
|
|||||||
Reference in New Issue
Block a user