ripping+tagging
This commit is contained in:
103
src/adopt.rs
Normal file
103
src/adopt.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
// i was gonna do a "filename hack" and name this file "adopte.rs" as in "adopters" but i would need to type
|
||||
// adopte::adopt which is odd so i didn't
|
||||
|
||||
use std::{
|
||||
fs::{create_dir_all, write},
|
||||
io::{Read, Write},
|
||||
path::Path,
|
||||
process::Command,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use crate::{import, musicbrainz::get_disc_info, tagging};
|
||||
|
||||
// adopt = rip a disc but ripping sounds like destroying so i'm not using it
|
||||
pub fn adopt(timbre_dir: &Path) {
|
||||
let disc = get_disc_info("http://catgpt-server:5000");
|
||||
let release = &disc.releases[0];
|
||||
println!("{}", release.id);
|
||||
let tmp_dir = timbre_dir.join(format!(
|
||||
".tmp/{}",
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("time went backwards?? ¯\\_(ツ)_/¯\nquit trying to make me timber!\n")
|
||||
.as_secs()
|
||||
));
|
||||
create_dir_all(&tmp_dir).expect("i couldn't make my workspace :(\n");
|
||||
write(tmp_dir.join("README.txt"), "I'M DOING IT CHILLAX BRO\n")
|
||||
.expect("couldn't even write my chillax note :( bro fix ur pc\n");
|
||||
// as of writing this line here i just donated $2.75 + transaction fees to Wikipedia!
|
||||
|
||||
println!("adopting...");
|
||||
|
||||
let tracks = &release.media[0].tracks;
|
||||
let tracks_len = tracks.len();
|
||||
|
||||
for track_num in 3..=tracks_len {
|
||||
let file_name = format!("track{track_num:02}.wav");
|
||||
let current_track = &tracks[track_num - 1];
|
||||
|
||||
let (mut pty, pts) = pty_process::blocking::open().expect("couldn't open a pty :(\n");
|
||||
pty.resize(pty_process::Size::new(24, 80))
|
||||
.expect("couldn't size the pty :(\n");
|
||||
|
||||
let cmd = pty_process::blocking::Command::new("cdparanoia")
|
||||
.arg("-e")
|
||||
.arg(track_num.to_string())
|
||||
.arg(&tmp_dir.join(&file_name));
|
||||
|
||||
let mut child = cmd.spawn(pts).expect("cdparanoia wouldn't start :(\n");
|
||||
|
||||
let mut buf = [0u8; 512];
|
||||
let mut acc = String::new();
|
||||
loop {
|
||||
let n = pty.read(&mut buf).unwrap_or(0);
|
||||
if n == 0 {
|
||||
// done
|
||||
println!();
|
||||
break;
|
||||
}
|
||||
acc.push_str(&String::from_utf8_lossy(&buf[..n]));
|
||||
|
||||
while let Some(pos) = acc.find('\r') {
|
||||
let chunk: String = acc.drain(..=pos).collect();
|
||||
if chunk.contains("PROGRESS") {
|
||||
let bar = chunk
|
||||
.split_once("== PROGRESS == [")
|
||||
.unwrap()
|
||||
.1
|
||||
.split_once("|")
|
||||
.unwrap()
|
||||
.0;
|
||||
print!(
|
||||
"\r[{}] {track_num}/{} {}",
|
||||
bar, tracks_len, current_track.title
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
child.wait().expect("cdparanoia didn't finish cleanly :(\n");
|
||||
|
||||
Command::new("flac")
|
||||
.arg("--best") // max compression, lossless
|
||||
.arg(&tmp_dir.join(&file_name)) // input WAV
|
||||
.output()
|
||||
.expect("flac wouldn't encode :( is it installed?\n");
|
||||
|
||||
tagging::write_tags(
|
||||
Path::new(&tmp_dir.join(format!("track{track_num:02}.flac"))),
|
||||
¤t_track.title,
|
||||
¤t_track.artist_credit[0].name,
|
||||
&release.title,
|
||||
track_num as u32,
|
||||
);
|
||||
|
||||
import::import_file(
|
||||
Path::new(&tmp_dir.join(format!("track{track_num:02}.flac"))),
|
||||
&timbre_dir,
|
||||
);
|
||||
}
|
||||
|
||||
println!("done!");
|
||||
}
|
||||
94
src/import.rs
Normal file
94
src/import.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use lofty::{prelude::*, probe::Probe};
|
||||
use std::{
|
||||
fs::{self, create_dir_all},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{SafeName, sanitize};
|
||||
|
||||
pub fn import_file(file: &Path, timbre_dir: &Path) {
|
||||
let tagged = Probe::open(&file)
|
||||
.expect("i couldn't open this file :(\n")
|
||||
.read()
|
||||
.expect("i couldn't read this file's tags :(\n");
|
||||
match tagged.primary_tag() {
|
||||
Some(tag) => {
|
||||
let track = tag.track();
|
||||
let artist = match tag.artist() {
|
||||
None => SafeName::Empty,
|
||||
Some(a) => sanitize(&a),
|
||||
};
|
||||
let album = match tag.album() {
|
||||
None => None,
|
||||
Some(a) => Some(sanitize(&a)),
|
||||
};
|
||||
let title = match tag.title() {
|
||||
None => SafeName::Empty,
|
||||
Some(t) => sanitize(&t),
|
||||
};
|
||||
|
||||
if matches!(artist, SafeName::Malicious)
|
||||
|| matches!(title, SafeName::Malicious)
|
||||
|| matches!(album, Some(SafeName::Malicious))
|
||||
{
|
||||
println!(
|
||||
"hey, help! i just found this file: {}\nit seems to be trying to do something bad and hurt your computer!\ni'm moving it to a Quarantine folder for you to check\nyou should probably check the source of it.\ntip: upload it to https://virustotal.com\n",
|
||||
file.display()
|
||||
);
|
||||
|
||||
create_dir_all(&timbre_dir.join("Quarantine")).expect("i can't create the Quarantine folder, could be something normal, potentially malware :(\nbe careful with the file! it's still in the Import folder.\ntip: upload it to https://virustotal.com\n");
|
||||
fs::rename(&file, &timbre_dir.join("Quarantine").join(file.file_name().expect("no filename??\n")))
|
||||
.expect("i couldn't move this file :(, still in the Import folder, be careful!\ntip: upload it to https://virustotal.com\n");
|
||||
return;
|
||||
}
|
||||
|
||||
let (SafeName::Ok(artist), SafeName::Ok(title)) = (artist, title) else {
|
||||
let unid = timbre_dir.join("Unidentified");
|
||||
fs::rename(&file, unid.join(file.file_name().unwrap()))
|
||||
.expect("i couldn't move an Unidentified file to a folder :(\nis the folder missing? try timbre repair!\n");
|
||||
return;
|
||||
};
|
||||
let ext = file.extension().and_then(|e| e.to_str()).unwrap_or("");
|
||||
|
||||
let dest = match album {
|
||||
Some(SafeName::Ok(album)) => {
|
||||
let filename = match track {
|
||||
Some(n) => format!("{:02} {}.{}", n, title, ext),
|
||||
None => format!("{}.{}", title, ext),
|
||||
};
|
||||
timbre_dir
|
||||
.join("Albums")
|
||||
.join(&artist)
|
||||
.join(&album)
|
||||
.join(filename)
|
||||
}
|
||||
_ => timbre_dir
|
||||
.join("Singles")
|
||||
.join(&artist)
|
||||
.join(format!("{}.{}", title, ext)),
|
||||
};
|
||||
|
||||
if !dest.starts_with(&timbre_dir) {
|
||||
println!(
|
||||
"hey, help! i just found this file: {}\nit seems to be trying to do something bad and hurt your computer!\ni'm moving it to a Quarantine folder for you to check\nyou should probably check the source of it.\ntip: upload it to https://virustotal.com\n",
|
||||
file.display()
|
||||
);
|
||||
|
||||
create_dir_all(&timbre_dir.join("Quarantine")).expect("i can't create the Quarantine folder, could be something normal, potentially malware :(\nbe careful with the file! it's still in the Import folder.\ntip: upload it to https://virustotal.com\n");
|
||||
fs::rename(&file, &timbre_dir.join("Quarantine").join(file.file_name().expect("no filename??\n")))
|
||||
.expect("i couldn't move this file :(, still in the Import folder, be careful!\ntip: upload it to https://virustotal.com\n");
|
||||
return;
|
||||
}
|
||||
if dest.exists() {
|
||||
println!("already there, skipping {} :(", file.display());
|
||||
return;
|
||||
}
|
||||
create_dir_all(dest.parent().unwrap())
|
||||
.expect("can't create directory to put a file in :(\n");
|
||||
fs::rename(&file, &dest).expect("can't move file to directory :(\n");
|
||||
}
|
||||
None => {
|
||||
println!("no tags on {} :(", file.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
92
src/main.rs
92
src/main.rs
@@ -1,6 +1,8 @@
|
||||
mod adopt;
|
||||
mod disc_id;
|
||||
mod import;
|
||||
mod musicbrainz;
|
||||
|
||||
mod tagging;
|
||||
use clap::{
|
||||
Parser, Subcommand,
|
||||
builder::styling::{AnsiColor, Effects, Styles},
|
||||
@@ -133,7 +135,6 @@ fn remove_empty_dirs(dir: &Path) {
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
musicbrainz::get_disc_info("http://catgpt-server:5000");
|
||||
let cat = include_bytes!("../cat_01.jpg"); // my kitty!
|
||||
|
||||
let cli = Cli::parse();
|
||||
@@ -141,7 +142,7 @@ fn main() {
|
||||
let timbre_dir = Path::new(&std::env::var("HOME").expect("NERD!!!"))
|
||||
.join("Music")
|
||||
.join("Timbre");
|
||||
|
||||
adopt::adopt(&timbre_dir);
|
||||
if cli.command != Commands::Init && cli.command != Commands::Repair {
|
||||
if timbre_dir.exists() {
|
||||
hashcat(&timbre_dir.join("cat_01.jpg")); // meow
|
||||
@@ -200,90 +201,7 @@ fn main() {
|
||||
println!("found {} files to import!", to_import.len());
|
||||
|
||||
for file in to_import {
|
||||
let tagged = Probe::open(&file)
|
||||
.expect("i couldn't open this file :(\n")
|
||||
.read()
|
||||
.expect("i couldn't read this file's tags :(\n");
|
||||
match tagged.primary_tag() {
|
||||
Some(tag) => {
|
||||
let track = tag.track();
|
||||
let artist = match tag.artist() {
|
||||
None => SafeName::Empty,
|
||||
Some(a) => sanitize(&a),
|
||||
};
|
||||
let album = match tag.album() {
|
||||
None => None,
|
||||
Some(a) => Some(sanitize(&a)),
|
||||
};
|
||||
let title = match tag.title() {
|
||||
None => SafeName::Empty,
|
||||
Some(t) => sanitize(&t),
|
||||
};
|
||||
|
||||
if matches!(artist, SafeName::Malicious)
|
||||
|| matches!(title, SafeName::Malicious)
|
||||
|| matches!(album, Some(SafeName::Malicious))
|
||||
{
|
||||
println!(
|
||||
"hey, help! i just found this file: {}\nit seems to be trying to do something bad and hurt your computer!\ni'm moving it to a Quarantine folder for you to check\nyou should probably check the source of it.\ntip: upload it to https://virustotal.com\n",
|
||||
file.display()
|
||||
);
|
||||
|
||||
create_dir_all(&timbre_dir.join("Quarantine")).expect("i can't create the Quarantine folder, could be something normal, potentially malware :(\nbe careful with the file! it's still in the Import folder.\ntip: upload it to https://virustotal.com\n");
|
||||
fs::rename(&file, &timbre_dir.join("Quarantine").join(file.file_name().expect("no filename??\n")))
|
||||
.expect("i couldn't move this file :(, still in the Import folder, be careful!\ntip: upload it to https://virustotal.com\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
let (SafeName::Ok(artist), SafeName::Ok(title)) = (artist, title) else {
|
||||
let unid = timbre_dir.join("Unidentified");
|
||||
fs::rename(&file, unid.join(file.file_name().unwrap()))
|
||||
.expect("i couldn't move an Unidentified file to a folder :(\nis the folder missing? try timbre repair!\n");
|
||||
continue;
|
||||
};
|
||||
let ext = file.extension().and_then(|e| e.to_str()).unwrap_or("");
|
||||
|
||||
let dest = match album {
|
||||
Some(SafeName::Ok(album)) => {
|
||||
let filename = match track {
|
||||
Some(n) => format!("{:02} {}.{}", n, title, ext),
|
||||
None => format!("{}.{}", title, ext),
|
||||
};
|
||||
timbre_dir
|
||||
.join("Albums")
|
||||
.join(&artist)
|
||||
.join(&album)
|
||||
.join(filename)
|
||||
}
|
||||
_ => timbre_dir
|
||||
.join("Singles")
|
||||
.join(&artist)
|
||||
.join(format!("{}.{}", title, ext)),
|
||||
};
|
||||
|
||||
if !dest.starts_with(&timbre_dir) {
|
||||
println!(
|
||||
"hey, help! i just found this file: {}\nit seems to be trying to do something bad and hurt your computer!\ni'm moving it to a Quarantine folder for you to check\nyou should probably check the source of it.\ntip: upload it to https://virustotal.com\n",
|
||||
file.display()
|
||||
);
|
||||
|
||||
create_dir_all(&timbre_dir.join("Quarantine")).expect("i can't create the Quarantine folder, could be something normal, potentially malware :(\nbe careful with the file! it's still in the Import folder.\ntip: upload it to https://virustotal.com\n");
|
||||
fs::rename(&file, &timbre_dir.join("Quarantine").join(file.file_name().expect("no filename??\n")))
|
||||
.expect("i couldn't move this file :(, still in the Import folder, be careful!\ntip: upload it to https://virustotal.com\n");
|
||||
continue;
|
||||
}
|
||||
if dest.exists() {
|
||||
println!("already there, skipping {} :(", file.display());
|
||||
continue;
|
||||
}
|
||||
create_dir_all(dest.parent().unwrap())
|
||||
.expect("can't create directory to put a file in :(\n");
|
||||
fs::rename(&file, &dest).expect("can't move file to directory :(\n");
|
||||
}
|
||||
None => {
|
||||
println!("no tags on {} :(", file.display());
|
||||
}
|
||||
}
|
||||
import::import_file(&file, &timbre_dir);
|
||||
}
|
||||
remove_empty_dirs(&import_dir);
|
||||
let _ = remove_dir(&import_dir);
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
// if there's musicbrainz why no musicorganz
|
||||
|
||||
use crate::disc_id;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct DiscResponse {
|
||||
releases: Vec<Release>,
|
||||
pub struct DiscResponse {
|
||||
pub releases: Vec<Release>,
|
||||
}
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Release {
|
||||
title: String,
|
||||
media: Vec<Medium>,
|
||||
pub struct Release {
|
||||
pub title: String,
|
||||
pub media: Vec<Medium>,
|
||||
pub id: String,
|
||||
}
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Medium {
|
||||
tracks: Vec<Track>,
|
||||
pub struct Medium {
|
||||
pub tracks: Vec<Track>,
|
||||
}
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Track {
|
||||
title: String,
|
||||
position: u32,
|
||||
pub struct Track {
|
||||
pub title: String,
|
||||
pub position: u32,
|
||||
#[serde(rename = "artist-credit")]
|
||||
artist_credit: Vec<ArtistCredit>,
|
||||
pub artist_credit: Vec<ArtistCredit>,
|
||||
}
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct ArtistCredit {
|
||||
name: String,
|
||||
pub struct ArtistCredit {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub fn get_disc_info(base_url: &str) {
|
||||
pub fn get_disc_info(base_url: &str) -> DiscResponse {
|
||||
let disc_id = disc_id::get_disc_id();
|
||||
let url = format!("{base_url}/ws/2/discid/{disc_id}");
|
||||
|
||||
@@ -38,10 +41,12 @@ pub fn get_disc_info(base_url: &str) {
|
||||
"Timbre/0.1 ( https://gitea.owendeed.com/Toasterkitten/timbre )",
|
||||
)
|
||||
.call()
|
||||
.expect("idk\n")
|
||||
.expect("i couldn't connect to the musicbrainz server\n¯\\_(ツ)_/¯\ncheck your internet or something?\n")
|
||||
.body_mut()
|
||||
.read_to_string()
|
||||
.expect("?");
|
||||
let parsed: DiscResponse = serde_json::from_str(&body).unwrap();
|
||||
println!("{parsed:?}");
|
||||
.expect("musicbrainz isn't happy with you. ¯\\_(ツ)_/¯\n");
|
||||
let parsed: DiscResponse = serde_json::from_str(&body).expect(
|
||||
"musicbrainz said something i don't understand\n¯\\_(ツ)_/¯\nmaybe ur disc is unique\n",
|
||||
);
|
||||
parsed
|
||||
}
|
||||
|
||||
29
src/tagging.rs
Normal file
29
src/tagging.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use lofty::{config::WriteOptions, prelude::*, probe::Probe, tag::Tag};
|
||||
use std::path::Path;
|
||||
|
||||
pub fn write_tags(path: &Path, title: &str, artist: &str, album: &str, track_num: u32) {
|
||||
let mut tagged_file = Probe::open(path)
|
||||
.expect("couldn't open the flac :(\n")
|
||||
.read()
|
||||
.expect("couldn't read the flac :(\n");
|
||||
let tag = match tagged_file.primary_tag_mut() {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
if let Some(first_tag) = tagged_file.first_tag_mut() {
|
||||
first_tag
|
||||
} else {
|
||||
let tag_type = tagged_file.primary_tag_type();
|
||||
tagged_file.insert_tag(Tag::new(tag_type));
|
||||
tagged_file.primary_tag_mut().unwrap()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tag.set_title(title.to_string());
|
||||
tag.set_artist(artist.to_string());
|
||||
tag.set_album(album.to_string());
|
||||
tag.set_track(track_num);
|
||||
|
||||
tag.save_to_path(path, WriteOptions::default())
|
||||
.expect("couldn't save the tags :(\n");
|
||||
}
|
||||
Reference in New Issue
Block a user