Compare commits

8 Commits

Author SHA1 Message Date
029e192f6a bugfix 2026-09-05 18:24:58 -04:00
064b385687 update 2026-08-24 18:05:21 -04:00
81b4bed8eb update readme 2026-08-24 18:04:43 -04:00
f0a35c1aa2 emergency fix 2026-08-24 17:54:30 -04:00
cabcc5fd34 update readme 2026-08-24 17:47:25 -04:00
dcd1a233e8 update readme 2026-08-24 17:36:57 -04:00
6cf0820a1f disc adoption 2026-08-24 17:35:33 -04:00
6b45477a9d cover art 2026-08-24 16:46:42 -04:00
5 changed files with 96 additions and 12 deletions

View File

@@ -3,8 +3,19 @@
**a music manager to finally solve this huge mess of music management.**
> ⚠️ **Timbre is in extreme alpha.** Right now it can create a library, import
> and organize your music, scan it, and repair itself. Everything else, CD
> ripping, automatic tagging, and more, is the planned future of the project.
> and organize your music, scan it, repair itself, and adopt (rip) CDs to
> tagged, lossless FLAC with cover art. More is planned. It works, but expect
> rough edges and breaking changes, so use it on music you have backed up
> elsewhere.
## 📑 Contents
- [Info](#-info)
- [Installation](#-installation)
- [Usage](#️-usage)
- [Building](#️-building)
- [The Cat](#-the-cat)
- [License](#-license)
## 📋 Info
@@ -16,6 +27,32 @@ features in one app. Rip your CDs (or bring your own media files!), auto-organiz
them, and play them back in your favorite music player, all with one person's
opinion instead of tons, seamlessly working, in a fast Rust app.
## 📦 Installation
You will need the following commands on your system for adopting a CD:
- `cdparanoia`
- `flac`
Here is some ways to get them depending on your system:
Arch-based systems
```
sudo pacman -S cdparanoia flac
```
Debian-based systems
```
sudo apt install cdparanoia flac
```
Fedora-based systems
```
sudo dnf install cdparanoia flac
```
Most are the same, you should be able to figure it out.
Afterwards, download the binary from the latest release and put it somewhere on path, usually `~/.local/bin/`.
Then run timbre init and get started.
Timbre is also in the AUR as the package [`timbre-bin`](https://aur.archlinux.org/packages/timbre-bin).
## ⌨️ Usage
- `timbre init` - give Timbre a home (`~/Music/Timbre/`)

View File

@@ -9,13 +9,12 @@ use std::{
time::{SystemTime, UNIX_EPOCH},
};
use crate::{import, musicbrainz::get_disc_info, tagging};
use crate::{coverartarchive::fetch_cover_art, 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 disc = get_disc_info("https://musicbrainz.org");
let release = &disc.releases[0];
println!("{}", release.id);
let tmp_dir = timbre_dir.join(format!(
".tmp/{}",
SystemTime::now()
@@ -79,12 +78,12 @@ pub fn adopt(timbre_dir: &Path) {
}
child.wait().expect("cdparanoia didn't finish cleanly :(\n");
let flac_name = format!("track{track_num:02}.flac");
let flac_path = tmp_dir.join(&flac_name);
let wav_path = tmp_dir.join(format!("track{track_num:02}.wav"));
let flac_path = tmp_dir.join(format!("track{track_num:02}.flac"));
Command::new("flac")
.arg("--best") // max compression, lossless
.arg(&flac_path) // input WAV
.arg(&wav_path) // input WAV
.output()
.expect("flac wouldn't encode :( is it installed?\n");
@@ -94,6 +93,7 @@ pub fn adopt(timbre_dir: &Path) {
&current_track.artist_credit[0].name,
&release.title,
track_num as u32,
fetch_cover_art(&release.id).as_deref(),
);
import::import_file(&flac_path, timbre_dir);

19
src/coverartarchive.rs Normal file
View File

@@ -0,0 +1,19 @@
use std::io::Read;
pub fn fetch_cover_art(release_id: &str) -> Option<Vec<u8>> {
let url = format!("https://coverartarchive.org/release/{}/front", release_id);
let response = ureq::get(&url)
.header("User-Agent", "Timbre/0.1 ( ...your repo... )")
.call();
match response {
Ok(resp) => {
let mut bytes = Vec::new();
resp.into_body()
.into_reader()
.read_to_end(&mut bytes)
.expect("i couldn't read the cover art :(\n");
Some(bytes)
}
Err(_) => None,
}
}

View File

@@ -1,10 +1,12 @@
mod adopt;
mod coverartarchive;
mod disc_id;
mod import;
mod musicbrainz;
mod tagging;
use clap::{
Parser, Subcommand,
Command, Parser, Subcommand,
builder::styling::{AnsiColor, Effects, Styles},
};
use sha2::{Digest, Sha256};
@@ -67,6 +69,8 @@ enum Commands {
Repair,
/// import music files
Import,
/// Adopt a CD, import it into your library, also getting metadata
Adopt,
}
fn is_audio(path: &Path) -> bool {
@@ -141,7 +145,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
@@ -257,5 +261,8 @@ fn main() {
);
}
}
Commands::Adopt => {
adopt::adopt(&timbre_dir);
}
}
}

View File

@@ -1,7 +1,20 @@
use lofty::{config::WriteOptions, prelude::*, probe::Probe, tag::Tag};
use lofty::{
config::WriteOptions,
picture::{MimeType, Picture, PictureType},
prelude::*,
probe::Probe,
tag::Tag,
};
use std::path::Path;
pub fn write_tags(path: &Path, title: &str, artist: &str, album: &str, track_num: u32) {
pub fn write_tags(
path: &Path,
title: &str,
artist: &str,
album: &str,
track_num: u32,
cover: Option<&[u8]>,
) {
let mut tagged_file = Probe::open(path)
.expect("couldn't open the flac :(\n")
.read()
@@ -24,6 +37,14 @@ pub fn write_tags(path: &Path, title: &str, artist: &str, album: &str, track_num
tag.set_album(album.to_string());
tag.set_track(track_num);
if let Some(image_bytes) = cover {
let picture = Picture::unchecked(image_bytes.to_vec())
.pic_type(PictureType::CoverFront)
.mime_type(MimeType::Jpeg)
.build();
tag.set_picture(0, picture);
}
tag.save_to_path(path, WriteOptions::default())
.expect("couldn't save the tags :(\n");
}