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

AlbumTrack: Implement search

parent c2a61d6f
......@@ -112,6 +112,11 @@ class IMediaLibrary
virtual bool addToHistory( const std::string& mrl ) = 0;
virtual std::vector<HistoryPtr> history() const = 0;
/**
* Search
*/
virtual std::vector<MediaPtr> searchAlbumTracks( const std::string& title ) const = 0;
/**
* @brief discover Launch a discovery on the provided entry point.
* The actuall discovery will run asynchronously, meaning this method will immediatly return.
......
......@@ -169,7 +169,7 @@ std::shared_ptr<AlbumTrack> Album::addTrack( std::shared_ptr<Media> media, unsig
{
auto t = m_dbConnection->newTransaction();
auto track = AlbumTrack::create( m_dbConnection, m_id, media->id(), trackNb, discNumber );
auto track = AlbumTrack::create( m_dbConnection, m_id, *media, trackNb, discNumber );
if ( track == nullptr )
return nullptr;
media->setAlbumTrack( track );
......
......@@ -112,18 +112,37 @@ bool AlbumTrack::createTable( DBConnection dbConnection )
" BEGIN"
" UPDATE " + policy::AlbumTrackTable::Name + " SET is_present = new.is_present WHERE media_id = new.id_media;"
" END";
static const std::string vtableReq = "CREATE VIRTUAL TABLE " + policy::AlbumTrackTable::Name + "Fts USING FTS3("
"title"
")";
static const std::string vtableTrigger = "CREATE TRIGGER IF NOT EXISTS delete_fts_track"
" AFTER DELETE ON " + policy::MediaTable::Name +
" BEGIN"
" DELETE FROM " + policy::AlbumTrackTable::Name + "Fts WHERE rowid = old.id_media;"
" END";
static const std::string vtableTrigger2 = "CREATE TRIGGER IF NOT EXISTS update_fts_track"
" AFTER UPDATE OF title ON " + policy::MediaTable::Name +
" BEGIN"
" UPDATE " + policy::AlbumTrackTable::Name + "Fts SET title = new.title WHERE rowid = new.id_media;"
" END";
return sqlite::Tools::executeRequest( dbConnection, req ) &&
sqlite::Tools::executeRequest( dbConnection, triggerReq );
sqlite::Tools::executeRequest( dbConnection, triggerReq ) &&
sqlite::Tools::executeRequest( dbConnection, vtableReq ) &&
sqlite::Tools::executeRequest( dbConnection, vtableTrigger ) &&
sqlite::Tools::executeRequest( dbConnection, vtableTrigger2 );
}
std::shared_ptr<AlbumTrack> AlbumTrack::create( DBConnection dbConnection, unsigned int albumId, unsigned int mediaId, unsigned int trackNb, unsigned int discNumber )
std::shared_ptr<AlbumTrack> AlbumTrack::create( DBConnection dbConnection, unsigned int albumId, const Media& media, unsigned int trackNb, unsigned int discNumber )
{
auto self = std::make_shared<AlbumTrack>( mediaId, trackNb, albumId, discNumber );
auto self = std::make_shared<AlbumTrack>( media.id(), trackNb, albumId, discNumber );
static const std::string req = "INSERT INTO " + policy::AlbumTrackTable::Name
+ "(media_id, track_number, album_id, disc_number) VALUES(?, ?, ?, ?)";
if ( insert( dbConnection, self, req, mediaId, trackNb, albumId, discNumber ) == false )
if ( insert( dbConnection, self, req, media.id(), trackNb, albumId, discNumber ) == false )
return nullptr;
self->m_dbConnection = dbConnection;
static const std::string ftsReq = "INSERT INTO " + policy::AlbumTrackTable::Name + "Fts"
"(rowid, title) VALUES(?, ?)";
sqlite::Tools::insert( dbConnection, ftsReq, self->m_id, media.title() );
return self;
}
......@@ -141,6 +160,14 @@ std::vector<AlbumTrackPtr> AlbumTrack::fromGenre( DBConnection dbConn, unsigned
return fetchAll<IAlbumTrack>( dbConn, req, genreId );
}
std::vector<MediaPtr> AlbumTrack::search( DBConnection dbConn, const std::string& title )
{
static const std::string req = "SELECT * FROM " + policy::MediaTable::Name + " WHERE"
" id_media IN (SELECT rowid FROM " + policy::AlbumTrackTable::Name + "Fts"
" WHERE title MATCH ?)";
return Media::fetchAll<IMedia>( dbConn, req, title + "*" );
}
GenrePtr AlbumTrack::genre()
{
auto l = m_genre.lock();
......
......@@ -66,10 +66,11 @@ class AlbumTrack : public IAlbumTrack, public DatabaseHelpers<AlbumTrack, policy
virtual std::shared_ptr<IAlbum> album() override;
static bool createTable( DBConnection dbConnection );
static std::shared_ptr<AlbumTrack> create( DBConnection dbConnection, unsigned int albumId,
unsigned int mediaId, unsigned int trackNb , unsigned int discNumber );
static std::shared_ptr<AlbumTrack> create(DBConnection dbConnection, unsigned int albumId,
const Media& media, unsigned int trackNb , unsigned int discNumber );
static AlbumTrackPtr fromMedia( DBConnection dbConnection, unsigned int mediaId );
static std::vector<AlbumTrackPtr> fromGenre( DBConnection dbConn, unsigned int genreId );
static std::vector<MediaPtr> search(DBConnection dbConn, const std::string& title );
private:
DBConnection m_dbConnection;
......
......@@ -405,6 +405,11 @@ std::vector<HistoryPtr> MediaLibrary::history() const
return History::fetch( m_dbConnection.get() );
}
std::vector<MediaPtr> MediaLibrary::searchAlbumTracks( const std::string& title ) const
{
return AlbumTrack::search( m_dbConnection.get(), title );
}
void MediaLibrary::startParser()
{
m_parser.reset( new Parser( m_dbConnection.get(), this, m_callback ) );
......
......@@ -90,6 +90,8 @@ class MediaLibrary : public IMediaLibrary
virtual bool addToHistory( const std::string& mrl );
virtual std::vector<HistoryPtr> history() const;
virtual std::vector<MediaPtr> searchAlbumTracks( const std::string& title ) const override;
virtual void discover( const std::string& entryPoint ) override;
bool banFolder( const std::string& path ) override;
bool unbanFolder( const std::string& path ) override;
......
......@@ -112,3 +112,59 @@ TEST_F( AlbumTracks, SetGenre )
auto t2 = tracks[0];
ASSERT_EQ( t->genre()->id(), t2->albumTrack()->genre()->id() );
}
TEST_F( AlbumTracks, Search )
{
auto a = ml->createAlbum( "album" );
for ( auto i = 1u; i <= 10u; ++i )
{
auto m = ml->addFile( "track " + std::to_string( i ) + ".mp3" );
a->addTrack( m, i, 1 );
}
auto tracks = ml->searchAlbumTracks( "tra" );
ASSERT_EQ( 10u, tracks.size() );
tracks = ml->searchAlbumTracks( "1" );
ASSERT_EQ( 2u, tracks.size() );
tracks = ml->searchAlbumTracks( "grouik" );
ASSERT_EQ( 0u, tracks.size() );
tracks = ml->searchAlbumTracks( "rack" );
ASSERT_EQ( 0u, tracks.size() );
}
TEST_F( AlbumTracks, SearchAfterEdit )
{
auto m = ml->addFile( "media.mp3" );
auto a = ml->createAlbum( "album" );
a->addTrack( m, 1, 1 );
auto tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 1u, tracks.size() );
m->setTitle( "otters are awesome" );
m->save();
tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 0u, tracks.size() );
tracks = ml->searchAlbumTracks( "otters" );
ASSERT_EQ( 1u, tracks.size() );
}
TEST_F( AlbumTracks, SearchAfterDelete )
{
auto m = ml->addFile( "media.mp3" );
auto a = ml->createAlbum( "album" );
a->addTrack( m, 1, 1 );
auto tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 1u, tracks.size() );
auto f = m->files()[0];
m->removeFile( static_cast<File&>( *f ) );
tracks = ml->searchAlbumTracks( "media" );
ASSERT_EQ( 0u, tracks.size() );
}
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