diff --git a/src-native/data.rs b/src-native/data.rs index a3674d7..5a6e9ac 100644 --- a/src-native/data.rs +++ b/src-native/data.rs @@ -9,7 +9,9 @@ use serde::Serialize; use std::env; use std::io::Write; use std::path::{Path, PathBuf}; +use std::sync::OnceLock; use std::time::Instant; +use tokio::sync::Mutex; pub fn path_to_string>(path: P) -> String { path.as_ref() @@ -25,6 +27,8 @@ pub struct Data { pub current_tag: Option, } +pub static DATA: OnceLock> = OnceLock::new(); + pub fn app_log_dir() -> Result { #[cfg(target_os = "macos")] { @@ -63,7 +67,7 @@ impl Data { is_dev: bool, local_data_path: Option, library_path: Option, - ) -> Result { + ) -> Result<()> { if is_dev { println!("Starting in dev mode"); } @@ -115,6 +119,12 @@ impl Data { library: loaded_library, current_tag: None, }; - return Ok(data); + match DATA.set(Mutex::new(data)) { + Ok(()) => (), + Err(_) => { + panic!("Failed to set DATA"); + } + }; + return Ok(()); } } diff --git a/src-native/data_js.rs b/src-native/data_js.rs index df3ffd7..ff92bcf 100644 --- a/src-native/data_js.rs +++ b/src-native/data_js.rs @@ -1,14 +1,11 @@ -use crate::data::{Data, app_log_dir, path_to_string}; +use crate::data::{DATA, Data, app_log_dir, path_to_string}; use crate::library::Paths; -use napi::{Env, Result}; +use napi::Result; use std::fs; +use tokio::sync::MutexGuard; -pub fn get_data(env: &Env) -> &mut Data { - let data = env - .get_instance_data::() - .expect("Error getting data") - .expect("No data"); - return data; +pub fn get_data() -> MutexGuard<'static, Data> { + DATA.get().expect("No data initialised").blocking_lock() } #[napi(js_name = "load_data")] @@ -17,7 +14,6 @@ pub fn load_data( is_dev: bool, local_data_path: Option, library_path: Option, - env: Env, ) -> Result<()> { std::panic::set_hook(Box::new(move |info| { let backtrace = std::backtrace::Backtrace::force_capture(); @@ -36,15 +32,14 @@ pub fn load_data( fs::write(&file_path, log_msg).expect("Could not save crash log"); println!("Crash message written to {}", file_path.to_string_lossy()); })); - let data = Data::load(is_dev, local_data_path, library_path)?; - env.set_instance_data(data, 0, |_ctx| {})?; + Data::load(is_dev, local_data_path, library_path)?; return Ok(()); } #[napi(js_name = "get_paths")] #[allow(dead_code)] -pub fn get_paths(env: Env) -> Paths { - let data: &Data = get_data(&env); +pub fn get_paths() -> Paths { + let data = get_data(); data.paths.clone() } #[napi(js_name = "get_logs_dir")] @@ -58,8 +53,8 @@ pub fn get_logs_dir() -> Result { #[napi(js_name = "save")] #[allow(dead_code)] -pub fn save(env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn save() -> Result<()> { + let mut data = get_data(); data.save()?; Ok(()) } diff --git a/src-native/itunes_import.rs b/src-native/itunes_import.rs index 175672e..a887093 100644 --- a/src-native/itunes_import.rs +++ b/src-native/itunes_import.rs @@ -8,7 +8,6 @@ use crate::tracks::generate_filename; use crate::tracks::import::{FileType, read_file_metadata}; use anyhow::{Context, Result, bail}; use lofty::file::{AudioFile, TaggedFileExt}; -use napi::Env; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; @@ -586,8 +585,8 @@ pub struct ItunesImport { #[napi] impl ItunesImport { #[napi(factory)] - pub fn new(env: Env) -> Self { - let data = get_data(&env); + pub fn new() -> Self { + let data = get_data(); Self { new_library: Some(data.library.clone()).into(), itunes_track_paths: HashMap::new().into(), @@ -599,8 +598,8 @@ impl ItunesImport { Ok(import_itunes(self, path).await?) } #[napi] - pub fn finish(&mut self, env: Env) -> napi::Result<()> { - let data = get_data(&env); + pub fn finish(&mut self) -> napi::Result<()> { + let mut data = get_data(); let itunes_track_paths = &mut *self.itunes_track_paths.lock().unwrap(); for (itunes_path, ferrum_file) in itunes_track_paths { let new_path = data.paths.get_track_file_path(ferrum_file); diff --git a/src-native/library.rs b/src-native/library.rs index 6f48bcf..c84966c 100644 --- a/src-native/library.rs +++ b/src-native/library.rs @@ -1,12 +1,9 @@ #[cfg(feature = "napi-rs")] -use crate::data::Data; #[cfg(feature = "napi-rs")] use crate::data_js::get_data; use crate::library_types::{ItemId, Library, SpecialTrackListName, TrackList, VersionedLibrary}; use anyhow::{Context, Result, bail}; use linked_hash_map::LinkedHashMap; -#[cfg(feature = "napi-rs")] -use napi::Env; use serde_json::{Value, json}; use std::fs::File; #[cfg(feature = "napi-rs")] @@ -194,8 +191,8 @@ pub fn get_track_field_type(field: &str) -> Result { #[cfg(feature = "napi-rs")] #[napi(js_name = "get_genres")] #[allow(dead_code)] -pub fn get_genres(env: Env) -> Vec { - let data: &mut Data = get_data(&env); +pub fn get_genres() -> Vec { + let mut data = get_data(); let genres = data.library.get_genres(); genres.clone() } @@ -203,8 +200,8 @@ pub fn get_genres(env: Env) -> Vec { #[cfg(feature = "napi-rs")] #[napi(js_name = "get_artists")] #[allow(dead_code)] -pub fn get_artists(env: Env) -> Vec { - let data: &mut Data = get_data(&env); +pub fn get_artists() -> Vec { + let mut data = get_data(); let genres = data.library.get_artists(); genres.clone() } diff --git a/src-native/page.rs b/src-native/page.rs index 052197c..6acd77d 100644 --- a/src-native/page.rs +++ b/src-native/page.rs @@ -1,12 +1,10 @@ #[cfg(feature = "napi-rs")] -use crate::data::Data; -#[cfg(feature = "napi-rs")] use crate::data_js::get_data; use crate::filter::filter; use crate::library_types::{ItemId, Library, TrackList}; use crate::sort::sort; #[cfg(feature = "napi-rs")] -use napi::{Env, Result}; +use napi::Result; use serde::{Deserialize, Serialize}; use specta::Type; @@ -33,8 +31,8 @@ pub struct TracksPage { #[cfg(feature = "napi-rs")] #[cfg_attr(feature = "napi", napi(js_name = "get_tracks_page"))] #[allow(dead_code)] -pub fn get_tracks_page(options: TracksPageOptions, env: Env) -> Result { - let data: &mut Data = get_data(&env); +pub fn get_tracks_page(options: TracksPageOptions) -> Result { + let data = get_data(); Ok(get_tracks_page_from_library(options, &data.library)?) } diff --git a/src-native/playlists.rs b/src-native/playlists.rs index f64caea..d164e88 100644 --- a/src-native/playlists.rs +++ b/src-native/playlists.rs @@ -25,8 +25,8 @@ pub struct TrackListDetails { #[napi(js_name = "get_track_lists_details")] #[allow(dead_code)] -pub fn get_track_lists_details(env: Env) -> HashMap { - let data: &Data = get_data(&env); +pub fn get_track_lists_details() -> HashMap { + let data = get_data(); data.library .trackLists .iter() @@ -59,7 +59,7 @@ pub fn get_track_lists_details(env: Env) -> HashMap { #[napi(js_name = "get_track_list", ts_return_type = "TrackList")] #[allow(dead_code)] pub fn get_track_list<'a>(id: String, env: Env) -> Result> { - let data: &mut Data = get_data(&env); + let data = get_data(); let tracklist = data.library.get_tracklist(&id)?; Ok(env.to_js_value(&tracklist)?) } @@ -122,8 +122,8 @@ fn remove_child_id(library: &mut Library, parent_id: &String, child_id: &String) /// Returns the deleted track lists, including folder children #[napi(js_name = "delete_track_list")] #[allow(dead_code)] -pub fn delete_track_list(id: String, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn delete_track_list(id: String) -> Result<()> { + let mut data = get_data(); let parent_id = data.library.get_parent_id(&id).context("No parent found")?; let mut ids = HashSet::new(); @@ -142,8 +142,8 @@ pub fn delete_track_list(id: String, env: Env) -> Result<()> { #[napi(js_name = "add_tracks_to_playlist")] #[allow(dead_code)] -pub fn add_tracks(playlist_id: String, track_ids: Vec, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn add_tracks(playlist_id: String, track_ids: Vec) -> Result<()> { + let mut data = get_data(); let playlist = match data.library.get_tracklist_mut(&playlist_id)? { TrackList::Playlist(playlist) => playlist, TrackList::Folder(_) => bail!("Cannot add track to folder"), @@ -156,8 +156,8 @@ pub fn add_tracks(playlist_id: String, track_ids: Vec, env: Env) -> Resu #[napi(js_name = "playlist_filter_duplicates")] #[allow(dead_code)] -pub fn filter_duplicates(playlist_id: TrackID, ids: Vec, env: Env) -> Result> { - let data: &mut Data = get_data(&env); +pub fn filter_duplicates(playlist_id: TrackID, ids: Vec) -> Result> { + let mut data = get_data(); let mut track_ids: HashSet = HashSet::from_iter(ids); let playlist = match data.library.get_tracklist_mut(&playlist_id)? { TrackList::Playlist(playlist) => playlist, @@ -174,8 +174,8 @@ pub fn filter_duplicates(playlist_id: TrackID, ids: Vec, env: Env) -> Re #[napi(js_name = "get_track_playlist_ids")] #[allow(dead_code)] -pub fn get_track_playlist_ids(track_id: TrackID, env: Env) -> Result> { - let data: &Data = get_data(&env); +pub fn get_track_playlist_ids(track_id: TrackID) -> Result> { + let data = get_data(); Ok(get_track_playlist_ids_in_library(&data.library, &track_id)) } @@ -202,8 +202,8 @@ pub fn get_track_playlist_ids_in_library(library: &Library, track_id: &str) -> V #[napi(js_name = "remove_from_playlist")] #[allow(dead_code)] -pub fn remove_from_playlist(playlist_id: TrackID, item_ids: Vec, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn remove_from_playlist(playlist_id: TrackID, item_ids: Vec) -> Result<()> { + let mut data = get_data(); let playlist = match data.library.get_tracklist_mut(&playlist_id)? { TrackList::Playlist(playlist) => playlist, _ => bail!("Cannot remove track from non-playlist"), @@ -248,8 +248,8 @@ pub fn delete_file(path: &PathBuf) -> Result<()> { #[napi(js_name = "delete_tracks_with_item_ids")] #[allow(dead_code)] -pub fn delete_tracks_with_item_ids(item_ids: Vec, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn delete_tracks_with_item_ids(item_ids: Vec) -> Result<()> { + let data = &mut *get_data(); let library = &mut data.library; let track_ids = get_track_ids_from_item_ids(&item_ids); for track_id in &track_ids { @@ -266,9 +266,8 @@ pub fn new_playlist( description: String, is_folder: bool, parent_id: String, - env: Env, ) -> Result<()> { - let data: &mut Data = get_data(&env); + let mut data = get_data(); let library = &mut data.library; let list = match is_folder { @@ -306,8 +305,8 @@ pub fn new_playlist( #[napi(js_name = "update_playlist")] #[allow(dead_code)] -pub fn update_playlist(id: String, name: String, description: String, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn update_playlist(id: String, name: String, description: String) -> Result<()> { + let mut data = get_data(); match data.library.trackLists.get_mut(&id) { Some(TrackList::Special(_)) => bail!("Cannot edit special playlists"), @@ -364,14 +363,8 @@ fn get_children_if_user_editable<'a>( #[napi(js_name = "move_playlist")] #[allow(dead_code)] -pub fn move_playlist( - id: String, - from_id: String, - to_id: String, - to_index: u32, - env: Env, -) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn move_playlist(id: String, from_id: String, to_id: String, to_index: u32) -> Result<()> { + let mut data = get_data(); match data.library.trackLists.get(&id) { Some(TrackList::Special(_)) => bail!("Cannot move special playlist"), @@ -412,13 +405,8 @@ pub fn move_playlist( #[napi(js_name = "move_tracks")] #[allow(dead_code)] -pub fn move_tracks( - playlist_id: String, - mut item_ids: Vec, - to_index: u32, - env: Env, -) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn move_tracks(playlist_id: String, mut item_ids: Vec, to_index: u32) -> Result<()> { + let mut data = get_data(); let playlist = match data.library.get_tracklist_mut(&playlist_id)? { TrackList::Playlist(playlist) => playlist, _ => bail!("Cannot rearrange tracks in non-playlist"), diff --git a/src-native/tracks/mod.rs b/src-native/tracks/mod.rs index d52428b..fad9068 100644 --- a/src-native/tracks/mod.rs +++ b/src-native/tracks/mod.rs @@ -1,10 +1,8 @@ -use crate::data::Data; use crate::data_js::get_data; use crate::get_now_timestamp; use crate::library::Paths; use crate::library_types::{ItemId, MsSinceUnixEpoch, TRACK_ID_MAP, Track, TrackID}; use anyhow::{Context, Result, bail}; -use napi::Env; use napi::bindgen_prelude::{ArrayBuffer, Buffer}; use std::fs; use std::path::Path; @@ -16,16 +14,10 @@ mod tag; pub use tag::Tag; -fn id_to_track<'a>(env: &'a Env, id: &String) -> Result<&'a mut Track> { - let data: &mut Data = get_data(env); - let track = data.library.get_track_mut(id)?; - return Ok(track); -} - #[napi(js_name = "get_track")] #[allow(dead_code)] -pub fn get_track(id: String, env: Env) -> Result { - let data: &mut Data = get_data(&env); +pub fn get_track(id: String) -> Result { + let data = get_data(); let track = data.library.get_track(&id)?; Ok(track.clone()) } @@ -38,8 +30,8 @@ pub struct KeyedTrack { #[napi(js_name = "get_track_by_item_id")] #[allow(dead_code)] -pub fn get_track_by_item_id(item_id: ItemId, env: Env) -> Result { - let data: &mut Data = get_data(&env); +pub fn get_track_by_item_id(item_id: ItemId) -> Result { + let data = get_data(); let id_map = TRACK_ID_MAP.read().unwrap(); let track_id = &id_map[item_id as usize]; let track = data.library.get_track(&track_id)?; @@ -62,16 +54,17 @@ pub fn get_track_ids(item_ids: Vec) -> Vec { #[napi(js_name = "track_exists")] #[allow(dead_code)] -pub fn track_exists(id: String, env: Env) -> bool { - let data: &mut Data = get_data(&env); +pub fn track_exists(id: String) -> bool { + let data = get_data(); let tracks = &data.library.get_tracks(); tracks.contains_key(&id) } #[napi(js_name = "add_play")] #[allow(dead_code)] -pub fn add_play(track_id: String, env: Env) -> Result<()> { - let track = id_to_track(&env, &track_id)?; +pub fn add_play(track_id: String) -> Result<()> { + let mut data = get_data(); + let track = data.library.get_track_mut(&track_id)?; let timestamp = get_now_timestamp(); match &mut track.plays { None => track.plays = Some(vec![timestamp]), @@ -86,8 +79,9 @@ pub fn add_play(track_id: String, env: Env) -> Result<()> { #[napi(js_name = "add_skip")] #[allow(dead_code)] -pub fn add_skip(track_id: String, env: Env) -> Result<()> { - let track = id_to_track(&env, &track_id)?; +pub fn add_skip(track_id: String) -> Result<()> { + let mut data = get_data(); + let track = data.library.get_track_mut(&track_id)?; let timestamp = get_now_timestamp(); match &mut track.skips { None => track.skips = Some(vec![timestamp]), @@ -102,8 +96,8 @@ pub fn add_skip(track_id: String, env: Env) -> Result<()> { #[napi(js_name = "add_play_time")] #[allow(dead_code)] -pub fn add_play_time(id: TrackID, start: MsSinceUnixEpoch, dur_ms: i64, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn add_play_time(id: TrackID, start: MsSinceUnixEpoch, dur_ms: i64) -> Result<()> { + let mut data = get_data(); let tracks = data.library.get_tracks(); tracks.get(&id).context("Track ID not found")?; data.library.playTime.push((id, start, dur_ms)); @@ -150,8 +144,8 @@ pub fn generate_filename(paths: &Paths, artist: &str, title: &str, ext: &str) -> #[napi(js_name = "import_file")] #[allow(dead_code)] -pub fn import_file(path: String, now: MsSinceUnixEpoch, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn import_file(path: String, now: MsSinceUnixEpoch) -> Result<()> { + let mut data = get_data(); let id = data.library.generate_id(); let track = import::import(&data, Path::new(&path), now)?; data.library.insert_track(id, track); @@ -160,10 +154,13 @@ pub fn import_file(path: String, now: MsSinceUnixEpoch, env: Env) -> Result<()> #[napi(js_name = "load_tags")] #[allow(dead_code)] -pub fn load_tags(track_id: String, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn load_tags(track_id: String) -> Result<()> { + let data = &mut *get_data(); data.current_tag = None; - let track = id_to_track(&env, &track_id).context("Could not load tags")?; + let track = data + .library + .get_track_mut(&track_id) + .context("Could not load tags")?; let path = data.paths.get_track_file_path(&track.file); let tag = Tag::read_from_path(&path).context("Could not load tags")?; @@ -181,8 +178,8 @@ pub struct JsImage { #[napi(js_name = "get_image")] #[allow(dead_code)] -pub fn get_image(index: u32, env: Env) -> Result> { - let data: &Data = get_data(&env); +pub fn get_image(index: u32) -> Result> { + let data = get_data(); let tag = match &data.current_tag { Some(tag) => tag, @@ -207,8 +204,8 @@ pub fn get_image(index: u32, env: Env) -> Result> { #[napi(js_name = "set_image")] #[allow(dead_code)] -pub fn set_image(index: u32, path: String, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn set_image(index: u32, path: String) -> Result<()> { + let mut data = get_data(); let tag = match &mut data.current_tag { Some(tag) => tag, None => bail!("No tag loaded"), @@ -220,8 +217,8 @@ pub fn set_image(index: u32, path: String, env: Env) -> Result<()> { #[napi(js_name = "set_image_data")] #[allow(dead_code)] -pub fn set_image_data(index: u32, bytes: ArrayBuffer, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); +pub fn set_image_data(index: u32, bytes: ArrayBuffer) -> Result<()> { + let mut data = get_data(); let tag = match &mut data.current_tag { Some(tag) => tag, None => bail!("No tag loaded"), @@ -232,8 +229,8 @@ pub fn set_image_data(index: u32, bytes: ArrayBuffer, env: Env) -> Result<()> { #[napi(js_name = "remove_image")] #[allow(dead_code)] -pub fn remove_image(index: u32, env: Env) -> () { - let data: &mut Data = get_data(&env); +pub fn remove_image(index: u32) -> () { + let mut data = get_data(); match data.current_tag { Some(ref mut tag) => { tag.remove_image(index as usize); @@ -244,9 +241,9 @@ pub fn remove_image(index: u32, env: Env) -> () { #[napi(js_name = "update_track_info")] #[allow(dead_code)] -pub fn update_track_info(track_id: String, info: md::TrackMD, env: Env) -> Result<()> { - let data: &mut Data = get_data(&env); - let track = id_to_track(&env, &track_id)?; +pub fn update_track_info(track_id: String, info: md::TrackMD) -> Result<()> { + let data = &mut *get_data(); + let track = data.library.get_track_mut(&track_id)?; let tag = match &mut data.current_tag { Some(tag) => tag, diff --git a/src-native/view_options.rs b/src-native/view_options.rs index 099171d..d03a9e7 100644 --- a/src-native/view_options.rs +++ b/src-native/view_options.rs @@ -1,11 +1,9 @@ -use crate::data::Data; use crate::data_js::get_data; use crate::library::Paths; use crate::path_to_json; use anyhow::{Context, Result}; use atomicwrites::AtomicFile; use atomicwrites::OverwriteBehavior::AllowOverwrite; -use napi::Env; use serde::{Deserialize, Serialize}; use std::io::Write; @@ -43,8 +41,8 @@ impl ViewOptions { #[napi(js_name = "load_view_options")] #[allow(dead_code)] -pub fn load_view_options(env: Env) -> Result { - let data: &Data = get_data(&env); +pub fn load_view_options() -> Result { + let data = get_data(); Ok(ViewOptions::load(&data.paths)) } #[napi(js_name = "save_view_options")]