read_playlist now uses a SQL join
This commit is contained in:
parent
9d0ecc5531
commit
488ef5169e
2 changed files with 25 additions and 41 deletions
|
@ -34,7 +34,7 @@ pub enum Command {
|
||||||
REINDEX,
|
REINDEX,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Queryable, Serialize)]
|
#[derive(Debug, Queryable, Serialize)]
|
||||||
pub struct Song {
|
pub struct Song {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
id: i32,
|
id: i32,
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
use core::clone::Clone;
|
use core::clone::Clone;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use diesel;
|
use diesel;
|
||||||
|
use diesel::expression::sql;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::BelongingToDsl;
|
use diesel::BelongingToDsl;
|
||||||
use std::collections::HashMap;
|
use diesel::types::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use db;
|
use db;
|
||||||
use db::ConnectionSource;
|
use db::ConnectionSource;
|
||||||
use db::{playlists, playlist_songs, songs, users};
|
use db::{playlists, playlist_songs, users};
|
||||||
use index::Song;
|
use index::Song;
|
||||||
use vfs::VFSSource;
|
use vfs::VFSSource;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
const PLAYLIST_CHUNK: usize = 500;
|
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name="playlists"]
|
#[table_name="playlists"]
|
||||||
struct NewPlaylist {
|
struct NewPlaylist {
|
||||||
|
@ -148,15 +147,13 @@ fn save_playlist<T>(name: &str, owner: &str, content: &Vec<String>, db: &T) -> R
|
||||||
fn read_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<Vec<Song>>
|
fn read_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<Vec<Song>>
|
||||||
where T: ConnectionSource + VFSSource
|
where T: ConnectionSource + VFSSource
|
||||||
{
|
{
|
||||||
let user: User;
|
|
||||||
let playlist: Playlist;
|
|
||||||
let song_paths: Vec<String>;
|
|
||||||
let unique_songs: Vec<Song>;
|
|
||||||
let vfs = db.get_vfs()?;
|
let vfs = db.get_vfs()?;
|
||||||
let mut songs_map: HashMap<String, Song> = HashMap::new();
|
let songs: Vec<Song>;
|
||||||
|
|
||||||
{
|
{
|
||||||
let connection = db.get_connection();
|
let connection = db.get_connection();
|
||||||
|
let user: User;
|
||||||
|
let playlist: Playlist;
|
||||||
|
|
||||||
// Find owner
|
// Find owner
|
||||||
{
|
{
|
||||||
|
@ -175,45 +172,32 @@ fn read_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<Vec<Song
|
||||||
.get_result(connection.deref())?;
|
.get_result(connection.deref())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find playlist songs
|
// Select songs. Not using Diesel because we need to LEFT JOIN using a custom column
|
||||||
// TODO If we could do a outer join with the songs table
|
let query = sql::<(Integer, Text, Text, Nullable<Integer>, Nullable<Integer>, Nullable<Text>, Nullable<Text>, Nullable<Text>, Nullable<Integer>, Nullable<Text>, Nullable<Text>)>(r#"
|
||||||
// using the path column, most of this function would go
|
SELECT s.id, s.path, s.parent, s.track_number, s.disc_number, s.title, s.artist, s.album_artist, s.year, s.album, s.artwork
|
||||||
// away.
|
FROM playlist_songs ps
|
||||||
song_paths = PlaylistSong::belonging_to(&playlist)
|
LEFT JOIN songs s ON ps.path = s.path
|
||||||
.select(playlist_songs::columns::path)
|
WHERE ps.playlist = ?
|
||||||
.order(playlist_songs::columns::ordering)
|
ORDER BY ps.ordering
|
||||||
.get_results(connection.deref())?;
|
"#);
|
||||||
|
let query = query.clone().bind::<Integer, _>(playlist.id);
|
||||||
// Find Song objects at the relevant paths
|
songs = query.get_results(connection.deref())?;
|
||||||
{
|
|
||||||
use self::songs::dsl::*;
|
|
||||||
for chunk in song_paths[..].chunks(PLAYLIST_CHUNK) {
|
|
||||||
let unique_songs: Vec<Song> = songs
|
|
||||||
.filter(path.eq_any(chunk))
|
|
||||||
.get_results(connection.deref())?;
|
|
||||||
for playlist_song in &unique_songs {
|
|
||||||
songs_map.insert(playlist_song.path.clone(), playlist_song.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build playlist
|
// Map real path to virtual paths
|
||||||
let mut playlist_songs = Vec::new();
|
let songs = songs.into_iter().filter_map(|mut s| {
|
||||||
for path in &song_paths {
|
let real_path = s.path.clone();
|
||||||
let song: &Song = songs_map.get(path.as_str()).unwrap();
|
|
||||||
let real_path = &song.path;
|
|
||||||
let mut song = (*song).clone();
|
|
||||||
let real_path = Path::new(&real_path);
|
let real_path = Path::new(&real_path);
|
||||||
if let Ok(virtual_path) = vfs.real_to_virtual(real_path) {
|
if let Ok(virtual_path) = vfs.real_to_virtual(real_path) {
|
||||||
if let Some(virtual_path) = virtual_path.to_str() {
|
if let Some(virtual_path) = virtual_path.to_str() {
|
||||||
song.path = virtual_path.to_owned();
|
s.path = virtual_path.to_owned();
|
||||||
playlist_songs.push(song);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return Some(s);
|
||||||
}
|
}
|
||||||
|
None
|
||||||
|
}).collect();
|
||||||
|
|
||||||
Ok(playlist_songs)
|
Ok(songs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<()>
|
fn delete_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<()>
|
||||||
|
|
Loading…
Add table
Reference in a new issue