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

Add blacklisting support

parent a0d8758a
......@@ -120,6 +120,10 @@ class IMediaLibrary
* @param entryPoint What to discover.
*/
virtual void discover( const std::string& entryPoint ) = 0;
/**
* @brief ignoreFolder will blacklist a folder for discovery
*/
virtual bool ignoreFolder( const std::string& path ) = 0;
virtual const std::string& snapshotPath() const = 0;
virtual void setLogger( ILogger* logger ) = 0;
/**
......
......@@ -40,7 +40,8 @@ Folder::Folder(DBConnection dbConnection, sqlite::Row& row )
>> m_path
>> m_parent
>> m_lastModificationDate
>> m_isRemovable;
>> m_isRemovable
>> m_isBlacklisted;
}
Folder::Folder( const std::string& path, time_t lastModificationDate, bool isRemovable, unsigned int parent )
......@@ -49,6 +50,7 @@ Folder::Folder( const std::string& path, time_t lastModificationDate, bool isRem
, m_parent( parent )
, m_lastModificationDate( lastModificationDate )
, m_isRemovable( isRemovable )
, m_isBlacklisted( false )
{
}
......@@ -61,6 +63,7 @@ bool Folder::createTable(DBConnection connection)
"id_parent UNSIGNED INTEGER,"
"last_modification_date UNSIGNED INTEGER,"
"is_removable INTEGER,"
"is_blacklisted INTEGER,"
"FOREIGN KEY (id_parent) REFERENCES " + policy::FolderTable::Name +
"(id_folder) ON DELETE CASCADE"
")";
......@@ -79,9 +82,16 @@ std::shared_ptr<Folder> Folder::create( DBConnection connection, const std::stri
return self;
}
bool Folder::blacklist( DBConnection connection, const std::string& path )
{
static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
"(path, id_parent, is_blacklisted) VALUES(?, ?, ?)";
return sqlite::Tools::insert( connection, req, path, nullptr, true ) != 0;
}
std::shared_ptr<Folder> Folder::fromPath( DBConnection conn, const std::string& path )
{
const std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ?";
const std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND is_blacklisted IS NULL";
return fetch( conn, req, path );
}
......@@ -105,7 +115,7 @@ std::vector<MediaPtr> Folder::files()
std::vector<FolderPtr> Folder::folders()
{
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name +
" WHERE id_parent = ?";
" WHERE id_parent = ? AND is_blacklisted IS NULL";
return fetchAll<IFolder>( m_dbConection, req, m_id );
}
......
......@@ -49,10 +49,11 @@ class Folder : public IFolder, public DatabaseHelpers<Folder, policy::FolderTabl
{
public:
Folder( DBConnection dbConnection, sqlite::Row& row );
Folder( const std::string& path, time_t lastModificationDate, bool isRemovable, unsigned int parent );
Folder(const std::string& path, time_t lastModificationDate, bool isRemovable, unsigned int parent );
static bool createTable( DBConnection connection );
static std::shared_ptr<Folder> create( DBConnection connection, const std::string& path, time_t lastModificationDate, bool isRemovable, unsigned int parentId );
static std::shared_ptr<Folder> create(DBConnection connection, const std::string& path, time_t lastModificationDate, bool isRemovable, unsigned int parentId );
static bool blacklist( DBConnection connection, const std::string& path );
static std::shared_ptr<Folder> fromPath( DBConnection conn, const std::string& path );
......@@ -73,6 +74,7 @@ private:
unsigned int m_parent;
unsigned int m_lastModificationDate;
bool m_isRemovable;
bool m_isBlacklisted;
friend struct policy::FolderTable;
};
......@@ -366,6 +366,17 @@ void MediaLibrary::discover( const std::string &entryPoint )
m_discoverer->discover( entryPoint );
}
bool MediaLibrary::ignoreFolder( const std::string& path )
{
auto f = Folder::fromPath( m_dbConnection.get(), path );
if ( f != nullptr )
{
// Let the foreign key destroy everything beneath this folder
Folder::destroy( m_dbConnection.get(), f->id() );
}
return Folder::blacklist( m_dbConnection.get(), path );
}
const std::string& MediaLibrary::snapshotPath() const
{
return m_snapshotPath;
......
......@@ -76,6 +76,7 @@ class MediaLibrary : public IMediaLibrary
virtual std::vector<ArtistPtr> artists() const override;
virtual void discover( const std::string& entryPoint ) override;
bool ignoreFolder( const std::string& path ) override;
virtual const std::string& snapshotPath() const override;
virtual void setLogger( ILogger* logger ) override;
......
......@@ -49,7 +49,7 @@ bool FsDiscoverer::discover( const std::string &entryPoint )
{
auto f = Folder::fromPath( m_dbConn, entryPoint );
// If the folder exists, we assume it is up to date
// If the folder exists, we assume it will be handled by reload()
if ( f != nullptr )
return true;
}
......@@ -64,13 +64,19 @@ bool FsDiscoverer::discover( const std::string &entryPoint )
LOG_ERROR("Failed to create an IDirectory for ", entryPoint, ": ", ex.what());
return false;
}
auto blist = blacklist();
auto it = std::find_if( begin( blist ), end( blist ), [&fsDir]( const std::shared_ptr<Folder>& f ) {
return f->path() == fsDir->path();
});
if ( it != end( blist ) )
return false;
// Force <0> as lastModificationDate, so this folder is detected as outdated
// by the modification checking code
auto f = Folder::create( m_dbConn, fsDir->path(), 0, fsDir->isRemovable(), 0 );
if ( f == nullptr )
return false;
checkFiles( fsDir.get(), f );
checkSubfolders( fsDir.get(), f );
checkSubfolders( fsDir.get(), f, blist );
f->setLastModificationDate( fsDir->lastModificationDate() );
return true;
}
......@@ -82,18 +88,19 @@ void FsDiscoverer::reload()
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
+ " WHERE id_parent IS NULL";
auto rootFolders = Folder::fetchAll<Folder>( m_dbConn, req );
auto blist = blacklist();
for ( const auto& f : rootFolders )
{
auto folder = m_fsFactory->createDirectory( f->path() );
if ( folder->lastModificationDate() == f->lastModificationDate() )
continue;
checkSubfolders( folder.get(), f );
checkSubfolders( folder.get(), f, blist );
checkFiles( folder.get(), f );
f->setLastModificationDate( folder->lastModificationDate() );
}
}
bool FsDiscoverer::checkSubfolders( fs::IDirectory* folder, FolderPtr parentFolder )
bool FsDiscoverer::checkSubfolders( fs::IDirectory* folder, FolderPtr parentFolder, const std::vector<std::shared_ptr<Folder>> blacklist )
{
// From here we can have:
// - New subfolder(s)
......@@ -118,11 +125,20 @@ bool FsDiscoverer::checkSubfolders( fs::IDirectory* folder, FolderPtr parentFold
// We don't know this folder, it's a new one
if ( it == end( subFoldersInDB ) )
{
// Check if it is blacklisted
auto it = std::find_if( begin( blacklist ), end( blacklist ), [subFolderPath](const std::shared_ptr<Folder>& f) {
return f->path() == subFolderPath;
});
if ( it != end( blacklist ) )
{
LOG_INFO( "Ignoring blacklisted folder: ", subFolderPath );
continue;
}
LOG_INFO( "New folder detected: ", subFolderPath );
// Force a scan by setting lastModificationDate to 0
auto f = Folder::create( m_dbConn, subFolder->path(), 0, subFolder->isRemovable(), parentFolder->id() );
checkFiles( subFolder.get(), f );
checkSubfolders( subFolder.get(), f );
checkSubfolders( subFolder.get(), f, blacklist );
f->setLastModificationDate( subFolder->lastModificationDate() );
continue;
}
......@@ -136,7 +152,7 @@ bool FsDiscoverer::checkSubfolders( fs::IDirectory* folder, FolderPtr parentFold
continue;
}
// This folder was modified, let's recurse
checkSubfolders( subFolder.get(), folderInDb );
checkSubfolders( subFolder.get(), folderInDb, blacklist );
checkFiles( subFolder.get(), folderInDb );
folderInDb->setLastModificationDate( subFolder->lastModificationDate() );
subFoldersInDB.erase( it );
......@@ -184,3 +200,9 @@ void FsDiscoverer::checkFiles( fs::IDirectory* folder, FolderPtr parentFolder )
m_ml->deleteFile( file.get() );
}
}
std::vector<std::shared_ptr<Folder> > FsDiscoverer::blacklist() const
{
static const std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE is_blacklisted = 1";
return sqlite::Tools::fetchAll<Folder, Folder>( m_dbConn, req );
}
......@@ -29,6 +29,7 @@
#include "factory/IFileSystem.h"
class MediaLibrary;
class Folder;
class FsDiscoverer : public IDiscoverer
{
......@@ -38,8 +39,9 @@ public:
virtual void reload() override;
private:
bool checkSubfolders( fs::IDirectory *folder, FolderPtr parentFolder );
bool checkSubfolders(fs::IDirectory *folder, FolderPtr parentFolder , const std::vector<std::shared_ptr<Folder> > blacklist);
void checkFiles( fs::IDirectory *folder, FolderPtr parentFolder );
std::vector<std::shared_ptr<Folder>> blacklist() const;
private:
MediaLibrary* m_ml;
......
......@@ -373,3 +373,37 @@ TEST_F( Folders, CheckRemovable )
subfolder = ml->folder( mock::FileSystemFactory::SubFolder );
ASSERT_TRUE( subfolder->isRemovable() );
}
TEST_F( Folders, Blacklist )
{
cbMock->prepareForWait( 1 );
ml->ignoreFolder( mock::FileSystemFactory::SubFolder );
ml->discover( "." );
bool discovered = cbMock->wait();
ASSERT_TRUE( discovered );
auto f = ml->folder( mock::FileSystemFactory::SubFolder );
ASSERT_EQ( nullptr, f );
}
TEST_F( Folders, BlacklistAfterDiscovery )
{
cbMock->prepareForWait( 1 );
ml->discover( "." );
bool discovered = cbMock->wait();
ASSERT_TRUE( discovered );
auto f = ml->folder( mock::FileSystemFactory::SubFolder );
ASSERT_NE( nullptr, f );
auto files = f->files();
ASSERT_NE( 0u, files.size() );
ml->ignoreFolder( mock::FileSystemFactory::SubFolder );
auto f2 = ml->folder( mock::FileSystemFactory::SubFolder );
ASSERT_EQ( nullptr, f2 );
for ( auto& file : files )
{
auto m = ml->file( file->mrl() );
ASSERT_EQ( nullptr, m );
}
}
......@@ -46,7 +46,6 @@ void Tests::TearDown()
unlink("test.db");
}
void Tests::Reload(std::shared_ptr<factory::IFileSystem> fs /*= nullptr*/, IMediaLibraryCb* metadataCb /*= nullptr*/ )
{
ml.reset( new MediaLibrary );
......
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