Commit 230e1320 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Folder: Split nb_media to nb_audio & nb_video

This allows folders to be listed based on the amount of audio media,
video media, or both, instead of "any media"
Fix #46
parent 607f127b
Pipeline #3189 passed with stages
in 12 minutes and 57 seconds
......@@ -29,6 +29,7 @@
#include "medialibrary/ILogger.h"
#include "Types.h"
#include "IQuery.h"
#include "IMedia.h"
namespace medialibrary
{
......@@ -66,6 +67,8 @@ enum class SortingCriteria
Filename,
TrackNumber,
// Valid for folders only. Default order is descending
NbVideo,
NbAudio,
NbMedia,
};
......@@ -404,7 +407,8 @@ class IMediaLibrary
*/
virtual bool isIndexed( const std::string& mrl ) const = 0;
/**
* @brief folders Returns a flattened list of all folders containing at least a media
* @brief folders Returns a flattened list of all folders containing at least a media of a given type
* @param type A required type of media, or IMedia::Type::Unknown if any media type is fine.
* @param params A query parameters object
* @return A query object to be used to fetch the results
*
......@@ -423,8 +427,10 @@ class IMediaLibrary
* In case a non flattened list is desired, the
* entryPoints() & IFolder::subFolders() functions should be used.
*/
virtual Query<IFolder> folders( const QueryParameters* params = nullptr ) const = 0;
virtual Query<IFolder> folders( IMedia::Type type,
const QueryParameters* params = nullptr ) const = 0;
virtual Query<IFolder> searchFolders( const std::string& pattern,
IMedia::Type type,
const QueryParameters* params = nullptr ) const = 0;
virtual FolderPtr folder( int64_t folderId ) const = 0;
virtual FolderPtr folder( const std::string& mrl ) const = 0;
......
......@@ -55,7 +55,7 @@ Folder::Folder( MediaLibraryPtr ml, sqlite::Row& row )
, m_isBanned( row.load<decltype(m_isBanned)>( 4 ) )
, m_deviceId( row.load<decltype(m_deviceId)>( 5 ) )
, m_isRemovable( row.load<decltype(m_isRemovable)>( 6 ) )
// Skip nbMedia
// Skip nb_audio/nb_video
{
}
......@@ -95,7 +95,57 @@ void Folder::createTriggers( sqlite::Connection* connection, uint32_t modelVersi
"AFTER INSERT ON " + Media::Table::Name + " "
"WHEN new.folder_id IS NOT NULL "
"BEGIN "
"UPDATE " + Folder::Table::Name + " SET nb_media = nb_media + 1 "
"UPDATE " + Folder::Table::Name + " SET "
"nb_audio = nb_audio + "
"(CASE new.type "
"WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Audio ) ) + " THEN 1 "
"ELSE 0 "
"END),"
"nb_video = nb_video + "
"(CASE new.type WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Video ) ) + " THEN 1 "
"ELSE 0 "
"END) "
"WHERE id_folder = new.folder_id;"
"END",
"CREATE TRIGGER IF NOT EXISTS update_folder_nb_media_on_update "
"AFTER UPDATE ON " + Media::Table::Name + " "
"WHEN new.folder_id IS NOT NULL AND old.type != new.type "
"BEGIN "
"UPDATE " + Folder::Table::Name + " SET "
"nb_audio = nb_audio + "
"(CASE old.type "
"WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Audio ) ) + " THEN -1 "
"ELSE 0 "
"END)"
"+"
"(CASE new.type "
"WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Audio ) ) + " THEN 1 "
"ELSE 0 "
"END)"
","
"nb_video = nb_video + "
"(CASE old.type "
"WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Video ) ) + " THEN -1 "
"ELSE 0 "
"END)"
"+"
"(CASE new.type "
"WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Video ) ) + " THEN 1 "
"ELSE 0 "
"END)"
"WHERE id_folder = new.folder_id;"
"END",
......@@ -103,7 +153,21 @@ void Folder::createTriggers( sqlite::Connection* connection, uint32_t modelVersi
"AFTER DELETE ON " + Media::Table::Name + " "
"WHEN old.folder_id IS NOT NULL "
"BEGIN "
"UPDATE " + Folder::Table::Name + " SET nb_media = nb_media - 1 "
"UPDATE " + Folder::Table::Name + " SET "
"nb_audio = nb_audio + "
"(CASE old.type "
"WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Audio ) ) + " THEN -1 "
"ELSE 0 "
"END),"
"nb_video = nb_video + "
"(CASE old.type "
"WHEN " +
std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Video ) ) + " THEN -1 "
"ELSE 0 "
"END) "
"WHERE id_folder = old.folder_id;"
"END",
};
......@@ -258,8 +322,16 @@ std::string Folder::sortRequest( const QueryParameters* params )
std::string req = "ORDER BY ";
switch ( sort )
{
case SortingCriteria::NbVideo:
req += "nb_video";
desc = !desc;
break;
case SortingCriteria::NbAudio:
req += "nb_audio";
desc = !desc;
break;
case SortingCriteria::NbMedia:
req += "nb_media";
req += "(nb_audio + nb_video)";
desc = !desc;
break;
default:
......@@ -274,19 +346,38 @@ std::string Folder::sortRequest( const QueryParameters* params )
return req;
}
Query<IFolder> Folder::withMedia(MediaLibraryPtr ml , const QueryParameters* params)
std::string Folder::filterByMediaType( IMedia::Type type )
{
switch ( type )
{
case IMedia::Type::Audio:
return " nb_audio > 0";
case IMedia::Type::Video:
return " nb_video > 0";
default:
assert( !"Only Audio/Video/Unknown types are supported when listing folders" );
/* Fall-through */
case IMedia::Type::Unknown:
return " (nb_audio > 0 OR nb_video > 0)";
}
}
Query<IFolder> Folder::withMedia( MediaLibraryPtr ml, IMedia::Type type,
const QueryParameters* params )
{
static std::string req = "FROM " + Table::Name + " WHERE nb_media > 0";
std::string req = "FROM " + Table::Name +
" WHERE " + filterByMediaType( type );
return make_query<Folder, IFolder>( ml, "*", req, sortRequest( params ) );
}
Query<IFolder> Folder::searchWithMedia( MediaLibraryPtr ml, const std::string& pattern,
const QueryParameters* params )
IMedia::Type type, const QueryParameters* params )
{
std::string req = "FROM " + Table::Name + " f WHERE f.id_folder IN "
"(SELECT rowid FROM " + Table::Name + "Fts WHERE " +
Table::Name + "Fts MATCH '*' || ? || '*') "
"AND nb_media > 0";
"AND";
req += filterByMediaType( type );
return make_query<Folder, IFolder>( ml, "*", req, sortRequest( params ), pattern );
}
......
......@@ -23,6 +23,7 @@
#pragma once
#include "medialibrary/IFolder.h"
#include "medialibrary/IMedia.h"
#include "medialibrary/filesystem/IDevice.h"
#include "database/DatabaseHelpers.h"
......@@ -64,8 +65,11 @@ public:
static std::shared_ptr<Folder> fromMrl(MediaLibraryPtr ml, const std::string& mrl );
static std::shared_ptr<Folder> bannedFolder(MediaLibraryPtr ml, const std::string& mrl );
static Query<IFolder> withMedia( MediaLibraryPtr ml, const QueryParameters* params );
static Query<IFolder> searchWithMedia( MediaLibraryPtr ml, const std::string& pattern,
static Query<IFolder> withMedia( MediaLibraryPtr ml, IMedia::Type type,
const QueryParameters* params );
static Query<IFolder> searchWithMedia( MediaLibraryPtr ml,
const std::string& pattern,
IMedia::Type type,
const QueryParameters* params );
static Query<IFolder> entryPoints( MediaLibraryPtr ml, int64_t deviceId );
......@@ -99,6 +103,7 @@ public:
private:
static std::string sortRequest( const QueryParameters* params );
static std::string filterByMediaType( IMedia::Type type );
private:
MediaLibraryPtr m_ml;
......
......@@ -500,6 +500,8 @@ std::string Media::addRequestJoin( const QueryParameters* params, bool forceFile
case SortingCriteria::TrackNumber:
albumTrack = true;
break;
case SortingCriteria::NbAudio:
case SortingCriteria::NbVideo:
case SortingCriteria::NbMedia:
// Unrelated to media requests
break;
......
......@@ -1382,17 +1382,19 @@ bool MediaLibrary::isIndexed( const std::string& mrl ) const
return folder != nullptr;
}
Query<IFolder> MediaLibrary::folders( const QueryParameters* params ) const
Query<IFolder> MediaLibrary::folders( IMedia::Type type,
const QueryParameters* params ) const
{
return Folder::withMedia( this, params );
return Folder::withMedia( this, type, params );
}
Query<IFolder> MediaLibrary::searchFolders( const std::string& pattern,
IMedia::Type type,
const QueryParameters* params ) const
{
if ( validateSearchPattern( pattern ) == false )
return {};
return Folder::searchWithMedia( this, pattern, params );
return Folder::searchWithMedia( this, pattern, type, params );
}
FolderPtr MediaLibrary::folder( int64_t id ) const
......
......@@ -145,8 +145,10 @@ class MediaLibrary : public IMediaLibrary
virtual bool setDiscoverNetworkEnabled( bool enabled ) override;
virtual Query<IFolder> entryPoints() const override;
virtual bool isIndexed( const std::string& mrl ) const override;
virtual Query<IFolder> folders( const QueryParameters* params = nullptr ) const override;
virtual Query<IFolder> folders( IMedia::Type type,
const QueryParameters* params = nullptr ) const override;
virtual Query<IFolder> searchFolders( const std::string& pattern,
IMedia::Type type,
const QueryParameters* params ) const override;
virtual FolderPtr folder( int64_t id ) const override;
virtual FolderPtr folder( const std::string& mrl ) const override;
......
......@@ -173,8 +173,15 @@ IMedia::Type::Unknown ) ),
"SELECT id_folder, path, parent_id, is_blacklisted, device_id, is_removable "
"FROM " + Folder::Table::Name + "_backup",
"UPDATE " + Folder::Table::Name + " SET nb_media = "
"(SELECT COUNT() FROM " + Media::Table::Name + " m WHERE m.folder_id = id_folder )",
"UPDATE " + Folder::Table::Name + " SET "
"nb_audio = (SELECT COUNT() FROM " + Media::Table::Name +
" m WHERE m.folder_id = id_folder AND m.type = "
+ std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Audio ) ) + "),"
"nb_video = (SELECT COUNT() FROM " + Media::Table::Name +
" m WHERE m.folder_id = id_folder AND m.type = "
+ std::to_string( static_cast<std::underlying_type<IMedia::Type>::type>(
IMedia::Type::Video ) ) + ")",
"DROP TABLE " + Folder::Table::Name + "_backup",
......
......@@ -7,7 +7,8 @@
"is_banned BOOLEAN NOT NULL DEFAULT 0,"
"device_id UNSIGNED INTEGER,"
"is_removable BOOLEAN NOT NULL,"
"nb_media UNSIGNED INTEGER NOT NULL DEFAULT 0,"
"nb_audio UNSIGNED INTEGER NOT NULL DEFAULT 0,"
"nb_video UNSIGNED INTEGER NOT NULL DEFAULT 0,"
"FOREIGN KEY (parent_id) REFERENCES " + Folder::Table::Name +
"(id_folder) ON DELETE CASCADE,"
......
......@@ -455,14 +455,35 @@ TEST_F( Folders, RemoveRootFolder )
ASSERT_EQ( 0u, ml->files().size() );
}
static void enforceFakeMediaTypes( MediaLibraryTester* ml )
{
auto media = std::static_pointer_cast<Media>( ml->media(
mock::FileSystemFactory::Root + "video.avi" ) );
media->setType( IMedia::Type::Video );
media->save();
media = std::static_pointer_cast<Media>( ml->media(
mock::FileSystemFactory::Root + "audio.mp3" ) );
media->setType( IMedia::Type::Audio );
media->save();
media = std::static_pointer_cast<Media>( ml->media(
mock::FileSystemFactory::SubFolder + "subfile.mp4" ) );
media->setType( IMedia::Type::Video );
media->save();
}
TEST_F( Folders, NbMedia )
{
enforceFakeMediaTypes( ml.get() );
auto root = ml->folder( 1 );
auto subFolder = ml->folder( 2 );
ASSERT_EQ( "file:///a/", root->mrl() );
ASSERT_EQ( "file:///a/folder/", subFolder->mrl() );
ASSERT_EQ( 2u, root->media( IMedia::Type::Unknown, nullptr )->count() );
ASSERT_EQ( 1u, subFolder->media( IMedia::Type::Unknown, nullptr )->count() );
// Do not watch for live changes
ml.reset();
fsMock->removeFile( mock::FileSystemFactory::SubFolder + "subfile.mp4" );
......@@ -473,7 +494,56 @@ TEST_F( Folders, NbMedia )
subFolder = ml->folder( 2 );
ASSERT_EQ( 2u, root->media( IMedia::Type::Unknown, nullptr )->count() );
ASSERT_EQ( 1u, root->media( IMedia::Type::Video, nullptr )->count() );
ASSERT_EQ( 1u, root->media( IMedia::Type::Audio, nullptr )->count() );
ASSERT_EQ( 0u, subFolder->media( IMedia::Type::Unknown, nullptr )->count() );
auto videoMedia = root->media( IMedia::Type::Video, nullptr )->all();
ASSERT_EQ( 1u, videoMedia.size() );
auto media = std::static_pointer_cast<Media>( videoMedia[0] );
media->setType( IMedia::Type::Audio );
media->save();
videoMedia = root->media( IMedia::Type::Video, nullptr )->all();
ASSERT_EQ( 0u, videoMedia.size() );
ASSERT_EQ( 0u, root->media( IMedia::Type::Video, nullptr )->count() );
auto audioMedia = root->media( IMedia::Type::Audio, nullptr )->all();
ASSERT_EQ( 2u, audioMedia.size() );
ASSERT_EQ( 2u, root->media( IMedia::Type::Audio, nullptr )->count() );
media->setType( IMedia::Type::Video );
media->save();
videoMedia = root->media( IMedia::Type::Video, nullptr )->all();
ASSERT_EQ( 1u, videoMedia.size() );
ASSERT_EQ( 1u, root->media( IMedia::Type::Video, nullptr )->count() );
audioMedia = root->media( IMedia::Type::Audio, nullptr )->all();
ASSERT_EQ( 1u, audioMedia.size() );
ASSERT_EQ( 1u, root->media( IMedia::Type::Audio, nullptr )->count() );
}
TEST_F( Folders, NbMediaDeletionTrigger )
{
enforceFakeMediaTypes( ml.get() );
auto root = ml->folder( 1 );
ASSERT_EQ( "file:///a/", root->mrl() );
ASSERT_EQ( 2u, root->media( IMedia::Type::Unknown, nullptr )->count() );
ASSERT_EQ( 1u, ml->folders( IMedia::Type::Audio, nullptr )->count() );
auto folders = ml->folders( IMedia::Type::Audio, nullptr )->all();
ASSERT_EQ( 1u, folders.size() );
auto media = root->media( IMedia::Type::Audio, nullptr )->all();
ASSERT_EQ( 1u, media.size() );
ml->deleteMedia( media[0]->id() );
media = root->media( IMedia::Type::Audio, nullptr )->all();
ASSERT_EQ( 0u, media.size() );
ASSERT_EQ( 0u, ml->folders( IMedia::Type::Audio, nullptr )->count() );
folders = ml->folders( IMedia::Type::Audio, nullptr )->all();
ASSERT_EQ( 0u, folders.size() );
}
TEST_F( Folders, IsIndexed )
......@@ -532,9 +602,11 @@ TEST_F( FoldersNoDiscover, ListWithMedia )
bool discovered = cbMock->waitDiscovery();
ASSERT_TRUE( discovered );
enforceFakeMediaTypes( ml.get() );
QueryParameters params{};
params.sort = SortingCriteria::NbMedia;
auto folders = ml->folders( &params )->all();
auto folders = ml->folders( IMedia::Type::Video, &params )->all();
ASSERT_EQ( 2u, folders.size() );
ASSERT_EQ( folders[0]->mrl(), mock::FileSystemFactory::Root );
ASSERT_EQ( 2u, folders[0]->media( IMedia::Type::Unknown, nullptr )->count() );
......@@ -542,12 +614,19 @@ TEST_F( FoldersNoDiscover, ListWithMedia )
ASSERT_EQ( 1u, folders[1]->media( IMedia::Type::Unknown, nullptr )->count() );
params.desc = true;
folders = ml->folders( &params )->all();
folders = ml->folders( IMedia::Type::Video, &params )->all();
ASSERT_EQ( 2u, folders.size() );
ASSERT_EQ( folders[1]->mrl(), mock::FileSystemFactory::Root );
ASSERT_EQ( 2u, folders[1]->media( IMedia::Type::Unknown, nullptr )->count() );
ASSERT_EQ( folders[0]->mrl(), mock::FileSystemFactory::SubFolder );
ASSERT_EQ( 1u, folders[0]->media( IMedia::Type::Unknown, nullptr )->count() );
// List folders with audio media only
folders = ml->folders( IMedia::Type::Audio, &params )->all();
ASSERT_EQ( 1u, folders.size() );
ASSERT_EQ( folders[0]->mrl(), mock::FileSystemFactory::Root );
// But check that we still have all the media when we filter with 'Unknown'
ASSERT_EQ( 2u, folders[0]->media( IMedia::Type::Unknown, nullptr )->count() );
}
TEST_F( FoldersNoDiscover, ListSubFolders )
......@@ -559,6 +638,8 @@ TEST_F( FoldersNoDiscover, ListSubFolders )
bool discovered = cbMock->waitDiscovery();
ASSERT_TRUE( discovered );
enforceFakeMediaTypes( ml.get() );
auto entryPoints = ml->entryPoints()->all();
ASSERT_EQ( 1u, entryPoints.size() );
......@@ -601,9 +682,20 @@ TEST_F( FoldersNoDiscover, SearchFolders )
bool discovered = cbMock->waitDiscovery();
ASSERT_TRUE( discovered );
enforceFakeMediaTypes( ml.get() );
auto media = std::static_pointer_cast<Media>(
ml->media( newSubFolder + "some file.avi" ) );
media->setType( IMedia::Type::Video );
media->save();
media = std::static_pointer_cast<Media>(
ml->media( newSubFolder + "some other file.avi" ) );
media->setType( IMedia::Type::Video );
media->save();
QueryParameters params{};
params.sort = SortingCriteria::NbMedia;
auto folders = ml->searchFolders( "fold", &params )->all();
auto folders = ml->searchFolders( "fold", IMedia::Type::Unknown, &params )->all();
ASSERT_EQ( 2u, folders.size() );
ASSERT_EQ( newSubFolder, folders[0]->mrl() );
ASSERT_EQ( mock::FileSystemFactory::SubFolder, folders[1]->mrl() );
......
......@@ -174,7 +174,7 @@ TEST_F( DbModel, Upgrade12to13 )
// We can't check for the number of albums anymore since they are deleted
// as part of 13 -> 14 migration
CheckNbTriggers( 34 );
CheckNbTriggers( 35 );
}
TEST_F( DbModel, Upgrade13to14 )
......@@ -221,5 +221,5 @@ TEST_F( DbModel, Upgrade13to14 )
ASSERT_EQ( 2u, folder->media( IMedia::Type::Unknown, nullptr )->count() );
ASSERT_EQ( "folder", folder->name() );
CheckNbTriggers( 34 );
CheckNbTriggers( 35 );
}
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