Media.cpp 10.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
const std::string policy::MediaTable::Name = "Media";
43
const std::string policy::MediaTable::PrimaryKeyColumn = "id_media";
44
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
    : m_dbConnection( dbConnection )
48
    , m_changed( false )
49
{
50 51 52 53 54 55
    row >> m_id
        >> m_type
        >> m_duration
        >> m_playCount
        >> m_showEpisodeId
        >> m_mrl
56
        >> m_artist
57 58 59
        >> m_movieId
        >> m_folderId
        >> m_lastModificationDate
60
        >> m_insertionDate
61 62
        >> m_snapshot
        >> m_isParsed
63 64
        >> m_title
        >> m_isPresent;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
65 66
}

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

85
std::shared_ptr<Media> Media::create( DBConnection dbConnection, Type type, const fs::IFile* file, unsigned int folderId )
86
{
87 88
    auto self = std::make_shared<Media>( file, folderId, file->name(), type );
    static const std::string req = "INSERT INTO " + policy::MediaTable::Name +
89
            "(type, mrl, folder_id, last_modification_date, insertion_date, title) VALUES(?, ?, ?, ?, ?, ?)";
90

91
    if ( insert( dbConnection, self, req, type, self->m_mrl, sqlite::ForeignKey( folderId ),
92
                         self->m_lastModificationDate, self->m_insertionDate, self->m_title) == false )
93
        return nullptr;
94 95
    self->m_dbConnection = dbConnection;
    return self;
96 97
}

98
AlbumTrackPtr Media::albumTrack()
99
{
100
    if ( m_albumTrack == nullptr )
101
    {
102 103
        std::string req = "SELECT * FROM " + policy::AlbumTrackTable::Name +
                " WHERE media_id = ?";
104
        m_albumTrack = AlbumTrack::fetch( m_dbConnection, req, m_id );
105 106
    }
    return m_albumTrack;
107 108
}

109
bool Media::setAlbumTrack( AlbumTrackPtr albumTrack )
110 111 112 113 114
{
    m_albumTrack = albumTrack;
    return true;
}

115
const std::string& Media::artist() const
116
{
117
    return m_artist;
118 119
}

120
void Media::setArtist(const std::string& artist)
121
{
122
    if ( m_artist == artist )
123
        return;
124
    m_artist = artist;
125
    m_changed = true;
126 127
}

128
int64_t Media::duration() const
129 130 131 132
{
    return m_duration;
}

133
void Media::setDuration( int64_t duration )
134
{
135 136
    if ( m_duration == duration )
        return;
137
    m_duration = duration;
138
    m_changed = true;
139 140
}

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
void Media::setShowEpisode( ShowEpisodePtr showEpisode )
151
{
152 153
    if ( showEpisode == nullptr || m_showEpisodeId == showEpisode->id() )
        return;
154 155
    m_showEpisodeId = showEpisode->id();
    m_showEpisode = showEpisode;
156
    m_changed = true;
157 158
}

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

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

172 173 174 175 176 177
void Media::increasePlayCount()
{
    m_playCount++;
    m_changed = true;
}

178
const std::string& Media::mrl() const
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
179 180 181 182
{
    return m_mrl;
}

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

192
void Media::setMovie( MoviePtr movie )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
193
{
194 195
    if ( movie == nullptr || m_movieId == movie->id() )
        return;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
196 197
    m_movie = movie;
    m_movieId = movie->id();
198
    m_changed = true;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
199 200
}

201
bool Media::addVideoTrack(const std::string& codec, unsigned int width, unsigned int height, float fps)
202
{
203
    return VideoTrack::create( m_dbConnection, codec, width, height, fps, m_id ) != nullptr;
204 205
}

206
std::vector<VideoTrackPtr> Media::videoTracks()
207
{
208 209
    static const std::string req = "SELECT * FROM " + policy::VideoTrackTable::Name +
            " WHERE media_id = ?";
210
    return VideoTrack::fetchAll<IVideoTrack>( m_dbConnection, req, m_id );
211 212
}

213
bool Media::addAudioTrack( const std::string& codec, unsigned int bitrate,
214 215
                          unsigned int sampleRate, unsigned int nbChannels,
                          const std::string& language, const std::string& desc )
216
{
217
    return AudioTrack::create( m_dbConnection, codec, bitrate, sampleRate, nbChannels, language, desc, m_id ) != nullptr;
218 219
}

220
std::vector<AudioTrackPtr> Media::audioTracks()
221
{
222 223
    static const std::string req = "SELECT * FROM " + policy::AudioTrackTable::Name +
            " WHERE media_id = ?";
224
    return AudioTrack::fetchAll<IAudioTrack>( m_dbConnection, req, m_id );
225 226
}

227
const std::string &Media::snapshot()
228 229 230 231
{
    return m_snapshot;
}

232 233 234 235 236
unsigned int Media::insertionDate() const
{
    return m_insertionDate;
}

237
void Media::setSnapshot( const std::string& snapshot )
238
{
239 240
    if ( m_snapshot == snapshot )
        return;
241
    m_snapshot = snapshot;
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    m_changed = true;
}

bool Media::save()
{
    static const std::string req = "UPDATE " + policy::MediaTable::Name + " SET "
            "type = ?, duration = ?, play_count = ?, show_episode_id = ?, artist = ?,"
            "movie_id = ?, last_modification_date = ?, snapshot = ?, parsed = ?, title = ? "
            "WHERE id_media = ?";
    if ( m_changed == false )
        return true;
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, m_type, m_duration, m_playCount,
                                       sqlite::ForeignKey{ m_showEpisodeId }, m_artist,
                                       sqlite::ForeignKey{ m_movieId }, m_lastModificationDate,
                                       m_snapshot, m_isParsed, m_title, m_id ) == false )
    {
        return false;
    }
    m_changed = false;
261 262 263
    return true;
}

264
bool Media::isStandAlone()
265 266 267 268
{
    return m_folderId == 0;
}

269
unsigned int Media::lastModificationDate()
270 271 272 273
{
    return m_lastModificationDate;
}

274
bool Media::isParsed() const
275
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
276
    return m_isParsed;
277 278
}

279
void Media::markParsed()
280
{
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
281
    if ( m_isParsed == true )
282
        return;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
283
    m_isParsed = true;
284
    m_changed = true;
285 286
}

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

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

297
void Media::setType( Type type )
298
{
299 300
    if ( m_type != type )
        return;
301
    m_type = type;
302
    m_changed = true;
303 304
}

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

310
void Media::setTitle( const std::string &title )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
311
{
312 313
    if ( m_title == title )
        return;
314
    m_title = title;
315
    m_changed = true;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
316 317
}

318
bool Media::createTable( DBConnection connection )
319
{
320 321
    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
322
            "type INTEGER,"
323
            "duration INTEGER DEFAULT -1,"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
324 325
            "play_count UNSIGNED INTEGER,"
            "show_episode_id UNSIGNED INTEGER,"
326
            "mrl TEXT UNIQUE ON CONFLICT FAIL,"
327
            "artist TEXT,"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
328
            "movie_id UNSIGNED INTEGER,"
329
            "folder_id UNSIGNED INTEGER,"
330
            "last_modification_date UNSIGNED INTEGER,"
331
            "insertion_date UNSIGNED INTEGER,"
332
            "snapshot TEXT,"
333
            "parsed BOOLEAN NOT NULL DEFAULT 0,"
334
            "title TEXT,"
335
            "is_present BOOLEAN NOT NULL DEFAULT 1,"
336
            "FOREIGN KEY (show_episode_id) REFERENCES " + policy::ShowEpisodeTable::Name
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
337 338
            + "(id_episode) ON DELETE CASCADE,"
            "FOREIGN KEY (movie_id) REFERENCES " + policy::MovieTable::Name
339 340 341
            + "(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
342
            ")";
343 344 345 346 347 348 349
    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 );
350
}
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
351

352
bool Media::addLabel( LabelPtr label )
353
{
354
    if ( m_id == 0 || label->id() == 0 )
355
    {
356
        LOG_ERROR( "Both file & label need to be inserted in database before being linked together" );
357 358 359
        return false;
    }
    const char* req = "INSERT INTO LabelFileRelation VALUES(?, ?)";
360
    return sqlite::Tools::insert( m_dbConnection, req, label->id(), m_id ) != 0;
361 362
}

363
bool Media::removeLabel( LabelPtr label )
364
{
365
    if ( m_id == 0 || label->id() == 0 )
366
    {
367
        LOG_ERROR( "Can't unlink a label/file not inserted in database" );
368 369
        return false;
    }
370
    const char* req = "DELETE FROM LabelFileRelation WHERE id_label = ? AND id_media = ?";
371
    return sqlite::Tools::executeDelete( m_dbConnection, req, label->id(), m_id );
372
}