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

Split File & Media in two entities

So far, we only handle the case where a Media has multiple files
parent 806e5b27
/*****************************************************************************
* 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 <string>
class IFile
{
public:
enum class Type
{
/// Unknown type, so far
Unknown,
/// The main file of a media
Main,
/// A part of a media (for instance, the first half of a movie)
Part,
/// The file is the entire media
Entire,
/// External soundtracks
Soundtracks,
/// External subtitles
Subtitles,
};
virtual ~IFile() = default;
virtual unsigned int id() const = 0;
virtual const std::string& mrl() const = 0;
virtual Type type() const = 0;
virtual unsigned int lastModificationDate() const = 0;
};
......@@ -53,7 +53,7 @@ class IMedia
virtual ShowEpisodePtr showEpisode() const = 0;
virtual int playCount() const = 0;
virtual void increasePlayCount() = 0;
virtual const std::string& mrl() const = 0;
virtual const std::vector<FilePtr>& files() const = 0;
///
/// \brief progress Returns the progress, in the [0;1] range
///
......
......@@ -53,14 +53,14 @@ class IMetadataService
virtual ~IMetadataService() = default;
virtual bool initialize( IMetadataServiceCb* callback, MediaLibrary* ml ) = 0;
virtual unsigned int priority() const = 0;
virtual void run( std::shared_ptr<Media> file, void* data ) = 0;
virtual void run( std::shared_ptr<Media> media, std::shared_ptr<File> file, void* data ) = 0;
};
class IMetadataServiceCb
{
public:
virtual ~IMetadataServiceCb() = default;
virtual void done( std::shared_ptr<Media> file, IMetadataService::Status status, void* data ) = 0;
virtual void done( std::shared_ptr<Media> media, std::shared_ptr<File> file, IMetadataService::Status status, void* data ) = 0;
};
#endif // IMETADATASERVICE_H
......@@ -29,6 +29,7 @@ class IAlbum;
class IAlbumTrack;
class IAudioTrack;
class IDiscoverer;
class IFile;
class IMedia;
class ILabel;
class IMetadataService;
......@@ -44,6 +45,7 @@ typedef std::shared_ptr<IMedia> MediaPtr;
typedef std::shared_ptr<ILabel> LabelPtr;
typedef std::shared_ptr<IAlbum> AlbumPtr;
typedef std::shared_ptr<IAlbumTrack> AlbumTrackPtr;
typedef std::shared_ptr<IFile> FilePtr;
typedef std::shared_ptr<IShow> ShowPtr;
typedef std::shared_ptr<IShowEpisode> ShowEpisodePtr;
typedef std::shared_ptr<IMovie> MoviePtr;
......
......@@ -66,6 +66,7 @@ list(APPEND SRC_LIST ${HEADERS_LIST}
Artist.cpp
Settings.cpp
Device.cpp
File.cpp
factory/FileSystem.cpp
filesystem/common/CommonFile.cpp
......
/*****************************************************************************
* 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 "File.h"
#include "Media.h"
#include "Folder.h"
const std::string policy::FileTable::Name = "File";
const std::string policy::FileTable::PrimaryKeyColumn = "id_file";
unsigned int File::* const policy::FileTable::PrimaryKey = &File::m_id;
File::File( DBConnection dbConnection, sqlite::Row& row )
: m_dbConnection( dbConnection )
{
row >> m_id
>> m_mediaId
>> m_mrl
>> m_type
>> m_lastModificationDate
>> m_isParsed
>> m_folderId
>> m_isPresent
>> m_isRemovable;
}
File::File( unsigned int mediaId, const fs::IFile& file, unsigned int folderId, bool isRemovable )
: m_id( 0 )
, m_mediaId( mediaId )
, m_mrl( isRemovable == true ? file.name() : file.fullPath() )
, m_type( Type::Unknown )
, m_lastModificationDate( file.lastModificationDate() )
, m_isParsed( false )
, m_folderId( folderId )
, m_isPresent( true )
, m_isRemovable( isRemovable )
{
}
unsigned int File::id() const
{
return m_id;
}
const std::string& File::mrl() const
{
if ( m_isRemovable == false )
return m_mrl;
auto lock = m_fullPath.lock();
if ( m_fullPath.isCached() )
return m_fullPath;
auto folder = Folder::fetch( m_dbConnection, m_folderId );
if ( folder == nullptr )
return m_mrl;
m_fullPath = folder->path() + m_mrl;
return m_fullPath;
}
IFile::Type File::type() const
{
return m_type;
}
unsigned int File::lastModificationDate() const
{
return m_lastModificationDate;
}
bool File::isParsed() const
{
return m_isParsed;
}
std::shared_ptr<Media> File::media() const
{
auto lock = m_media.lock();
if ( m_media.isCached() == false )
{
m_media = Media::fetch( m_dbConnection, m_mediaId );
}
return m_media.get();
}
bool File::destroy()
{
return DatabaseHelpers::destroy( m_dbConnection, m_id );
}
void File::markParsed()
{
if ( m_isParsed == true )
return;
static const std::string req = "UPDATE " + policy::FileTable::Name + " SET parsed = 1 WHERE id_file = ?";
if ( sqlite::Tools::executeUpdate( m_dbConnection, req, m_id ) == false )
return;
m_isParsed = true;
}
bool File::createTable( DBConnection dbConnection )
{
std::string req = "CREATE TABLE IF NOT EXISTS " + policy::FileTable::Name + "("
"id_file INTEGER PRIMARY KEY AUTOINCREMENT,"
"media_id INT NOT NULL,"
"mrl TEXT,"
"type UNSIGNED INTEGER,"
"last_modification_date UNSIGNED INT,"
"parsed BOOLEAN NOT NULL DEFAULT 0,"
"folder_id UNSIGNED INTEGER,"
"is_present BOOLEAN NOT NULL DEFAULT 1,"
"is_removable BOOLEAN NOT NULL,"
"FOREIGN KEY (media_id) REFERENCES " + policy::MediaTable::Name
+ "(id_media) ON DELETE CASCADE,"
"FOREIGN KEY (folder_id) REFERENCES " + policy::FolderTable::Name
+ "(id_folder) ON DELETE CASCADE,"
"UNIQUE( mrl, folder_id ) ON CONFLICT FAIL"
")";
std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS is_folder_present AFTER UPDATE OF is_present ON "
+ policy::FolderTable::Name +
" BEGIN"
" UPDATE " + policy::FileTable::Name + " SET is_present = new.is_present WHERE folder_id = new.id_folder;"
" END";
return sqlite::Tools::executeRequest( dbConnection, req ) &&
sqlite::Tools::executeRequest( dbConnection, triggerReq );
}
std::shared_ptr<File> File::create( DBConnection dbConnection, unsigned int mediaId, const fs::IFile& fileFs, unsigned int folderId, bool isRemovable )
{
auto self = std::make_shared<File>( mediaId, fileFs, folderId, isRemovable );
static const std::string req = "INSERT INTO " + policy::FileTable::Name +
"(media_id, mrl, folder_id, last_modification_date, is_removable) VALUES(?, ?, ?, ?, ?)";
if ( insert( dbConnection, self, req, mediaId, self->m_mrl, sqlite::ForeignKey( folderId ),
self->m_lastModificationDate, isRemovable ) == false )
return nullptr;
self->m_dbConnection = dbConnection;
self->m_fullPath = fileFs.fullPath();
return self;
}
/*****************************************************************************
* 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 "IFile.h"
#include "database/DatabaseHelpers.h"
#include "database/SqliteConnection.h"
#include "filesystem/IFile.h"
#include "utils/Cache.h"
class File;
class Media;
namespace policy
{
struct FileTable
{
static const std::string Name;
static const std::string PrimaryKeyColumn;
static unsigned int File::*const PrimaryKey;
};
}
class File : public IFile, public DatabaseHelpers<File, policy::FileTable>
{
public:
File( DBConnection dbConnection, sqlite::Row& row );
File(unsigned int mediaId, const fs::IFile& file, unsigned int folderId, bool isRemovable );
virtual unsigned int id() const override;
virtual const std::string& mrl() const override;
virtual Type type() const override;
virtual unsigned int lastModificationDate() const override;
/// Explicitely mark a media as fully parsed, meaning no metadata service needs to run anymore.
//FIXME: This lacks granularity as we don't have a straight forward way to know which service
//needs to run or not.
void markParsed();
bool isParsed() const;
std::shared_ptr<Media> media() const;
bool destroy();
static bool createTable( DBConnection dbConnection );
static std::shared_ptr<File> create(DBConnection dbConnection, unsigned int mediaId, const fs::IFile& file, unsigned int folderId, bool isRemovable );
private:
unsigned int m_id;
unsigned int m_mediaId;
std::string m_mrl;
Type m_type;
unsigned int m_lastModificationDate;
bool m_isParsed;
unsigned int m_folderId;
bool m_isPresent;
bool m_isRemovable;
DBConnection m_dbConnection;
mutable Cache<std::string> m_fullPath;
mutable Cache<std::shared_ptr<Media>> m_media;
friend class policy::FileTable;
};
......@@ -20,6 +20,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "File.h"
#include "Folder.h"
#include "Device.h"
#include "Media.h"
......@@ -194,11 +195,11 @@ const std::string& Folder::path() const
return m_fullPath;
}
std::vector<MediaPtr> Folder::files()
std::vector<std::shared_ptr<File>> Folder::files()
{
static const std::string req = "SELECT * FROM " + policy::MediaTable::Name +
static const std::string req = "SELECT * FROM " + policy::FileTable::Name +
" WHERE folder_id = ?";
return Media::fetchAll<IMedia>( m_dbConection, req, m_id );
return File::fetchAll<File>( m_dbConection, req, m_id );
}
std::vector<std::shared_ptr<Folder>> Folder::folders()
......
......@@ -28,6 +28,7 @@
#include <sqlite3.h>
class File;
class Folder;
class Device;
......@@ -71,7 +72,7 @@ public:
unsigned int id() const;
const std::string& path() const;
std::vector<MediaPtr> files();
std::vector<std::shared_ptr<File>> files();
std::vector<std::shared_ptr<Folder>> folders();
std::shared_ptr<Folder> parent();
unsigned int deviceId() const;
......
......@@ -20,6 +20,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring>
......@@ -30,6 +31,7 @@
#include "Artist.h"
#include "AudioTrack.h"
#include "Media.h"
#include "File.h"
#include "Folder.h"
#include "Label.h"
#include "logging/Logger.h"
......@@ -38,6 +40,8 @@
#include "database/SqliteTools.h"
#include "VideoTrack.h"
#include "filesystem/IFile.h"
#include "filesystem/IDirectory.h"
#include "filesystem/IDevice.h"
#include "utils/Filename.h"
const std::string policy::MediaTable::Name = "Media";
......@@ -55,18 +59,12 @@ Media::Media( DBConnection dbConnection, sqlite::Row& row )
>> m_playCount
>> m_progress
>> m_rating
>> m_mrl
>> m_folderId
>> m_lastModificationDate
>> m_insertionDate
>> m_thumbnail
>> m_isParsed
>> m_title
>> m_isPresent
>> m_isRemovable;
>> m_title;
}
Media::Media( const fs::IFile* file, unsigned int folderId, const std::string& title, Type type, bool isRemovable )
Media::Media( const std::string& title, Type type )
: m_id( 0 )
, m_type( type )
, m_subType( SubType::Unknown )
......@@ -74,29 +72,21 @@ Media::Media( const fs::IFile* file, unsigned int folderId, const std::string& t
, m_playCount( 0 )
, m_progress( .0f )
, m_rating( -1 )
, m_mrl( isRemovable == true ? file->name() : file->fullPath() )
, m_folderId( folderId )
, m_lastModificationDate( file->lastModificationDate() )
, m_insertionDate( time( nullptr ) )
, m_isParsed( false )
, m_title( title )
, m_isPresent( true )
, m_isRemovable( isRemovable )
, m_changed( false )
{
}
std::shared_ptr<Media> Media::create( DBConnection dbConnection, Type type, const fs::IFile* file, unsigned int folderId, bool isRemovable )
std::shared_ptr<Media> Media::create( DBConnection dbConnection, Type type, const fs::IFile* file )
{
auto self = std::make_shared<Media>( file, folderId, file->name(), type, isRemovable );
auto self = std::make_shared<Media>( file->name(), type );
static const std::string req = "INSERT INTO " + policy::MediaTable::Name +
"(type, mrl, folder_id, last_modification_date, insertion_date, title, is_removable) VALUES(?, ?, ?, ?, ?, ?, ?)";
"(type, insertion_date, title) VALUES(?, ?, ?)";
if ( insert( dbConnection, self, req, type, self->m_mrl, sqlite::ForeignKey( folderId ),
self->m_lastModificationDate, self->m_insertionDate, self->m_title, isRemovable ) == false )
if ( insert( dbConnection, self, req, type, self->m_insertionDate, self->m_title ) == false )
return nullptr;
self->m_dbConnection = dbConnection;
self->m_fullPath = file->fullPath();
return self;
}
......@@ -192,19 +182,16 @@ void Media::setRating( int rating )
m_changed = true;
}
const std::string& Media::mrl() const
const std::vector<FilePtr>& Media::files() const
{
if ( m_isRemovable == false )
return m_mrl;
auto lock = m_fullPath.lock();
if ( m_fullPath.isCached() )
return m_fullPath;
auto folder = Folder::fetch( m_dbConnection, m_folderId );
if ( folder == nullptr )
return m_mrl;
m_fullPath = folder->path() + m_mrl;
return m_fullPath;
auto lock = m_files.lock();
if ( m_files.isCached() == false )
{
static const std::string req = "SELECT * FROM " + policy::FileTable::Name
+ " WHERE media_id = ?";
m_files = File::fetchAll<IFile>( m_dbConnection, req, m_id );
}
return m_files;
}
MoviePtr Media::movie() const
......@@ -272,13 +259,11 @@ bool Media::save()
{
static const std::string req = "UPDATE " + policy::MediaTable::Name + " SET "
"type = ?, subtype = ?, duration = ?, play_count = ?, progress = ?, rating = ?,"
"last_modification_date = ?, thumbnail = ?, parsed = ?,"
"title = ? WHERE id_media = ?";
"thumbnail = ?, title = ? WHERE id_media = ?";
if ( m_changed == false )
return true;
if ( sqlite::Tools::executeUpdate( m_dbConnection, req, m_type, m_subType, m_duration, m_playCount,
m_progress, m_rating, m_lastModificationDate,
m_thumbnail, m_isParsed, m_title, m_id ) == false )
m_progress, m_rating, m_thumbnail, m_title, m_id ) == false )
{
return false;
}
......@@ -286,22 +271,26 @@ bool Media::save()
return true;
}
unsigned int Media::lastModificationDate()
{
return m_lastModificationDate;
}
bool Media::isParsed() const
std::shared_ptr<File> Media::addFile( const fs::IFile& fileFs, Folder& parentFolder, fs::IDirectory& parentFolderFs )
{
return m_isParsed;
auto file = File::create( m_dbConnection, m_id, fileFs, parentFolder.id(), parentFolderFs.device()->isRemovable() );
if ( file == nullptr )
return nullptr;
auto lock = m_files.lock();
if ( m_files.isCached() )
m_files.get().push_back( file );
return file;
}
void Media::markParsed()
void Media::removeFile( File& file )
{
if ( m_isParsed == true )
file.destroy();
auto lock = m_files.lock();
if ( m_files.isCached() == false )
return;
m_isParsed = true;
m_changed = true;
m_files.get().erase( std::remove_if( begin( m_files.get() ), end( m_files.get() ), [&file]( const FilePtr& f ) {
return f->id() == file.id();
}));
}
unsigned int Media::id() const
......@@ -345,26 +334,32 @@ bool Media::createTable( DBConnection connection )
"play_count UNSIGNED INTEGER,"
"progress REAL,"
"rating INTEGER DEFAULT -1,"
"mrl TEXT,"
"folder_id UNSIGNED INTEGER,"
"last_modification_date UNSIGNED INTEGER,"
"insertion_date UNSIGNED INTEGER,"
"thumbnail TEXT,"
"parsed BOOLEAN NOT NULL DEFAULT 0,"
"title TEXT,"
"is_present BOOLEAN NOT NULL DEFAULT 1,"
"is_removable BOOLEAN NOT NULL,"
"FOREIGN KEY (folder_id) REFERENCES " + policy::FolderTable::Name
+ "(id_folder) ON DELETE CASCADE,"
"UNIQUE( mrl, folder_id ) ON CONFLICT FAIL"
"is_present BOOLEAN NOT NULL DEFAULT 1"
")";
std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS is_folder_present AFTER UPDATE OF is_present ON "
+ policy::FolderTable::Name +
" BEGIN"
" UPDATE " + policy::MediaTable::Name + " SET is_present = new.is_present WHERE folder_id = new.id_folder;"
" END";
return sqlite::Tools::executeRequest( connection, req ) &&
sqlite::Tools::executeRequest( connection, triggerReq );
return sqlite::Tools::executeRequest( connection, req );
}
bool Media::createTriggers( DBConnection connection )
{
static const std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS has_files_present AFTER UPDATE OF "
"is_present ON " + policy::FileTable::Name +
" BEGIN "
" UPDATE " + policy::MediaTable::Name + " SET is_present="
"(SELECT COUNT(id_file) FROM " + policy::FileTable::Name + " WHERE media_id=new.media_id AND is_present=1) "
"WHERE id_media=new.media_id;"
" END;";
static const std::string triggerReq2 = "CREATE TRIGGER IF NOT EXISTS cascade_file_deletion AFTER DELETE ON "
+ policy::FileTable::Name +
" BEGIN "
" DELETE FROM " + policy::MediaTable::Name + " WHERE "
"(SELECT COUNT(id_file) FROM " + policy::FileTable::Name + " WHERE media_id=old.media_id) = 0"
" AND id_media=old.media_id;"
" END;";
return sqlite::Tools::executeRequest( connection, triggerReq ) &&
sqlite::Tools::executeRequest( connection, triggerReq2 );
}
bool Media::addLabel( LabelPtr label )
......
......@@ -31,6 +31,8 @@
#include "utils/Cache.h"
class Album;
class File;
class Folder;
class ShowEpisode;
class AlbumTrack;
......@@ -62,10 +64,11 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
// shall be well-formed, and private constructor would prevent that.
// There might be a way with a user-defined allocator, but we'll see that later...
Media(DBConnection dbConnection , sqlite::Row& row);
Media( const fs::IFile* file, unsigned int folderId, const std::string &title, Type type, bool isRemovable );
Media( const std::string &title, Type type);
static std::shared_ptr<Media> create( DBConnection dbConnection, Type type, const fs::IFile* file , unsigned int folderId, bool isRemovable );
static std::shared_ptr<Media> create( DBConnection dbConnection, Type type, const fs::IFile* file );
static bool createTable( DBConnection connection );
static bool createTriggers( DBConnection connection );
virtual unsigned int id() const override;
virtual Type type() override;
......@@ -87,7 +90,7 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
virtual void setProgress( float progress ) override;
virtual int rating() const override;
virtual void setRating( int rating ) override;
virtual const std::string& mrl() const override;
virtual const std::vector<FilePtr>& files() const override;
virtual MoviePtr movie() const override;
void setMovie( MoviePtr movie );
bool addVideoTrack( const std::string& codec, unsigned int width,
......@@ -101,13 +104,8 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
void setThumbnail( const std::string& thumbnail );
bool save();
unsigned int lastModificationDate();
/// Explicitely mark a media as fully parsed, meaning no metadata service needs to run anymore.
//FIXME: This lacks granularity as we don't have a straight forward way to know which service
//needs to run or not.
void markParsed();
bool isParsed() const;
std::shared_ptr<File> addFile( const fs::IFile& fileFs, Folder& parentFolder, fs::IDirectory& parentFolderFs );
void removeFile( File& file );
private:
DBConnection m_dbConnection;
......@@ -120,22 +118,16 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
unsigned int m_playCount;
float m_progress;
int m_rating;
std::string m_mrl;
unsigned int m_folderId;
unsigned int m_lastModificationDate;
unsigned int m_insertionDate;
std::string m_thumbnail;
bool m_isParsed;
std::string m_title;
bool m_isPresent;
bool m_isRemovable;
// Auto fetched related properties
mutable Cache<AlbumTrackPtr> m_albumTrack;
mutable Cache<ShowEpisodePtr> m_showEpisode;
mutable Cache<MoviePtr> m_movie;
mutable Cache<std::vector<FilePtr>> m_files;
bool m_changed;
mutable Cache<std::string> m_fullPath;
friend struct policy::MediaTable;
};
......
......@@ -30,9 +30,10 @@
#include "Artist.h"
#include "AudioTrack.h"
#include "discoverer/DiscovererWorker.h"
#include "Media.h"
#include "Device.h"
#include "File.h"
#include "Folder.h"
#include "Media.h"
#include "MediaLibrary.h"
#include "IMetadataService.h"
#include "Label.h"
......@@ -101,6 +102,7 @@ MediaLibrary::~MediaLibrary()
AudioTrack::clear();
Artist::clear();
Device::clear();
File::clear();
// Explicitely release the connection's TLS
if ( m_dbConnection != nullptr )
m_dbConnection->release();
......@@ -118,6 +120,7 @@ bool MediaLibrary::createAllTables()
auto res = Device::createTable( m_dbConnection.get() ) &&
Folder::createTable( m_dbConnection.get() ) &&
Media::createTable( m_dbConnection.get() ) &&
File::createTable( m_dbConnection.get() ) &&
Label::createTable( m_dbConnection.get() ) &&
Album::createTable( m_dbConnection.get() ) &&
AlbumTrack::createTable( m_dbConnection.get() ) &&
......@@ -130,6 +133,7 @@ bool MediaLibrary::createAllTables()
Artist::createTable( m_dbConnection.get() ) &&
Artist::createDefaultArtists( m_dbConnection.get() ) &&
Artist::createTriggers( m_dbConnection.get() ) &&
Media::createTriggers( m_dbConnection.get() ) &&
Settings::createTable( m_dbConnection.get() );
if ( res == false )
return false;
......@@ -193,10 +197,10 @@ std::vector<MediaPtr> MediaLibrary::videoFiles()
std::shared_ptr<Media> MediaLibrary::addFile( const std::string& path, Folder& parentFolder, fs::IDirectory& parentFolderFs )
{
std::unique_ptr<fs::IFile> file;
std::unique_ptr<fs::IFile> fileFs;
try
{
file = m_fsFactory->createFile( path );
fileFs = m_fsFactory->createFile( path );
}
catch (std::exception& ex)
{
......@@ -205,7 +209,7 @@ std::shared_ptr<Media> MediaLibrary::addFile( const std::string& path, Folder& p
}
auto type = IMedia::Type::UnknownType;
auto ext = file->extension();
auto ext = fileFs->extension();
auto predicate = [ext](const std::string& v) {
return strcasecmp(v.c_str(), ext.c_str()) == 0;
};
......@@ -224,23 +228,24 @@ std::shared_ptr<Media> MediaLibrary::addFile( const std::string& path, Folder& p
return nullptr;
LOG_INFO( "Adding ", path );
auto fptr = Media::create( m_dbConnection.get(), type, file.get(), parentFolder.id(),
parentFolderFs.device()->isRemovable() );
if ( fptr == nullptr )
auto mptr = Media::create( m_dbConnection.get(), type, fileFs.get() );
if ( mptr == nullptr )
{
LOG_ERROR( "Failed to add media ", fileFs->fullPath(), " to the media library" );
return nullptr;
}
auto file = mptr->addFile( *fileFs, parentFolder, parentFolderFs );
if ( file == nullptr )
{