From efca53b4bdcc3b0e7ca31f3b6d012409fcdf9412 Mon Sep 17 00:00:00 2001 From: MrDulfin Date: Sun, 8 Dec 2024 14:08:30 -0500 Subject: [PATCH] Grabbing songs from the backend almost works + other minor changes --- dmp-core/src/music_controller/controller.rs | 11 +- dmp-core/src/music_storage/library.rs | 19 ++ package-lock.json | 224 +++++++++++++++++++- package.json | 2 +- src-tauri/Cargo.toml | 6 +- src-tauri/src/lib.rs | 65 ++++-- src-tauri/src/wrappers.rs | 72 ++++++- src-tauri/tauri.conf.json | 6 +- src/App.css | 7 + src/App.tsx | 132 ++++++++++-- 10 files changed, 484 insertions(+), 60 deletions(-) diff --git a/dmp-core/src/music_controller/controller.rs b/dmp-core/src/music_controller/controller.rs index 950d7d5..f7bf664 100644 --- a/dmp-core/src/music_controller/controller.rs +++ b/dmp-core/src/music_controller/controller.rs @@ -5,6 +5,7 @@ use kushi::{Queue, QueueItemType}; use kushi::{QueueError, QueueItem}; +use serde::Serialize; use std::error::Error; use std::marker::PhantomData; use std::sync::{Arc, RwLock}; @@ -74,9 +75,10 @@ pub enum PlayerCommand { SetVolume(f64), } -#[derive(Debug, PartialEq, PartialOrd, Clone)] +#[derive(Debug, PartialEq, Clone)] pub enum PlayerResponse { Empty, + NowPlaying(Song) } pub enum LibraryCommand { @@ -270,7 +272,8 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { _ => unimplemented!(), }; player.write().unwrap().enqueue_next(uri).unwrap(); - player_mail.send(PlayerResponse::Empty).await.unwrap(); + let QueueItemType::Single(x) = item.item else { panic!("This is temporary, handle queueItemTypes at some point")}; + player_mail.send(PlayerResponse::NowPlaying(x.song.clone())).await.unwrap(); } } PlayerCommand::PrevSong => { @@ -328,14 +331,11 @@ impl<'c, P: Player + Send + Sync> Controller<'c, P> { let x = inner_lib_mail.recv().await.unwrap(); } LibraryCommand::AllSongs => { - println!("got command"); inner_lib_mail .send(InnerLibraryCommand::AllSongs) .await .unwrap(); - println!("sent"); let x = inner_lib_mail.recv().await.unwrap(); - println!("recieved"); if let InnerLibraryResponse::AllSongs(songs) = x { lib_mail.send(LibraryResponse::AllSongs(songs.clone())).await.unwrap(); } else { @@ -497,3 +497,4 @@ mod test_super { a.join().unwrap(); } } + diff --git a/dmp-core/src/music_storage/library.rs b/dmp-core/src/music_storage/library.rs index fc24915..de57956 100644 --- a/dmp-core/src/music_storage/library.rs +++ b/dmp-core/src/music_storage/library.rs @@ -7,6 +7,7 @@ use std::cmp::Ordering; // Various std things use std::collections::{BTreeMap, HashMap}; use std::error::Error; +use std::io::Read; use std::ops::ControlFlow::{Break, Continue}; use std::vec::IntoIter; @@ -473,6 +474,24 @@ impl Song { None => Err("No valid URIs for this song".into()), } } + + pub fn album_art(&self, i: usize) -> Result, Box> { + match self.album_art[i] { + AlbumArt::Embedded(j) => { + let file = lofty::read_from_path(self.primary_uri()?.0.path())?; + if file.contains_tag_type(TagType::Id3v2) { + Ok(file.tag(TagType::Id3v2).unwrap().pictures()[j].data().to_vec()) + } else { + unimplemented!() + } + }, + AlbumArt::External(ref path) => { + let mut buf = vec![]; + std::fs::File::open(path.path())?.read_to_end(&mut buf)?; + Ok(buf) + } + } + } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] diff --git a/package-lock.json b/package-lock.json index 5fa3824..8652733 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,12 @@ "name": "dango-music-player", "version": "0.1.0", "dependencies": { + "@jprochazk/cbor": "github:jprochazk/cbor", "@tauri-apps/api": "^2", "@tauri-apps/plugin-shell": "^2", + "cbor": "github:jprochazk/cbor", + "cbor-x": "^1.6.0", + "node-fetch": "^3.3.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -19,7 +23,7 @@ "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.2.1", "typescript": "^5.2.2", - "vite": "^5.3.1" + "vite": "^5.4.11" } }, "node_modules/@ampproject/remapping": { @@ -289,6 +293,78 @@ "node": ">=6.9.0" } }, + "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-darwin-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz", + "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz", + "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz", + "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz", + "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-win32-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz", + "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -657,6 +733,10 @@ "node": ">=12" } }, + "node_modules/@jprochazk/cbor": { + "version": "0.5.0", + "resolved": "git+ssh://git@github.com/jprochazk/cbor.git#4824b43c60f8a1c38fd8ef3d51d1f24e30a55743" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -1287,6 +1367,40 @@ } ] }, + "node_modules/cbor": { + "name": "@jprochazk/cbor", + "version": "0.5.0", + "resolved": "git+ssh://git@github.com/jprochazk/cbor.git#4824b43c60f8a1c38fd8ef3d51d1f24e30a55743" + }, + "node_modules/cbor-extract": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz", + "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.1.1" + }, + "bin": { + "download-cbor-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", + "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", + "@cbor-extract/cbor-extract-linux-x64": "2.2.0", + "@cbor-extract/cbor-extract-win32-x64": "2.2.0" + } + }, + "node_modules/cbor-x": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.6.0.tgz", + "integrity": "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==", + "optionalDependencies": { + "cbor-extract": "^2.2.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1299,6 +1413,14 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -1316,6 +1438,15 @@ } } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.65", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.65.tgz", @@ -1369,6 +1500,39 @@ "node": ">=6" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1474,6 +1638,55 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -1657,6 +1870,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -1711,6 +1925,14 @@ } } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index a5d3524..a0a8c5f 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,6 @@ "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.2.1", "typescript": "^5.2.2", - "vite": "^5.3.1" + "vite": "^5.4.11" } } diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ee57f0a..9d9d66f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -18,17 +18,19 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2", features = ["unstable"] } +dmp-core = { path = "../dmp-core" } +tauri = { version = "2", features = [ "protocol-asset", "unstable"] } tauri-plugin-shell = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" -dmp-core = { path = "../dmp-core" } futures = "0.3.31" crossbeam = "0.8.4" directories = "5.0.1" uuid = { version = "1.11.0", features = ["v4"] } ciborium = "0.2.2" mime = "0.3.17" +file-format = "0.26.0" +chrono = { version = "0.4.38", features = ["serde"] } [features] default = [ "custom-protocol" ] diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 09d9e57..15a27e5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,9 +1,10 @@ -use std::{fs, path::PathBuf, str::FromStr, thread::spawn}; +use std::{fs, io::Read, path::PathBuf, str::FromStr, thread::spawn, time::Duration}; use crossbeam::channel::{unbounded, Receiver, Sender}; -use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{Controller, ControllerHandle}, music_player::gstreamer::GStreamer, music_storage::library::MusicLibrary}; -use tauri::{Manager, State, WebviewWindowBuilder, Wry}; +use dmp_core::{config::{Config, ConfigLibrary}, music_controller::controller::{Controller, ControllerHandle}, music_player::gstreamer::GStreamer, music_storage::library::{AlbumArt, MusicLibrary}}; +use tauri::{http::Response, Manager, State, Url, WebviewWindowBuilder, Wry}; use uuid::Uuid; +use wrappers::ArtworkRx; use crate::wrappers::{get_library, play, pause, prev, set_volume, get_song, next}; @@ -12,13 +13,14 @@ pub mod wrappers; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let (rx, tx) = unbounded::(); - let (lib_rx, lib_tx) = unbounded::(); + let (lib_rx, lib_tx) = unbounded::>(); let (handle_rx, handle_tx) = unbounded::(); + let (art_rx, art_tx) = unbounded::>(); - let t1 = spawn(move || { + let controller_thread = spawn(move || { let mut config = { tx.recv().unwrap() } ; let scan_path = { lib_tx.recv().unwrap() }; - let save_path = config.libraries.library_folder.join("library.dlib"); + let save_path = dbg!(config.libraries.library_folder.join("library.dlib")); let mut library = MusicLibrary::init( save_path.clone(), @@ -28,19 +30,25 @@ pub fn run() { Uuid::new_v4() } ).unwrap(); + + let scan_path = scan_path.unwrap_or_else(|| config.libraries.get_default().unwrap().scan_folders.as_ref().unwrap()[0].clone()); library.scan_folder(&scan_path).unwrap(); - config.push_library( ConfigLibrary::new(save_path, String::from("Library"), Some(vec![scan_path.clone()]))); - // config.write_file().unwrap(); - + if config.libraries.get_default().is_err() { + config.push_library( ConfigLibrary::new(save_path, String::from("Library"), Some(vec![scan_path.clone()]))); + } + if library.library.is_empty() { + println!("library is empty"); + } else { + config.write_file().unwrap(); + } + println!("scan_path: {}", scan_path.display()); let (handle, input) = ControllerHandle::new( library, std::sync::Arc::new(std::sync::RwLock::new(config)) ); - - handle_rx.send(handle).unwrap(); let controller = futures::executor::block_on(Controller::::start(input)).unwrap(); @@ -59,10 +67,25 @@ pub fn run() { next, prev, get_song, + lib_already_created, ]).manage(ConfigRx(rx)) .manage(LibRx(lib_rx)) .manage(HandleTx(handle_tx)) + .manage(ArtworkRx(art_rx)) + .register_asynchronous_uri_scheme_protocol("asset", move |_, req, res| { + dbg!(req); + let buf = art_tx.recv().unwrap_or_else(|_| Vec::new()); + res.respond( + Response::builder() + .header("Origin", "*") + .header("Content-Length", buf.len()) + .status(200) + .body(buf) + .unwrap() + ); + println!("res sent") + }) .build(tauri::generate_context!()) .expect("error while building tauri application"); @@ -73,19 +96,21 @@ pub fn run() { } _ => {} }); - t1.join().unwrap(); + // controller_thread.join().unwrap(); } struct ConfigRx(Sender); -struct LibRx(Sender); +struct LibRx(Sender>); struct HandleTx(Receiver); + #[tauri::command] async fn get_config(state: State<'_, ConfigRx>) -> Result { if let Some(dir) = directories::ProjectDirs::from("", "Dangoware", "dmp") { let path = dir.config_dir(); - fs::create_dir(path).or_else(|err| { + // dbg!(&path); + fs::create_dir_all(path).or_else(|err| { if err.kind() == std::io::ErrorKind::AlreadyExists { Ok(()) } else { @@ -139,7 +164,7 @@ async fn create_library( path: String ) -> Result<(), String> { println!("{path}"); - let path = PathBuf::from(path); + let path = PathBuf::from(path.trim().trim_matches('"')); if !path.exists() { panic!("Path {} does not exist!", path.display()) @@ -147,9 +172,17 @@ async fn create_library( panic!("Path {} is not a directory!", path.display()) } - lib_rx.inner().0.send(path).unwrap(); + lib_rx.inner().0.send(Some(path)).unwrap(); app.manage(handle_tx.inner().0.recv().unwrap()); window.close().unwrap(); Ok(()) } + +#[tauri::command] + async fn lib_already_created(app: tauri::AppHandle, lib_rx: State<'_, LibRx>, handle_tx: State<'_, HandleTx>) -> Result<(), String> { + println!("lib already created"); + lib_rx.inner().0.send(None); + app.manage(handle_tx.inner().0.recv().unwrap()); + Ok(()) +} diff --git a/src-tauri/src/wrappers.rs b/src-tauri/src/wrappers.rs index b6f8251..d65b2f7 100644 --- a/src-tauri/src/wrappers.rs +++ b/src-tauri/src/wrappers.rs @@ -1,7 +1,14 @@ -use dmp_core::{music_controller::controller::{ControllerHandle, LibraryCommand, LibraryResponse, PlayerResponse}, music_storage::library::Song}; -use tauri::{ipc::Response, State}; +use std::collections::BTreeMap; + +use chrono::{DateTime, Utc, serde::ts_milliseconds_option}; +use crossbeam::channel::Sender; +use dmp_core::{music_controller::controller::{ControllerHandle, LibraryCommand, LibraryResponse, PlayerResponse}, music_storage::library::{BannedType, Song, URI}}; +use serde::Serialize; +use tauri::{ipc::Response, AppHandle, Emitter, State, Wry}; use uuid::Uuid; +pub struct ArtworkRx(pub Sender>); + #[tauri::command] pub async fn play(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> { ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::Play).await.unwrap(); @@ -21,7 +28,8 @@ pub async fn pause(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), Strin } #[tauri::command] -pub async fn set_volume(ctrl_handle: State<'_, ControllerHandle>, volume: f64) -> Result<(), String> { +pub async fn set_volume(ctrl_handle: State<'_, ControllerHandle>, volume: String) -> Result<(), String> { + let volume = volume.parse::().unwrap() / 1000.0; ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::SetVolume(volume)).await.unwrap(); let PlayerResponse::Empty = ctrl_handle.player_mail.recv().await.unwrap() else { unreachable!() @@ -36,11 +44,14 @@ pub async fn get_volume(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), } #[tauri::command] -pub async fn next(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), String> { +pub async fn next(app: AppHandle, ctrl_handle: State<'_, ControllerHandle>, art_rx: State<'_, ArtworkRx>) -> Result<(), String> { ctrl_handle.player_mail.send(dmp_core::music_controller::controller::PlayerCommand::NextSong).await.unwrap(); - let PlayerResponse::Empty = ctrl_handle.player_mail.recv().await.unwrap() else { + let PlayerResponse::NowPlaying(song) = ctrl_handle.player_mail.recv().await.unwrap() else { unreachable!() }; + let _song = _Song::from(&song); + art_rx.0.send(song.album_art(0).unwrap()).unwrap(); + app.emit("now_playing_change", _song).unwrap(); Ok(()) } @@ -59,16 +70,48 @@ pub async fn now_playing(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), Ok(()) } + +//Grab Album art from custom protocol +#[derive(Serialize, Debug, Clone)] +pub struct _Song { + pub location: Vec, + pub uuid: Uuid, + pub plays: i32, + pub format: Option, + pub duration: String, + #[serde(with = "ts_milliseconds_option")] + pub last_played: Option>, + #[serde(with = "ts_milliseconds_option")] + pub date_added: Option>, + #[serde(with = "ts_milliseconds_option")] + pub date_modified: Option>, + pub tags: BTreeMap, +} + +impl From<&Song> for _Song { + fn from(value: &Song) -> Self { + _Song { + location: value.location.clone(), + uuid: value.uuid.clone(), + plays: value.plays.clone(), + duration: value.duration.as_secs().to_string(), + format: value.format.map(|format| format.to_string()), + last_played: value.last_played, + date_added: value.date_added, + date_modified: value.date_modified, + tags: value.tags.iter().map(|(k, v)| (k.to_string(), v.clone())).collect() + } + } +} + #[tauri::command] -pub async fn get_library(ctrl_handle: State<'_, ControllerHandle>) -> Result { - println!("getting songs"); +pub async fn get_library(ctrl_handle: State<'_, ControllerHandle>) -> Result, String> { ctrl_handle.lib_mail.send(LibraryCommand::AllSongs).await.unwrap(); let LibraryResponse::AllSongs(songs) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; - println!("got songs"); - let mut buf = vec![]; - ciborium::into_writer(&songs, &mut buf); - Ok(Response::new(buf)) + let _songs = songs.iter().map(|song| _Song::from(song)).collect::>(); + + Ok(_songs) } #[tauri::command] @@ -77,4 +120,11 @@ pub async fn get_song(ctrl_handle: State<'_, ControllerHandle>) -> Result<(), St let LibraryResponse::Song(_) = ctrl_handle.lib_mail.recv().await.unwrap() else { unreachable!("It has been reached") }; println!("got songs"); Ok(()) +} + +#[derive(Serialize, Debug)] +pub struct NowPlaying { + title: String, + artist: String, + album: String, } \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ee5ea94..5b3ff94 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -18,7 +18,11 @@ } ], "security": { - "csp": null + "assetProtocol": { + "enable": true, + "scope": { "allow": ["asset://localhost*"] } + }, + "csp": "default-src 'self'; img-src 'self'; asset: asset://localhost/*" } }, "bundle": { diff --git a/src/App.css b/src/App.css index fdd980e..03a16ec 100644 --- a/src/App.css +++ b/src/App.css @@ -36,6 +36,7 @@ .mainView { background-color: #f5eab2; height: 91.5%; + overflow-y: scroll; } #playBar { @@ -70,4 +71,10 @@ bottom: -25%; background-color: burlywood; height: 50%; +} + +.song { + display: flex; + justify-content: left; + gap: 1%; } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 596f728..9854b4f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,29 +1,40 @@ import { useEffect, useRef, useState } from "react"; -import reactLogo from "./assets/react.svg"; -import { invoke } from "@tauri-apps/api/core"; +import { convertFileSrc, invoke } from "@tauri-apps/api/core"; import "./App.css"; -import { Config, Song } from "./types"; - -import { decode, encode } from 'cbor-x'; -import CBOR from "cbor"; +import { Config } from "./types"; +import { EventEmitter } from "@tauri-apps/plugin-shell"; +import { listen } from "@tauri-apps/api/event"; function App() { + const library = useState(); + const [artwork, setArtwork] = useState(<>); + const [nowPlaying, setNowPlaying] = useState(); + + listen("now_playing_change", (event) => { + console.log(event.payload); + + setNowPlaying( ) + setArtwork( ) + }) useEffect(() => { getConfig(); - invoke('set_volume', { volume: 0.04 }).then( () => {} ) + invoke('set_volume', { volume: "1" }).then( () => {} ) }, []) - return (
- +
- + { nowPlaying }
@@ -38,6 +49,9 @@ function getConfig(): any { let config = _config as Config; if (config.libraries.libraries.length == 0) { newWindow() + } else { + console.log("else"); + invoke('lib_already_created').then(() => {}) } }) } @@ -60,17 +74,78 @@ function PlaylistHead() { ) } -function MainView() { +interface MainViewProps { + lib_ref: [JSX.Element[] | undefined, React.Dispatch>], +} + +function MainView({ lib_ref }: MainViewProps) { + const [library, setLibrary] = lib_ref; + // useEffect(() => { + // console.log(lib_ref); + // console.log(typeof lib_ref); + // if (typeof lib_ref.current !== "undefined") { + + // setLibrary(lib_ref.current.map((song) => { + // + // })) + // } + + // }, [lib_ref]) + return (
main view - + invoke('get_library').then((lib) => { + setLibrary([...(lib as any[]).map((song) => { + console.log(song); + + return ( + + ) + })]) + })} }>get library +
{ library }
+
+ ) +} + +interface SongProps { + location: any, + uuid: string, + plays: number, + format?: string, + duration: string, + last_played?: string, + date_added?: string, + date_modified?: string, + tags: any +} + +function Song(props: SongProps) { + console.log(props.tags); + + return( +
+

{ props.tags.TrackTitle }

+

{ props.tags.Album }

+

{ props.tags.AlbumArtist }

+

{ props.duration }

) } @@ -95,20 +170,31 @@ function PlayBar() { } }}>{ playing } - + { + invoke('set_volume', { volume: volume.target.value }).then(() => {}) + }} /> ) } -function NowPlaying() { +interface NowPlayingProps { + title: string, + artist: string, + album: string, + artwork: JSX.Element +} + +function NowPlaying({ title, artist, album, artwork }: NowPlayingProps) { + console.log(convertFileSrc("abc")); + return (
- -

やけにインザレイン

-

t+pazolite; 小林私

-

Heartache Debug

+ { artwork } +

{ title }

+

{ artist }

+

{ album }

) }