File.cpp 10.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*****************************************************************************
 * 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.
 *****************************************************************************/

23 24 25 26
#if HAVE_CONFIG_H
# include "config.h"
#endif

27 28 29 30
#include "File.h"

#include "Media.h"
#include "Folder.h"
31
#include "Playlist.h"
32

33 34 35
namespace medialibrary
{

36 37
const std::string policy::FileTable::Name = "File";
const std::string policy::FileTable::PrimaryKeyColumn = "id_file";
38
int64_t File::* const policy::FileTable::PrimaryKey = &File::m_id;
39

40 41
File::File( MediaLibraryPtr ml, sqlite::Row& row )
    : m_ml( ml )
42 43 44
{
    row >> m_id
        >> m_mediaId
45
        >> m_playlistId
46 47 48
        >> m_mrl
        >> m_type
        >> m_lastModificationDate
49
        >> m_size
50 51
        >> m_folderId
        >> m_isPresent
52
        >> m_isRemovable
53
        >> m_isExternal;
54 55
}

56
File::File( MediaLibraryPtr ml, int64_t mediaId, int64_t playlistId, Type type, const fs::IFile& file, int64_t folderId, bool isRemovable )
57 58
    : m_ml( ml )
    , m_id( 0 )
59
    , m_mediaId( mediaId )
60
    , m_playlistId( playlistId )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
61
    , m_mrl( isRemovable == true ? file.name() : file.mrl() )
62
    , m_type( type )
63
    , m_lastModificationDate( file.lastModificationDate() )
64
    , m_size( file.size() )
65 66 67
    , m_folderId( folderId )
    , m_isPresent( true )
    , m_isRemovable( isRemovable )
68
    , m_isExternal( false )
69
{
70
    assert( ( mediaId == 0 && playlistId != 0 ) || ( mediaId != 0 && playlistId == 0 ) );
71 72
}

73
File::File(MediaLibraryPtr ml, int64_t mediaId, int64_t playlistId, IFile::Type type, const std::string& mrl )
74 75 76
    : m_ml( ml )
    , m_id( 0 )
    , m_mediaId( mediaId )
77
    , m_playlistId( playlistId )
78 79 80 81 82 83 84
    , m_mrl( mrl )
    , m_type( type )
    , m_lastModificationDate( 0 )
    , m_size( 0 )
    , m_folderId( 0 )
    , m_isPresent( true )
    , m_isRemovable( false )
85
    , m_isExternal( true )
86 87
    , m_fullPath( mrl )
{
88
    assert( ( mediaId == 0 && playlistId != 0 ) || ( mediaId != 0 && playlistId == 0 ) );
89 90
}

91
int64_t File::id() const
92 93 94 95 96 97 98 99 100 101 102 103
{
    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;
104
    auto folder = Folder::fetch( m_ml, m_folderId );
105 106
    if ( folder == nullptr )
        return m_mrl;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
107
    m_fullPath = folder->mrl() + m_mrl;
108 109 110
    return m_fullPath;
}

111 112 113 114 115
const std::string& File::rawMrl() const
{
    return m_mrl;
}

116 117 118 119 120 121 122 123 124 125 126
void File::setMrl( const std::string& mrl )
{
    if ( m_mrl == mrl )
        return;
    const static std::string req = "UPDATE " + policy::FileTable::Name + " SET "
            "mrl = ? WHERE id_file = ?";
    if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, mrl, m_id ) == false )
        return;
    m_mrl = mrl;
}

127
IFile::Type File::type() const
128 129 130 131 132 133
{
    return m_type;
}

unsigned int File::lastModificationDate() const
{
134
    return static_cast<unsigned int>( m_lastModificationDate );
135 136
}

137 138 139 140 141
unsigned int File::size() const
{
    return m_size;
}

142 143 144 145 146
bool File::isExternal() const
{
    return m_isExternal;
}

147 148
std::shared_ptr<Media> File::media() const
{
149 150
    if ( m_mediaId == 0 )
        return nullptr;
151 152 153
    auto lock = m_media.lock();
    if ( m_media.isCached() == false )
    {
154 155 156
        auto media = Media::fetch( m_ml, m_mediaId );
        assert( isDeleted() == true || media != nullptr );
        m_media = media;
157
    }
158
    return m_media.get().lock();
159 160 161 162
}

bool File::destroy()
{
163
    return DatabaseHelpers::destroy( m_ml, m_id );
164 165
}

166 167 168 169 170
int64_t File::folderId()
{
    return m_folderId;
}

171
void File::createTable( sqlite::Connection* dbConnection )
172 173 174
{
    std::string req = "CREATE TABLE IF NOT EXISTS " + policy::FileTable::Name + "("
            "id_file INTEGER PRIMARY KEY AUTOINCREMENT,"
175 176
            "media_id UNSIGNED INT DEFAULT NULL,"
            "playlist_id UNSIGNED INT DEFAULT NULL,"
177 178 179
            "mrl TEXT,"
            "type UNSIGNED INTEGER,"
            "last_modification_date UNSIGNED INT,"
180
            "size UNSIGNED INT,"
181 182 183
            "folder_id UNSIGNED INTEGER,"
            "is_present BOOLEAN NOT NULL DEFAULT 1,"
            "is_removable BOOLEAN NOT NULL,"
184
            "is_external BOOLEAN NOT NULL,"
185 186
            "FOREIGN KEY (media_id) REFERENCES " + policy::MediaTable::Name
            + "(id_media) ON DELETE CASCADE,"
187 188
            "FOREIGN KEY (playlist_id) REFERENCES " + policy::PlaylistTable::Name
            + "(id_playlist) ON DELETE CASCADE,"
189 190 191 192
            "FOREIGN KEY (folder_id) REFERENCES " + policy::FolderTable::Name
            + "(id_folder) ON DELETE CASCADE,"
            "UNIQUE( mrl, folder_id ) ON CONFLICT FAIL"
        ")";
193 194 195 196 197 198

    sqlite::Tools::executeRequest( dbConnection, req );
}

void File::createTriggers(sqlite::Connection* dbConnection)
{
199 200 201 202 203
    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";
204
    std::string mediaIndexReq = "CREATE INDEX IF NOT EXISTS file_media_id_index ON " +
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
205
            policy::FileTable::Name + "(media_id)";
206 207
    std::string folderIndexReq = "CREATE INDEX IF NOT EXISTS file_folder_id_index ON " +
            policy::FileTable::Name + "(folder_id)";
208 209 210
    sqlite::Tools::executeRequest( dbConnection, triggerReq );
    sqlite::Tools::executeRequest( dbConnection, mediaIndexReq );
    sqlite::Tools::executeRequest( dbConnection, folderIndexReq );
211 212
}

213 214
std::shared_ptr<File> File::createFromMedia( MediaLibraryPtr ml, int64_t mediaId, Type type, const fs::IFile& fileFs,
                                             int64_t folderId, bool isRemovable )
215
{
216 217
    assert( mediaId > 0 );
    auto self = std::make_shared<File>( ml, mediaId, 0, type, fileFs, folderId, isRemovable );
218
    static const std::string req = "INSERT INTO " + policy::FileTable::Name +
219
            "(media_id, mrl, type, folder_id, last_modification_date, size, is_removable, is_external) VALUES(?, ?, ?, ?, ?, ?, ?, 0)";
220

221
    if ( insert( ml, self, req, mediaId, self->m_mrl, type, sqlite::ForeignKey( folderId ),
222
                         self->m_lastModificationDate, self->m_size, isRemovable ) == false )
223
        return nullptr;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
224
    self->m_fullPath = fileFs.mrl();
225 226
    return self;
}
227

228 229
std::shared_ptr<File> File::createFromMedia( MediaLibraryPtr ml, int64_t mediaId, IFile::Type type,
                                             const std::string& mrl )
230
{
231
    assert( mediaId > 0 );
232 233 234 235 236 237 238 239
    // Sqlite won't ensure uniqueness for (folder_id, mrl) when folder_id is null, so we have to ensure
    // of it ourselves
    static const std::string existingReq = "SELECT * FROM " + policy::FileTable::Name +
            " WHERE folder_id IS NULL AND mrl = ?";
    auto existing = fetch( ml, existingReq, mrl );
    if ( existing != nullptr )
        return nullptr;

240
    auto self = std::make_shared<File>( ml, mediaId, 0, type, mrl );
241
    static const std::string req = "INSERT INTO " + policy::FileTable::Name +
242
            "(media_id, mrl, type, folder_id, is_removable, is_external) VALUES(?, ?, ?, NULL, 0, 1)";
243

244
    if ( insert( ml, self, req, mediaId, mrl, type ) == false )
245 246 247 248
        return nullptr;
    return self;
}

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
std::shared_ptr<File> File::createFromPlaylist( MediaLibraryPtr ml, int64_t playlistId, const fs::IFile& fileFs,
                                                int64_t folderId, bool isRemovable )
{
    assert( playlistId > 0 );
    const auto type = IFile::Type::Playlist;
    auto self = std::make_shared<File>( ml, 0, playlistId, type , fileFs, folderId, isRemovable );
    static const std::string req = "INSERT INTO " + policy::FileTable::Name +
            "(playlist_id, mrl, type, folder_id, last_modification_date, size, is_removable, is_external) VALUES(?, ?, ?, ?, ?, ?, ?, 0)";

    if ( insert( ml, self, req, playlistId, self->m_mrl, type, sqlite::ForeignKey( folderId ),
                 self->m_lastModificationDate, self->m_size, isRemovable ) == false )
        return nullptr;
    self->m_fullPath = fileFs.mrl();
    return self;
}

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
265
std::shared_ptr<File> File::fromMrl( MediaLibraryPtr ml, const std::string& mrl )
266
{
267
    static const std::string req = "SELECT * FROM " + policy::FileTable::Name +  " WHERE mrl = ? AND folder_id IS NOT NULL";
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
268
    auto file = fetch( ml, req, mrl );
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    if ( file == nullptr )
        return nullptr;
    // safety checks: since this only works for files on non removable devices, isPresent must be true
    // and isRemovable must be false
    assert( file->m_isPresent == true );
    assert( file->m_isRemovable == false );
    return file;
}

std::shared_ptr<File> File::fromFileName( MediaLibraryPtr ml, const std::string& fileName, int64_t folderId )
{
    static const std::string req = "SELECT * FROM " + policy::FileTable::Name +  " WHERE mrl = ? "
            "AND folder_id = ?";
    auto file = fetch( ml, req, fileName, folderId );
    if ( file == nullptr )
        return nullptr;
    assert( file->m_isRemovable == true );
    return file;
}

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
289
std::shared_ptr<File> File::fromExternalMrl( MediaLibraryPtr ml, const std::string& mrl )
290 291 292 293 294 295 296 297 298 299
{
    static const std::string req = "SELECT * FROM " + policy::FileTable::Name +  " WHERE mrl = ? "
            "AND folder_id IS NULL";
    auto file = fetch( ml, req, mrl );
    if ( file == nullptr )
        return nullptr;
    assert( file->m_isExternal == true );
    return file;
}

300

301
}