Implemented writing songs to playlist

This commit is contained in:
Antoine Gersant 2017-07-09 23:09:37 -07:00
parent 09b7eca2b5
commit 729bf7a653
3 changed files with 170 additions and 28 deletions

View file

@ -11,7 +11,6 @@ CREATE TABLE playlist_songs (
playlist INTEGER NOT NULL,
path TEXT NOT NULL,
ordering INTEGER NOT NULL,
FOREIGN KEY(path) REFERENCES songs(path),
FOREIGN KEY(playlist) REFERENCES playlists(id) ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE(playlist, ordering) ON CONFLICT REPLACE
);

Binary file not shown.

View file

@ -2,9 +2,11 @@ use core::ops::Deref;
use diesel;
use diesel::prelude::*;
use diesel::BelongingToDsl;
use std::path::Path;
use db::{self, ConnectionSource};
use db::{playlists, users};
use db::{playlists, playlist_songs, songs, users};
use index::Song;
use vfs::VFSSource;
use errors::*;
@ -17,18 +19,33 @@ struct NewPlaylist {
#[derive(Identifiable, Queryable)]
pub struct User {
id: i32,
id: i32,
name: String,
}
#[derive(Identifiable, Queryable, Associations)]
#[belongs_to(User, foreign_key="owner")]
struct Playlist {
pub struct Playlist {
id: i32,
owner: i32,
name: String,
}
struct PlaylistSong {
#[derive(Identifiable, Queryable, Associations)]
#[belongs_to(Playlist, foreign_key="playlist")]
pub struct PlaylistSong {
id: i32,
playlist: i32,
path: String,
ordering: i32,
}
#[derive(Insertable)]
#[table_name="playlist_songs"]
pub struct NewPlaylistSong {
playlist: i32,
path: String,
ordering: i32,
}
fn list_playlists<T>(owner: &str, db: &T) -> Result<Vec<String>>
@ -38,41 +55,138 @@ fn list_playlists<T>(owner: &str, db: &T) -> Result<Vec<String>>
let connection = connection.lock().unwrap();
let connection = connection.deref();
let user : User;
let user: User;
{
use self::users::dsl::*;
user = users.filter(name.eq(owner)).select((id, name)).first(connection)?;
use self::users::dsl::*;
user = users
.filter(name.eq(owner))
.select((id, name))
.first(connection)?;
}
{
use self::playlists::dsl::*;
let found_playlists : Vec<String> = Playlist::belonging_to(&user).select(name).load(connection)?;
let found_playlists: Vec<String> = Playlist::belonging_to(&user)
.select(name)
.load(connection)?;
Ok(found_playlists)
}
}
fn save_playlist<T>(name: &str, owner: &str, content: &Vec<PlaylistSong>, db: &T) -> Result<()>
fn save_playlist<T>(name: &str, owner: &str, content: &Vec<String>, db: &T) -> Result<()>
where T: ConnectionSource + VFSSource
{
let connection = db.get_connection();
let connection = connection.lock().unwrap();
let connection = connection.deref();
// TODO transaction for content delete+add
let user: User;
let new_playlist: NewPlaylist;
let playlist: Playlist;
let new_playlist = NewPlaylist {
name: name.into(),
owner: users::table
.filter(users::columns::name.eq(owner))
.select(users::columns::id)
.get_result(connection)?,
};
{
let connection = db.get_connection();
let connection = connection.lock().unwrap();
let connection = connection.deref();
diesel::insert(&new_playlist)
// Find owner
{
use self::users::dsl::*;
user = users
.filter(name.eq(owner))
.select((id, name))
.get_result(connection)?;
}
// Create playlist
new_playlist = NewPlaylist {
name: name.into(),
owner: user.id,
};
diesel::insert(&new_playlist)
.into(playlists::table)
.execute(connection)?;
{
use self::playlists::dsl::*;
playlist = playlists
.filter(name.eq(name).and(owner.eq(user.id)))
.get_result(connection)?;
}
// Delete old content (if any)
let old_songs = PlaylistSong::belonging_to(&playlist);
diesel::delete(old_songs).execute(connection)?;
}
// Insert content
let vfs = db.get_vfs()?;
let mut new_songs: Vec<NewPlaylistSong> = Vec::new();
new_songs.reserve(content.len());
for (i, path) in content.iter().enumerate() {
let virtual_path = Path::new(&path);
if let Some(real_path) = vfs.virtual_to_real(virtual_path)
.ok()
.and_then(|p| p.to_str().map(|s| s.to_owned())) {
new_songs.push(NewPlaylistSong {
playlist: playlist.id,
path: real_path.into(),
ordering: i as i32,
});
}
}
{
let connection = db.get_connection();
let connection = connection.lock().unwrap();
let connection = connection.deref();
diesel::insert(&new_songs)
.into(playlist_songs::table)
.execute(connection)?;
}
Ok(())
}
fn read_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<Vec<Song>>
where T: ConnectionSource + VFSSource
{
let user: User;
let playlist: Playlist;
let playlist_songs: Vec<PlaylistSong>;
let vfs = db.get_vfs()?;
{
let connection = db.get_connection();
let connection = connection.lock().unwrap();
let connection = connection.deref();
// Find owner
{
use self::users::dsl::*;
user = users
.filter(name.eq(owner))
.select((id, name))
.get_result(connection)?;
}
// Find playlist
{
use self::playlists::dsl::*;
playlist = playlists
.filter(name.eq(playlist_name).and(owner.eq(user.id)))
.get_result(connection)?;
}
// Find content
playlist_songs = PlaylistSong::belonging_to(&playlist)
.order(playlist_songs::columns::ordering)
.get_results(connection)?;
}
// TODO
Ok(Vec::new())
}
fn delete_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<()>
where T: ConnectionSource + VFSSource
{
@ -80,29 +194,32 @@ fn delete_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<()>
let connection = connection.lock().unwrap();
let connection = connection.deref();
let user : User;
let user: User;
{
use self::users::dsl::*;
user = users.filter(name.eq(owner)).select((id, name)).first(connection)?;
use self::users::dsl::*;
user = users
.filter(name.eq(owner))
.select((id, name))
.first(connection)?;
}
{
use self::playlists::dsl::*;
let q = Playlist::belonging_to(&user).filter(name.eq(playlist_name));
diesel::delete(q).execute(connection)?;
}
Ok(())
}
#[test]
fn test_create_playlist() {
let db = db::_get_test_db("create_playlist.sqlite");
let playlist_content = Vec::new();
let found_playlists = list_playlists("test_user", &db).unwrap();
assert!(found_playlists.is_empty());
save_playlist("chill_and_grill", "test_user", &playlist_content, &db).unwrap();
save_playlist("chill_and_grill", "test_user", &Vec::new(), &db).unwrap();
let found_playlists = list_playlists("test_user", &db).unwrap();
assert_eq!(found_playlists.len(), 1);
assert_eq!(found_playlists[0], "chill_and_grill");
@ -129,3 +246,29 @@ fn test_delete_playlist() {
let delete_result = delete_playlist("mellow_bungalow", "someone_else", &db);
assert!(delete_result.is_err());
}
#[test]
fn test_fill_playlist() {
use index;
let db = db::_get_test_db("fill_playlist.sqlite");
index::update(&db).unwrap();
let mut playlist_content: Vec<String> = index::flatten(&db, Path::new("root"))
.unwrap()
.into_iter()
.map(|s| s.path)
.collect();
assert_eq!(playlist_content.len(), 12);
let first_song = playlist_content[0].clone();
playlist_content.push(first_song);
assert_eq!(playlist_content.len(), 13);
save_playlist("all_the_music", "test_user", &playlist_content, &db).unwrap();
// let songs = read_playlist("all_the_music", "test_user", &db).unwrap();
// assert_eq!(songs.len(), 13);
// assert_eq!(songs[0].title, Some("Above The Water".to_owned()));
// assert_eq!(songs[12].title, Some("Above The Water".to_owned()));
}