artworks implemented for soundcloud

This commit is contained in:
Michael Wain 2025-02-22 04:14:26 +03:00
parent 7a75c45fa0
commit a724560d58
5 changed files with 99 additions and 41 deletions

14
Cargo.lock generated
View File

@ -1284,8 +1284,8 @@ checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "itunesdb"
version = "0.1.89"
source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#2a867778b7bbd82956247f2f17b98fdb6865a910"
version = "0.1.90"
source = "git+https://gitea.awain.net/alterwain/ITunesDB.git#d37957a9a0b66577dadf3b305b02c72f1ca50a62"
dependencies = [
"bincode",
"env_logger",
@ -1441,6 +1441,7 @@ dependencies = [
"tokio-util",
"toml",
"tui-big-text",
"twox-hash",
]
[[package]]
@ -2789,6 +2790,15 @@ dependencies = [
"ratatui",
]
[[package]]
name = "twox-hash"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7b17f197b3050ba473acf9181f7b1d3b66d1cf7356c6cc57886662276e65908"
dependencies = [
"rand",
]
[[package]]
name = "unicode-ident"
version = "1.0.16"

View File

@ -21,9 +21,10 @@ futures = "0.3"
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7.12", features = ["codec"] }
soundcloud = { version = "0.1.8", git = "https://gitea.awain.net/alterwain/soundcloud_api.git" }
itunesdb = { version = "0.1.89", git = "https://gitea.awain.net/alterwain/ITunesDB.git" }
itunesdb = { version = "0.1.90", git = "https://gitea.awain.net/alterwain/ITunesDB.git" }
rand = "0.8.5"
tui-big-text = "0.7.1"
throbber-widgets-tui = "0.8.0"
audiotags = "0.5.0"
image = "0.25.5"
image = "0.25.5"
twox-hash = "2.1.0"

View File

@ -14,6 +14,11 @@ pub fn get_temp_dl_dir() -> PathBuf {
p
}
pub fn clear_temp_dl_dir() {
let path = get_temp_dl_dir();
let _ = std::fs::remove_dir_all(path);
}
pub fn get_config_path() -> PathBuf {
let mut p = get_configs_dir();
p.push("config");

View File

@ -1,7 +1,7 @@
use audiotags::{Picture, Tag};
use audiotags::Tag;
use color_eyre::owo_colors::OwoColorize;
use image::imageops::FilterType;
use image::ImageReader;
use image::{GenericImageView, ImageReader};
use itunesdb::artworkdb::aobjects::ADatabase;
use itunesdb::objects::{ListSortOrder, PlaylistItem};
use itunesdb::serializer;
@ -51,15 +51,20 @@ pub struct DBPlaylist {
pub tracks: Vec<XTrackItem>,
}
async fn track_from_soundcloud(value: &CloudTrack) -> Option<XTrackItem> {
async fn track_from_soundcloud(value: &CloudTrack, ipod_path: String) -> Option<XTrackItem> {
let mut track_path = get_temp_dl_dir();
track_path.push(value.id.to_string());
track_path.set_extension("mp3");
let mut image_path = get_temp_dl_dir();
image_path.push(value.id.to_string());
image_path.set_extension("jpg");
let audio_file = audio_file_info::from_path(track_path.to_str().unwrap())
.await
.unwrap();
let audio_info = &audio_file.audio_file.tracks.track;
let song_dbid = util::hash_from_path(track_path);
let mut track = XTrackItem::new(
value.id as u32,
audio_info.audio_bytes as u32,
@ -67,10 +72,34 @@ async fn track_from_soundcloud(value: &CloudTrack) -> Option<XTrackItem> {
0,
(audio_info.bit_rate / 1000) as u32,
audio_info.sample_rate as u32,
hash(),
song_dbid,
0,
);
if image_path.exists() {
let mut adb = get_artwork_db(&ipod_path);
let image_data = std::fs::read(image_path).unwrap();
let (small_img_name, large_img_name) = adb.add_images(song_dbid, util::hash(&image_data));
let mut dst = PathBuf::from(&ipod_path);
dst.push("iPod_Control");
dst.push("Artwork");
let size = image_data.len();
make_cover_image(&image_data, &ipod_path, &small_img_name, (100, 100));
make_cover_image(&image_data, &ipod_path, &large_img_name, (200, 200));
write_artwork_db(adb, &ipod_path);
track.data.artwork_size = size as u32;
track.data.mhii_link = 0;
track.data.has_artwork = 1;
track.data.artwork_count = 1;
}
audio_file.modify_xtrack(&mut track);
track.set_title(value.title.clone().unwrap());
@ -84,11 +113,6 @@ async fn track_from_soundcloud(value: &CloudTrack) -> Option<XTrackItem> {
Some(track)
}
// note: this hash function is used to make unique ids for each track. It doesn't aim to generate secure ones.
fn hash() -> u64 {
rand::random::<u64>()
}
fn get_track_location(unique_id: u32, extension: &str) -> String {
let mut tp = PathBuf::new();
tp.push(":iPod_Control");
@ -330,7 +354,7 @@ async fn load_from_fs(
.unwrap();
let audio_info = &audio_file.audio_file.tracks.track;
let song_dbid = hash();
let song_dbid = util::hash_from_path(path.clone());
let mut track = XTrackItem::new(
id,
@ -362,18 +386,20 @@ async fn load_from_fs(
if let Some(cover) = tag.album_cover() {
let mut adb = get_artwork_db(&ipod_path);
let (small_img_name, large_img_name) = adb.add_images(song_dbid, id);
let (small_img_name, large_img_name) = adb.add_images(song_dbid, util::hash(cover.data));
let mut dst = PathBuf::from(&ipod_path);
dst.push("iPod_Control");
dst.push("Artwork");
make_small_image(cover.clone(), &ipod_path, &small_img_name);
make_large_image(cover, &ipod_path, &large_img_name);
let size = cover.data.len();
make_cover_image(cover.data, &ipod_path, &small_img_name, (100, 100));
make_cover_image(cover.data, &ipod_path, &large_img_name, (200, 200));
write_artwork_db(adb, &ipod_path);
track.data.artwork_size = 1134428;
track.data.artwork_size = size as u32;
track.data.mhii_link = 0;
track.data.has_artwork = 1;
track.data.artwork_count = 1;
@ -441,32 +467,34 @@ fn get_artwork_db(ipod_path: &str) -> ADatabase {
itunesdb::artworkdb::deserializer::new_db()
}
fn make_small_image(cover: Picture, ipod_path: &str, file_name: &str) {
let img: IPodImage = ImageReader::new(Cursor::new(cover.data))
fn make_cover_image(cover: &[u8], ipod_path: &str, file_name: &str, dim: (u32, u32)) {
let mut dynamic_im = ImageReader::new(Cursor::new(cover))
.with_guessed_format()
.unwrap()
.decode()
.unwrap()
.resize_exact(100, 100, FilterType::Lanczos3)
.into();
.unwrap();
let mut dst = PathBuf::from(ipod_path);
dst.push("iPod_Control");
dst.push("Artwork");
if dynamic_im.height() != dynamic_im.width() {
let side = if dynamic_im.height() < dynamic_im.width() {
dynamic_im.height()
} else {
dynamic_im.width()
};
let x = if dynamic_im.height() < dynamic_im.width() {
(dynamic_im.width() - side) / 2
} else {
0
};
let y = if dynamic_im.height() < dynamic_im.width() {
0
} else {
(dynamic_im.height() - side) / 2
};
dynamic_im = dynamic_im.crop(x, y, side, side);
}
let _ = std::fs::create_dir_all(dst.clone());
dst.push(file_name);
img.write(dst);
}
fn make_large_image(cover: Picture, ipod_path: &str, file_name: &str) {
let img: IPodImage = ImageReader::new(Cursor::new(cover.data))
.with_guessed_format()
.unwrap()
.decode()
.unwrap()
.resize_exact(200, 200, FilterType::Lanczos3)
let img: IPodImage = dynamic_im
.resize_exact(dim.0, dim.1, FilterType::Lanczos3)
.into();
let mut dst = PathBuf::from(ipod_path);
@ -494,7 +522,7 @@ async fn download_track(
{
let p: PathBuf = Path::new(&ipod_path).into();
if let Some(mut t) = track_from_soundcloud(&track).await {
if let Some(mut t) = track_from_soundcloud(&track, ipod_path.clone()).await {
t.data.unique_id = database.get_unique_id();
t.set_location(get_track_location(t.data.unique_id, "mp3"));
let dest = get_full_track_location(p.clone(), t.data.unique_id, "mp3");
@ -518,6 +546,8 @@ async fn download_track(
.await;
overwrite_database(database, &ipod_path);
crate::config::clear_temp_dl_dir();
}
async fn download_playlist(
@ -542,7 +572,7 @@ async fn download_playlist(
if track.title.is_none() {
continue;
}
if let Some(mut t) = track_from_soundcloud(&track).await {
if let Some(mut t) = track_from_soundcloud(&track, ipod_path.clone()).await {
t.data.unique_id = database.get_unique_id();
new_playlist.add_elem(t.data.unique_id);
t.set_location(get_track_location(t.data.unique_id, "mp3"));
@ -569,6 +599,8 @@ async fn download_playlist(
.await;
overwrite_database(database, &ipod_path);
crate::config::clear_temp_dl_dir();
}
fn get_playlists(db: &mut XDatabase) -> Vec<DBPlaylist> {

View File

@ -3,6 +3,7 @@ use regex::Regex;
use std::io::Write;
use std::path::PathBuf;
use std::{error::Error, process::Command, str, str::FromStr};
use twox_hash::XxHash3_64;
const VENDOR_ID: u16 = 1452;
const PRODUCT_ID: u16 = 4617;
@ -110,6 +111,15 @@ fn get_ipod_path() -> Option<String> {
}
}
// note: this hash function is used to make unique ids for each track. It doesn't aim to generate secure ones.
pub fn hash(data: &[u8]) -> u64 {
XxHash3_64::oneshot(data)
}
pub fn hash_from_path(path: PathBuf) -> u64 {
hash(&std::fs::read(path).unwrap())
}
pub struct IPodImage {
pixels: Vec<u16>,
}