Album.cpp 9.46 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
#include <algorithm>

25
#include "Album.h"
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
26
#include "AlbumTrack.h"
27
#include "Artist.h"
28
#include "Media.h"
29

30
#include "database/SqliteTools.h"
31

Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
32
const std::string policy::AlbumTable::Name = "Album";
33
const std::string policy::AlbumTable::PrimaryKeyColumn = "id_album";
34
unsigned int Album::* const policy::AlbumTable::PrimaryKey = &Album::m_id;
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
35

36
Album::Album(DBConnection dbConnection, sqlite::Row& row)
37 38
    : m_dbConnection( dbConnection )
{
39 40
    row >> m_id
        >> m_title
41
        >> m_artistId
42
        >> m_releaseYear
43
        >> m_shortSummary
44
        >> m_artworkMrl
45 46
        >> m_nbTracks
        >> m_isPresent;
47 48
}

49
Album::Album(const std::string& title )
50
    : m_id( 0 )
51
    , m_title( title )
52
    , m_artistId( 0 )
53
    , m_releaseYear( ~0u )
54
    , m_nbTracks( 0 )
55
    , m_isPresent( true )
56 57 58
{
}

59 60 61 62 63
Album::Album( const Artist* artist )
    : m_id( 0 )
    , m_artistId( artist->id() )
    , m_releaseYear( ~0u )
    , m_nbTracks( 0 )
64
    , m_isPresent( true )
65 66 67
{
}

68 69 70
unsigned int Album::id() const
{
    return m_id;
71 72
}

73
const std::string& Album::title() const
74
{
75
    return m_title;
76 77
}

78
unsigned int Album::releaseYear() const
79
{
80 81
    if ( m_releaseYear == ~0U )
        return 0;
82 83 84
    return m_releaseYear;
}

85
bool Album::setReleaseYear( unsigned int date, bool force )
86
{
87 88 89 90 91 92 93 94 95 96 97 98
    if ( date == m_releaseYear )
        return true;
    if ( force == false )
    {
        if ( m_releaseYear != ~0u && date != m_releaseYear )
        {
            // If we already have set the date back to 0, don't do it again.
            if ( m_releaseYear == 0 )
                return true;
            date = 0;
        }
    }
99 100 101 102 103 104 105 106
    static const std::string req = "UPDATE " + policy::AlbumTable::Name
            + " SET release_year = ? WHERE id_album = ?";
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, date, m_id ) == false )
        return false;
    m_releaseYear = date;
    return true;
}

107
const std::string& Album::shortSummary() const
108 109 110 111
{
    return m_shortSummary;
}

112 113
bool Album::setShortSummary( const std::string& summary )
{
114
    static const std::string req = "UPDATE " + policy::AlbumTable::Name
115
            + " SET short_summary = ? WHERE id_album = ?";
116
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, summary, m_id ) == false )
117 118 119 120 121
        return false;
    m_shortSummary = summary;
    return true;
}

122
const std::string& Album::artworkMrl() const
123
{
124
    return m_artworkMrl;
125 126
}

127
bool Album::setArtworkMrl( const std::string& artworkMrl )
128
{
129
    static const std::string req = "UPDATE " + policy::AlbumTable::Name
130 131
            + " SET artwork_mrl = ? WHERE id_album = ?";
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, artworkMrl, m_id ) == false )
132
        return false;
133
    m_artworkMrl = artworkMrl;
134 135 136
    return true;
}

137
std::vector<MediaPtr> Album::tracks() const
138
{
139 140
    static const std::string req = "SELECT med.* FROM " + policy::MediaTable::Name + " med "
            " LEFT JOIN " + policy::AlbumTrackTable::Name + " att ON att.media_id = med.id_media "
141
            " WHERE att.album_id = ? AND med.is_present = 1 ORDER BY att.disc_number, att.track_number";
142
    return Media::fetchAll<IMedia>( m_dbConnection, req, m_id );
143 144
}

145
std::shared_ptr<AlbumTrack> Album::addTrack(std::shared_ptr<Media> media, unsigned int trackNb, unsigned int discNumber )
146
{
147 148
    auto t = m_dbConnection->newTransaction();

149
    auto track = AlbumTrack::create( m_dbConnection, m_id, media.get(), trackNb, discNumber );
150 151 152 153 154 155 156 157 158
    if ( track == nullptr )
        return nullptr;
    if ( media->setAlbumTrack( track ) == false )
        return nullptr;
    static const std::string req = "UPDATE " + policy::AlbumTable::Name +
            " SET nb_tracks = nb_tracks + 1 WHERE id_album = ?";
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, m_id ) == false )
        return nullptr;
    m_nbTracks++;
159
    t->commit();
160
    return track;
161 162
}

163 164 165 166 167
unsigned int Album::nbTracks() const
{
    return m_nbTracks;
}

168 169 170 171 172 173 174
ArtistPtr Album::albumArtist() const
{
    if ( m_artistId == 0 )
        return nullptr;
    return Artist::fetch( m_dbConnection, m_artistId );
}

175
bool Album::setAlbumArtist( Artist* artist )
176 177 178 179 180 181 182 183 184
{
    if ( m_artistId == artist->id() )
        return true;
    if ( artist->id() == 0 )
        return false;
    static const std::string req = "UPDATE " + policy::AlbumTable::Name + " SET "
            "artist_id = ? WHERE id_album = ?";
    if ( sqlite::Tools::executeUpdate( m_dbConnection, req, artist->id(), m_id ) == false )
        return false;
185 186 187 188 189
    if ( m_artistId != 0 )
    {
        auto previousArtist = Artist::fetch( m_dbConnection, m_artistId );
        previousArtist->updateNbAlbum( -1 );
    }
190
    m_artistId = artist->id();
191
    artist->updateNbAlbum( 1 );
192 193 194
    return true;
}

195 196 197
std::vector<ArtistPtr> Album::artists() const
{
    static const std::string req = "SELECT art.* FROM " + policy::ArtistTable::Name + " art "
198 199
            "LEFT JOIN AlbumArtistRelation aar ON aar.artist_id = art.id_artist "
            "WHERE aar.album_id = ?";
200
    return Artist::fetchAll<IArtist>( m_dbConnection, req, m_id );
201 202
}

203
bool Album::addArtist( std::shared_ptr<Artist> artist )
204
{
205
    static const std::string req = "INSERT OR IGNORE INTO AlbumArtistRelation VALUES(?, ?)";
206 207
    if ( m_id == 0 || artist->id() == 0 )
    {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
208
        LOG_ERROR("Both artist & album need to be inserted in database before being linked together" );
209 210
        return false;
    }
211
    return sqlite::Tools::insert( m_dbConnection, req, m_id, artist->id() ) != 0;
212 213
}

214 215
bool Album::removeArtist(Artist* artist)
{
216
    static const std::string req = "DELETE FROM AlbumArtistRelation WHERE album_id = ? "
217 218 219 220
            "AND id_artist = ?";
    return sqlite::Tools::executeDelete( m_dbConnection, req, m_id, artist->id() );
}

221
bool Album::createTable(DBConnection dbConnection )
222
{
223 224 225
    static const std::string req = "CREATE TABLE IF NOT EXISTS " +
            policy::AlbumTable::Name +
            "("
226
                "id_album INTEGER PRIMARY KEY AUTOINCREMENT,"
227
                "title TEXT COLLATE NOCASE,"
228
                "artist_id UNSIGNED INTEGER,"
229
                "release_year UNSIGNED INTEGER,"
230
                "short_summary TEXT,"
231
                "artwork_mrl TEXT,"
232
                "nb_tracks UNSIGNED INTEGER DEFAULT 0,"
233 234 235
                "is_present BOOLEAN NOT NULL DEFAULT 1,"
                "FOREIGN KEY( artist_id ) REFERENCES " + policy::ArtistTable::Name
                + "(id_artist) ON DELETE CASCADE"
236
            ")";
237
    static const std::string reqRel = "CREATE TABLE IF NOT EXISTS AlbumArtistRelation("
238 239 240 241
                "album_id INTEGER,"
                "artist_id INTEGER,"
                "PRIMARY KEY (album_id, artist_id),"
                "FOREIGN KEY(album_id) REFERENCES " + policy::AlbumTable::Name + "("
242
                    + policy::AlbumTable::PrimaryKeyColumn + ") ON DELETE CASCADE,"
243
                "FOREIGN KEY(artist_id) REFERENCES " + policy::ArtistTable::Name + "("
244
                    + policy::ArtistTable::PrimaryKeyColumn + ") ON DELETE CASCADE"
245 246 247
            ")";
    return sqlite::Tools::executeRequest( dbConnection, req ) &&
            sqlite::Tools::executeRequest( dbConnection, reqRel );
248 249
}

250 251 252 253 254 255 256 257 258 259 260 261
bool Album::createTriggers(DBConnection dbConnection)
{
    static const std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS is_album_present AFTER UPDATE OF "
            "is_present ON " + policy::AlbumTrackTable::Name +
            " BEGIN "
            " UPDATE " + policy::AlbumTable::Name + " SET is_present="
                "(SELECT COUNT(id_track) FROM " + policy::AlbumTrackTable::Name + " WHERE album_id=new.album_id AND is_present=1) "
                "WHERE id_album=new.album_id;"
            " END";
    return sqlite::Tools::executeRequest( dbConnection, triggerReq );
}

262
std::shared_ptr<Album> Album::create(DBConnection dbConnection, const std::string& title )
263
{
264
    auto album = std::make_shared<Album>( title );
265
    static const std::string req = "INSERT INTO " + policy::AlbumTable::Name +
266
            "(id_album, title) VALUES(NULL, ?)";
267
    if ( insert( dbConnection, album, req, title ) == false )
268 269 270
        return nullptr;
    album->m_dbConnection = dbConnection;
    return album;
271
}
272 273 274 275 276 277

std::shared_ptr<Album> Album::createUnknownAlbum( DBConnection dbConnection, const Artist* artist )
{
    auto album = std::make_shared<Album>( artist );
    static const std::string req = "INSERT INTO " + policy::AlbumTable::Name +
            "(id_album, artist_id) VALUES(NULL, ?)";
278
    if ( insert( dbConnection, album, req, artist->id() ) == false )
279 280 281 282
        return nullptr;
    album->m_dbConnection = dbConnection;
    return album;
}