Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
403 results
Show changes
Commits on Source (14)
Showing
with 1199 additions and 204 deletions
/******************************************************************************
* vlc_diffutil.h
******************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*****************************************************************************/
#ifndef VLC_DIFFUTIL_H
#define VLC_DIFFUTIL_H
#include <vlc_common.h>
#include <vlc_vector.h>
/**
* this struture defines callback to access and compare elements from
* the old and the new list
*/
typedef struct {
/// return the size of the old list @a list
uint32_t (*getOldSize)(const void* list);
/// return the size of the new list @a list
uint32_t (*getNewSize)(const void* list);
/// compare 2 elements
bool (*isSame)(const void* listOld, uint32_t oldIndex, const void* listNew, uint32_t newIndex);
} vlc_diffutil_callback_t;
typedef struct {
/**
* notify that the item from @a listNew at position @a posNew is inserted in list @a listOld at position @a posOld
*
* @param opaque user data from function vlc_diffutil_walk_snake
* @param listOld pointer to the old model
* @param posOld position of the element inserted in the old model (before removal)
* @param listNew pointer to the new model
* @param posNew position of the element inserted in the new model
*/
void (*insert)(void* opaque, const void* listOld, uint32_t posOld, const void* listNew, uint32_t posNew);
/**
* notify that the item from @a listOld at position @a posOld is removed
* @param opaque user data from function vlc_diffutil_walk_snake
* @param listOld pointer to the old model
* @param posOld position of the element removed in the old model
* @param listNew pointer to the new model
* @param posNew position of the element removed in the new model (before removal)
*/
void (*remove)(void* opaque, const void* listOld, uint32_t posOld, const void* listNew, uint32_t posNew);
/**
* notify that the item as @a posOld from the old list @a listOld is unchanged, the respective item
* position in the new list is at the positoin @a posNew in @a listNew
*/
void (*equal)(void* opaque, const void* listOld, uint32_t posOld, const void* listNew, uint32_t posNew);
} vlc_diffutil_snake_callback_t;
typedef struct diffutil_snake_t diffutil_snake_t;
enum vlc_diffutil_op_type {
///items have been added to the list
VLC_DIFFUTIL_OP_INSERT,
///items have been removed from the list
VLC_DIFFUTIL_OP_REMOVE,
///items have been moved within the list
VLC_DIFFUTIL_OP_MOVE,
///current change should be ignored
VLC_DIFFUTIL_OP_IGNORE,
};
/**
* represent a change to the model, each change assumes that previous changes
* have already been applied
*
* for instance with a model "aBcDef", the operations [remove(index=1, count=1), remove(index=2, count=1)]
* will result in "acef" (with "acDef" as intermediary step)
*/
typedef struct {
union {
/**
* the data positionned at newModel[ y ] is inserted at position index in the current model
*
* @example
* model = "abcdefg"
* newModel[3] = 'X'
* after operation insert(y=3, index = 3), model will be
* model = "abcXdefg"
*/
struct {
/// data position in the old model
uint32_t x;
/// data position in the new model
uint32_t y;
/// insertion position in the updated model
uint32_t index;
} insert;
/**
* the data positionned at oldModel[ y ] is removed at position index in the current model
*
* @example
* model = "abCdefg"
* oldModel[4] = 'C'
* after operation remove(x=4, index = 2), model will be
* model = "abdefg"
*/
struct {
/// data position in the old model
uint32_t x;
/// data position in the new model
uint32_t y;
/// removal position in the updated model
uint32_t index;
} remove;
/**
* moves the data from position model[ from ] to model[ to ]
* the data is available either at newModel[ y ] or oldModel[ x ]
*
* the positions @a from and @a to are given in the referenrial before the operation
*
* @example
* model = "aBCdefg"
* after operation move(from=1, to=5, count=2), model will be
* model = "adeCBfg"
*/
struct {
/// move origin
uint32_t from;
/// move destination
uint32_t to;
/// data position in the old model
uint32_t x;
/// data position in the new model
uint32_t y;
} move;
} op;
/// type of change operation
enum vlc_diffutil_op_type type;
/// number of elements to be inserted/removed/moved
uint32_t count;
} vlc_diffutil_change_t;
typedef struct VLC_VECTOR(vlc_diffutil_change_t) vlc_diffutil_changelist_t;
enum vlc_diffutil_result_flag {
/// try to transform an insertion with a matching supression into a move operation
VLC_DIFFUTIL_RESULT_MOVE = 0x1,
/**
* aggreate similar consecutive operations into a single operation
* for instance this:
* [{INSERT, i=5}{INSERT, x=6}{REMOVE, i=10}{REMOVE, i=10}{REMOVE, i=10}]
* would be tranformed into:
* [{INSERT, i=5, count=2}{REMOVE, i=10, count=3}]
*/
VLC_DIFFUTIL_RESULT_AGGREGATE = 0x2,
};
/**
* vlc_diffutil_build_snake compute a diff model
* between the @a dataOld model and the @a dataNew model. This model can be
* processed manually using vlc_diffutil_walk_snake or translated into a change list using
* vlc_diffutil_build_change_list
*
* @param diffOp callback to compare the elements from the old and new model
* @param dataOld old model
* @param dataNew new model
* @return the diff model, NULL on error
*/
VLC_API struct diffutil_snake_t* vlc_diffutil_build_snake(const vlc_diffutil_callback_t* diffOp, const void* dataOld, const void* dataNew);
/// free the snake created by vlc_diffutil_build_snake
VLC_API void vlc_diffutil_free_snake(struct diffutil_snake_t* snake);
/**
* iterate over the changelist and callback user on each operation (keep/insert/remove)
*
* @param snake the snake created with vlc_diffutil_build_snake
* @param snakeOp snake callback
* @param cbData user data for snake callbacks
* @param diffOp callbacks used in vlc_diffutil_build_snake
* @param dataOld old model
* @param dataNew new model
* @return false on error
*
* @warning @a dataOld and @a dataNew should not be altered during the operation
*/
VLC_API bool vlc_diffutil_walk_snake(
const diffutil_snake_t* snake,
const vlc_diffutil_snake_callback_t* snakeOp, void* cbData,
const vlc_diffutil_callback_t* diffOp, const void* dataOld, const void* dataNew);
/**
* vlc_diffutil_build_change_list creates a list of changes to apply to transform @a dataOld into @a dataNew
*
* @param snake the snake created with vlc_diffutil_build_snake
* @param diffOp callbacks used in vlc_diffutil_build_snake
* @param dataOld old model
* @param dataNew new model
* @param flags vlc_diffutil_result_flag flags
* @return the list of changes, NULL on error
*/
VLC_API vlc_diffutil_changelist_t* vlc_diffutil_build_change_list(
const struct diffutil_snake_t* snake,
const vlc_diffutil_callback_t* diffOp, const void* dataOld, const void* dataNew,
int flags);
/// free the changelist created by vlc_diffutil_build_change_list
VLC_API void vlc_diffutil_free_change_list(vlc_diffutil_changelist_t* changelist);
#endif // VLC_DIFFUTIL_H
......@@ -1223,6 +1223,11 @@ static inline vlc_ml_group_t* vlc_ml_get_group( vlc_medialibrary_t* p_ml, int64_
return (vlc_ml_group_t*)vlc_ml_get( p_ml, VLC_ML_GET_GROUP, i_group_id );
}
static inline vlc_ml_folder_t* vlc_ml_get_folder( vlc_medialibrary_t* p_ml, int64_t i_folder_id )
{
return (vlc_ml_folder_t*)vlc_ml_get( p_ml, VLC_ML_GET_FOLDER, i_folder_id);
}
static inline vlc_ml_playlist_t* vlc_ml_get_playlist( vlc_medialibrary_t* p_ml, int64_t i_playlist_id )
{
return (vlc_ml_playlist_t*)vlc_ml_get( p_ml, VLC_ML_GET_PLAYLIST, i_playlist_id );
......
......@@ -63,23 +63,36 @@ void MLAlbumModel::onVlcMlEvent(const MLEvent &event)
switch( event.i_type )
{
case VLC_ML_EVENT_ALBUM_ADDED:
{
emit resetRequested();
return;
}
case VLC_ML_EVENT_ALBUM_DELETED:
{
MLItemId itemId(event.deletion.i_entity_id, VLC_ML_PARENT_UNKNOWN);
deleteItemInCache(itemId);
return;
}
case VLC_ML_EVENT_ALBUM_UPDATED:
m_need_reset = true;
break;
{
MLItemId itemId(event.modification.i_entity_id, VLC_ML_PARENT_UNKNOWN);
updateItemInCache(itemId);
return;
}
case VLC_ML_EVENT_ARTIST_DELETED:
if ( m_parent.id != 0 && m_parent.type == VLC_ML_PARENT_ARTIST &&
event.deletion.i_entity_id == m_parent.id )
m_need_reset = true;
break;
emit resetRequested();
return;
case VLC_ML_EVENT_GENRE_DELETED:
if ( m_parent.id != 0 && m_parent.type == VLC_ML_PARENT_GENRE &&
event.deletion.i_entity_id == m_parent.id )
m_need_reset = true;
break;
emit resetRequested();;
return;
default:
break;
}
MLBaseModel::onVlcMlEvent( event );
}
......@@ -132,10 +145,10 @@ QVariant MLAlbumModel::itemRoleData(MLItem *item, const int role) const
}
}
ListCacheLoader<std::unique_ptr<MLItem>> *
std::unique_ptr<MLBaseModel::BaseLoader>
MLAlbumModel::createLoader() const
{
return new Loader(*this);
return std::make_unique<Loader>(*this);
}
size_t MLAlbumModel::Loader::count(vlc_medialibrary_t* ml) const
......@@ -166,3 +179,13 @@ MLAlbumModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count) c
res.emplace_back( std::make_unique<MLAlbum>( &album ) );
return res;
}
std::unique_ptr<MLItem>
MLAlbumModel::Loader::loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const
{
assert(itemId.type == VLC_ML_PARENT_ALBUM);
ml_unique_ptr<vlc_ml_album_t> album(vlc_ml_get_album(ml, itemId.id));
if (!album)
return nullptr;
return std::make_unique<MLAlbum>(album.get());
}
......@@ -57,7 +57,7 @@ public:
protected:
QVariant itemRoleData(MLItem *item, int role) const override;
ListCacheLoader<std::unique_ptr<MLItem>> *createLoader() const override;
std::unique_ptr<BaseLoader> createLoader() const override;
private:
vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
......@@ -72,6 +72,7 @@ private:
Loader(const MLAlbumModel &model) : BaseLoader(model) {}
size_t count(vlc_medialibrary_t* ml) const override;
std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override;
std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override;
};
};
......
......@@ -115,37 +115,44 @@ void MLAlbumTrackModel::onVlcMlEvent(const MLEvent &event)
{
case VLC_ML_EVENT_MEDIA_ADDED:
if ( event.creation.media.i_subtype == VLC_ML_MEDIA_SUBTYPE_ALBUMTRACK )
m_need_reset = true;
break;
emit resetRequested();
return;
case VLC_ML_EVENT_MEDIA_UPDATED:
{
MLItemId itemId(event.modification.i_entity_id, VLC_ML_PARENT_UNKNOWN);
updateItemInCache(itemId);
return;
}
case VLC_ML_EVENT_MEDIA_DELETED:
// FIXME: Not optimal, this will trigger a clean/refresh for video
// media as well, but this needs fixing in the medialibrary
m_need_reset = true;
break;
{
MLItemId itemId(event.deletion.i_entity_id, VLC_ML_PARENT_UNKNOWN);
deleteItemInCache(itemId);
return;
}
case VLC_ML_EVENT_ALBUM_UPDATED:
if ( m_parent.id != 0 && m_parent.type == VLC_ML_PARENT_ALBUM &&
m_parent.id == event.modification.i_entity_id )
m_need_reset = true;
break;
emit resetRequested();
return;
case VLC_ML_EVENT_ALBUM_DELETED:
if ( m_parent.id != 0 && m_parent.type == VLC_ML_PARENT_ALBUM &&
m_parent.id == event.deletion.i_entity_id )
m_need_reset = true;
break;
emit resetRequested();
return;
case VLC_ML_EVENT_GENRE_DELETED:
if ( m_parent.id != 0 && m_parent.type == VLC_ML_PARENT_GENRE &&
m_parent.id == event.deletion.i_entity_id )
m_need_reset = true;
break;
emit resetRequested();
return;
}
MLBaseModel::onVlcMlEvent( event );
}
ListCacheLoader<std::unique_ptr<MLItem>> *
std::unique_ptr<MLBaseModel::BaseLoader>
MLAlbumTrackModel::createLoader() const
{
return new Loader(*this);
return std::make_unique<Loader>(*this);
}
size_t MLAlbumTrackModel::Loader::count(vlc_medialibrary_t* ml) const
......@@ -177,3 +184,14 @@ MLAlbumTrackModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t cou
res.emplace_back( std::make_unique<MLAlbumTrack>( ml, &media ) );
return res;
}
std::unique_ptr<MLItem>
MLAlbumTrackModel::Loader::loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const
{
assert(itemId.type == VLC_ML_PARENT_UNKNOWN);
ml_unique_ptr<vlc_ml_media_t> media(vlc_ml_get_media(ml, itemId.id));
if (!media)
return nullptr;
return std::make_unique<MLAlbumTrack>(ml, media.get());
}
......@@ -56,7 +56,7 @@ public:
protected:
QVariant itemRoleData(MLItem *item, int role) const override;
ListCacheLoader<std::unique_ptr<MLItem>> *createLoader() const override;
std::unique_ptr<MLBaseModel::BaseLoader> createLoader() const override;
private:
vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
......@@ -71,6 +71,7 @@ private:
Loader(const MLAlbumTrackModel &model) : BaseLoader(model) {}
size_t count(vlc_medialibrary_t* ml) const override;
std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override;
std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override;
};
};
#endif // MLTRACKMODEL_HPP
......@@ -89,23 +89,34 @@ void MLArtistModel::onVlcMlEvent(const MLEvent &event)
switch (event.i_type)
{
case VLC_ML_EVENT_ARTIST_ADDED:
emit resetRequested();
return;
case VLC_ML_EVENT_ARTIST_UPDATED:
{
MLItemId itemId(event.deletion.i_entity_id, VLC_ML_PARENT_UNKNOWN);
updateItemInCache(itemId);
return;
}
case VLC_ML_EVENT_ARTIST_DELETED:
m_need_reset = true;
break;
{
MLItemId itemId(event.deletion.i_entity_id, VLC_ML_PARENT_UNKNOWN);
deleteItemInCache(itemId);
return;
}
case VLC_ML_EVENT_GENRE_DELETED:
if ( m_parent.id != 0 && m_parent.type == VLC_ML_PARENT_GENRE &&
m_parent.id == event.deletion.i_entity_id )
m_need_reset = true;
break;
emit resetRequested();
return;
}
MLBaseModel::onVlcMlEvent(event);
}
ListCacheLoader<std::unique_ptr<MLItem>> *
std::unique_ptr<MLBaseModel::BaseLoader>
MLArtistModel::createLoader() const
{
return new Loader(*this);
return std::make_unique<Loader>(*this);
}
size_t MLArtistModel::Loader::count(vlc_medialibrary_t* ml) const
......@@ -136,3 +147,14 @@ MLArtistModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count)
res.emplace_back( std::make_unique<MLArtist>( &artist ) );
return res;
}
std::unique_ptr<MLItem>
MLArtistModel::Loader::loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const
{
assert(itemId.type == VLC_ML_PARENT_ARTIST);
ml_unique_ptr<vlc_ml_artist_t> artist(vlc_ml_get_artist(ml, itemId.id));
if (!artist)
return nullptr;
return std::make_unique<MLArtist>(artist.get());
}
......@@ -49,7 +49,7 @@ public:
protected:
QVariant itemRoleData(MLItem *item, int role) const override;
ListCacheLoader<std::unique_ptr<MLItem>> *createLoader() const override;
std::unique_ptr<MLBaseModel::BaseLoader> createLoader() const override;
private:
vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
......@@ -64,6 +64,7 @@ private:
Loader(const MLArtistModel &model) : BaseLoader(model) {}
size_t count(vlc_medialibrary_t* ml) const override;
std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override;
std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override;
};
};
......
......@@ -46,11 +46,14 @@ MLBaseModel::~MLBaseModel() = default;
void MLBaseModel::sortByColumn(QByteArray name, Qt::SortOrder order)
{
beginResetModel();
vlc_ml_sorting_criteria_t sort = nameToCriteria(name);
bool desc = (order == Qt::SortOrder::DescendingOrder);
if (m_sort_desc == desc && m_sort == sort)
return;
m_sort_desc = (order == Qt::SortOrder::DescendingOrder);
m_sort = nameToCriteria(name);
clear();
endResetModel();
resetCache();
}
//-------------------------------------------------------------------------------------------------
......@@ -85,7 +88,7 @@ void MLBaseModel::getData(const QModelIndexList &indexes, QJSValue callback)
return index.row();
});
QSharedPointer<ListCacheLoader<std::unique_ptr<MLItem>>> loader{ createLoader() };
QSharedPointer<BaseLoader> loader{ createLoader().release() };
struct Ctx {
std::vector<std::unique_ptr<MLItem>> items;
};
......@@ -171,32 +174,14 @@ QVariant MLBaseModel::data(const QModelIndex &index, int role) const
void MLBaseModel::onResetRequested()
{
beginResetModel();
clear();
endResetModel();
}
void MLBaseModel::onLocalSizeAboutToBeChanged(size_t size)
{
(void) size;
beginResetModel();
invalidateCache();
}
void MLBaseModel::onLocalSizeChanged(size_t size)
{
(void) size;
endResetModel();
emit countChanged(size);
}
void MLBaseModel::onLocalDataChanged(size_t offset, size_t count)
{
assert(count);
auto first = index(offset);
auto last = index(offset + count - 1);
emit dataChanged(first, last);
}
void MLBaseModel::onVlcMlEvent(const MLEvent &event)
{
switch(event.i_type)
......@@ -271,19 +256,15 @@ MLItemId MLBaseModel::parentId() const
void MLBaseModel::setParentId(MLItemId parentId)
{
beginResetModel();
m_parent = parentId;
clear();
endResetModel();
invalidateCache();
emit parentIdChanged();
}
void MLBaseModel::unsetParentId()
{
beginResetModel();
m_parent = MLItemId();
clear();
endResetModel();
invalidateCache();
emit parentIdChanged();
}
......@@ -312,10 +293,8 @@ void MLBaseModel::setSearchPattern( const QString& pattern )
/* No changes */
return;
beginResetModel();
m_search_pattern = patternToApply;
clear();
endResetModel();
invalidateCache();
}
Qt::SortOrder MLBaseModel::getSortOrder() const
......@@ -325,10 +304,11 @@ Qt::SortOrder MLBaseModel::getSortOrder() const
void MLBaseModel::setSortOder(Qt::SortOrder order)
{
beginResetModel();
m_sort_desc = (order == Qt::SortOrder::DescendingOrder);
clear();
endResetModel();
bool desc = (order == Qt::SortOrder::DescendingOrder);
if (m_sort_desc == desc)
return;
m_sort_desc = desc;
resetCache();
emit sortOrderChanged();
}
......@@ -339,19 +319,21 @@ const QString MLBaseModel::getSortCriteria() const
void MLBaseModel::setSortCriteria(const QString& criteria)
{
beginResetModel();
m_sort = nameToCriteria(criteria.toUtf8());
clear();
endResetModel();
vlc_ml_sorting_criteria_t sort = nameToCriteria(qtu(criteria));
if (m_sort == sort)
return;
m_sort = sort;
resetCache();
emit sortCriteriaChanged();
}
void MLBaseModel::unsetSortCriteria()
{
beginResetModel();
if (m_sort == VLC_ML_SORTING_DEFAULT)
return;
m_sort = VLC_ML_SORTING_DEFAULT;
clear();
endResetModel();
resetCache();
emit sortCriteriaChanged();
}
......@@ -365,12 +347,6 @@ int MLBaseModel::rowCount(const QModelIndex &parent) const
return m_cache->count();
}
void MLBaseModel::clear()
{
invalidateCache();
emit countChanged( static_cast<unsigned int>(0) );
}
QVariant MLBaseModel::getIdForIndex(QVariant index) const
{
MLItem* obj = nullptr;
......@@ -431,26 +407,76 @@ unsigned MLBaseModel::getCount() const
return static_cast<unsigned>(m_cache->count());
}
void MLBaseModel::onCacheDataChanged(int first, int last)
{
emit dataChanged(index(first), index(last));
}
void MLBaseModel::onCacheBeginInsertRows(int first, int last)
{
emit beginInsertRows({}, first, last);
}
void MLBaseModel::onCacheBeginRemoveRows(int first, int last)
{
emit beginRemoveRows({}, first, last);
}
void MLBaseModel::onCacheBeginMoveRows(int first, int last, int destination)
{
emit beginMoveRows({}, first, last, {}, destination);
}
void MLBaseModel::validateCache() const
{
if (m_cache)
return;
if (!m_mediaLib)
return;
auto loader = createLoader();
m_cache.reset(new MLListCache(m_mediaLib, loader));
connect(&*m_cache, &MLListCache::localSizeAboutToBeChanged,
this, &MLBaseModel::onLocalSizeAboutToBeChanged);
connect(&*m_cache, &MLListCache::localSizeChanged,
m_cache = std::make_unique<MLListCache>(m_mediaLib, std::move(loader), false);
connect(m_cache.get(), &MLListCache::localSizeChanged,
this, &MLBaseModel::onLocalSizeChanged);
connect(&*m_cache, &MLListCache::localDataChanged,
this, &MLBaseModel::onLocalDataChanged);
connect(m_cache.get(), &MLListCache::localDataChanged,
this, &MLBaseModel::onCacheDataChanged);
connect(m_cache.get(), &MLListCache::beginInsertRows,
this, &MLBaseModel::onCacheBeginInsertRows);
connect(m_cache.get(), &MLListCache::endInsertRows,
this, &MLBaseModel::endInsertRows);
connect(m_cache.get(), &MLListCache::beginRemoveRows,
this, &MLBaseModel::onCacheBeginRemoveRows);
connect(m_cache.get(), &MLListCache::endRemoveRows,
this, &MLBaseModel::endRemoveRows);
connect(m_cache.get(), &MLListCache::endMoveRows,
this, &MLBaseModel::endMoveRows);
connect(m_cache.get(), &MLListCache::beginMoveRows,
this, &MLBaseModel::onCacheBeginMoveRows);
m_cache->initCount();
}
void MLBaseModel::invalidateCache()
void MLBaseModel::resetCache()
{
beginResetModel();
m_cache.reset();
endResetModel();
validateCache();
}
void MLBaseModel::invalidateCache()
{
if (m_cache)
m_cache->invalidate();
else
validateCache();
}
//-------------------------------------------------------------------------------------------------
......@@ -459,9 +485,12 @@ MLItem *MLBaseModel::item(int signedidx) const
{
validateCache();
if (!m_cache)
return nullptr;
ssize_t count = m_cache->count();
if (count == COUNT_UNINITIALIZED || signedidx < 0 || signedidx >= count)
if (count == 0 || signedidx < 0 || signedidx >= count)
return nullptr;
unsigned int idx = static_cast<unsigned int>(signedidx);
......@@ -482,6 +511,9 @@ MLItem *MLBaseModel::itemCache(int signedidx) const
{
unsigned int idx = static_cast<unsigned int>(signedidx);
if (!m_cache)
return nullptr;
const std::unique_ptr<MLItem> *item = m_cache->get(idx);
if (!item)
......@@ -502,6 +534,67 @@ MLItem *MLBaseModel::findInCache(const MLItemId& id, int *index) const
return item ? item->get() : nullptr;
}
void MLBaseModel::updateItemInCache(const MLItemId& mlid)
{
if (!m_cache)
{
emit resetRequested();
return;
}
MLItem* item = findInCache(mlid, nullptr);
if (!item) // items isn't loaded
return;
if (!m_itemLoader)
m_itemLoader = createLoader();
struct Ctx {
std::unique_ptr<MLItem> item;
};
m_mediaLib->runOnMLThread<Ctx>(this,
//ML thread
[mlid, itemLoader = m_itemLoader](vlc_medialibrary_t* ml, Ctx& ctx){
ctx.item = itemLoader->loadItemById(ml, mlid);
},
//UI thread
[this](qint64, Ctx& ctx) {
if (!ctx.item)
return;
m_cache->updateItem(std::move(ctx.item));
});
}
void MLBaseModel::deleteItemInCache(const MLItemId& mlid)
{
if (!m_cache)
{
emit resetRequested();
return;
}
m_cache->deleteItem(mlid);
}
void MLBaseModel::moveRangeInCache(int first, int last, int to)
{
if (!m_cache)
{
emit resetRequested();
return;
}
m_cache->moveRange(first, last, to);
}
void MLBaseModel::deleteRangeInCache(int first, int last)
{
if (!m_cache)
{
emit resetRequested();
return;
}
m_cache->deleteRange(first, last);
}
//-------------------------------------------------------------------------------------------------
MLBaseModel::BaseLoader::BaseLoader(MLItemId parent, QString searchPattern,
......
......@@ -89,15 +89,12 @@ signals:
protected slots:
void onResetRequested();
void onLocalSizeAboutToBeChanged(size_t size);
void onLocalSizeChanged(size_t size);
void onLocalDataChanged(size_t index, size_t count);
private:
static void onVlcMlEvent( void* data, const vlc_ml_event_t* event );
protected:
virtual void clear();
virtual vlc_ml_sorting_criteria_t roleToCriteria(int role) const = 0;
static QString getFirstSymbol(QString str);
virtual vlc_ml_sorting_criteria_t nameToCriteria(QByteArray) const {
......@@ -109,6 +106,7 @@ protected:
}
void validateCache() const;
void resetCache();
void invalidateCache();
MLItem *item(int signedidx) const;
......@@ -118,9 +116,18 @@ protected:
MLItem *findInCache(const MLItemId& id, int *index) const;
//update and notify changes on an item if this item is in the cache
void updateItemInCache(const MLItemId& id);
//delete and notify deletion of an item if this item is in the cache
//this is only to reflect changes from the ML, it won't alter the database
void deleteItemInCache(const MLItemId& mlid);
void moveRangeInCache(int first, int last, int to);
void deleteRangeInCache(int first, int last);
virtual void onVlcMlEvent( const MLEvent &event );
virtual ListCacheLoader<std::unique_ptr<MLItem>> *createLoader() const = 0;
virtual void thumbnailUpdated(const QModelIndex& , MLItem* , const QString& , vlc_ml_thumbnail_status_t ) {}
......@@ -133,6 +140,8 @@ protected:
MLQueryParams getParams(size_t index = 0, size_t count = 0) const;
virtual std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const = 0;
protected:
MLItemId m_parent;
QString m_searchPattern;
......@@ -140,6 +149,8 @@ protected:
bool m_sort_desc;
};
virtual std::unique_ptr<BaseLoader> createLoader() const = 0;
public:
MLItemId parentId() const;
void setParentId(MLItemId parentId);
......@@ -160,6 +171,12 @@ public:
int rowCount(const QModelIndex &parent = {}) const override;
virtual unsigned int getCount() const;
private:
void onCacheDataChanged(int first, int last);
void onCacheBeginInsertRows(int first, int last);
void onCacheBeginRemoveRows(int first, int last);
void onCacheBeginMoveRows(int first, int last, int destination);
protected:
MLItemId m_parent;
......@@ -174,6 +191,9 @@ protected:
mutable std::unique_ptr<MLListCache> m_cache;
//loader used to load single items
std::shared_ptr<BaseLoader> m_itemLoader;
private: // Friends
friend QString getVideoListCover(const MLBaseModel*, MLItemCover*, int, int, int);
};
......
......@@ -33,6 +33,7 @@ struct MLEvent
int64_t i_entity_id;
union {
struct {
vlc_ml_media_type_t i_type;
vlc_ml_media_subtype_t i_subtype;
} media;
};
......@@ -66,6 +67,7 @@ struct MLEvent
{
case VLC_ML_EVENT_MEDIA_ADDED:
creation.i_entity_id = event->creation.p_media->i_id;
creation.media.i_type = event->creation.p_media->i_type;
creation.media.i_subtype = event->creation.p_media->i_subtype;
break;
case VLC_ML_EVENT_ARTIST_ADDED:
......
......@@ -87,11 +87,22 @@ void MLGenreModel::onVlcMlEvent(const MLEvent &event)
switch (event.i_type)
{
case VLC_ML_EVENT_GENRE_ADDED:
emit resetRequested();
return;
case VLC_ML_EVENT_GENRE_UPDATED:
{
MLItemId itemId(event.modification.i_entity_id, VLC_ML_PARENT_UNKNOWN);
updateItemInCache(itemId);
return;
}
case VLC_ML_EVENT_GENRE_DELETED:
m_need_reset = true;
break;
{
MLItemId itemId(event.deletion.i_entity_id, VLC_ML_PARENT_UNKNOWN);
deleteItemInCache(itemId);
return;
}
}
MLBaseModel::onVlcMlEvent(event);
}
......@@ -174,10 +185,10 @@ QString MLGenreModel::getCover(MLGenre * genre) const
//-------------------------------------------------------------------------------------------------
ListCacheLoader<std::unique_ptr<MLItem>> *
std::unique_ptr<MLBaseModel::BaseLoader>
MLGenreModel::createLoader() const
{
return new Loader(*this);
return std::make_unique<Loader>(*this);
}
size_t MLGenreModel::Loader::count(vlc_medialibrary_t* ml) const
......@@ -205,3 +216,13 @@ MLGenreModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count) c
return res;
}
std::unique_ptr<MLItem>
MLGenreModel::Loader::loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const
{
assert(itemId.type == VLC_ML_PARENT_GENRE);
ml_unique_ptr<vlc_ml_genre_t> genre(vlc_ml_get_genre(ml, itemId.id));
if (!genre)
return nullptr;
return std::make_unique<MLGenre>(genre.get());
}
......@@ -52,7 +52,7 @@ public:
protected:
QVariant itemRoleData(MLItem *item, int role) const override;
ListCacheLoader<std::unique_ptr<MLItem>> *createLoader() const override;
std::unique_ptr<MLBaseModel::BaseLoader> createLoader() const override;
private:
void onVlcMlEvent(const MLEvent &event) override;
......@@ -67,6 +67,7 @@ private:
Loader(const MLGenreModel &model) : BaseLoader(model) {}
size_t count(vlc_medialibrary_t* ml) const override;
std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override;
std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override;
};
private: // Variables
......
......@@ -17,118 +17,500 @@
*****************************************************************************/
#include "mllistcache.hpp"
#include "vlc_diffutil.h"
namespace {
//callbacks for the diff algorithm to access the data
uint32_t cacheDataLength(const void* data)
{
auto list = static_cast<const std::vector<MLListCache::ItemType>*>(data);
assert(list);
return list->size();
}
bool cacheDataCompare(const void* dataOld, uint32_t oldIndex, const void* dataNew, uint32_t newIndex)
{
auto listOld = static_cast<const std::vector<MLListCache::ItemType>*>(dataOld);
auto listNew = static_cast<const std::vector<MLListCache::ItemType>*>(dataNew);
assert(listOld);
assert(listNew);
assert(oldIndex < listOld->size());
assert(newIndex < listNew->size());
return listOld->at(oldIndex)->getId() == listNew->at(newIndex)->getId();
}
}
MLListCache::MLListCache(MediaLib* medialib, std::unique_ptr<ListCacheLoader<MLListCache::ItemType>>&& loader, bool useMove, size_t chunkSize)
: m_medialib(medialib)
, m_useMove(useMove)
, m_loader(loader.release())
, m_chunkSize(chunkSize)
{
assert(medialib);
}
size_t MLListCache::fixupIndexForMove(size_t index) const
{
//theses elements have already been moved
for (const PartialIndexRedirect& hole : m_partialIndexRedirect)
{
if (hole.op == PartialIndexRedirect::Operation::DEL)
{
if (hole.index <= index)
index += hole.count;
else
break;
}
else
{
if (hole.index <= index)
{
if (index <= hole.index + hole.count - 1)
return hole.val.add.x + (index - hole.index);
else
index -= hole.count;
}
else
break;
}
}
return index;
}
const MLListCache::ItemType* MLListCache::get(size_t index) const
{
assert(m_total_count >= 0 && index < static_cast<size_t>(m_total_count));
if (index < m_offset || index >= m_offset + m_list.size())
//the view may access the model while we're updating it
//everything before m_partialIndex is updated in the new model,
//everything after m_partialIndex is still valid in the old model
if (unlikely(m_oldData))
{
if (m_cachedData)
{
if (index >= m_partialLoadedCount)
return nullptr;
else if (index >= m_partialIndex)
{
if (m_useMove)
{
index = fixupIndexForMove(index);
}
return &m_oldData->list.at(index + (m_partialX - m_partialIndex));
}
else
return &m_cachedData->list.at(index);
}
else
{
if (index >= m_oldData->loadedCount)
return nullptr;
return &m_oldData->list.at(index);
}
}
if (!m_cachedData)
return nullptr;
return &m_list[index - m_offset];
if (index + 1 > m_cachedData->loadedCount)
return nullptr;
return &m_cachedData->list.at(index);
}
const MLListCache::ItemType* MLListCache::find(const std::function<bool (const MLListCache::ItemType&)> &&f, int *index) const
{
if (m_total_count <= 0)
if (!m_cachedData || m_cachedData->totalCount == 0)
return nullptr;
for (auto iter = std::begin(m_list); iter != std::end(m_list); ++iter)
{
if (f(*iter))
{
if (index)
*index = m_offset + std::distance(std::begin(m_list), iter);
auto it = std::find_if(m_cachedData->list.cbegin(), m_cachedData->list.cend(), f);
if (it == m_cachedData->list.cend())
return nullptr;
return &(*iter);
}
if (index)
*index = std::distance(m_cachedData->list.cbegin(), it);
return &(*it);
}
int MLListCache::updateItem(std::unique_ptr<MLItem>&& newItem)
{
//we can't update an item locally while the model has pending updates
//no worry, we'll receive the update once the actual model notifies us
if (m_oldData)
return -1;
MLItemId mlid = newItem->getId();
//this may be inneficient to look at every items, maybe we can have a hashmap to access the items by id
auto it = std::find_if(m_cachedData->list.begin(), m_cachedData->list.end(), [mlid](const ItemType& item) {
return (item->getId() == mlid);
});
//item not found
if (it == m_cachedData->list.end())
return -1;
int pos = std::distance(m_cachedData->list.begin(), it);
*it = std::move(newItem);
emit localDataChanged(pos, pos);
return pos;
}
int MLListCache::deleteItem(const MLItemId& mlid)
{
//we can't update an item locally while the model has pending updates
//no worry, we'll receive the update once the actual model notifies us
if (m_oldData)
return -1;
auto it = std::find_if(m_cachedData->list.begin(), m_cachedData->list.end(), [mlid](const ItemType& item) {
return (item->getId() == mlid);
});
//item not found
if (it == m_cachedData->list.end())
return -1;
int pos = std::distance(m_cachedData->list.begin(), it);
emit beginRemoveRows(pos, pos);
m_cachedData->list.erase(it, it+1);
size_t delta = m_cachedData->loadedCount - m_cachedData->list.size();
m_cachedData->loadedCount -= delta;
m_cachedData->totalCount -= delta;
emit endRemoveRows();
emit localSizeChanged(m_cachedData->totalCount);
return pos;
}
void MLListCache::moveRange(int first, int last, int to)
{
assert(first <= last);
if (first <= to && to <= last)
return;
emit beginMoveRows(first, last, to);
auto it = m_cachedData->list.begin();
//build a temporary list with the items in order
std::vector<ItemType> tmpList;
if (to < first)
{
std::move(it, it+to, std::back_inserter(tmpList));
std::move(it+first, it+(last+1), std::back_inserter(tmpList));
std::move(it+to, it+first, std::back_inserter(tmpList));
std::move(it+(last+1), m_cachedData->list.end(), std::back_inserter(tmpList));
}
else //last < to
{
std::move(it, it+first, std::back_inserter(tmpList));
std::move(it+last+1, it+to, std::back_inserter(tmpList));
std::move(it+first, it+(last+1), std::back_inserter(tmpList));
std::move(it+to, m_cachedData->list.end(), std::back_inserter(tmpList));
}
return nullptr;
m_cachedData->list = std::move(tmpList);
emit endMoveRows();
}
void MLListCache::deleteRange(int first, int last)
{
assert(first <= last);
emit beginRemoveRows(first, last);
auto it = m_cachedData->list.begin();
m_cachedData->list.erase(it+first, it+(last+1));
size_t delta = m_cachedData->loadedCount - m_cachedData->list.size();
m_cachedData->loadedCount -= delta;
m_cachedData->totalCount -= delta;
emit endRemoveRows();
emit localSizeChanged(m_cachedData->totalCount);
}
ssize_t MLListCache::count() const
{
return m_total_count;
if (!m_cachedData)
return -1;
return m_cachedData->totalCount;
}
void MLListCache::initCount()
{
assert(!m_countRequested);
asyncCount();
assert(!m_cachedData);
asyncCountAndLoad();
}
void MLListCache::refer(size_t index)
{
if (m_total_count == -1 || index >= static_cast<size_t>(m_total_count))
{
/*
* The request is incompatible with the total count of the list.
*
* Either the count is not retrieved yet, or the content has changed in
* the loader source.
*/
//m_maxReferedIndex is in terms of number of item, not the index
index++;
if (!m_cachedData)
return;
if (index > m_cachedData->totalCount)
return;
/* index is already in the list */
if (index <= m_cachedData->loadedCount)
return;
if (index > m_maxReferedIndex)
{
m_maxReferedIndex = index;
if (!m_appendTask && !m_countTask)
{
if (m_cachedData)
asyncFetchMore();
else
asyncCountAndLoad();
}
}
}
void MLListCache::invalidate()
{
if (m_cachedData)
{
if (m_cachedData && !m_oldData)
{
m_oldData = std::move(m_cachedData);
m_partialX = 0;
}
else
m_cachedData.reset();
}
if (m_appendTask)
{
m_medialib->cancelMLTask(this, m_appendTask);
m_appendTask = 0;
}
if (m_countTask)
{
m_needReload = true;
}
else
{
asyncCountAndLoad();
}
}
void MLListCache::partialUpdate()
{
//compare the model the user have and the updated model
//and notify for changes
vlc_diffutil_callback_t diffOp = {
cacheDataLength,
cacheDataLength,
cacheDataCompare
};
diffutil_snake_t* snake = vlc_diffutil_build_snake(&diffOp, &m_oldData->list, &m_cachedData->list);
int diffutilFlags = VLC_DIFFUTIL_RESULT_AGGREGATE;
if (m_useMove)
diffutilFlags |= VLC_DIFFUTIL_RESULT_MOVE;
vlc_diffutil_changelist_t* changes = vlc_diffutil_build_change_list(
snake, &diffOp, &m_oldData->list, &m_cachedData->list,
diffutilFlags);
m_partialIndex = 0;
m_partialLoadedCount = m_oldData->loadedCount;
size_t partialTotalCount = m_oldData->totalCount;
for (size_t i = 0; i < changes->size; i++)
{
vlc_diffutil_change_t& op = changes->data[i];
switch (op.type)
{
case VLC_DIFFUTIL_OP_IGNORE:
break;
case VLC_DIFFUTIL_OP_INSERT:
m_partialX = op.op.insert.x;
m_partialIndex = op.op.insert.index;
emit beginInsertRows(op.op.insert.index, op.op.insert.index + op.count - 1);
m_partialIndex += op.count;
m_partialLoadedCount += op.count;
partialTotalCount += op.count;
emit endInsertRows();
emit localSizeChanged(partialTotalCount);
break;
case VLC_DIFFUTIL_OP_REMOVE:
m_partialX = op.op.remove.x;
m_partialIndex = op.op.remove.index + op.count - 1;
emit beginRemoveRows(op.op.remove.index, op.op.remove.index + op.count - 1);
m_partialLoadedCount -= op.count;
m_partialX += op.count;
m_partialIndex = op.op.remove.index + 1;
partialTotalCount -= op.count;
emit endRemoveRows();
emit localSizeChanged(partialTotalCount);
break;
case VLC_DIFFUTIL_OP_MOVE:
m_partialX = op.op.move.x;
if (op.op.move.from > op.op.move.to)
{
m_partialIndex = op.op.move.to;
emit beginMoveRows(op.op.move.from, op.op.move.from + op.count - 1, op.op.move.to);
m_partialIndexRedirect.insert(PartialIndexRedirect(PartialIndexRedirect::Operation::DEL, op.op.move.from, op.count));
m_partialIndex += op.count;
}
else
{
m_partialIndex = op.op.move.from + op.count - 1;
emit beginMoveRows(op.op.move.from, op.op.move.from + op.count - 1, op.op.move.to);
m_partialIndexRedirect.insert(PartialIndexRedirect(PartialIndexRedirect::Operation::ADD, op.op.move.to, op.count, op.op.move.x));
m_partialIndex = op.op.move.from + 1;
m_partialX += op.count;
}
emit endMoveRows();
break;
}
}
vlc_diffutil_free_change_list(changes);
vlc_diffutil_free_snake(snake);
//ditch old model
if (m_useMove)
m_partialIndexRedirect.clear();
m_oldData.reset();
/* index outside the known portion of the list */
if (!m_lastRangeRequested.contains(index))
//if we have change outside our cache
//just notify for addition/removal at a the end of the list
if (partialTotalCount != m_cachedData->totalCount)
{
/* FIXME bad heuristic if the interval of visible items crosses a cache
* page boundary */
size_t offset = index - index % m_chunkSize;
size_t count = qMin(m_total_count - offset, m_chunkSize);
asyncLoad(offset, count);
if (partialTotalCount > m_cachedData->totalCount)
{
emit beginRemoveRows(m_cachedData->totalCount - 1, partialTotalCount - 1);
emit endRemoveRows();
emit localSizeChanged(m_cachedData->totalCount);
}
else
{
emit beginInsertRows(partialTotalCount - 1, m_cachedData->totalCount - 1);
emit endInsertRows();
emit localSizeChanged(m_cachedData->totalCount);
}
}
}
void MLListCache::asyncCount()
void MLListCache::asyncCountAndLoad()
{
assert(!m_countTask);
if (m_countTask)
m_medialib->cancelMLTask(this, m_countTask);
size_t count = std::max(m_maxReferedIndex, m_chunkSize);
m_countRequested = true;
struct Ctx {
ssize_t totalCount;
size_t totalCount;
std::vector<ItemType> list;
};
m_medialib->runOnMLThread<Ctx>(this,
m_countTask = m_medialib->runOnMLThread<Ctx>(this,
//ML thread
[loader = m_loader](vlc_medialibrary_t* ml, Ctx& ctx)
[loader = m_loader, count = count](vlc_medialibrary_t* ml, Ctx& ctx)
{
ctx.list = loader->load(ml, 0, count);
ctx.totalCount = loader->count(ml);
},
//UI thread
[this](quint64, Ctx& ctx){
m_total_count = ctx.totalCount;
[this](quint64 taskId, Ctx& ctx)
{
if (m_countTask != taskId)
return;
//quite unlikley but model may change between count and load
if (unlikely(ctx.list.size() > ctx.totalCount))
{
ctx.totalCount = ctx.list.size();
m_needReload = true;
}
m_cachedData = std::make_unique<CacheData>(std::move(ctx.list), ctx.totalCount);
if (m_oldData)
{
partialUpdate();
}
else
{
if (m_cachedData->totalCount > 0)
{
//no previous data, we insert everything
emit beginInsertRows(0, m_cachedData->totalCount - 1);
emit endInsertRows();
emit localSizeChanged(m_cachedData->totalCount);
}
}
m_countTask = 0;
emit localSizeChanged(m_total_count);
if (m_needReload)
{
m_needReload = false;
m_oldData = std::move(m_cachedData);
m_partialX = 0;
asyncCountAndLoad();
}
else if (m_maxReferedIndex < m_cachedData->loadedCount)
{
m_maxReferedIndex = m_cachedData->loadedCount;
}
else if (m_maxReferedIndex > m_cachedData->loadedCount
&& m_maxReferedIndex <= m_cachedData->totalCount)
{
asyncFetchMore();
}
}
);
}
void MLListCache::asyncLoad(size_t offset, size_t count)
void MLListCache::asyncFetchMore()
{
if (m_loadTask)
m_medialib->cancelMLTask(this, m_loadTask);
if (m_maxReferedIndex <= m_cachedData->loadedCount)
return;
assert(m_cachedData);
if (m_appendTask)
m_medialib->cancelMLTask(this, m_appendTask);
m_maxReferedIndex = std::min(m_cachedData->totalCount, m_maxReferedIndex);
size_t count = ((m_maxReferedIndex - m_cachedData->loadedCount) / m_chunkSize + 1 ) * m_chunkSize;
m_lastRangeRequested = { offset, count };
struct Ctx {
std::vector<ItemType> list;
};
m_loadTask = m_medialib->runOnMLThread<Ctx>(this,
m_appendTask = m_medialib->runOnMLThread<Ctx>(this,
//ML thread
[loader = m_loader, offset, count]
[loader = m_loader, offset = m_cachedData->loadedCount, count]
(vlc_medialibrary_t* ml, Ctx& ctx)
{
ctx.list = loader->load(ml, offset, count);
},
//UI thread
[this, offset](quint64, Ctx& ctx)
[this](quint64 taskId, Ctx& ctx)
{
m_loadTask = 0;
if (taskId != m_appendTask)
return;
assert(m_cachedData);
int updatedCount = ctx.list.size();
if (updatedCount >= 0)
{
int updatedOffset = m_cachedData->loadedCount;
std::move(ctx.list.begin(), ctx.list.end(), std::back_inserter(m_cachedData->list));
m_cachedData->loadedCount += updatedCount;
emit localDataChanged(updatedOffset, updatedOffset + updatedCount - 1);
}
m_offset = offset;
m_list = std::move(ctx.list);
if (m_list.size())
emit localDataChanged(offset, m_list.size());
m_appendTask = 0;
if (m_maxReferedIndex > m_cachedData->loadedCount)
{
asyncFetchMore();
}
}
);
}
......@@ -27,6 +27,7 @@
#include <cassert>
#include <memory>
#include <vector>
#include <set>
#include <QObject>
#include <QSharedPointer>
#include "util/listcacheloader.hpp"
......@@ -72,13 +73,44 @@ struct MLRange
{
}
bool isEmpty() {
MLRange(const MLRange& other)
: offset(other.offset)
, count(other.count)
{}
MLRange& operator=(const MLRange& other)
{
offset = other.offset;
count = other.count;
return *this;
}
bool isEmpty() const {
return count == 0;
}
bool contains(size_t index) {
bool contains(size_t index) const {
return index >= offset && index < offset + count;
}
void reset()
{
offset = 0;
count = 0;
}
///returns the overlapping range of the current range and @a other
MLRange overlap(const MLRange& other) const
{
if (isEmpty() || other.isEmpty())
return MLRange{};
if (contains(other.offset))
return MLRange(other.offset, (offset + count) - other.offset);
else if (other.contains(offset))
return MLRange(offset, (other.offset + other.count) - offset);
else
return MLRange{};
}
};
class MLListCache : public QObject
......@@ -87,14 +119,12 @@ class MLListCache : public QObject
public:
typedef std::unique_ptr<MLItem> ItemType;
public:
static constexpr ssize_t COUNT_UNINITIALIZED = -1;
MLListCache(MediaLib* medialib, ListCacheLoader<ItemType> *loader,
size_t chunkSize = 100)
: m_medialib(medialib)
, m_loader(loader)
, m_chunkSize(chunkSize) {}
MLListCache(MediaLib* medialib, std::unique_ptr<ListCacheLoader<ItemType>>&& loader,
bool useMove, size_t chunkSize = 100);
/**
* Return the item at specified index
......@@ -112,6 +142,32 @@ public:
*/
const ItemType *find(const std::function<bool (const ItemType&)> &&f, int *index = nullptr) const;
/**
* replace item in the cache with the one provided. replacement is based on newItem MLItemId
*
* this returns the index of the replaced item, or -1 if the item is not in the cache
*/
int updateItem(std::unique_ptr<MLItem>&& newItem);
/**
* Removes from the cache list given its item id
*
* it returns the index row when the id is found and removed, -1 otherwise
*/
int deleteItem(const MLItemId& mlid);
/**
* move items in the cache contained between @a first
* and @a last to the index @a to
*/
void moveRange(int first, int last, int to);
/**
* remove items in the cache from the index @a first up to the
* index @a last
*/
void deleteRange(int first, int last);
/**
* Return the number of items or `COUNT_UNINITIALIZED`
......@@ -134,33 +190,110 @@ public:
*/
void refer(size_t index);
signals:
/* useful for signaling QAbstractItemModel::modelAboutToBeReset() */
void localSizeAboutToBeChanged(size_t size);
/*
* reload
*/
void invalidate();
signals:
void localSizeChanged(size_t size);
void localDataChanged(size_t index, size_t count);
void localDataChanged(int sourceFirst, int sourceLast);
void beginInsertRows(int sourceFirst, int sourceLast);
void endInsertRows();
void beginRemoveRows(int sourceFirst, int sourceLast);
void endRemoveRows();
void beginMoveRows(int sourceFirst, int sourceLast, int destination);
void endMoveRows();
private:
void asyncLoad(size_t offset, size_t count);
void asyncCount();
void asyncFetchMore();
void asyncCountAndLoad();
void partialUpdate();
size_t fixupIndexForMove(size_t index) const;
MediaLib* m_medialib = nullptr;
bool m_useMove = false;
/* Ownershipshared between this cache and the runnable spawned to execute
* loader callbacks */
QSharedPointer<ListCacheLoader<ItemType>> m_loader;
size_t m_chunkSize;
std::vector<ItemType> m_list;
ssize_t m_total_count = COUNT_UNINITIALIZED;
size_t m_offset = 0;
//highest index requested by the view (1 based, 0 is nothing referenced)
size_t m_maxReferedIndex = 0;
bool m_countRequested = false;
MLRange m_lastRangeRequested;
bool m_needReload = false;
uint64_t m_loadTask = 0;
uint64_t m_appendTask = 0;
uint64_t m_countTask = 0;
struct CacheData {
explicit CacheData(std::vector<ItemType>&& list_, size_t totalCount_)
: list(std::move(list_))
, totalCount(totalCount_)
{
loadedCount = list.size();
}
std::vector<ItemType> list;
size_t totalCount = 0;
size_t loadedCount = 0;
};
std::unique_ptr<CacheData> m_cachedData;
std::unique_ptr<CacheData> m_oldData;
MLRange m_rangeRequested;
//access the list while it's being updated
size_t m_partialIndex = 0;
size_t m_partialX = 0;
size_t m_partialLoadedCount = 0;
//represent a redirection of items that are after m_partialIndex
struct PartialIndexRedirect {
enum class Operation
{
ADD,
DEL
};
explicit PartialIndexRedirect(Operation op_, size_t index_, size_t count_, size_t x_ = 0)
: op(op_)
, index(index_)
, count(count_)
{
if (op == Operation::ADD)
{
val.add.x = x_;
}
}
Operation op;
union {
struct {
size_t x;
} add;
struct {
} del;
} val;
size_t index;
size_t count;
//for set ordering
friend bool operator<(const PartialIndexRedirect& l, const PartialIndexRedirect& r)
{
return l.index < r.index;
}
};
//store index redirection keeping index order
std::set<PartialIndexRedirect> m_partialIndexRedirect;
};
#endif
......@@ -275,9 +275,9 @@ vlc_ml_sorting_criteria_t MLPlaylistListModel::roleToCriteria(int role) const /*
return VLC_ML_SORTING_DEFAULT;
}
ListCacheLoader<std::unique_ptr<MLItem>> * MLPlaylistListModel::createLoader() const /* override */
std::unique_ptr<MLBaseModel::BaseLoader> MLPlaylistListModel::createLoader() const /* override */
{
return new Loader(*this);
return std::make_unique<Loader>(*this);
}
//-------------------------------------------------------------------------------------------------
......@@ -351,20 +351,33 @@ QString MLPlaylistListModel::getCover(MLPlaylist * playlist) const
void MLPlaylistListModel::onVlcMlEvent(const MLEvent & event) /* override */
{
if (m_transactionPending
&& (event.i_type == VLC_ML_EVENT_PLAYLIST_UPDATED
|| event.i_type == VLC_ML_EVENT_PLAYLIST_DELETED
|| event.i_type == VLC_ML_EVENT_PLAYLIST_ADDED))
{
m_resetAfterTransaction = true;
return;
}
switch (event.i_type)
{
case VLC_ML_EVENT_PLAYLIST_DELETED:
{
MLItemId itemId(event.deletion.i_entity_id, VLC_ML_PARENT_PLAYLIST);
deleteItemInCache(itemId);
return;
}
case VLC_ML_EVENT_PLAYLIST_UPDATED:
{
MLItemId itemId(event.modification.i_entity_id, VLC_ML_PARENT_PLAYLIST);
updateItemInCache(itemId);
return;
}
case VLC_ML_EVENT_PLAYLIST_ADDED:
case VLC_ML_EVENT_PLAYLIST_DELETED:
{
if(m_transactionPending)
m_resetAfterTransaction = true;
else
{
m_need_reset = true;
// NOTE: Maybe we should call this from MLBaseModel ?
emit resetRequested();
}
emit resetRequested();
break;
}
default:
......@@ -456,3 +469,13 @@ MLPlaylistListModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t c
return result;
}
std::unique_ptr<MLItem>
MLPlaylistListModel::Loader::loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const
{
assert(itemId.type == VLC_ML_PARENT_PLAYLIST);
ml_unique_ptr<vlc_ml_playlist_t> playlist(vlc_ml_get_playlist(ml, itemId.id));
if (!playlist)
return nullptr;
return std::make_unique<MLPlaylist>(playlist.get());
}
......@@ -71,7 +71,7 @@ protected: // MLBaseModel implementation
vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
ListCacheLoader<std::unique_ptr<MLItem>> * createLoader() const override;
std::unique_ptr<MLBaseModel::BaseLoader> createLoader() const override;
private: // Functions
QString getCover(MLPlaylist * playlist) const;
......@@ -112,6 +112,8 @@ private:
size_t count(vlc_medialibrary_t* ml) const override;
std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override;
std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override;
};
};
......
......@@ -137,10 +137,9 @@ void MLPlaylistModel::moveImpl(int64_t playlistId, HighLowRanges&& ranges)
},
//UI thread
[this, playlistId, high, low, r = std::move(ranges)](quint64, Ctx& ctx) mutable {
beginMoveRows(QModelIndex(), low, high, QModelIndex(), r.lowTo);
moveRangeInCache(low, high, r.lowTo);
r.lowTo = ctx.newTo;
--r.lowRangeIt;
endMoveRows();
moveImpl(playlistId, std::move(r));
});
}
......@@ -162,10 +161,9 @@ void MLPlaylistModel::moveImpl(int64_t playlistId, HighLowRanges&& ranges)
},
//UI thread
[this, playlistId, low, high, r = std::move(ranges)](quint64, Ctx& ctx) mutable {
beginMoveRows(QModelIndex(), low, high, QModelIndex(), r.highTo);
moveRangeInCache(low, high, r.highTo);
r.highTo = ctx.newTo;
++r.highRangeIt;
endMoveRows();
moveImpl(playlistId, std::move(r));
});
}
......@@ -263,8 +261,7 @@ void MLPlaylistModel::removeImpl(int64_t playlistId, const std::vector<std::pair
},
//UI thread
[this, playlistId, range, rows = std::move(rangeList), index]() {
beginRemoveRows(QModelIndex(), range.first, range.second);
endRemoveRows();
deleteRangeInCache(range.first, range.second);
removeImpl(playlistId, std::move(rows), index+1);
});
}
......@@ -406,9 +403,10 @@ QByteArray MLPlaylistModel::criteriaToName(vlc_ml_sorting_criteria_t criteria) c
//-------------------------------------------------------------------------------------------------
ListCacheLoader<std::unique_ptr<MLItem>> * MLPlaylistModel::createLoader() const /* override */
std::unique_ptr<MLBaseModel::BaseLoader>
MLPlaylistModel::createLoader() const /* override */
{
return new Loader(*this);
return std::make_unique<Loader>(*this);
}
//-------------------------------------------------------------------------------------------------
......@@ -419,17 +417,23 @@ void MLPlaylistModel::onVlcMlEvent(const MLEvent & event) /* override */
{
switch (event.i_type)
{
case VLC_ML_EVENT_MEDIA_UPDATED:
{
MLItemId itemId(event.modification.i_entity_id, VLC_ML_PARENT_UNKNOWN);
updateItemInCache(itemId);
return;
}
case VLC_ML_EVENT_PLAYLIST_UPDATED:
{
if (m_transactionPending)
m_resetAfterTransaction = true;
else
MLItemId itemId(event.modification.i_entity_id, VLC_ML_PARENT_PLAYLIST);
if (m_parent == itemId)
{
m_need_reset = true;
// NOTE: Maybe we should call this from MLBaseModel ?
emit resetRequested();
if (m_transactionPending)
m_resetAfterTransaction = true;
else
emit resetRequested();
}
break;
return;
}
default:
break;
......@@ -536,3 +540,13 @@ MLPlaylistModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count
return result;
}
std::unique_ptr<MLItem>
MLPlaylistModel::Loader::loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const
{
assert(itemId.type == VLC_ML_PARENT_UNKNOWN);
ml_unique_ptr<vlc_ml_media_t> media(vlc_ml_get_media(ml, itemId.id));
if (!media)
return nullptr;
return std::make_unique<MLPlaylistMedia>(media.get());
}
......@@ -67,7 +67,7 @@ protected: // MLBaseModel implementation
QByteArray criteriaToName(vlc_ml_sorting_criteria_t criteria) const override;
ListCacheLoader<std::unique_ptr<MLItem>> * createLoader() const override;
std::unique_ptr<MLBaseModel::BaseLoader> createLoader() const override;
protected: // MLBaseModel reimplementation
void onVlcMlEvent(const MLEvent & event) override;
......@@ -108,6 +108,8 @@ private:
size_t count(vlc_medialibrary_t* ml) const override;
std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override;
std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override;
};
};
......
......@@ -80,13 +80,10 @@ void MLRecentsModel::onVlcMlEvent( const MLEvent &event )
switch ( event.i_type )
{
case VLC_ML_EVENT_HISTORY_CHANGED:
{
emit resetRequested();
break;
case VLC_ML_EVENT_MEDIA_ADDED:
case VLC_ML_EVENT_MEDIA_UPDATED:
case VLC_ML_EVENT_MEDIA_DELETED:
m_need_reset = true;
break;
return;
}
default:
break;
}
......@@ -100,10 +97,10 @@ int MLRecentsModel::getNumberOfItemsToShow() const {
return m_numberOfItemsToShow;
}
ListCacheLoader<std::unique_ptr<MLItem>> *
std::unique_ptr<MLBaseModel::BaseLoader>
MLRecentsModel::createLoader() const
{
return new Loader(*this, m_numberOfItemsToShow);
return std::make_unique<Loader>(*this, m_numberOfItemsToShow);
}
MLRecentsModel::Loader::Loader(const MLRecentsModel &model, int numberOfItemsToShow)
......@@ -146,3 +143,13 @@ MLRecentsModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count)
res.emplace_back( std::make_unique<MLRecentMedia>( &media ) );
return res;
}
std::unique_ptr<MLItem>
MLRecentsModel::Loader::loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const
{
assert(itemId.type == VLC_ML_PARENT_UNKNOWN);
ml_unique_ptr<vlc_ml_media_t> media(vlc_ml_get_media(ml, itemId.id));
if (!media)
return nullptr;
return std::make_unique<MLRecentMedia>(media.get());
}