Commit 6dfa05cb authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Add playlists support

parent 67a5fd3c
......@@ -97,6 +97,13 @@ class IMediaLibrary
virtual ArtistPtr artist( unsigned int id ) = 0;
virtual std::vector<ArtistPtr> artists() const = 0;
/***
* Playlists
*/
virtual PlaylistPtr createPlaylist( const std::string& name ) = 0;
virtual std::vector<PlaylistPtr> playlists() = 0;
virtual bool deletePlaylist( unsigned int playlistId ) = 0;
/**
* @brief discover Launch a discovery on the provided entry point.
* The actuall discovery will run asynchronously, meaning this method will immediatly return.
......
/*****************************************************************************
* Media Library
*****************************************************************************
* Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
*
* Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#pragma once
#include <vector>
#include "Types.h"
class IPlaylist
{
public:
virtual ~IPlaylist() = default;
virtual unsigned int id() const = 0;
virtual const std::string& name() const = 0;
virtual bool setName( const std::string& name ) = 0;
virtual std::vector<MediaPtr> media() const = 0;
///
/// \brief append Appends a media to a playlist
/// The media will be the last element of a subsequent call to media()
/// This is equivalent to calling add( media, 0 )
/// \param media The media to add
/// \return true on success, false on failure.
///
virtual bool append( unsigned int mediaId ) = 0;
///
/// \brief add Add a media to the playlist at the given position.
/// Valid positions start at 1. 0 means appending.
/// \param media The media to add
/// \param position The position of this new media
/// \return true on success, false on failure
///
virtual bool add( unsigned int mediaId, unsigned int position ) = 0;
///
/// \brief move Change the position of a media
/// \param mediaId The media to move reorder
/// \param position The new position within the playlist.
/// 0 is an invalid value when moving.
/// In case there is already a media at the given position, it will be placed after
/// the media being moved. This will cascade to any media placed afterward.
/// For instance, a playlist with <media,position> like
/// [<1,1>, <2,2>, <3,3>] on which move(1, 2) is called will result in the playlist
/// being changed to
/// [<1,2>, <2,3>, <3,4>]
/// \return true on success, false on failure
///
virtual bool move( unsigned int mediaId, unsigned int position ) = 0;
///
/// \brief remove Removes a media from the playlist
/// \param mediaId The media to remove.
/// \return true on success, false on failure
///
virtual bool remove( unsigned int mediaId ) = 0;
};
......@@ -39,6 +39,7 @@ class IShowEpisode;
class IVideoTrack;
class ILogger;
class IArtist;
class IPlaylist;
class SqliteConnection;
typedef std::shared_ptr<IMedia> MediaPtr;
......@@ -52,6 +53,7 @@ typedef std::shared_ptr<IMovie> MoviePtr;
typedef std::shared_ptr<IAudioTrack> AudioTrackPtr;
typedef std::shared_ptr<IVideoTrack> VideoTrackPtr;
typedef std::shared_ptr<IArtist> ArtistPtr;
typedef std::shared_ptr<IPlaylist> PlaylistPtr;
typedef SqliteConnection* DBConnection;
......
......@@ -34,6 +34,7 @@ list(APPEND HEADERS_LIST
${CMAKE_SOURCE_DIR}/include/factory/IFileSystem.h
${CMAKE_SOURCE_DIR}/include/ILogger.h
${CMAKE_SOURCE_DIR}/include/IArtist.h
${CMAKE_SOURCE_DIR}/include/IPlaylist.h
database/SqliteTools.h
......@@ -67,6 +68,7 @@ list(APPEND SRC_LIST ${HEADERS_LIST}
Settings.cpp
Device.cpp
File.cpp
Playlist.cpp
factory/FileSystem.cpp
filesystem/common/CommonFile.cpp
......
......@@ -40,6 +40,7 @@
#include "logging/Logger.h"
#include "Movie.h"
#include "Parser.h"
#include "Playlist.h"
#include "Show.h"
#include "ShowEpisode.h"
#include "database/SqliteTools.h"
......@@ -103,6 +104,7 @@ MediaLibrary::~MediaLibrary()
Artist::clear();
Device::clear();
File::clear();
Playlist::clear();
// Explicitely release the connection's TLS
if ( m_dbConnection != nullptr )
m_dbConnection->release();
......@@ -122,6 +124,7 @@ bool MediaLibrary::createAllTables()
Media::createTable( m_dbConnection.get() ) &&
File::createTable( m_dbConnection.get() ) &&
Label::createTable( m_dbConnection.get() ) &&
Playlist::createTable( m_dbConnection.get() ) &&
Album::createTable( m_dbConnection.get() ) &&
AlbumTrack::createTable( m_dbConnection.get() ) &&
Album::createTriggers( m_dbConnection.get() ) &&
......@@ -134,6 +137,7 @@ bool MediaLibrary::createAllTables()
Artist::createDefaultArtists( m_dbConnection.get() ) &&
Artist::createTriggers( m_dbConnection.get() ) &&
Media::createTriggers( m_dbConnection.get() ) &&
Playlist::createTriggers( m_dbConnection.get() ) &&
Settings::createTable( m_dbConnection.get() );
if ( res == false )
return false;
......@@ -346,6 +350,21 @@ std::vector<ArtistPtr> MediaLibrary::artists() const
return Artist::fetchAll<IArtist>( m_dbConnection.get(), req );
}
PlaylistPtr MediaLibrary::createPlaylist( const std::string& name )
{
return Playlist::create( m_dbConnection.get(), name );
}
std::vector<PlaylistPtr> MediaLibrary::playlists()
{
return Playlist::fetchAll<IPlaylist>( m_dbConnection.get() );
}
bool MediaLibrary::deletePlaylist( unsigned int playlistId )
{
return Playlist::destroy( m_dbConnection.get(), playlistId );
}
void MediaLibrary::addMetadataService(std::unique_ptr<IMetadataService> service)
{
if ( service->initialize( m_parser.get(), this ) == false )
......
......@@ -77,6 +77,10 @@ class MediaLibrary : public IMediaLibrary
std::shared_ptr<Artist> createArtist( const std::string& name );
virtual std::vector<ArtistPtr> artists() const override;
virtual PlaylistPtr createPlaylist( const std::string& name ) override;
virtual std::vector<PlaylistPtr> playlists() override;
virtual bool deletePlaylist( unsigned int playlistId ) override;
virtual void discover( const std::string& entryPoint ) override;
bool ignoreFolder( const std::string& path ) override;
......
/*****************************************************************************
* Media Library
*****************************************************************************
* Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
*
* Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "Playlist.h"
#include "Media.h"
namespace policy
{
const std::string PlaylistTable::Name = "Playlist";
const std::string PlaylistTable::PrimaryKeyColumn = "id_playlist";
unsigned int Playlist::* const PlaylistTable::PrimaryKey = &Playlist::m_id;
}
Playlist::Playlist( DBConnection dbConn, sqlite::Row& row )
: m_dbConnection( dbConn )
{
row >> m_id
>> m_name;
}
Playlist::Playlist( const std::string& name )
: m_id( 0 )
, m_name( name )
{
}
std::shared_ptr<Playlist> Playlist::create( DBConnection dbConn, const std::string& name )
{
auto self = std::make_shared<Playlist>( name );
static const std::string req = "INSERT INTO " + policy::PlaylistTable::Name + "(name) VALUES(?)";
if ( insert( dbConn, self, req, name ) == false )
return nullptr;
self->m_dbConnection = dbConn;
return self;
}
unsigned int Playlist::id() const
{
return m_id;
}
const std::string& Playlist::name() const
{
return m_name;
}
bool Playlist::setName( const std::string& name )
{
if ( name == m_name )
return true;
static const std::string req = "UPDATE " + policy::PlaylistTable::Name + " SET name = ? WHERE id_playlist = ?";
if ( sqlite::Tools::executeUpdate( m_dbConnection, req, name, m_id ) == false )
return false;
m_name = name;
return true;
}
std::vector<MediaPtr> Playlist::media() const
{
static const std::string req = "SELECT m.* FROM " + policy::MediaTable::Name + " m "
"LEFT JOIN PlaylistMediaRelation pmr ON pmr.media_id = m.id_media "
"WHERE pmr.playlist_id = ? AND m.is_present = 1 "
"ORDER BY pmr.position";
return Media::fetchAll<IMedia>( m_dbConnection, req, m_id );
}
bool Playlist::append( unsigned int mediaId )
{
return add( mediaId, 0 );
}
bool Playlist::add( unsigned int mediaId, unsigned int position )
{
static const std::string req = "INSERT INTO PlaylistMediaRelation(media_id, playlist_id, position) VALUES(?, ?, ?)";
// position isn't a foreign key, but we want it to be passed as NULL if it equals to 0
// When the position is NULL, the insertion triggers takes care of counting the number of records to auto append.
return sqlite::Tools::insert( m_dbConnection, req, mediaId, m_id, sqlite::ForeignKey{ position } );
}
bool Playlist::move( unsigned int mediaId, unsigned int position )
{
if ( position == 0 )
return false;
static const std::string req = "UPDATE PlaylistMediaRelation SET position = ? WHERE "
"playlist_id = ? AND media_id = ?";
return sqlite::Tools::executeUpdate( m_dbConnection, req, position, m_id, mediaId );
}
bool Playlist::remove( unsigned int mediaId )
{
static const std::string req = "DELETE FROM PlaylistMediaRelation WHERE playlist_id = ? AND media_id = ?";
return sqlite::Tools::executeDelete( m_dbConnection, req, m_id, mediaId );
}
bool Playlist::createTable( DBConnection dbConn )
{
static const std::string req = "CREATE TABLE IF NOT EXISTS " + policy::PlaylistTable::Name + "("
+ policy::PlaylistTable::PrimaryKeyColumn + " INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT"
")";
static const std::string relTableReq = "CREATE TABLE IF NOT EXISTS PlaylistMediaRelation("
"media_id INTEGER,"
"playlist_id INTEGER,"
"position INTEGER,"
"PRIMARY KEY(media_id, playlist_id),"
"FOREIGN KEY(media_id) REFERENCES " + policy::MediaTable::Name + "("
+ policy::MediaTable::PrimaryKeyColumn + ") ON DELETE CASCADE,"
"FOREIGN KEY(playlist_id) REFERENCES " + policy::PlaylistTable::Name + "("
+ policy::PlaylistTable::PrimaryKeyColumn + ") ON DELETE CASCADE"
")";
//FIXME Enforce (playlist_id,position) uniqueness
return sqlite::Tools::executeRequest( dbConn, req ) &&
sqlite::Tools::executeRequest( dbConn, relTableReq );
}
bool Playlist::createTriggers( DBConnection dbConn )
{
static const std::string req = "CREATE TRIGGER IF NOT EXISTS update_playlist_order AFTER UPDATE OF position"
" ON PlaylistMediaRelation"
" BEGIN "
"UPDATE PlaylistMediaRelation SET position = position + 1"
" WHERE playlist_id = new.playlist_id"
" AND position = new.position"
// We don't to trigger a self-update when the insert trigger fires.
" AND media_id != new.media_id;"
" END";
static const std::string autoAppendReq = "CREATE TRIGGER IF NOT EXISTS append_new_playlist_record AFTER INSERT"
" ON PlaylistMediaRelation"
" WHEN new.position IS NULL"
" BEGIN "
" UPDATE PlaylistMediaRelation SET position = ("
"SELECT COUNT(media_id) FROM PlaylistMediaRelation WHERE playlist_id = new.playlist_id"
") WHERE playlist_id=new.playlist_id AND media_id = new.media_id;"
" END";
static const std::string autoShiftPosReq = "CREATE TRIGGER IF NOT EXISTS update_playlist_order_on_insert AFTER INSERT"
" ON PlaylistMediaRelation"
" WHEN new.position IS NOT NULL"
" BEGIN "
"UPDATE PlaylistMediaRelation SET position = position + 1"
" WHERE playlist_id = new.playlist_id"
" AND position = new.position"
" AND media_id != new.media_id;"
" END";
return sqlite::Tools::executeRequest( dbConn, req ) &&
sqlite::Tools::executeRequest( dbConn, autoAppendReq ) &&
sqlite::Tools::executeRequest( dbConn, autoShiftPosReq );
}
/*****************************************************************************
* Media Library
*****************************************************************************
* Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
*
* Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#pragma once
#include "IPlaylist.h"
#include "database/SqliteTools.h"
#include "database/DatabaseHelpers.h"
#include "utils/Cache.h"
class Playlist;
namespace policy
{
struct PlaylistTable
{
static const std::string Name;
static const std::string PrimaryKeyColumn;
static unsigned int Playlist::*const PrimaryKey;
};
}
class Playlist : public IPlaylist, public DatabaseHelpers<Playlist, policy::PlaylistTable>
{
public:
Playlist( DBConnection dbConn, sqlite::Row& row );
Playlist( const std::string& name );
static std::shared_ptr<Playlist> create( DBConnection dbConn, const std::string& name );
virtual unsigned int id() const override;
virtual const std::string& name() const override;
virtual bool setName( const std::string& name ) override;
virtual std::vector<MediaPtr> media() const override;
virtual bool append( unsigned int mediaId ) override;
virtual bool add( unsigned int mediaId, unsigned int position ) override;
virtual bool move( unsigned int mediaId, unsigned int position ) override;
virtual bool remove( unsigned int mediaId ) override;
static bool createTable( DBConnection dbConn );
static bool createTriggers( DBConnection dbConn );
private:
DBConnection m_dbConnection;
unsigned int m_id;
std::string m_name;
friend class policy::PlaylistTable;
};
......@@ -50,6 +50,8 @@ sqlite3 *SqliteConnection::getConn()
lock.unlock();
if ( sqlite::Tools::executeRequestLocked( this, "PRAGMA foreign_keys = ON" ) == false )
throw std::runtime_error( "Failed to enable foreign keys" );
if ( sqlite::Tools::executeRequestLocked( this, "PRAGMA recursive_triggers = ON" ) == false )
throw std::runtime_error( "Failed to enable recursive triggers" );
return dbConnection;
}
return it->second.get();
......
/*****************************************************************************
* Media Library
*****************************************************************************
* Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
*
* Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "Tests.h"
#include "Playlist.h"
#include "Media.h"
class Playlists : public Tests
{
protected:
std::shared_ptr<Playlist> pl;
std::shared_ptr<Media> m;
virtual void SetUp() override
{
Tests::SetUp();
pl = std::static_pointer_cast<Playlist>( ml->createPlaylist( "test playlist" ) );
}
};
TEST_F( Playlists, Create )
{
ASSERT_NE( nullptr, pl );
ASSERT_NE( 0u, pl->id() );
ASSERT_EQ( "test playlist", pl->name() );
}
TEST_F( Playlists, Fetch )
{
auto pl2 = ml->playlist( pl->id() );
ASSERT_NE( nullptr, pl2 );
ASSERT_EQ( pl->id(), pl2->id() );
auto playlists = ml->playlists();
ASSERT_EQ( 1u, playlists.size() );
ASSERT_EQ( pl->id(), playlists[0]->id() );
}
TEST_F( Playlists, DeletePlaylist )
{
auto res = ml->deletePlaylist( pl->id() );
ASSERT_TRUE( res );
auto playlists = ml->playlists();
ASSERT_EQ( 0u, playlists.size() );
}
TEST_F( Playlists, SetName )
{
ASSERT_EQ( "test playlist", pl->name() );
auto newName = "new name";
auto res = pl->setName( newName );
ASSERT_TRUE( res );
ASSERT_EQ( newName, pl->name() );
Reload();
pl = ml->playlist( pl->id() );
ASSERT_EQ( newName, pl->name() );
}
TEST_F( Playlists, FetchAll )
{
pl->setName( "pl 1" );
ml->createPlaylist( "pl 2" );
ml->createPlaylist( "pl 3" );
ml->createPlaylist( "pl 4" );
auto playlists = ml->playlists();
ASSERT_EQ( 4u, playlists.size() );
for ( auto& p : playlists )
{
auto name = std::string{ "pl " } + std::to_string( p->id() );
ASSERT_EQ( name, p->name() );
}
}
TEST_F( Playlists, Add )
{
auto m = ml->addFile( "file.mkv" );
auto res = pl->append( m->id() );
ASSERT_TRUE( res );
auto media = pl->media();
ASSERT_EQ( 1u, media.size() );
ASSERT_EQ( m->id(), media[0]->id() );
}
TEST_F( Playlists, Append )
{
for ( auto i = 0; i < 5; ++i )
{
auto m = ml->addFile( "media" + std::to_string( i ) + ".mkv" );
ASSERT_NE( nullptr, m );
pl->append( m->id() );
}
auto media = pl->media();
ASSERT_EQ( 5u, media.size() );
for ( auto i = 0u; i < media.size(); ++i )
{
auto name = "media" + std::to_string( i ) + ".mkv";
ASSERT_EQ( media[i]->title(), name );
}
}
TEST_F( Playlists, Insert )
{
for ( auto i = 1; i < 4; ++i )
{
auto m = ml->addFile( "media" + std::to_string( i ) + ".mkv" );
ASSERT_NE( nullptr, m );
auto res = pl->append( m->id() );
ASSERT_TRUE( res );
}
// [<1,1>,<2,2>,<3,3>]
auto firstMedia = ml->addFile( "first.mkv" );
pl->add( firstMedia->id(), 1 );
// [<4,1>,<1,2>,<2,3>,<3,4>]
auto middleMedia = ml->addFile( "middle.mkv" );
pl->add( middleMedia->id(), 3 );
// [<4,1>,<1,2>,<5,3>,<2,4>,<3,5>]
auto media = pl->media();
ASSERT_EQ( 5u, media.size() );
ASSERT_EQ( 4u, media[0]->id() );
ASSERT_EQ( 1u, media[1]->id() );
ASSERT_EQ( 5u, media[2]->id() );
ASSERT_EQ( 2u, media[3]->id() );
ASSERT_EQ( 3u, media[4]->id() );
}
TEST_F( Playlists, Move )
{
for ( auto i = 1; i < 6; ++i )
{
auto m = ml->addFile( "media" + std::to_string( i ) + ".mkv" );
ASSERT_NE( nullptr, m );
auto res = pl->append( m->id() );
ASSERT_TRUE( res );
}
// [<1,1>,<2,2>,<3,3>,<4,4>,<5,5>]
pl->move( 5, 1 );
// [<5,1>,<1,2>,<2,3>,<3,4>,<4,5>]
auto media = pl->media();
ASSERT_EQ( 5u, media.size() );
ASSERT_EQ( 5u, media[0]->id() );
ASSERT_EQ( 1u, media[1]->id() );
ASSERT_EQ( 2u, media[2]->id() );
ASSERT_EQ( 3u, media[3]->id() );
ASSERT_EQ( 4u, media[4]->id() );
}
TEST_F( Playlists, Remove )
{
for ( auto i = 1; i < 6; ++i )
{
auto m = ml->addFile( "media" + std::to_string( i ) + ".mkv" );
ASSERT_NE( nullptr, m );
auto res = pl->append( m->id() );
ASSERT_TRUE( res );
}
// [<1,1>,<2,2>,<3,3>,<4,4>,<5,5>]
auto media = pl->media();
ASSERT_EQ( 5u, media.size() );
pl->remove( 3 );
// [<1,1>,<2,2>,<4,4>,<5,5>]
media = pl->media();
ASSERT_EQ( 4u, media.size() );
ASSERT_EQ( 1u, media[0]->id() );
ASSERT_EQ( 2u, media[1]->id() );
ASSERT_EQ( 4u, media[2]->id() );
ASSERT_EQ( 5u, media[3]->id() );
}
TEST_F( Playlists, DeleteFile )
{
for ( auto i = 1; i < 6; ++i )
{
auto m = ml->addFile( "media" + std::to_string( i ) + ".mkv" );
ASSERT_NE( nullptr, m );
auto res = pl->append( m->id() );
ASSERT_TRUE( res );
}
// [<1,1>,<2,2>,<3,3>,<4,4>,<5,5>]
auto media = pl->media();
ASSERT_EQ( 5u, media.size() );
auto m = std::static_pointer_cast<Media>( media[2] );
auto fs = m->files();
ASSERT_EQ( 1u, fs.size() );
m->removeFile( static_cast<File&>( *fs[0] ) );
// This should trigger the Media removal, which should in
// turn trigger the playlist item removal
// So we should now have
// [<1,1>,<2,2>,<4,4>,<5,5>]
media = pl->media();
ASSERT_EQ( 4u, media.size() );
ASSERT_EQ( 1u, media[0]->id() );
ASSERT_EQ( 2u, media[1]->id() );
ASSERT_EQ( 4u, media[2]->id() );
ASSERT_EQ( 5u, media[3]->id() );
// Ensure we don't delete an empty playlist:
auto ms = ml->files();
for ( auto &mptr : ms )
{
auto m = std::static_pointer_cast<Media>( mptr );
auto fs = m->files();
ASSERT_EQ( 1u, fs.size() );
m->removeFile( static_cast<File&>( *fs[0] ) );
}
media = pl->media();
ASSERT_EQ( 0u, media.size() );
pl = ml->playlist( pl->id() );
ASSERT_NE( nullptr, pl );
}
......@@ -32,6 +32,7 @@
#include "mocks/FileSystem.h"
#include "Media.h"
#include "Folder.h"
#include "Playlist.h"
class TestEnv : public ::testing::Environment
{
......@@ -118,3 +119,8 @@ std::shared_ptr<Media> MediaLibraryTester::addFile( const std::string& path )
{
return MediaLibrary::addFile( path, dummyFolder, *dummyDirectory );
}
std::shared_ptr<Playlist> MediaLibraryTester::playlist(unsigned int playlistId)
{
return Playlist::fetch( m_dbConnection.get(), playlistId );
}
......@@ -22,6 +22,8 @@
#include "gtest/gtest.h"
class Playlist;
#include "factory/IFileSystem.h"
#include "MediaLibrary.h"
#include "Folder.h"
......@@ -34,6 +36,7 @@ public:
MediaPtr media( const std::string& path );
std::shared_ptr<Folder> folder( const std::string& path );
std::shared_ptr<Media> addFile( const std::string& path );
std::shared_ptr<Playlist> playlist( unsigned int playlistId );
private:
std::unique_ptr<fs::IDirectory> dummyDirectory;
......
......@@ -13,6 +13,7 @@ list(APPEND TEST_SRCS
unittest/AlbumTrackTests.cpp
unittest/DeviceTests.cpp
unittest/FileTests.cpp
unittest/PlaylistTests.cpp
mocks/FileSystem.h
mocks/FileSystem.cpp
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment