Compare commits
8 Commits
e6769164d6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 029e192f6a | |||
| 064b385687 | |||
| 81b4bed8eb | |||
| f0a35c1aa2 | |||
| cabcc5fd34 | |||
| dcd1a233e8 | |||
| 6cf0820a1f | |||
| 6b45477a9d |
41
README.md
41
README.md
@@ -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/`)
|
||||
|
||||
12
src/adopt.rs
12
src/adopt.rs
@@ -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) {
|
||||
¤t_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
19
src/coverartarchive.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
11
src/main.rs
11
src/main.rs
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user