Commit 7c24fb0b authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Media: Add a metadata system

parent 3ffbca8b
......@@ -34,6 +34,15 @@ class IAlbumTrack;
class IShowEpisode;
class ITrackInformation;
class IMediaMetadata
{
public:
virtual ~IMediaMetadata() = default;
virtual bool isSet() const = 0;
virtual int64_t integer() const = 0;
virtual const std::string& str() const = 0;
};
class IMedia
{
public:
......@@ -50,6 +59,11 @@ class IMedia
Movie,
AlbumTrack,
};
enum class MetadataType : uint8_t
{
AspectRatio,
Speed,
};
virtual ~IMedia() = default;
......@@ -95,6 +109,19 @@ class IMedia
virtual const std::string& thumbnail() = 0;
virtual unsigned int insertionDate() const = 0;
virtual unsigned int releaseDate() const = 0;
/// Metadata
///
/// \brief metadata Fetch (or return a cached) metadata value for this media
/// \param type The metadata type
/// \return A reference to a wrapper object representing the metadata.
///
virtual const IMediaMetadata& metadata( MetadataType type ) const = 0;
///
/// \brief setMetadata Immediatly saves a metadata in database
///
virtual bool setMetadata( MetadataType type, const std::string& value ) = 0;
virtual bool setMetadata( MetadataType type, int64_t value ) = 0;
};
}
......@@ -55,6 +55,8 @@ const std::string policy::MediaTable::Name = "Media";
const std::string policy::MediaTable::PrimaryKeyColumn = "id_media";
int64_t Media::* const policy::MediaTable::PrimaryKey = &Media::m_id;
const std::string policy::MediaMetadataTable::Name = "MediaMetadata";
Media::Media( MediaLibraryPtr ml, sqlite::Row& row )
: m_ml( ml )
, m_changed( false )
......@@ -306,6 +308,61 @@ unsigned int Media::releaseDate() const
return m_releaseDate;
}
const IMediaMetadata& Media::metadata( IMedia::MetadataType type ) const
{
auto lock = m_metadata.lock();
if ( m_metadata.isCached() == false )
{
std::vector<MediaMetadata> res;
static const std::string req = "SELECT * FROM " + policy::MediaMetadataTable::Name +
" WHERE id_media = ?";
sqlite::Statement stmt( m_ml->getConn()->getConn(), req );
stmt.execute( m_id );
for ( sqlite::Row row = stmt.row(); row != nullptr; row = stmt.row() )
{
assert( row.load<int64_t>( 0 ) == m_id );
res.emplace_back( row.load<decltype(MediaMetadata::m_type)>( 1 ),
row.load<decltype(MediaMetadata::m_value)>( 2 ) );
}
m_metadata = res;
}
auto it = std::find_if( begin( m_metadata.get() ), end( m_metadata.get() ), [type](const MediaMetadata& m ) {
return m.m_type == type;
});
if ( it == end( m_metadata.get() ) )
{
m_metadata.get().emplace_back( type );
return *m_metadata.get().rbegin();
}
return (*it);
}
bool Media::setMetadata( IMedia::MetadataType type, const std::string& value )
{
{
auto lock = m_metadata.lock();
if ( m_metadata.isCached() == true )
{
auto it = std::find_if( begin( m_metadata.get() ), end( m_metadata.get() ), [type](const MediaMetadata& m ) {
return m.m_type == type;
});
if ( it != end( m_metadata.get() ) )
(*it).m_value = value;
else
m_metadata.get().emplace_back( type, value );
}
}
static const std::string req = "INSERT OR REPLACE INTO " + policy::MediaMetadataTable::Name +
"(id_media, type, value) VALUES(?, ?, ?)";
return sqlite::Tools::executeInsert( m_ml->getConn(), req, m_id, type, value );
}
bool Media::setMetadata( IMedia::MetadataType type, int64_t value )
{
auto str = std::to_string( value );
return setMetadata( type, str );
}
void Media::setReleaseDate( unsigned int date )
{
if ( m_releaseDate == date )
......@@ -461,9 +518,16 @@ bool Media::createTable( DBConnection connection )
"title,"
"labels"
")";
const std::string metadataReq = "CREATE TABLE IF NOT EXISTS " + policy::MediaMetadataTable::Name + "("
"id_media INTEGER,"
"type INTEGER,"
"value TEXT,"
"PRIMARY KEY (id_media, type)"
")";
return sqlite::Tools::executeRequest( connection, req ) &&
sqlite::Tools::executeRequest( connection, indexReq ) &&
sqlite::Tools::executeRequest( connection, vtableReq );
sqlite::Tools::executeRequest( connection, vtableReq ) &&
sqlite::Tools::executeRequest( connection, metadataReq );
}
bool Media::createTriggers( DBConnection connection )
......@@ -564,4 +628,19 @@ void Media::clearHistory( MediaLibraryPtr ml )
sqlite::Tools::executeUpdate( dbConn, req );
}
bool Media::MediaMetadata::isSet() const
{
return m_isSet;
}
int64_t Media::MediaMetadata::integer() const
{
return atoll( m_value.c_str() );
}
const std::string& Media::MediaMetadata::str() const
{
return m_value;
}
}
......@@ -49,10 +49,31 @@ struct MediaTable
static const std::string PrimaryKeyColumn;
static int64_t Media::*const PrimaryKey;
};
struct MediaMetadataTable
{
static const std::string Name;
};
}
class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
{
class MediaMetadata : public IMediaMetadata
{
public:
MediaMetadata(MetadataType t, std::string v) : m_type( t ), m_value( std::move( v ) ), m_isSet( true ) {}
MediaMetadata(MetadataType t) : m_type( t ), m_isSet( false ) {}
virtual bool isSet() const override;
virtual int64_t integer() const override;
virtual const std::string& str() const override;
private:
MetadataType m_type;
std::string m_value;
bool m_isSet;
friend class Media;
};
public:
// Those should be private, however the standard states that the expression
// ::new (pv) T(std::forward(args)...)
......@@ -100,6 +121,11 @@ class Media : public IMedia, public DatabaseHelpers<Media, policy::MediaTable>
virtual const std::string& thumbnail() override;
virtual unsigned int insertionDate() const override;
virtual unsigned int releaseDate() const override;
virtual const IMediaMetadata& metadata( MetadataType type ) const override;
virtual bool setMetadata( MetadataType type, const std::string& value ) override;
virtual bool setMetadata( MetadataType type, int64_t value ) override;
void setReleaseDate( unsigned int date );
void setThumbnail( const std::string& thumbnail );
bool save();
......@@ -140,6 +166,7 @@ private:
mutable Cache<ShowEpisodePtr> m_showEpisode;
mutable Cache<MoviePtr> m_movie;
mutable Cache<std::vector<FilePtr>> m_files;
mutable Cache<std::vector<MediaMetadata>> m_metadata;
bool m_changed;
friend policy::MediaTable;
......
......@@ -438,6 +438,49 @@ TEST_F( Medias, SetType )
ASSERT_EQ( IMedia::Type::Video, m2->type() );
}
TEST_F( Medias, Metadata )
{
auto m = ml->addFile( "media.mp3" );
{
const auto& md = m->metadata( Media::MetadataType::Speed );
ASSERT_FALSE( md.isSet() );
}
auto res = m->setMetadata( Media::MetadataType::Speed, "foo" );
ASSERT_TRUE( res );
{
const auto& md = m->metadata( Media::MetadataType::Speed );
ASSERT_EQ( "foo", md.str() );
}
Reload();
m = ml->media( m->id() );
const auto& md = m->metadata( Media::MetadataType::Speed );
ASSERT_EQ( "foo", md.str() );
}
TEST_F( Medias, MetadataOverride )
{
auto m = ml->addFile( "media.mp3" );
auto res = m->setMetadata( Media::MetadataType::Speed, "foo" );
ASSERT_TRUE( res );
m->setMetadata( Media::MetadataType::Speed, "otter" );
{
const auto& md = m->metadata( Media::MetadataType::Speed );
ASSERT_EQ( "otter", md.str() );
}
Reload();
m = ml->media( m->id() );
const auto& md = m->metadata( Media::MetadataType::Speed );
ASSERT_EQ( "otter", md.str() );
}
class FetchMedia : public Tests
{
protected:
......
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