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

MediaLibrary: Pass shared_ptr to addFile

This will make it easier to postpone some of those operations
parent f8a42fb9
......@@ -393,31 +393,33 @@ std::vector<MediaPtr> MediaLibrary::videoFiles( SortingCriteria sort, bool desc
return Media::listAll( this, IMedia::Type::Video, sort, desc );
}
std::shared_ptr<Media> MediaLibrary::addFile( const fs::IFile& fileFs, Folder& parentFolder, fs::IDirectory& parentFolderFs )
std::shared_ptr<Media> MediaLibrary::addFile( std::shared_ptr<fs::IFile> fileFs,
std::shared_ptr<Folder> parentFolder,
std::shared_ptr<fs::IDirectory> parentFolderFs )
{
auto type = IMedia::Type::Unknown;
if ( std::binary_search( std::begin( supportedExtensions ), std::end( supportedExtensions ),
fileFs.extension().c_str(),
fileFs->extension().c_str(),
[](const char* l, const char* r) { return strcasecmp( l, r ) < 0; }
) == false )
{
LOG_INFO( "Rejecting file ", fileFs.mrl(), " due to its extension" );
LOG_INFO( "Rejecting file ", fileFs->mrl(), " due to its extension" );
return nullptr;
}
LOG_INFO( "Adding ", fileFs.mrl() );
auto mptr = Media::create( this, type, fileFs.name() );
LOG_INFO( "Adding ", fileFs->mrl() );
auto mptr = Media::create( this, type, fileFs->name() );
if ( mptr == nullptr )
{
LOG_ERROR( "Failed to add media ", fileFs.mrl(), " to the media library" );
LOG_ERROR( "Failed to add media ", fileFs->mrl(), " to the media library" );
return nullptr;
}
// For now, assume all media are made of a single file
auto file = mptr->addFile( fileFs, parentFolder, parentFolderFs, File::Type::Main );
auto file = mptr->addFile( *fileFs, *parentFolder, *parentFolderFs, File::Type::Main );
if ( file == nullptr )
{
LOG_ERROR( "Failed to add file ", fileFs.mrl(), " to media #", mptr->id() );
LOG_ERROR( "Failed to add file ", fileFs->mrl(), " to media #", mptr->id() );
Media::destroy( this, mptr->id() );
return nullptr;
}
......
......@@ -72,7 +72,9 @@ class MediaLibrary : public IMediaLibrary, public IDeviceListerCb
virtual std::vector<MediaPtr> audioFiles( SortingCriteria sort, bool desc) const override;
virtual std::vector<MediaPtr> videoFiles( SortingCriteria sort, bool desc) const override;
std::shared_ptr<Media> addFile( const fs::IFile& fileFs, Folder& parentFolder, fs::IDirectory& parentFolderFs );
std::shared_ptr<Media> addFile( std::shared_ptr<fs::IFile> fileFs,
std::shared_ptr<Folder> parentFolder,
std::shared_ptr<fs::IDirectory> parentFolderFs );
bool deleteFolder(const Folder& folder );
......
......@@ -85,7 +85,7 @@ bool FsDiscoverer::discover( const std::string &entryPoint )
{
if ( hasDotNoMediaFile( *fsDir ) )
return true;
return addFolder( *fsDir, nullptr );
return addFolder( std::move( fsDir ), nullptr );
}
catch ( std::system_error& ex )
{
......@@ -103,16 +103,16 @@ bool FsDiscoverer::discover( const std::string &entryPoint )
return true;
}
void FsDiscoverer::reloadFolder( Folder& f )
void FsDiscoverer::reloadFolder( std::shared_ptr<Folder> f )
{
auto folder = m_fsFactory->createDirectory( f.mrl() );
auto folder = m_fsFactory->createDirectory( f->mrl() );
try
{
checkFolder( *folder, f, false );
checkFolder( std::move( folder ), std::move( f ), false );
}
catch ( DeviceRemovedException& )
{
LOG_INFO( "Reloading of ", f.mrl(), " was stopped after the device was removed" );
LOG_INFO( "Reloading of ", f->mrl(), " was stopped after the device was removed" );
}
}
......@@ -120,8 +120,8 @@ bool FsDiscoverer::reload()
{
LOG_INFO( "Reloading all folders" );
auto rootFolders = Folder::fetchRootFolders( m_ml );
for ( const auto& f : rootFolders )
reloadFolder( *f );
for ( const auto f : rootFolders )
reloadFolder( std::move( f ) );
return true;
}
......@@ -136,25 +136,27 @@ bool FsDiscoverer::reload( const std::string& entryPoint )
LOG_ERROR( "Can't reload ", entryPoint, ": folder wasn't found in database" );
return false;
}
reloadFolder( *folder );
reloadFolder( std::move( folder ) );
return true;
}
void FsDiscoverer::checkFolder( fs::IDirectory& currentFolderFs, Folder& currentFolder, bool newFolder ) const
void FsDiscoverer::checkFolder( std::shared_ptr<fs::IDirectory> currentFolderFs,
std::shared_ptr<Folder> currentFolder,
bool newFolder ) const
{
try
{
// We already know of this folder, though it may now contain a .nomedia file.
// In this case, simply delete the folder.
if ( hasDotNoMediaFile( currentFolderFs ) )
if ( hasDotNoMediaFile( *currentFolderFs ) )
{
if ( newFolder == false )
{
LOG_INFO( "Deleting folder ", currentFolderFs.mrl(), " due to a .nomedia file" );
m_ml->deleteFolder( currentFolder );
LOG_INFO( "Deleting folder ", currentFolderFs->mrl(), " due to a .nomedia file" );
m_ml->deleteFolder( *currentFolder );
}
else
LOG_INFO( "Ignoring folder ", currentFolderFs.mrl(), " due to a .nomedia file" );
LOG_INFO( "Ignoring folder ", currentFolderFs->mrl(), " due to a .nomedia file" );
return;
}
}
......@@ -162,18 +164,18 @@ void FsDiscoverer::checkFolder( fs::IDirectory& currentFolderFs, Folder& current
// within, and hasDotMediaFile is the first place when this is done
catch ( std::system_error& ex )
{
LOG_WARN( "Failed to browse ", currentFolderFs.mrl(), ": ", ex.what() );
LOG_WARN( "Failed to browse ", currentFolderFs->mrl(), ": ", ex.what() );
// Even when we're discovering a new folder, we want to rule out device removal as the cause of
// an IO error. If this is the cause, simply abort the discovery. All the folder we have
// discovered so far will be marked as non-present through sqlite hooks, and we'll resume the
// discovery when the device gets plugged back in
if ( currentFolderFs.device()->isRemovable() )
if ( currentFolderFs->device()->isRemovable() )
{
// If the device is removable, check if it was indeed removed.
LOG_INFO( "The device containing ", currentFolderFs.mrl(), " is removable. Checking for device removal..." );
LOG_INFO( "The device containing ", currentFolderFs->mrl(), " is removable. Checking for device removal..." );
m_ml->refreshDevices( *m_fsFactory );
// The device presence flag will be changed in place, so simply retest it
if ( currentFolderFs.device()->isPresent() == false )
if ( currentFolderFs->device()->isPresent() == false )
throw DeviceRemovedException();
LOG_INFO( "Device was not removed" );
}
......@@ -186,19 +188,19 @@ void FsDiscoverer::checkFolder( fs::IDirectory& currentFolderFs, Folder& current
if ( newFolder == false )
{
// If we ever came across this folder, its content is now unaccessible: let's remove it.
m_ml->deleteFolder( currentFolder );
m_ml->deleteFolder( *currentFolder );
}
return;
}
m_cb->onDiscoveryProgress( currentFolderFs.mrl() );
m_cb->onDiscoveryProgress( currentFolderFs->mrl() );
// Load the folders we already know of:
LOG_INFO( "Checking for modifications in ", currentFolderFs.mrl() );
LOG_INFO( "Checking for modifications in ", currentFolderFs->mrl() );
// Don't try to fetch any potential sub folders if the folder was freshly added
std::vector<std::shared_ptr<Folder>> subFoldersInDB;
if ( newFolder == false )
subFoldersInDB = currentFolder.folders();
for ( const auto& subFolder : currentFolderFs.dirs() )
subFoldersInDB = currentFolder->folders();
for ( const auto& subFolder : currentFolderFs->dirs() )
{
auto it = std::find_if( begin( subFoldersInDB ), end( subFoldersInDB ), [&subFolder](const std::shared_ptr<Folder>& f) {
return f->mrl() == subFolder->mrl();
......@@ -214,7 +216,7 @@ void FsDiscoverer::checkFolder( fs::IDirectory& currentFolderFs, Folder& current
LOG_INFO( "New folder detected: ", subFolder->mrl() );
try
{
addFolder( *subFolder, &currentFolder );
addFolder( subFolder, currentFolder.get() );
continue;
}
catch ( sqlite::errors::ConstraintViolation& ex )
......@@ -235,7 +237,7 @@ void FsDiscoverer::checkFolder( fs::IDirectory& currentFolderFs, Folder& current
// In any case, check for modifications, as a change related to a mountpoint might
// not update the folder modification date.
// Also, relying on the modification date probably isn't portable
checkFolder( *subFolder, *folderInDb, false );
checkFolder( subFolder, folderInDb, false );
subFoldersInDB.erase( it );
}
// Now all folders we had in DB but haven't seen from the FS must have been deleted.
......@@ -245,25 +247,26 @@ void FsDiscoverer::checkFolder( fs::IDirectory& currentFolderFs, Folder& current
m_ml->deleteFolder( *f );
}
checkFiles( currentFolderFs, currentFolder );
LOG_INFO( "Done checking subfolders in ", currentFolderFs.mrl() );
LOG_INFO( "Done checking subfolders in ", currentFolderFs->mrl() );
}
void FsDiscoverer::checkFiles( fs::IDirectory& parentFolderFs, Folder& parentFolder ) const
void FsDiscoverer::checkFiles( std::shared_ptr<fs::IDirectory> parentFolderFs,
std::shared_ptr<Folder> parentFolder ) const
{
LOG_INFO( "Checking file in ", parentFolderFs.mrl() );
LOG_INFO( "Checking file in ", parentFolderFs->mrl() );
static const std::string req = "SELECT * FROM " + policy::FileTable::Name
+ " WHERE folder_id = ?";
auto files = File::fetchAll<File>( m_ml, req, parentFolder.id() );
auto files = File::fetchAll<File>( m_ml, req, parentFolder->id() );
std::vector<std::shared_ptr<fs::IFile>> filesToAdd;
std::vector<std::shared_ptr<File>> filesToRemove;
for ( const auto& fileFs: parentFolderFs.files() )
for ( const auto& fileFs: parentFolderFs->files() )
{
auto it = std::find_if( begin( files ), end( files ), [fileFs](const std::shared_ptr<File>& f) {
return f->mrl() == fileFs->mrl();
});
if ( it == end( files ) )
{
filesToAdd.push_back( std::move( fileFs ) );
filesToAdd.push_back( fileFs );
continue;
}
if ( fileFs->lastModificationDate() == (*it)->lastModificationDate() )
......@@ -278,7 +281,7 @@ void FsDiscoverer::checkFiles( fs::IDirectory& parentFolderFs, Folder& parentFol
// out of a write context, since that way, other threads can also read the database.
file->media();
filesToRemove.push_back( std::move( file ) );
filesToAdd.push_back( std::move( fileFs ) );
filesToAdd.push_back( fileFs );
files.erase( it );
}
using FilesT = decltype( files );
......@@ -315,9 +318,9 @@ void FsDiscoverer::checkFiles( fs::IDirectory& parentFolderFs, Folder& parentFol
}
// Insert all files at once to avoid SQL write contention
for ( auto& p : filesToAdd )
m_ml->addFile( *p, parentFolder, parentFolderFs );
m_ml->addFile( p, parentFolder, parentFolderFs );
t->commit();
LOG_INFO( "Done checking files in ", parentFolderFs.mrl() );
LOG_INFO( "Done checking files in ", parentFolderFs->mrl() );
}, std::move( files ), std::move( filesToAdd ), std::move( filesToRemove ) );
}
......@@ -329,25 +332,27 @@ bool FsDiscoverer::hasDotNoMediaFile( const fs::IDirectory& directory )
}) != end( files );
}
bool FsDiscoverer::addFolder( fs::IDirectory& folder, Folder* parentFolder ) const
bool FsDiscoverer::addFolder( std::shared_ptr<fs::IDirectory> folder,
Folder* parentFolder ) const
{
auto deviceFs = folder.device();
auto deviceFs = folder->device();
// We are creating a folder, there has to be a device containing it.
assert( deviceFs != nullptr );
auto device = Device::fromUuid( m_ml, deviceFs->uuid() );
if ( device == nullptr )
{
LOG_INFO( "Creating new device in DB ", deviceFs->uuid() );
device = Device::create( m_ml, deviceFs->uuid(), utils::file::scheme( folder.mrl() ),
device = Device::create( m_ml, deviceFs->uuid(),
utils::file::scheme( folder->mrl() ),
deviceFs->isRemovable() );
}
auto f = Folder::create( m_ml, folder.mrl(),
auto f = Folder::create( m_ml, folder->mrl(),
parentFolder != nullptr ? parentFolder->id() : 0,
*device, *deviceFs );
if ( f == nullptr )
return false;
checkFolder( folder, *f, true );
checkFolder( std::move( folder ), std::move( f ), true );
return true;
}
......
......@@ -47,11 +47,14 @@ private:
/// \brief checkSubfolders
/// \return true if files in this folder needs to be listed, false otherwise
///
void checkFolder( fs::IDirectory& currentFolderFs, Folder& currentFolder, bool newFolder ) const;
void checkFiles(fs::IDirectory& parentFolderFs, Folder& parentFolder ) const;
void checkFolder( std::shared_ptr<fs::IDirectory> currentFolderFs,
std::shared_ptr<Folder> currentFolder, bool newFolder ) const;
void checkFiles( std::shared_ptr<fs::IDirectory> parentFolderFs,
std::shared_ptr<Folder> parentFolder ) const;
static bool hasDotNoMediaFile( const fs::IDirectory& directory );
bool addFolder( fs::IDirectory& folder, Folder* parentFolder ) const;
void reloadFolder( Folder& folder );
bool addFolder( std::shared_ptr<fs::IDirectory> folder,
Folder* parentFolder ) const;
void reloadFolder( std::shared_ptr<Folder> folder );
private:
MediaLibrary* m_ml;
......
......@@ -39,7 +39,7 @@
MediaLibraryTester::MediaLibraryTester()
: dummyDirectory( new mock::NoopDirectory )
, dummyFolder( nullptr, "./", 0, 0, false )
, dummyFolder( std::make_shared<Folder>( nullptr, "./", 0, 0, false ) )
{
}
......@@ -63,13 +63,13 @@ std::shared_ptr<Folder> MediaLibraryTester::folder( const std::string& mrl )
std::shared_ptr<Media> MediaLibraryTester::addFile( const std::string& path )
{
mock::NoopFile file( path );
return MediaLibrary::addFile( file, dummyFolder, *dummyDirectory );
auto file = std::make_shared<mock::NoopFile>( path );
return MediaLibrary::addFile( file, dummyFolder, dummyDirectory );
}
std::shared_ptr<Media> MediaLibraryTester::addFile( fs::IFile& file )
std::shared_ptr<Media> MediaLibraryTester::addFile( std::shared_ptr<fs::IFile> file )
{
return MediaLibrary::addFile( file, dummyFolder, *dummyDirectory );
return MediaLibrary::addFile( std::move( file ), dummyFolder, dummyDirectory );
}
void MediaLibraryTester::addLocalFsFactory()
......
......@@ -55,7 +55,7 @@ public:
void deleteTrack( int64_t trackId );
std::shared_ptr<AlbumTrack> albumTrack( int64_t id );
// Use to run tests that fiddles with file properties (modification dates, size...)
std::shared_ptr<Media> addFile(fs::IFile& file);
std::shared_ptr<Media> addFile( std::shared_ptr<fs::IFile> file);
// Used when we need an actual file instead of an external media
std::shared_ptr<Media> addFile( const std::string& path );
virtual void addLocalFsFactory() override;
......@@ -63,9 +63,9 @@ public:
std::vector<const char*> getSupportedExtensions() const;
private:
std::unique_ptr<fs::IDirectory> dummyDirectory;
std::shared_ptr<fs::IDirectory> dummyDirectory;
std::shared_ptr<factory::IFileSystem> fsFactory;
Folder dummyFolder;
std::shared_ptr<Folder> dummyFolder;
};
class MediaLibraryWithoutParser : public MediaLibraryTester
......
......@@ -380,13 +380,13 @@ TEST_F( Medias, SortByLastModifDate )
{
auto file1 = std::make_shared<mock::NoopFile>( "media.mkv" );
file1->setLastModificationDate( 666 );
auto m1 = ml->addFile( *file1 );
auto m1 = ml->addFile( file1 );
m1->setType( Media::Type::Video );
m1->save();
auto file2 = std::make_shared<mock::NoopFile>( "media2.mkv" );
file2->setLastModificationDate( 111 );
auto m2 = ml->addFile( *file2 );
auto m2 = ml->addFile( file2 );
m2->setType( Media::Type::Video );
m2->save();
......@@ -405,13 +405,13 @@ TEST_F( Medias, SortByFileSize )
{
auto file1 = std::make_shared<mock::NoopFile>( "media.mkv" );
file1->setSize( 666 );
auto m1 = ml->addFile( *file1 );
auto m1 = ml->addFile( file1 );
m1->setType( Media::Type::Video );
m1->save();
auto file2 = std::make_shared<mock::NoopFile>( "media2.mkv" );
file2->setSize( 111 );
auto m2 = ml->addFile( *file2 );
auto m2 = ml->addFile( file2 );
m2->setType( Media::Type::Video );
m2->save();
......
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