mirror of
https://github.com/agersant/polaris
synced 2024-12-11 22:03:11 +00:00
Implemented writing songs to playlist
This commit is contained in:
parent
09b7eca2b5
commit
729bf7a653
3 changed files with 170 additions and 28 deletions
|
@ -11,7 +11,6 @@ CREATE TABLE playlist_songs (
|
||||||
playlist INTEGER NOT NULL,
|
playlist INTEGER NOT NULL,
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
ordering INTEGER NOT NULL,
|
ordering INTEGER NOT NULL,
|
||||||
FOREIGN KEY(path) REFERENCES songs(path),
|
|
||||||
FOREIGN KEY(playlist) REFERENCES playlists(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
FOREIGN KEY(playlist) REFERENCES playlists(id) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
UNIQUE(playlist, ordering) ON CONFLICT REPLACE
|
UNIQUE(playlist, ordering) ON CONFLICT REPLACE
|
||||||
);
|
);
|
||||||
|
|
Binary file not shown.
197
src/playlist.rs
197
src/playlist.rs
|
@ -2,9 +2,11 @@ use core::ops::Deref;
|
||||||
use diesel;
|
use diesel;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::BelongingToDsl;
|
use diesel::BelongingToDsl;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use db::{self, ConnectionSource};
|
use db::{self, ConnectionSource};
|
||||||
use db::{playlists, users};
|
use db::{playlists, playlist_songs, songs, users};
|
||||||
|
use index::Song;
|
||||||
use vfs::VFSSource;
|
use vfs::VFSSource;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
|
@ -17,18 +19,33 @@ struct NewPlaylist {
|
||||||
|
|
||||||
#[derive(Identifiable, Queryable)]
|
#[derive(Identifiable, Queryable)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
id: i32,
|
id: i32,
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Identifiable, Queryable, Associations)]
|
#[derive(Identifiable, Queryable, Associations)]
|
||||||
#[belongs_to(User, foreign_key="owner")]
|
#[belongs_to(User, foreign_key="owner")]
|
||||||
struct Playlist {
|
pub struct Playlist {
|
||||||
id: i32,
|
id: i32,
|
||||||
owner: 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>>
|
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.lock().unwrap();
|
||||||
let connection = connection.deref();
|
let connection = connection.deref();
|
||||||
|
|
||||||
let user : User;
|
let user: User;
|
||||||
{
|
{
|
||||||
use self::users::dsl::*;
|
use self::users::dsl::*;
|
||||||
user = users.filter(name.eq(owner)).select((id, name)).first(connection)?;
|
user = users
|
||||||
|
.filter(name.eq(owner))
|
||||||
|
.select((id, name))
|
||||||
|
.first(connection)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
use self::playlists::dsl::*;
|
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)
|
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
|
where T: ConnectionSource + VFSSource
|
||||||
{
|
{
|
||||||
let connection = db.get_connection();
|
// TODO transaction for content delete+add
|
||||||
let connection = connection.lock().unwrap();
|
let user: User;
|
||||||
let connection = connection.deref();
|
let new_playlist: NewPlaylist;
|
||||||
|
let playlist: Playlist;
|
||||||
|
|
||||||
let new_playlist = NewPlaylist {
|
{
|
||||||
name: name.into(),
|
let connection = db.get_connection();
|
||||||
owner: users::table
|
let connection = connection.lock().unwrap();
|
||||||
.filter(users::columns::name.eq(owner))
|
let connection = connection.deref();
|
||||||
.select(users::columns::id)
|
|
||||||
.get_result(connection)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
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)
|
.into(playlists::table)
|
||||||
.execute(connection)?;
|
.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(())
|
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<()>
|
fn delete_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<()>
|
||||||
where T: ConnectionSource + VFSSource
|
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.lock().unwrap();
|
||||||
let connection = connection.deref();
|
let connection = connection.deref();
|
||||||
|
|
||||||
let user : User;
|
let user: User;
|
||||||
{
|
{
|
||||||
use self::users::dsl::*;
|
use self::users::dsl::*;
|
||||||
user = users.filter(name.eq(owner)).select((id, name)).first(connection)?;
|
user = users
|
||||||
|
.filter(name.eq(owner))
|
||||||
|
.select((id, name))
|
||||||
|
.first(connection)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
use self::playlists::dsl::*;
|
use self::playlists::dsl::*;
|
||||||
let q = Playlist::belonging_to(&user).filter(name.eq(playlist_name));
|
let q = Playlist::belonging_to(&user).filter(name.eq(playlist_name));
|
||||||
diesel::delete(q).execute(connection)?;
|
diesel::delete(q).execute(connection)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_playlist() {
|
fn test_create_playlist() {
|
||||||
let db = db::_get_test_db("create_playlist.sqlite");
|
let db = db::_get_test_db("create_playlist.sqlite");
|
||||||
let playlist_content = Vec::new();
|
|
||||||
|
|
||||||
let found_playlists = list_playlists("test_user", &db).unwrap();
|
let found_playlists = list_playlists("test_user", &db).unwrap();
|
||||||
assert!(found_playlists.is_empty());
|
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();
|
let found_playlists = list_playlists("test_user", &db).unwrap();
|
||||||
assert_eq!(found_playlists.len(), 1);
|
assert_eq!(found_playlists.len(), 1);
|
||||||
assert_eq!(found_playlists[0], "chill_and_grill");
|
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);
|
let delete_result = delete_playlist("mellow_bungalow", "someone_else", &db);
|
||||||
assert!(delete_result.is_err());
|
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()));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue