Media.cpp 12.5 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
#include <cassert>
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
24 25
#include <cstdlib>
#include <cstring>
26
#include <ctime>
27

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
28
#include "Album.h"
29
#include "AlbumTrack.h"
30
#include "Artist.h"
31
#include "AudioTrack.h"
32
#include "Media.h"
33
#include "Folder.h"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
34
#include "Label.h"
35
#include "logging/Logger.h"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
36
#include "Movie.h"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
37
#include "ShowEpisode.h"
38
#include "database/SqliteTools.h"
39
#include "VideoTrack.h"
40
#include "filesystem/IFile.h"
41

42 43 44
const std::string policy::MediaTable::Name = "Media";
const std::string policy::MediaTable::CacheColumn = "mrl";
unsigned int Media::* const policy::MediaTable::PrimaryKey = &Media::m_id;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
45

46
Media::Media( DBConnection dbConnection, sqlite::Row& row )
47 48
    : m_dbConnection( dbConnection )
{
49 50 51 52 53 54
    row >> m_id
        >> m_type
        >> m_duration
        >> m_playCount
        >> m_showEpisodeId
        >> m_mrl
55
        >> m_artist
56 57 58 59 60 61
        >> m_movieId
        >> m_folderId
        >> m_lastModificationDate
        >> m_snapshot
        >> m_isParsed
        >> m_title;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
62 63
}

64
Media::Media( const fs::IFile* file, unsigned int folderId, const std::string& title, Type type )
65
    : m_id( 0 )
66
    , m_type( type )
67
    , m_duration( -1 )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
68 69
    , m_playCount( 0 )
    , m_showEpisodeId( 0 )
70
    , m_mrl( file->fullPath() )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
71
    , m_movieId( 0 )
72
    , m_folderId( folderId )
73
    , m_lastModificationDate( file->lastModificationDate() )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
74
    , m_isParsed( false )
75
    , m_title( title )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
76
{
77 78
}

79
std::shared_ptr<Media> Media::create( DBConnection dbConnection, Type type, const fs::IFile* file, unsigned int folderId )
80
{
81 82
    auto self = std::make_shared<Media>( file, folderId, file->name(), type );
    static const std::string req = "INSERT INTO " + policy::MediaTable::Name +
83
            "(type, mrl, folder_id, last_modification_date, title) VALUES(?, ?, ?, ?, ?)";
84

85
    if ( _Cache::insert( dbConnection, self, req, type, self->m_mrl, sqlite::ForeignKey( folderId ),
86
                         self->m_lastModificationDate, self->m_title) == false )
87
        return nullptr;
88 89
    self->m_dbConnection = dbConnection;
    return self;
90 91
}

92
AlbumTrackPtr Media::albumTrack()
93
{
94
    if ( m_albumTrack == nullptr )
95
    {
96 97 98
        std::string req = "SELECT * FROM " + policy::AlbumTrackTable::Name +
                " WHERE media_id = ?";
        m_albumTrack = AlbumTrack::fetchOne( m_dbConnection, req, m_id );
99 100
    }
    return m_albumTrack;
101 102
}

103
bool Media::setAlbumTrack( AlbumTrackPtr albumTrack )
104 105 106 107 108
{
    m_albumTrack = albumTrack;
    return true;
}

109
const std::string& Media::artist() const
110
{
111
    return m_artist;
112 113
}

114
bool Media::setArtist(const std::string& artist)
115
{
116 117 118 119 120 121 122 123
    if ( m_artist == artist )
        return true;
    static const std::string req = "UPDATE " + policy::MediaTable::Name + " SET artist = ? "
            "WHERE id_media = ?";
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, artist, m_id ) == false )
        return false;
    m_artist = artist;
    return true;
124 125
}

126
int64_t Media::duration() const
127 128 129 130
{
    return m_duration;
}

131
bool Media::setDuration( int64_t duration )
132
{
133 134
    static const std::string req = "UPDATE " + policy::MediaTable::Name + " SET duration = ? "
            "WHERE id_media = ?";
135 136 137 138 139 140
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, duration, m_id ) == false )
        return false;
    m_duration = duration;
    return true;
}

141
std::shared_ptr<IShowEpisode> Media::showEpisode()
142
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
143
    if ( m_showEpisode == nullptr && m_showEpisodeId != 0 )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
144
    {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
145
        m_showEpisode = ShowEpisode::fetch( m_dbConnection, m_showEpisodeId );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
146 147
    }
    return m_showEpisode;
148 149
}

150
bool Media::setShowEpisode(ShowEpisodePtr showEpisode)
151
{
152 153
    static const std::string req = "UPDATE " + policy::MediaTable::Name
            + " SET show_episode_id = ?  WHERE id_media = ?";
154
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, showEpisode->id(), m_id ) == false )
155 156 157 158 159 160
        return false;
    m_showEpisodeId = showEpisode->id();
    m_showEpisode = showEpisode;
    return true;
}

161
std::vector<std::shared_ptr<ILabel> > Media::labels()
162
{
163
    static const std::string req = "SELECT l.* FROM " + policy::LabelTable::Name + " l "
164
            "LEFT JOIN LabelFileRelation lfr ON lfr.id_label = l.id_label "
165
            "WHERE lfr.id_media = ?";
166
    return Label::fetchAll( m_dbConnection, req, m_id );
167 168
}

169
int Media::playCount() const
170
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
171
    return m_playCount;
172 173
}

174
const std::string& Media::mrl() const
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
175 176 177 178
{
    return m_mrl;
}

179
MoviePtr Media::movie()
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
180 181 182 183 184 185 186 187
{
    if ( m_movie == nullptr && m_movieId != 0 )
    {
        m_movie = Movie::fetch( m_dbConnection, m_movieId );
    }
    return m_movie;
}

188
bool Media::setMovie( MoviePtr movie )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
189
{
190 191
    static const std::string req = "UPDATE " + policy::MediaTable::Name
            + " SET movie_id = ? WHERE id_media = ?";
192
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, movie->id(), m_id ) == false )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
193 194 195 196 197 198
        return false;
    m_movie = movie;
    m_movieId = movie->id();
    return true;
}

199
bool Media::addVideoTrack(const std::string& codec, unsigned int width, unsigned int height, float fps)
200 201 202 203 204 205 206 207 208 209
{
    static const std::string req = "INSERT INTO VideoTrackFileRelation VALUES(?, ?)";

    auto track = VideoTrack::fetch( m_dbConnection, codec, width, height, fps );
    if ( track == nullptr )
    {
        track = VideoTrack::create( m_dbConnection, codec, width, height, fps );
        if ( track == nullptr )
            return false;
    }
210
    return sqlite::Tools::executeRequest( m_dbConnection, req, track->id(), m_id );
211 212
}

213
std::vector<VideoTrackPtr> Media::videoTracks()
214 215 216
{
    static const std::string req = "SELECT t.* FROM " + policy::VideoTrackTable::Name +
            " t LEFT JOIN VideoTrackFileRelation vtfr ON vtfr.id_track = t.id_track"
217
            " WHERE vtfr.id_media = ?";
218
    return VideoTrack::fetchAll( m_dbConnection, req, m_id );
219 220
}

221
bool Media::addAudioTrack( const std::string& codec, unsigned int bitrate,
222
                          unsigned int sampleRate, unsigned int nbChannels )
223 224 225
{
    static const std::string req = "INSERT INTO AudioTrackFileRelation VALUES(?, ?)";

226
    auto track = AudioTrack::fetch( m_dbConnection, codec, bitrate, sampleRate, nbChannels );
227 228
    if ( track == nullptr )
    {
229
        track = AudioTrack::create( m_dbConnection, codec, bitrate, sampleRate, nbChannels );
230 231 232
        if ( track == nullptr )
            return false;
    }
233
    return sqlite::Tools::executeRequest( m_dbConnection, req, track->id(), m_id );
234 235
}

236
std::vector<AudioTrackPtr> Media::audioTracks()
237 238 239
{
    static const std::string req = "SELECT t.* FROM " + policy::AudioTrackTable::Name +
            " t LEFT JOIN AudioTrackFileRelation atfr ON atfr.id_track = t.id_track"
240
            " WHERE atfr.id_media = ?";
241
    return AudioTrack::fetchAll( m_dbConnection, req, m_id );
242 243
}

244
const std::string &Media::snapshot()
245 246 247 248
{
    return m_snapshot;
}

249
bool Media::setSnapshot(const std::string &snapshot)
250
{
251 252
    static const std::string req = "UPDATE " + policy::MediaTable::Name
            + " SET snapshot = ? WHERE id_media = ?";
253 254 255 256 257 258
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, snapshot, m_id ) == false )
        return false;
    m_snapshot = snapshot;
    return true;
}

259
bool Media::isStandAlone()
260 261 262 263
{
    return m_folderId == 0;
}

264
unsigned int Media::lastModificationDate()
265 266 267 268
{
    return m_lastModificationDate;
}

269
bool Media::isParsed() const
270
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
271
    return m_isParsed;
272 273
}

274
bool Media::markParsed()
275
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
276 277
    if ( m_isParsed == true )
        return true;
278 279
    static const std::string req = "UPDATE " + policy::MediaTable::Name
            + " SET parsed = ? WHERE id_media = ?";
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
280 281 282 283
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, true, m_id ) == false )
        return false;
    m_isParsed = true;
    return true;
284 285
}

286
unsigned int Media::id() const
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
287 288 289 290
{
    return m_id;
}

291
IMedia::Type Media::type()
292 293 294 295
{
    return m_type;
}

296
bool Media::setType( Type type )
297
{
298 299
    static const std::string req = "UPDATE " + policy::MediaTable::Name
            + " SET type = ? WHERE id_media = ?";
300
    // We need to convert to an integer representation for the sqlite traits to work properly
301
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, type, m_id ) == false )
302 303 304 305 306
        return false;
    m_type = type;
    return true;
}

307
const std::string &Media::title()
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
308
{
309
    return m_title;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
310 311
}

312
bool Media::setTitle( const std::string &title )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
313
{
314
    static const std::string req = "UPDATE " + policy::MediaTable::Name
315 316
            + " SET title = ? WHERE id_media = ?";
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, title, m_id ) == false )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
317
        return false;
318
    m_title = title;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
319 320 321
    return true;
}

322
bool Media::createTable( DBConnection connection )
323
{
324 325
    std::string req = "CREATE TABLE IF NOT EXISTS " + policy::MediaTable::Name + "("
            "id_media INTEGER PRIMARY KEY AUTOINCREMENT,"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
326
            "type INTEGER,"
327
            "duration INTEGER,"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
328 329
            "play_count UNSIGNED INTEGER,"
            "show_episode_id UNSIGNED INTEGER,"
330
            "mrl TEXT UNIQUE ON CONFLICT FAIL,"
331
            "artist TEXT,"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
332
            "movie_id UNSIGNED INTEGER,"
333
            "folder_id UNSIGNED INTEGER,"
334
            "last_modification_date UNSIGNED INTEGER,"
335
            "snapshot TEXT,"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
336
            "parsed BOOLEAN,"
337
            "title TEXT,"
338
            "FOREIGN KEY (show_episode_id) REFERENCES " + policy::ShowEpisodeTable::Name
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
339 340
            + "(id_episode) ON DELETE CASCADE,"
            "FOREIGN KEY (movie_id) REFERENCES " + policy::MovieTable::Name
341 342 343
            + "(id_movie) ON DELETE CASCADE,"
            "FOREIGN KEY (folder_id) REFERENCES " + policy::FolderTable::Name
            + "(id_folder) ON DELETE CASCADE"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
344
            ")";
345
    if ( sqlite::Tools::executeRequest( connection, req ) == false )
346 347 348
        return false;
    req = "CREATE TABLE IF NOT EXISTS VideoTrackFileRelation("
                "id_track INTEGER,"
349 350
                "id_media INTEGER,"
                "PRIMARY KEY ( id_track, id_media ), "
351 352
                "FOREIGN KEY ( id_track ) REFERENCES " + policy::VideoTrackTable::Name +
                    "(id_track) ON DELETE CASCADE,"
353 354
                "FOREIGN KEY ( id_media ) REFERENCES " + policy::MediaTable::Name
                + "(id_media) ON DELETE CASCADE"
355
            ")";
356
    if ( sqlite::Tools::executeRequest( connection, req ) == false )
357 358 359
        return false;
    req = "CREATE TABLE IF NOT EXISTS AudioTrackFileRelation("
                "id_track INTEGER,"
360 361
                "id_media INTEGER,"
                "PRIMARY KEY ( id_track, id_media ), "
362 363
                "FOREIGN KEY ( id_track ) REFERENCES " + policy::AudioTrackTable::Name +
                    "(id_track) ON DELETE CASCADE,"
364 365
                "FOREIGN KEY ( id_media ) REFERENCES " + policy::MediaTable::Name
                + "(id_media) ON DELETE CASCADE"
366
            ")";
367
    return sqlite::Tools::executeRequest( connection, req );
368
}
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
369

370
bool Media::addLabel( LabelPtr label )
371
{
372
    if ( m_id == 0 || label->id() == 0 )
373
    {
374
        LOG_ERROR( "Both file & label need to be inserted in database before being linked together" );
375 376 377
        return false;
    }
    const char* req = "INSERT INTO LabelFileRelation VALUES(?, ?)";
378
    return sqlite::Tools::executeRequest( m_dbConnection, req, label->id(), m_id );
379 380
}

381
bool Media::removeLabel( LabelPtr label )
382
{
383
    if ( m_id == 0 || label->id() == 0 )
384
    {
385
        LOG_ERROR( "Can't unlink a label/file not inserted in database" );
386 387
        return false;
    }
388
    const char* req = "DELETE FROM LabelFileRelation WHERE id_label = ? AND id_media = ?";
389
    return sqlite::Tools::executeDelete( m_dbConnection, req, label->id(), m_id );
390
}
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
391

392
const std::string& policy::MediaCache::key(const std::shared_ptr<Media> self )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
393 394 395 396
{
    return self->mrl();
}

397
std::string policy::MediaCache::key(sqlite::Row& row)
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
398
{
399
    return row.load<std::string>( 5 );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
400
}