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

Album: Use external thumbnail table to store artworks

parent a839bc18
......@@ -31,6 +31,7 @@
#include "Artist.h"
#include "Genre.h"
#include "Media.h"
#include "Thumbnail.h"
#include "database/SqliteTools.h"
......@@ -49,19 +50,19 @@ Album::Album(MediaLibraryPtr ml, sqlite::Row& row)
>> m_artistId
>> m_releaseYear
>> m_shortSummary
>> m_artworkMrl
>> m_thumbnailId
>> m_nbTracks
>> m_duration
>> m_isPresent;
}
Album::Album( MediaLibraryPtr ml, const std::string& title, const std::string& artworkMrl )
Album::Album( MediaLibraryPtr ml, const std::string& title, int64_t thumbnailId )
: m_ml( ml )
, m_id( 0 )
, m_title( title )
, m_artistId( 0 )
, m_releaseYear( ~0u )
, m_artworkMrl( artworkMrl )
, m_thumbnailId( thumbnailId )
, m_nbTracks( 0 )
, m_duration( 0 )
, m_isPresent( true )
......@@ -73,6 +74,7 @@ Album::Album( MediaLibraryPtr ml, const Artist* artist )
, m_id( 0 )
, m_artistId( artist->id() )
, m_releaseYear( ~0u )
, m_thumbnailId( 0 )
, m_nbTracks( 0 )
, m_duration( 0 )
, m_isPresent( true )
......@@ -135,16 +137,40 @@ bool Album::setShortSummary( const std::string& summary )
const std::string& Album::artworkMrl() const
{
return m_artworkMrl;
if ( m_thumbnailId == 0 )
return Thumbnail::EmptyMrl;
auto lock = m_thumbnail.lock();
if ( m_thumbnail.isCached() == false )
{
auto thumbnail = Thumbnail::fetch( m_ml, m_thumbnailId );
if ( thumbnail == nullptr )
return Thumbnail::EmptyMrl;
m_thumbnail = std::move( thumbnail );
}
return m_thumbnail.get()->mrl();
}
bool Album::setArtworkMrl( const std::string& artworkMrl )
{
if ( m_thumbnailId != 0 )
return Thumbnail::setMrlFromPrimaryKey( m_ml, m_thumbnail, m_thumbnailId,
artworkMrl );
std::unique_ptr<sqlite::Transaction> t;
if ( sqlite::Transaction::transactionInProgress() == false )
t = m_ml->getConn()->newTransaction();
auto lock = m_thumbnail.lock();
m_thumbnail = Thumbnail::create( m_ml, artworkMrl, Thumbnail::Origin::Album );
if ( m_thumbnail.get() == nullptr )
return false;
static const std::string req = "UPDATE " + policy::AlbumTable::Name
+ " SET artwork_mrl = ? WHERE id_album = ?";
if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, artworkMrl, m_id ) == false )
+ " SET thumbnail_id = ? WHERE id_album = ?";
if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, m_thumbnail.get()->id(), m_id ) == false )
return false;
m_artworkMrl = artworkMrl;
m_thumbnailId = m_thumbnail.get()->id();
if ( t != nullptr )
t->commit();
return true;
}
......@@ -341,12 +367,14 @@ void Album::createTable( sqlite::Connection* dbConnection )
"artist_id UNSIGNED INTEGER,"
"release_year UNSIGNED INTEGER,"
"short_summary TEXT,"
"artwork_mrl TEXT,"
"thumbnail_id UNSIGNED INT,"
"nb_tracks UNSIGNED INTEGER DEFAULT 0,"
"duration UNSIGNED INTEGER NOT NULL DEFAULT 0,"
"is_present BOOLEAN NOT NULL DEFAULT 1,"
"FOREIGN KEY( artist_id ) REFERENCES " + policy::ArtistTable::Name
+ "(id_artist) ON DELETE CASCADE"
+ "(id_artist) ON DELETE CASCADE,"
"FOREIGN KEY(thumbnail_id) REFERENCES " + policy::ThumbnailTable::Name
+ "(id_thumbnail)"
")";
const std::string reqRel = "CREATE TABLE IF NOT EXISTS AlbumArtistRelation("
"album_id INTEGER,"
......@@ -423,12 +451,12 @@ void Album::createTriggers( sqlite::Connection* dbConnection )
sqlite::Tools::executeRequest( dbConnection, vtriggerDelete );
}
std::shared_ptr<Album> Album::create( MediaLibraryPtr ml, const std::string& title, const std::string& artworkMrl )
std::shared_ptr<Album> Album::create( MediaLibraryPtr ml, const std::string& title, int64_t thumbnailId )
{
auto album = std::make_shared<Album>( ml, title, artworkMrl );
auto album = std::make_shared<Album>( ml, title, thumbnailId );
static const std::string req = "INSERT INTO " + policy::AlbumTable::Name +
"(id_album, title, artwork_mrl) VALUES(NULL, ?, ?)";
if ( insert( ml, album, req, title, artworkMrl ) == false )
"(id_album, title, thumbnail_id) VALUES(NULL, ?, ?)";
if ( insert( ml, album, req, title, sqlite::ForeignKey( thumbnailId ) ) == false )
return nullptr;
return album;
}
......
......@@ -39,6 +39,7 @@ class Album;
class AlbumTrack;
class Artist;
class Media;
class Thumbnail;
namespace policy
{
......@@ -54,7 +55,7 @@ class Album : public IAlbum, public DatabaseHelpers<Album, policy::AlbumTable>
{
public:
Album( MediaLibraryPtr ml, sqlite::Row& row );
Album( MediaLibraryPtr ml, const std::string& title, const std::string& artworkMrl );
Album( MediaLibraryPtr ml, const std::string& title, int64_t thumbnailId );
Album( MediaLibraryPtr ml, const Artist* artist );
virtual int64_t id() const override;
......@@ -100,7 +101,7 @@ class Album : public IAlbum, public DatabaseHelpers<Album, policy::AlbumTable>
static void createTable( sqlite::Connection* dbConnection );
static void createTriggers( sqlite::Connection* dbConnection );
static std::shared_ptr<Album> create( MediaLibraryPtr ml, const std::string& title, const std::string& artworkMrl );
static std::shared_ptr<Album> create( MediaLibraryPtr ml, const std::string& title, int64_t thumbnailId );
static std::shared_ptr<Album> createUnknownAlbum( MediaLibraryPtr ml, const Artist* artist );
///
/// \brief search search for an album, through its albumartist or title
......@@ -122,13 +123,14 @@ class Album : public IAlbum, public DatabaseHelpers<Album, policy::AlbumTable>
int64_t m_artistId;
unsigned int m_releaseYear;
std::string m_shortSummary;
std::string m_artworkMrl;
int64_t m_thumbnailId;
unsigned int m_nbTracks;
unsigned int m_duration;
bool m_isPresent;
mutable Cache<std::vector<MediaPtr>> m_tracks;
mutable Cache<std::shared_ptr<Artist>> m_albumArtist;
mutable Cache<std::shared_ptr<Thumbnail>> m_thumbnail;
friend struct policy::AlbumTable;
};
......
......@@ -492,9 +492,9 @@ AlbumPtr MediaLibrary::album( int64_t id ) const
return Album::fetch( this, id );
}
std::shared_ptr<Album> MediaLibrary::createAlbum( const std::string& title, const std::string& artworkMrl )
std::shared_ptr<Album> MediaLibrary::createAlbum( const std::string& title, int64_t thumbnailId )
{
return Album::create( this, title, artworkMrl );
return Album::create( this, title, thumbnailId );
}
std::vector<AlbumPtr> MediaLibrary::albums( SortingCriteria sort, bool desc ) const
......@@ -850,6 +850,9 @@ InitializeResult MediaLibrary::updateDatabaseModel( unsigned int previousVersion
}
if ( previousVersion == 13 )
{
// We need to recreate many thumbnail records, and hopefully
// generate better ones
needRescan = true;
migrateModel13to14();
previousVersion = 14;
}
......@@ -1046,15 +1049,21 @@ void MediaLibrary::migrateModel13to14()
sqlite::Connection::WeakDbContext weakConnCtx{ getConn() };
auto t = getConn()->newTransaction();
using namespace policy;
using ThumbnailType = typename std::underlying_type<Thumbnail::Origin>::type;
std::string reqs[] = {
# include "database/migrations/migration13-14.sql"
};
for ( const auto& req : reqs )
sqlite::Tools::executeRequest( getConn(), req );
// Re-create tables that we just removed
// We will run a re-scan, so we don't care about keeping their content
Album::createTable( getConn() );
// Re-create triggers removed in the process
Media::createTriggers( getConn() );
AlbumTrack::createTriggers( getConn() );
Album::createTriggers( getConn() );
Artist::createTriggers( getConn(), 14 );
t->commit();
}
......
......@@ -86,7 +86,7 @@ class MediaLibrary : public IMediaLibrary, public IDeviceListerCb
virtual bool deleteLabel( LabelPtr label ) override;
virtual AlbumPtr album( int64_t id ) const override;
std::shared_ptr<Album> createAlbum( const std::string& title, const std::string& artworkMrl );
std::shared_ptr<Album> createAlbum( const std::string& title, int64_t thumbnailId );
virtual std::vector<AlbumPtr> albums(SortingCriteria sort, bool desc) const override;
virtual std::vector<GenrePtr> genres( SortingCriteria sort, bool desc ) const override;
......
/******************* Migrate Media table **************************************/
"CREATE TEMPORARY TABLE " + MediaTable::Name + "_backup("
"id_media INTEGER PRIMARY KEY AUTOINCREMENT,"
"type INTEGER,"
......@@ -18,8 +19,7 @@
"INSERT INTO " + ThumbnailTable::Name + "(id_thumbnail, mrl, origin) "
"SELECT id_media, thumbnail, " +
std::to_string( static_cast<typename std::underlying_type<Thumbnail::Origin>::type>(
Thumbnail::Origin::UserProvided ) ) +
std::to_string( static_cast<ThumbnailType>( Thumbnail::Origin::UserProvided ) ) +
" FROM " + MediaTable::Name + " WHERE thumbnail IS NOT NULL AND thumbnail != ''",
"DROP TABLE " + MediaTable::Name,
......@@ -53,3 +53,7 @@
"CASE thumbnail WHEN NULL THEN 0 WHEN '' THEN 0 ELSE 1 END,"
"title, filename, is_favorite, is_present FROM " + MediaTable::Name + "_backup",
/******************* Delete other tables **************************************/
"DROP TABLE " + AlbumTable::Name,
"DELETE FROM " + AlbumTable::Name + "Fts",
......@@ -395,7 +395,15 @@ bool MetadataParser::parseAudioFile( parser::Task& task )
if ( album == nullptr )
{
const auto& albumName = task.vlcMedia.meta( libvlc_meta_Album );
album = m_ml->createAlbum( albumName, artworkMrl );
int64_t thumbnailId = 0;
if ( artworkMrl.empty() == false )
{
auto thumbnail = Thumbnail::create( m_ml, artworkMrl,
Thumbnail::Origin::Album );
if ( thumbnail != nullptr )
thumbnailId = thumbnail->id();
}
album = m_ml->createAlbum( albumName, thumbnailId );
if ( album == nullptr )
return false;
m_notifier->notifyAlbumCreation( album );
......
......@@ -120,7 +120,7 @@ void MediaLibraryTester::deleteAlbum( int64_t albumId )
std::shared_ptr<Album> MediaLibraryTester::createAlbum( const std::string& title )
{
return MediaLibrary::createAlbum( title, "dummy artwork" );
return MediaLibrary::createAlbum( title, 0 );
}
std::shared_ptr<Genre> MediaLibraryTester::createGenre( const std::string& name )
......
......@@ -169,9 +169,8 @@ TEST_F( DbModel, Upgrade12to13 )
LoadFakeDB( SRC_DIR "/test/unittest/db_v12.sql" );
auto res = ml->initialize( "test.db", "/tmp", cbMock.get() );
ASSERT_EQ( InitializeResult::Success, res );
// Check that we also recovered from the invalid album track trigger
auto albums = ml->albums( SortingCriteria::Default, false );
ASSERT_EQ( 1u, albums.size() );
// We can't check for the number of albums anymore since they are deleted
// as part of 13 -> 14 migration
}
TEST_F( DbModel, Upgrade13to14 )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment