Organization
This commit is contained in:
parent
b014c63af4
commit
e6483cf138
4 changed files with 262 additions and 246 deletions
|
@ -1,5 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
path::{Path, PathBuf},
|
path::PathBuf,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,9 +14,11 @@ use crate::db::DB;
|
||||||
mod browser;
|
mod browser;
|
||||||
mod collection;
|
mod collection;
|
||||||
mod search;
|
mod search;
|
||||||
|
mod storage;
|
||||||
|
|
||||||
pub use browser::File;
|
pub use browser::File;
|
||||||
pub use collection::{Album, AlbumKey, Artist, ArtistKey, Song, SongKey};
|
pub use collection::{Album, Artist, Song};
|
||||||
|
use storage::{AlbumKey, ArtistKey, InternPath, SongKey};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
|
@ -179,12 +181,10 @@ impl Manager {
|
||||||
let index_manager = self.clone();
|
let index_manager = self.clone();
|
||||||
move || {
|
move || {
|
||||||
let index = index_manager.index.read().unwrap();
|
let index = index_manager.index.read().unwrap();
|
||||||
let Some(path_id) = virtual_path.get(&index.strings) else {
|
let Some(virtual_path) = virtual_path.get(&index.strings) else {
|
||||||
return Err(Error::SongNotFound);
|
return Err(Error::SongNotFound);
|
||||||
};
|
};
|
||||||
let song_key = SongKey {
|
let song_key = SongKey { virtual_path };
|
||||||
virtual_path: path_id,
|
|
||||||
};
|
|
||||||
index
|
index
|
||||||
.collection
|
.collection
|
||||||
.get_song(&index.strings, song_key)
|
.get_song(&index.strings, song_key)
|
||||||
|
@ -229,7 +229,7 @@ impl Builder {
|
||||||
|
|
||||||
pub fn add_song(&mut self, song: scanner::Song) {
|
pub fn add_song(&mut self, song: scanner::Song) {
|
||||||
self.browser_builder.add_song(&mut self.strings, &song);
|
self.browser_builder.add_song(&mut self.strings, &song);
|
||||||
self.collection_builder.add_song(&mut self.strings, song);
|
self.collection_builder.add_song(&mut self.strings, &song);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Index {
|
pub fn build(self) -> Index {
|
||||||
|
@ -246,39 +246,3 @@ impl Default for Builder {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
|
||||||
pub(self) struct PathID(lasso2::Spur);
|
|
||||||
|
|
||||||
pub(self) trait InternPath {
|
|
||||||
fn get_or_intern(self, strings: &mut ThreadedRodeo) -> Option<PathID>;
|
|
||||||
fn get(self, strings: &ThreadedRodeo) -> Option<PathID>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: AsRef<Path>> InternPath for P {
|
|
||||||
fn get_or_intern(self, strings: &mut ThreadedRodeo) -> Option<PathID> {
|
|
||||||
let id = self
|
|
||||||
.as_ref()
|
|
||||||
.as_os_str()
|
|
||||||
.to_str()
|
|
||||||
.map(|s| strings.get_or_intern(s))
|
|
||||||
.map(PathID);
|
|
||||||
if id.is_none() {
|
|
||||||
error!("Unsupported path: `{}`", self.as_ref().to_string_lossy());
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(self, strings: &ThreadedRodeo) -> Option<PathID> {
|
|
||||||
let id = self
|
|
||||||
.as_ref()
|
|
||||||
.as_os_str()
|
|
||||||
.to_str()
|
|
||||||
.and_then(|s| strings.get(s))
|
|
||||||
.map(PathID);
|
|
||||||
if id.is_none() {
|
|
||||||
error!("Unsupported path: `{}`", self.as_ref().to_string_lossy());
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,10 @@ use serde::{Deserialize, Serialize};
|
||||||
use tinyvec::TinyVec;
|
use tinyvec::TinyVec;
|
||||||
use trie_rs::{Trie, TrieBuilder};
|
use trie_rs::{Trie, TrieBuilder};
|
||||||
|
|
||||||
use crate::app::index::{InternPath, PathID};
|
use crate::app::index::{
|
||||||
|
storage::{self, PathKey},
|
||||||
|
InternPath,
|
||||||
|
};
|
||||||
use crate::app::{scanner, Error};
|
use crate::app::{scanner, Error};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
@ -21,7 +24,7 @@ pub enum File {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Browser {
|
pub struct Browser {
|
||||||
directories: HashMap<PathID, BTreeSet<storage::File>>,
|
directories: HashMap<PathKey, BTreeSet<storage::File>>,
|
||||||
flattened: Trie<lasso2::Spur>,
|
flattened: Trie<lasso2::Spur>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,23 +43,23 @@ impl Browser {
|
||||||
strings: &ThreadedRodeo,
|
strings: &ThreadedRodeo,
|
||||||
virtual_path: P,
|
virtual_path: P,
|
||||||
) -> Result<Vec<File>, Error> {
|
) -> Result<Vec<File>, Error> {
|
||||||
let path_id = virtual_path
|
let path = virtual_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.get(strings)
|
.get(strings)
|
||||||
.ok_or_else(|| Error::DirectoryNotFound(virtual_path.as_ref().to_owned()))?;
|
.ok_or_else(|| Error::DirectoryNotFound(virtual_path.as_ref().to_owned()))?;
|
||||||
|
|
||||||
let Some(files) = self.directories.get(&path_id) else {
|
let Some(files) = self.directories.get(&path) else {
|
||||||
return Err(Error::DirectoryNotFound(virtual_path.as_ref().to_owned()));
|
return Err(Error::DirectoryNotFound(virtual_path.as_ref().to_owned()));
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(files
|
Ok(files
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| {
|
.map(|f| {
|
||||||
let path_id = match f {
|
let path = match f {
|
||||||
storage::File::Directory(p) => p,
|
storage::File::Directory(p) => p,
|
||||||
storage::File::Song(p) => p,
|
storage::File::Song(p) => p,
|
||||||
};
|
};
|
||||||
let path = Path::new(OsStr::new(strings.resolve(&path_id.0))).to_owned();
|
let path = Path::new(OsStr::new(strings.resolve(&path.0))).to_owned();
|
||||||
match f {
|
match f {
|
||||||
storage::File::Directory(_) => File::Directory(path),
|
storage::File::Directory(_) => File::Directory(path),
|
||||||
storage::File::Song(_) => File::Song(path),
|
storage::File::Song(_) => File::Song(path),
|
||||||
|
@ -97,44 +100,44 @@ impl Browser {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
directories: HashMap<PathID, BTreeSet<storage::File>>,
|
directories: HashMap<PathKey, BTreeSet<storage::File>>,
|
||||||
flattened: TrieBuilder<lasso2::Spur>,
|
flattened: TrieBuilder<lasso2::Spur>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
pub fn add_directory(&mut self, strings: &mut ThreadedRodeo, directory: scanner::Directory) {
|
pub fn add_directory(&mut self, strings: &mut ThreadedRodeo, directory: scanner::Directory) {
|
||||||
let Some(path_id) = directory.virtual_path.get_or_intern(strings) else {
|
let Some(virtual_path) = directory.virtual_path.get_or_intern(strings) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(parent_id) = directory
|
let Some(virtual_parent) = directory
|
||||||
.virtual_parent
|
.virtual_parent
|
||||||
.and_then(|p| p.get_or_intern(strings))
|
.and_then(|p| p.get_or_intern(strings))
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.directories.entry(path_id.clone()).or_default();
|
self.directories.entry(virtual_path).or_default();
|
||||||
|
|
||||||
self.directories
|
self.directories
|
||||||
.entry(parent_id)
|
.entry(virtual_parent)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(storage::File::Directory(path_id));
|
.insert(storage::File::Directory(virtual_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_song(&mut self, strings: &mut ThreadedRodeo, song: &scanner::Song) {
|
pub fn add_song(&mut self, strings: &mut ThreadedRodeo, song: &scanner::Song) {
|
||||||
let Some(path_id) = (&song.virtual_path).get_or_intern(strings) else {
|
let Some(virtual_path) = (&song.virtual_path).get_or_intern(strings) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(parent_id) = (&song.virtual_parent).get_or_intern(strings) else {
|
let Some(virtual_parent) = (&song.virtual_parent).get_or_intern(strings) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.directories
|
self.directories
|
||||||
.entry(parent_id)
|
.entry(virtual_parent)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(storage::File::Song(path_id));
|
.insert(storage::File::Song(virtual_path));
|
||||||
|
|
||||||
self.flattened.push(
|
self.flattened.push(
|
||||||
song.virtual_path
|
song.virtual_path
|
||||||
|
@ -152,16 +155,6 @@ impl Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod storage {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
||||||
pub enum File {
|
|
||||||
Directory(PathID),
|
|
||||||
Song(PathID),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
use std::{
|
use std::{
|
||||||
borrow::BorrowMut,
|
borrow::BorrowMut,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
hash::Hash,
|
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use lasso2::ThreadedRodeo;
|
use lasso2::ThreadedRodeo;
|
||||||
use rand::{rngs::ThreadRng, seq::IteratorRandom};
|
use rand::{rngs::ThreadRng, seq::IteratorRandom};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tinyvec::TinyVec;
|
|
||||||
|
|
||||||
use crate::app::index::{InternPath, PathID};
|
use crate::app::index::storage::{self, store_song, AlbumKey, ArtistKey, SongKey};
|
||||||
use crate::app::scanner;
|
use crate::app::scanner;
|
||||||
|
|
||||||
|
use super::storage::fetch_song;
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct Artist {
|
pub struct Artist {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
|
@ -148,49 +148,7 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_song(&self, strings: &ThreadedRodeo, song_key: SongKey) -> Option<Song> {
|
pub fn get_song(&self, strings: &ThreadedRodeo, song_key: SongKey) -> Option<Song> {
|
||||||
self.songs.get(&song_key).map(|s| Song {
|
self.songs.get(&song_key).map(|s| fetch_song(strings, s))
|
||||||
path: PathBuf::from(strings.resolve(&s.path.0)),
|
|
||||||
virtual_path: PathBuf::from(strings.resolve(&s.virtual_path.0)),
|
|
||||||
virtual_parent: PathBuf::from(strings.resolve(&s.virtual_parent.0)),
|
|
||||||
track_number: s.track_number,
|
|
||||||
disc_number: s.disc_number,
|
|
||||||
title: s.title.map(|s| strings.resolve(&s).to_string()),
|
|
||||||
artists: s
|
|
||||||
.artists
|
|
||||||
.iter()
|
|
||||||
.map(|s| strings.resolve(&s).to_string())
|
|
||||||
.collect(),
|
|
||||||
album_artists: s
|
|
||||||
.album_artists
|
|
||||||
.iter()
|
|
||||||
.map(|s| strings.resolve(&s).to_string())
|
|
||||||
.collect(),
|
|
||||||
year: s.year,
|
|
||||||
album: s.album.map(|s| strings.resolve(&s).to_string()),
|
|
||||||
artwork: s.artwork.map(|a| PathBuf::from(strings.resolve(&a.0))),
|
|
||||||
duration: s.duration,
|
|
||||||
lyricists: s
|
|
||||||
.lyricists
|
|
||||||
.iter()
|
|
||||||
.map(|s| strings.resolve(&s).to_string())
|
|
||||||
.collect(),
|
|
||||||
composers: s
|
|
||||||
.composers
|
|
||||||
.iter()
|
|
||||||
.map(|s| strings.resolve(&s).to_string())
|
|
||||||
.collect(),
|
|
||||||
genres: s
|
|
||||||
.genres
|
|
||||||
.iter()
|
|
||||||
.map(|s| strings.resolve(&s).to_string())
|
|
||||||
.collect(),
|
|
||||||
labels: s
|
|
||||||
.labels
|
|
||||||
.iter()
|
|
||||||
.map(|s| strings.resolve(&s).to_string())
|
|
||||||
.collect(),
|
|
||||||
date_added: s.date_added,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,73 +160,17 @@ pub struct Builder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
pub fn add_song(&mut self, strings: &mut ThreadedRodeo, song: scanner::Song) {
|
pub fn add_song(&mut self, strings: &mut ThreadedRodeo, song: &scanner::Song) {
|
||||||
let Some(path_id) = song.path.get_or_intern(strings) else {
|
let Some(song) = store_song(strings, song) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(virtual_path_id) = song.virtual_path.get_or_intern(strings) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(virtual_parent_id) = song.virtual_parent.get_or_intern(strings) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(artwork_id) = song.artwork.map(|s| s.get_or_intern(strings)) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let song = storage::Song {
|
|
||||||
path: path_id,
|
|
||||||
virtual_path: virtual_path_id,
|
|
||||||
virtual_parent: virtual_parent_id,
|
|
||||||
track_number: song.track_number,
|
|
||||||
disc_number: song.disc_number,
|
|
||||||
title: song.title.map(|s| strings.get_or_intern(s)),
|
|
||||||
artists: song
|
|
||||||
.artists
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| strings.get_or_intern(s))
|
|
||||||
.collect(),
|
|
||||||
album_artists: song
|
|
||||||
.album_artists
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| strings.get_or_intern(s))
|
|
||||||
.collect(),
|
|
||||||
year: song.year,
|
|
||||||
album: song.album.map(|s| strings.get_or_intern(s)),
|
|
||||||
artwork: artwork_id,
|
|
||||||
duration: song.duration,
|
|
||||||
lyricists: song
|
|
||||||
.lyricists
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| strings.get_or_intern(s))
|
|
||||||
.collect(),
|
|
||||||
composers: song
|
|
||||||
.composers
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| strings.get_or_intern(s))
|
|
||||||
.collect(),
|
|
||||||
genres: song
|
|
||||||
.genres
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| strings.get_or_intern(s))
|
|
||||||
.collect(),
|
|
||||||
labels: song
|
|
||||||
.labels
|
|
||||||
.into_iter()
|
|
||||||
.map(|s| strings.get_or_intern(s))
|
|
||||||
.collect(),
|
|
||||||
date_added: song.date_added,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.add_song_to_album(&song);
|
self.add_song_to_album(&song);
|
||||||
self.add_album_to_artists(&song);
|
self.add_album_to_artists(&song);
|
||||||
|
|
||||||
self.songs.insert(
|
self.songs.insert(
|
||||||
SongKey {
|
SongKey {
|
||||||
virtual_path: virtual_path_id,
|
virtual_path: song.virtual_path,
|
||||||
},
|
},
|
||||||
song,
|
song,
|
||||||
);
|
);
|
||||||
|
@ -351,78 +253,6 @@ impl Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod storage {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Artist {
|
|
||||||
pub name: Option<lasso2::Spur>,
|
|
||||||
pub albums: HashSet<AlbumKey>,
|
|
||||||
pub album_appearances: HashSet<AlbumKey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
|
||||||
pub struct Album {
|
|
||||||
pub name: Option<lasso2::Spur>,
|
|
||||||
pub artwork: Option<PathID>,
|
|
||||||
pub artists: Vec<lasso2::Spur>,
|
|
||||||
pub year: Option<i64>,
|
|
||||||
pub date_added: i64,
|
|
||||||
pub songs: HashSet<SongKey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
|
||||||
pub struct Song {
|
|
||||||
pub path: PathID,
|
|
||||||
pub virtual_path: PathID,
|
|
||||||
pub virtual_parent: PathID,
|
|
||||||
pub track_number: Option<i64>,
|
|
||||||
pub disc_number: Option<i64>,
|
|
||||||
pub title: Option<lasso2::Spur>,
|
|
||||||
pub artists: Vec<lasso2::Spur>,
|
|
||||||
pub album_artists: Vec<lasso2::Spur>,
|
|
||||||
pub year: Option<i64>,
|
|
||||||
pub album: Option<lasso2::Spur>,
|
|
||||||
pub artwork: Option<PathID>,
|
|
||||||
pub duration: Option<i64>,
|
|
||||||
pub lyricists: Vec<lasso2::Spur>,
|
|
||||||
pub composers: Vec<lasso2::Spur>,
|
|
||||||
pub genres: Vec<lasso2::Spur>,
|
|
||||||
pub labels: Vec<lasso2::Spur>,
|
|
||||||
pub date_added: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Song {
|
|
||||||
pub fn album_key(&self) -> AlbumKey {
|
|
||||||
let album_artists = match self.album_artists.is_empty() {
|
|
||||||
true => &self.artists,
|
|
||||||
false => &self.album_artists,
|
|
||||||
};
|
|
||||||
|
|
||||||
AlbumKey {
|
|
||||||
artists: album_artists.iter().cloned().collect(),
|
|
||||||
name: self.album.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct ArtistKey {
|
|
||||||
pub name: Option<lasso2::Spur>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct AlbumKey {
|
|
||||||
pub artists: TinyVec<[lasso2::Spur; 4]>,
|
|
||||||
pub name: Option<lasso2::Spur>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct SongKey {
|
|
||||||
pub virtual_path: PathID,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
|
229
src/app/index/storage.rs
Normal file
229
src/app/index/storage.rs
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use lasso2::ThreadedRodeo;
|
||||||
|
use log::error;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tinyvec::TinyVec;
|
||||||
|
|
||||||
|
use crate::app::scanner;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
pub enum File {
|
||||||
|
Directory(PathKey),
|
||||||
|
Song(PathKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Artist {
|
||||||
|
pub name: Option<lasso2::Spur>,
|
||||||
|
pub albums: HashSet<AlbumKey>,
|
||||||
|
pub album_appearances: HashSet<AlbumKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||||
|
pub struct Album {
|
||||||
|
pub name: Option<lasso2::Spur>,
|
||||||
|
pub artwork: Option<PathKey>,
|
||||||
|
pub artists: Vec<lasso2::Spur>,
|
||||||
|
pub year: Option<i64>,
|
||||||
|
pub date_added: i64,
|
||||||
|
pub songs: HashSet<SongKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Song {
|
||||||
|
pub path: PathKey,
|
||||||
|
pub virtual_path: PathKey,
|
||||||
|
pub virtual_parent: PathKey,
|
||||||
|
pub track_number: Option<i64>,
|
||||||
|
pub disc_number: Option<i64>,
|
||||||
|
pub title: Option<lasso2::Spur>,
|
||||||
|
pub artists: Vec<lasso2::Spur>,
|
||||||
|
pub album_artists: Vec<lasso2::Spur>,
|
||||||
|
pub year: Option<i64>,
|
||||||
|
pub album: Option<lasso2::Spur>,
|
||||||
|
pub artwork: Option<PathKey>,
|
||||||
|
pub duration: Option<i64>,
|
||||||
|
pub lyricists: Vec<lasso2::Spur>,
|
||||||
|
pub composers: Vec<lasso2::Spur>,
|
||||||
|
pub genres: Vec<lasso2::Spur>,
|
||||||
|
pub labels: Vec<lasso2::Spur>,
|
||||||
|
pub date_added: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct PathKey(pub lasso2::Spur);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct ArtistKey {
|
||||||
|
pub name: Option<lasso2::Spur>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct AlbumKey {
|
||||||
|
pub artists: TinyVec<[lasso2::Spur; 4]>,
|
||||||
|
pub name: Option<lasso2::Spur>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct SongKey {
|
||||||
|
pub virtual_path: PathKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Song {
|
||||||
|
pub fn album_key(&self) -> AlbumKey {
|
||||||
|
let album_artists = match self.album_artists.is_empty() {
|
||||||
|
true => &self.artists,
|
||||||
|
false => &self.album_artists,
|
||||||
|
};
|
||||||
|
|
||||||
|
AlbumKey {
|
||||||
|
artists: album_artists.iter().cloned().collect(),
|
||||||
|
name: self.album.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_song(strings: &mut ThreadedRodeo, song: &scanner::Song) -> Option<Song> {
|
||||||
|
let Some(path) = (&song.path).get_or_intern(strings) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(virtual_path) = (&song.virtual_path).get_or_intern(strings) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(virtual_parent) = (&song.virtual_parent).get_or_intern(strings) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(artwork) = song.artwork.as_ref().map(|s| s.get_or_intern(strings)) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Song {
|
||||||
|
path,
|
||||||
|
virtual_path,
|
||||||
|
virtual_parent,
|
||||||
|
track_number: song.track_number,
|
||||||
|
disc_number: song.disc_number,
|
||||||
|
title: song.title.as_ref().map(|s| strings.get_or_intern(s)),
|
||||||
|
artists: song
|
||||||
|
.artists
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.get_or_intern(s))
|
||||||
|
.collect(),
|
||||||
|
album_artists: song
|
||||||
|
.album_artists
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.get_or_intern(s))
|
||||||
|
.collect(),
|
||||||
|
year: song.year,
|
||||||
|
album: song.album.as_ref().map(|s| strings.get_or_intern(s)),
|
||||||
|
artwork: artwork,
|
||||||
|
duration: song.duration,
|
||||||
|
lyricists: song
|
||||||
|
.lyricists
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.get_or_intern(s))
|
||||||
|
.collect(),
|
||||||
|
composers: song
|
||||||
|
.composers
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.get_or_intern(s))
|
||||||
|
.collect(),
|
||||||
|
genres: song
|
||||||
|
.genres
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.get_or_intern(s))
|
||||||
|
.collect(),
|
||||||
|
labels: song
|
||||||
|
.labels
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.get_or_intern(s))
|
||||||
|
.collect(),
|
||||||
|
date_added: song.date_added,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_song(strings: &ThreadedRodeo, song: &Song) -> super::Song {
|
||||||
|
super::Song {
|
||||||
|
path: PathBuf::from(strings.resolve(&song.path.0)),
|
||||||
|
virtual_path: PathBuf::from(strings.resolve(&song.virtual_path.0)),
|
||||||
|
virtual_parent: PathBuf::from(strings.resolve(&song.virtual_parent.0)),
|
||||||
|
track_number: song.track_number,
|
||||||
|
disc_number: song.disc_number,
|
||||||
|
title: song.title.map(|s| strings.resolve(&s).to_string()),
|
||||||
|
artists: song
|
||||||
|
.artists
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.resolve(&s).to_string())
|
||||||
|
.collect(),
|
||||||
|
album_artists: song
|
||||||
|
.album_artists
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.resolve(&s).to_string())
|
||||||
|
.collect(),
|
||||||
|
year: song.year,
|
||||||
|
album: song.album.map(|s| strings.resolve(&s).to_string()),
|
||||||
|
artwork: song.artwork.map(|a| PathBuf::from(strings.resolve(&a.0))),
|
||||||
|
duration: song.duration,
|
||||||
|
lyricists: song
|
||||||
|
.lyricists
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.resolve(&s).to_string())
|
||||||
|
.collect(),
|
||||||
|
composers: song
|
||||||
|
.composers
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.resolve(&s).to_string())
|
||||||
|
.collect(),
|
||||||
|
genres: song
|
||||||
|
.genres
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.resolve(&s).to_string())
|
||||||
|
.collect(),
|
||||||
|
labels: song
|
||||||
|
.labels
|
||||||
|
.iter()
|
||||||
|
.map(|s| strings.resolve(&s).to_string())
|
||||||
|
.collect(),
|
||||||
|
date_added: song.date_added,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InternPath {
|
||||||
|
fn get_or_intern(self, strings: &mut ThreadedRodeo) -> Option<PathKey>;
|
||||||
|
fn get(self, strings: &ThreadedRodeo) -> Option<PathKey>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: AsRef<Path>> InternPath for P {
|
||||||
|
fn get_or_intern(self, strings: &mut ThreadedRodeo) -> Option<PathKey> {
|
||||||
|
let id = self
|
||||||
|
.as_ref()
|
||||||
|
.as_os_str()
|
||||||
|
.to_str()
|
||||||
|
.map(|s| strings.get_or_intern(s))
|
||||||
|
.map(PathKey);
|
||||||
|
if id.is_none() {
|
||||||
|
error!("Unsupported path: `{}`", self.as_ref().to_string_lossy());
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(self, strings: &ThreadedRodeo) -> Option<PathKey> {
|
||||||
|
let id = self
|
||||||
|
.as_ref()
|
||||||
|
.as_os_str()
|
||||||
|
.to_str()
|
||||||
|
.and_then(|s| strings.get(s))
|
||||||
|
.map(PathKey);
|
||||||
|
if id.is_none() {
|
||||||
|
error!("Unsupported path: `{}`", self.as_ref().to_string_lossy());
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue