Commit 0f9f55dc authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Revert "Remove streams history."

This reverts commit e9fb1cbb.
parent 34af64c5
......@@ -16,6 +16,7 @@ libmedialibrary_la_HEADERS = \
include/medialibrary/IAudioTrack.h \
include/medialibrary/IFile.h \
include/medialibrary/IGenre.h \
include/medialibrary/IHistoryEntry.h \
include/medialibrary/ILabel.h \
include/medialibrary/ILogger.h \
include/medialibrary/IMedia.h \
......@@ -40,6 +41,7 @@ libmedialibrary_la_SOURCES = \
src/File.cpp \
src/Folder.cpp \
src/Genre.cpp \
src/History.cpp \
src/Label.cpp \
src/Media.cpp \
src/MediaLibrary.cpp \
......@@ -88,6 +90,7 @@ noinst_HEADERS = \
include/medialibrary/IAudioTrack.h \
include/medialibrary/IFile.h \
include/medialibrary/IGenre.h \
include/medialibrary/IHistoryEntry.h \
include/medialibrary/ILabel.h \
include/medialibrary/ILogger.h \
include/medialibrary/IMedia.h \
......@@ -130,6 +133,7 @@ noinst_HEADERS = \
src/filesystem/win32/File.h \
src/Folder.h \
src/Genre.h \
src/History.h \
src/Label.h \
src/logging/IostreamLogger.h \
src/logging/Logger.h \
......@@ -269,6 +273,7 @@ unittest_SOURCES = \
test/unittest/FolderTests.cpp \
test/unittest/FsUtilsTests.cpp \
test/unittest/GenreTests.cpp \
test/unittest/HistoryTests.cpp \
test/unittest/LabelTests.cpp \
test/unittest/MediaTests.cpp \
test/unittest/MovieTests.cpp \
......
/*****************************************************************************
* 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.
*****************************************************************************/
#pragma once
#include <string>
namespace medialibrary
{
class IHistoryEntry
{
public:
virtual ~IHistoryEntry() = default;
virtual const std::string& mrl() const = 0;
virtual const std::string& title() const = 0;
virtual unsigned int insertionDate() const = 0;
virtual bool isFavorite() const = 0;
virtual bool setFavorite( bool isFavorite ) = 0;
};
}
......@@ -196,6 +196,8 @@ class IMediaLibrary
/**
* History
*/
virtual bool addToHistory( const std::string& mrl, const std::string& title ) = 0;
virtual std::vector<HistoryPtr> lastStreamsPlayed() const = 0;
virtual std::vector<MediaPtr> lastMediaPlayed() const = 0;
/**
* @brief clearHistory will clear both streams history & media history.
......
/*****************************************************************************
* 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.
*****************************************************************************/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include "History.h"
#include "database/SqliteTools.h"
namespace medialibrary
{
namespace policy
{
const std::string HistoryTable::Name = "History";
const std::string HistoryTable::PrimaryKeyColumn = "id_record";
int64_t History::* const HistoryTable::PrimaryKey = &History::m_id;
}
constexpr unsigned int History::MaxEntries;
History::History( MediaLibraryPtr ml, sqlite::Row& row )
: m_ml( ml )
{
row >> m_id
>> m_mrl
>> m_title
>> m_date
>> m_favorite;
}
bool History::createTable( DBConnection dbConnection )
{
const std::string req = "CREATE TABLE IF NOT EXISTS " + policy::HistoryTable::Name +
"("
"id_record INTEGER PRIMARY KEY AUTOINCREMENT,"
"mrl TEXT UNIQUE ON CONFLICT FAIL,"
"title TEXT,"
"insertion_date UNSIGNED INT NOT NULL DEFAULT (strftime('%s', 'now')),"
"favorite BOOLEAN NOT NULL DEFAULT 0"
")";
const std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS limit_nb_records AFTER INSERT ON "
+ policy::HistoryTable::Name +
" BEGIN "
"DELETE FROM " + policy::HistoryTable::Name + " WHERE id_record in "
"(SELECT id_record FROM " + policy::HistoryTable::Name +
" ORDER BY insertion_date DESC LIMIT -1 OFFSET " + std::to_string( MaxEntries ) + ");"
" END";
return sqlite::Tools::executeRequest( dbConnection, req ) &&
sqlite::Tools::executeRequest( dbConnection, triggerReq );
}
bool History::insert( DBConnection dbConn, const std::string& mrl, const std::string& title )
{
History::clear();
static const std::string req = "INSERT OR REPLACE INTO " + policy::HistoryTable::Name +
"(id_record, mrl, title, insertion_date, favorite)"
" SELECT id_record, mrl, ?, strftime('%s', 'now'), favorite FROM " +
policy::HistoryTable::Name + " WHERE mrl = ?"
" UNION SELECT NULL, ?, ?, NULL, NULL"
" ORDER BY id_record DESC"
" LIMIT 1";
return sqlite::Tools::executeInsert( dbConn, req, title, mrl, mrl, title ) != 0;
}
std::vector<HistoryPtr> History::fetch( MediaLibraryPtr ml )
{
static const std::string req = "SELECT * FROM " + policy::HistoryTable::Name + " ORDER BY insertion_date DESC";
return fetchAll<IHistoryEntry>( ml, req );
}
bool History::clearStreams( MediaLibraryPtr ml )
{
static const std::string req = "DROP TABLE " + policy::HistoryTable::Name;
auto dbConn = ml->getConn();
if ( sqlite::Tools::executeRequest( dbConn, req ) == false )
return false;
DatabaseHelpers<History, policy::HistoryTable>::clear();
if ( createTable( dbConn ) == false )
return false;
return true;
}
const std::string& History::mrl() const
{
return m_mrl;
}
const std::string& History::title() const
{
return m_title;
}
unsigned int History::insertionDate() const
{
return m_date;
}
bool History::isFavorite() const
{
return m_favorite;
}
bool History::setFavorite( bool isFavorite )
{
if ( isFavorite == m_favorite )
return true;
static const std::string req = "UPDATE " + policy::HistoryTable::Name + " SET favorite = ? WHERE id_record = ?";
if ( sqlite::Tools::executeUpdate( m_ml->getConn(), req, isFavorite, m_id ) == false )
return false;
m_favorite = isFavorite;
return true;
}
}
/*****************************************************************************
* 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.
*****************************************************************************/
#pragma once
#include "Types.h"
#include "database/DatabaseHelpers.h"
#include "medialibrary/IHistoryEntry.h"
#include <vector>
#include <string>
namespace medialibrary
{
class History;
class Media;
namespace policy
{
struct HistoryTable
{
static const std::string Name;
static const std::string PrimaryKeyColumn;
static int64_t History::* const PrimaryKey;
};
}
class History : public IHistoryEntry, public DatabaseHelpers<History, policy::HistoryTable>
{
public:
History( MediaLibraryPtr ml, sqlite::Row& row );
static bool createTable( DBConnection dbConnection );
static bool insert( DBConnection dbConn, const std::string& mrl, const std::string& title );
static std::vector<HistoryPtr> fetch( MediaLibraryPtr ml );
static bool clearStreams( MediaLibraryPtr ml );
virtual const std::string& mrl() const override;
virtual const std::string& title() const override;
virtual unsigned int insertionDate() const override;
virtual bool isFavorite() const override;
virtual bool setFavorite( bool isFavorite ) override;
static constexpr unsigned int MaxEntries = 100u;
private:
MediaLibraryPtr m_ml;
int64_t m_id;
std::string m_mrl;
std::string m_title;
unsigned int m_date;
bool m_favorite;
friend policy::HistoryTable;
};
}
......@@ -592,7 +592,8 @@ std::vector<MediaPtr> Media::fetchHistory( MediaLibraryPtr ml )
void Media::clearHistory( MediaLibraryPtr ml )
{
auto dbConn = ml->getConn();
auto t = dbConn->newTransaction();
// There should already be an active transaction, from MediaLibrary::clearHistory
assert( sqlite::Transaction::transactionInProgress() == true );
static const std::string req = "UPDATE " + policy::MediaTable::Name + " SET "
"play_count = 0,"
"last_played_date = NULL";
......@@ -602,7 +603,6 @@ void Media::clearHistory( MediaLibraryPtr ml )
clear();
sqlite::Tools::executeUpdate( dbConn, req );
sqlite::Tools::executeDelete( dbConn, flushProgress, IMedia::MetadataType::Progress );
t->commit();
}
bool Media::MediaMetadata::isSet() const
......
......@@ -38,6 +38,7 @@
#include "File.h"
#include "Folder.h"
#include "Genre.h"
#include "History.h"
#include "Media.h"
#include "MediaLibrary.h"
#include "Label.h"
......@@ -117,6 +118,7 @@ MediaLibrary::~MediaLibrary()
Device::clear();
File::clear();
Playlist::clear();
History::clear();
Genre::clear();
}
......@@ -148,6 +150,7 @@ bool MediaLibrary::createAllTables()
Artist::createTriggers( m_dbConnection.get() ) &&
Media::createTriggers( m_dbConnection.get() ) &&
Playlist::createTriggers( m_dbConnection.get() ) &&
History::createTable( m_dbConnection.get() ) &&
Settings::createTable( m_dbConnection.get() );
if ( res == false )
return false;
......@@ -488,6 +491,16 @@ bool MediaLibrary::deletePlaylist( int64_t playlistId )
return Playlist::destroy( this, playlistId );
}
bool MediaLibrary::addToHistory( const std::string& mrl, const std::string& title )
{
return History::insert( getConn(), mrl, title );
}
std::vector<HistoryPtr> MediaLibrary::lastStreamsPlayed() const
{
return History::fetch( this );
}
std::vector<MediaPtr> MediaLibrary::lastMediaPlayed() const
{
return Media::fetchHistory( this );
......@@ -495,7 +508,11 @@ std::vector<MediaPtr> MediaLibrary::lastMediaPlayed() const
bool MediaLibrary::clearHistory()
{
auto t = getConn()->newTransaction();
Media::clearHistory( this );
if ( History::clearStreams( this ) == false )
return false;
t->commit();
return true;
}
......
......@@ -101,6 +101,8 @@ class MediaLibrary : public IMediaLibrary, public IDeviceListerCb
virtual PlaylistPtr playlist( int64_t id ) const override;
virtual bool deletePlaylist( int64_t playlistId ) override;
virtual bool addToHistory( const std::string& mrl, const std::string& title ) override;
virtual std::vector<HistoryPtr> lastStreamsPlayed() const override;
virtual std::vector<MediaPtr> lastMediaPlayed() const override;
virtual bool clearHistory() override;
......
/*****************************************************************************
* 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.
*****************************************************************************/
#if HAVE_CONFIG_H
# include "config.h"
#endif
#include "Tests.h"
#include "medialibrary/IHistoryEntry.h"
#include "History.h"
#include "Media.h"
#include "compat/Thread.h"
class HistoryTest : public Tests
{
};
TEST_F( HistoryTest, InsertMrl )
{
ml->addToHistory( "upnp://stream", "title" );
auto hList = ml->lastStreamsPlayed();
ASSERT_EQ( 1u, hList.size() );
auto h = hList[0];
ASSERT_EQ( h->mrl(), "upnp://stream" );
ASSERT_EQ( h->title(), "title" );
ASSERT_NE( 0u, h->insertionDate() );
}
TEST_F( HistoryTest, updateTitle )
{
ml->addToHistory( "upnp://stream", "title" );
ml->addToHistory( "upnp://stream", "title2" );
auto hList = ml->lastStreamsPlayed();
ASSERT_EQ( 1u, hList.size() );
auto h = hList[0];
ASSERT_EQ( h->mrl(), "upnp://stream" );
ASSERT_EQ( h->title(), "title2" );
}
TEST_F( HistoryTest, MaxEntries )
{
for ( auto i = 0u; i < History::MaxEntries; ++i )
{
ml->addToHistory( std::to_string( i ), std::to_string( i ) );
}
auto hList = ml->lastStreamsPlayed();
ASSERT_EQ( History::MaxEntries, hList.size() );
ml->addToHistory( "new-media", "title" );
hList = ml->lastStreamsPlayed();
ASSERT_EQ( History::MaxEntries, hList.size() );
}
TEST_F( HistoryTest, Ordering )
{
ml->addToHistory( "first-stream", "title 1" );
compat::this_thread::sleep_for( std::chrono::seconds( 1 ) );
ml->addToHistory( "second-stream", "title 2" );
auto hList = ml->lastStreamsPlayed();
ASSERT_EQ( 2u, hList.size() );
ASSERT_EQ( hList[0]->mrl(), "second-stream" );
ASSERT_EQ( hList[1]->mrl(), "first-stream" );
}
TEST_F( HistoryTest, UpdateInsertionDate )
{
ml->addToHistory( "stream", "title" );
auto hList = ml->lastStreamsPlayed();
ASSERT_EQ( 1u, hList.size() );
auto date = hList[0]->insertionDate();
compat::this_thread::sleep_for( std::chrono::seconds( 1 ) );
ml->addToHistory( "stream", "title" );
hList = ml->lastStreamsPlayed();
ASSERT_EQ( 1u, hList.size() );
ASSERT_NE( date, hList[0]->insertionDate() );
}
TEST_F( HistoryTest, FavoriteMrl )
{
ml->addToHistory( "stream", "title" );
auto hList = ml->lastStreamsPlayed();
ASSERT_EQ( 1u, hList.size() );
auto item = hList[0];
ASSERT_FALSE( item->isFavorite() );
item->setFavorite( true );
ASSERT_TRUE( item->isFavorite() );
}
TEST_F( HistoryTest, ReinsertFavorited )
{
ml->addToHistory( "stream", "title" );
auto hList = ml->lastStreamsPlayed();
auto item = hList[0];
auto date = item->insertionDate();
item->setFavorite( true );
compat::this_thread::sleep_for( std::chrono::seconds{ 1 } );
ml->addToHistory( "stream", "title" );
hList = ml->lastStreamsPlayed();
item = hList[0];
ASSERT_NE( date, item->insertionDate() );
ASSERT_TRUE( item->isFavorite() );
}
TEST_F( HistoryTest, ClearStreamHistory )
{
ml->addToHistory( "f00", "f00" );
ml->addToHistory( "bar", "bar" );
auto history = ml->lastStreamsPlayed();
ASSERT_EQ( 2u, history.size() );
ml->clearHistory();
history = ml->lastStreamsPlayed();
ASSERT_EQ( 0u, history.size() );
Reload();
history = ml->lastStreamsPlayed();
ASSERT_EQ( 0u, history.size() );
}
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