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 (28)
Showing
with 935 additions and 459 deletions
......@@ -174,6 +174,10 @@ libqt_plugin_la_SOURCES = \
gui/qt/medialibrary/mlhelper.hpp \
gui/qt/medialibrary/mlitemcover.cpp \
gui/qt/medialibrary/mlitemcover.hpp \
gui/qt/medialibrary/mllistcache.cpp \
gui/qt/medialibrary/mllistcache.hpp \
gui/qt/medialibrary/mlthreadpool.cpp \
gui/qt/medialibrary/mlthreadpool.hpp \
gui/qt/medialibrary/mlqmltypes.hpp \
gui/qt/medialibrary/mlqueryparams.cpp \
gui/qt/medialibrary/mlqueryparams.hpp \
......@@ -235,7 +239,6 @@ libqt_plugin_la_SOURCES = \
gui/qt/util/imagehelper.cpp gui/qt/util/imagehelper.hpp \
gui/qt/util/i18n.cpp gui/qt/util/i18n.hpp \
gui/qt/util/keyhelper.cpp gui/qt/util/keyhelper.hpp \
gui/qt/util/listcache.hpp \
gui/qt/util/listcacheloader.hpp \
gui/qt/util/navigation_history.cpp gui/qt/util/navigation_history.hpp \
gui/qt/util/item_key_event_filter.cpp \
......@@ -383,6 +386,8 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/medialibrary/mlfoldersmodel.moc.cpp \
gui/qt/medialibrary/mlgenremodel.moc.cpp \
gui/qt/medialibrary/mlgrouplistmodel.moc.cpp \
gui/qt/medialibrary/mllistcache.moc.cpp \
gui/qt/medialibrary/mlthreadpool.moc.cpp \
gui/qt/medialibrary/mlqmltypes.moc.cpp \
gui/qt/medialibrary/mlrecentsmodel.moc.cpp \
gui/qt/medialibrary/mlrecentsvideomodel.moc.cpp \
......@@ -409,11 +414,9 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/util/asynctask.moc.cpp \
gui/qt/util/audio_device_model.moc.cpp \
gui/qt/util/color_scheme_model.moc.cpp \
gui/qt/util/covergenerator.moc.cpp \
gui/qt/util/imageluminanceextractor.moc.cpp \
gui/qt/util/i18n.moc.cpp \
gui/qt/util/keyhelper.moc.cpp \
gui/qt/util/listcache.moc.cpp \
gui/qt/util/navigation_history.moc.cpp \
gui/qt/util/item_key_event_filter.moc.cpp \
gui/qt/util/mouse_event_filter.moc.cpp \
......
......@@ -27,12 +27,14 @@
#include "bookmarks.hpp"
#include "player/player_controller.hpp"
#include "medialibrary/mlbookmarkmodel.hpp"
#include "maininterface/mainctx.hpp"
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QPushButton>
#include <QDialogButtonBox>
#include <QModelIndexList>
#include <QTreeView>
BookmarksDialog::BookmarksDialog( qt_intf_t *_p_intf ):QVLCFrame( _p_intf )
{
......@@ -63,7 +65,7 @@ BookmarksDialog::BookmarksDialog( qt_intf_t *_p_intf ):QVLCFrame( _p_intf )
QDialogButtonBox::RejectRole);
bookmarksList = new QTreeView( this );
m_model = new MLBookmarkModel( vlc_ml_instance_get(_p_intf ),
m_model = new MLBookmarkModel( _p_intf->p_mi->getMediaLibrary(),
_p_intf->p_player,
bookmarksList );
bookmarksList->setModel( m_model );
......@@ -82,19 +84,17 @@ BookmarksDialog::BookmarksDialog( qt_intf_t *_p_intf ):QVLCFrame( _p_intf )
layout->addWidget( buttonsBox );
layout->addWidget( bookmarksList );
CONNECT( bookmarksList, activated( QModelIndex ), this,
activateItem( QModelIndex ) );
CONNECT( m_model, modelReset(), this, updateButtons() );
CONNECT( bookmarksList->selectionModel(), selectionChanged( const QItemSelection &, const QItemSelection & ),
this, updateButtons() );
BUTTONACT( addButton, add() );
BUTTONACT( delButton, del() );
BUTTONACT( clearButton, clear() );
connect( bookmarksList, &QTreeView::activated, this, &BookmarksDialog::activateItem);
connect( m_model, &QAbstractListModel::modelReset, this, &BookmarksDialog::updateButtons );
connect( bookmarksList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &BookmarksDialog::updateButtons );
connect( addButton, &QAbstractButton::clicked, this, &BookmarksDialog::add );
connect( delButton, &QAbstractButton::clicked, this, &BookmarksDialog::del );
connect( clearButton, &QAbstractButton::clicked, this, &BookmarksDialog::clear );
#if 0
BUTTONACT( extractButton, extract() );
connect( extractButton, &QAbstractButton::clicked, this, &BookmarksDialog::extract );
#endif
CONNECT( buttonsBox, rejected(), this, close() );
connect( buttonsBox, &QDialogButtonBox::rejected, this, &BookmarksDialog::close);
updateButtons();
restoreWidgetPosition( "Bookmarks", QSize( 435, 280 ) );
......
......@@ -25,12 +25,11 @@
#define QVLC_BOOKMARKS_H_ 1
#include "widgets/native/qvlcframe.hpp"
#include <QStandardItemModel>
#include <QTreeView>
#include <QTreeWidget>
#include "util/singleton.hpp"
class QPushButton;
class MLBookmarkModel;
class QTreeView;
class BookmarksDialog : public QVLCFrame, public Singleton<BookmarksDialog>
{
......
......@@ -69,7 +69,7 @@ PlaylistsDialog::PlaylistsDialog(qt_intf_t * _p_intf) : QVLCFrame(_p_intf)
connect(m_playlists, &QTreeView::clicked, this, &PlaylistsDialog::onClicked);
connect(m_playlists, &QTreeView::doubleClicked, this, &PlaylistsDialog::onDoubleClicked);
m_model = new MLPlaylistListModel(vlc_ml_instance_get(_p_intf), m_playlists);
m_model = new MLPlaylistListModel(m_playlists);
m_model->setMl(mainCtx->getMediaLibrary());
......@@ -216,9 +216,6 @@ void PlaylistsDialog::onAccepted()
if (text.isEmpty())
id = m_model->getItemId(m_playlists->currentIndex().row());
else
id = m_model->create(text);
m_model->append(id, m_ids);
m_model->create(text, m_ids);
close();
}
......@@ -138,7 +138,7 @@ MainCtx::MainCtx(qt_intf_t *_p_intf)
vlc_medialibrary_t* ml = vlc_ml_instance_get( p_intf );
b_hasMedialibrary = (ml != NULL);
if (b_hasMedialibrary) {
m_medialib = new MediaLib(p_intf, this);
m_medialib = new MediaLib(p_intf);
}
/* Controlbar Profile Model Creation */
......@@ -233,6 +233,9 @@ MainCtx::~MainCtx()
var_DelCallback( libvlc, "intf-toggle-fscontrol", IntfShowCB, p_intf );
var_DelCallback( libvlc, "intf-popupmenu", PopupMenuCB, p_intf );
if (m_medialib)
m_medialib->destroy();
p_intf->p_mi = NULL;
}
......
......@@ -28,7 +28,7 @@
MediaLib::MediaLib(qt_intf_t *_intf, QObject *_parent)
: QObject( _parent )
, m_intf( _intf )
, m_ml( vlcMl() )
, m_ml( vlc_ml_instance_get( _intf ) )
, m_event_cb( nullptr, [this](vlc_ml_event_callback_t* cb ) {
vlc_ml_event_unregister_callback( m_ml, cb );
})
......@@ -37,22 +37,38 @@ MediaLib::MediaLib(qt_intf_t *_intf, QObject *_parent)
this ) );
/* https://xkcd.com/221/ */
m_threadPool.setMaxThreadCount(4);
m_mlThreadPool.setMaxThreadCount(4);
}
void MediaLib::addToPlaylist(const QString& mrl, const QStringList &options)
MediaLib::~MediaLib()
{
vlc::playlist::Media media{ mrl, mrl, options };
m_intf->p_mainPlaylistController->append( {media}, false );
}
void MediaLib::addToPlaylist(const QUrl& mrl, const QStringList &options)
void MediaLib::destroy()
{
vlc::playlist::Media media{ mrl.toString(QUrl::None), mrl.fileName(), options };
m_intf->p_mainPlaylistController->append( {media} , false );
m_shuttingDown = true;
//try to cancel as many tasks as possible
for (auto taskIt = m_objectTasks.begin(); taskIt != m_objectTasks.end(); /**/)
{
quint64 key = taskIt.value();
auto task = m_runningTasks.value(key, nullptr);
if (m_mlThreadPool.tryTake(task))
{
delete task;
m_runningTasks.remove(key);
taskIt = m_objectTasks.erase(taskIt);
}
else
++taskIt;
}
if (m_runningTasks.empty())
{
deleteLater();
}
}
void MediaLib::convertMLItemToPlaylistMedias(const MLItemId & itemId, const QStringList &options, QVector<vlc::playlist::Media>& medias)
static void convertMLItemToPlaylistMedias(vlc_medialibrary_t* ml, const MLItemId & itemId, const QStringList &options, QVector<vlc::playlist::Media>& medias)
{
//invalid item
if (itemId.id == 0)
......@@ -60,7 +76,7 @@ void MediaLib::convertMLItemToPlaylistMedias(const MLItemId & itemId, const QStr
if (itemId.type == VLC_ML_PARENT_UNKNOWN)
{
vlc::playlist::InputItemPtr item( vlc_ml_get_input_item( m_ml, itemId.id ), false );
vlc::playlist::InputItemPtr item( vlc_ml_get_input_item( ml, itemId.id ), false );
if (item)
medias.push_back(vlc::playlist::Media(item.get(), options));
}
......@@ -68,57 +84,127 @@ void MediaLib::convertMLItemToPlaylistMedias(const MLItemId & itemId, const QStr
{
vlc_ml_query_params_t query;
memset(&query, 0, sizeof(vlc_ml_query_params_t));
ml_unique_ptr<vlc_ml_media_list_t> media_list(vlc_ml_list_media_of( m_ml, &query, itemId.type, itemId.id));
ml_unique_ptr<vlc_ml_media_list_t> media_list(vlc_ml_list_media_of( ml, &query, itemId.type, itemId.id));
if (media_list == nullptr || media_list->i_nb_items == 0)
return;
auto mediaRange = ml_range_iterate<vlc_ml_media_t>( media_list );
std::transform(mediaRange.begin(), mediaRange.end(), std::back_inserter(medias), [&](vlc_ml_media_t& m) {
vlc::playlist::InputItemPtr item(vlc_ml_get_input_item( m_ml, m.i_id ), false);
vlc::playlist::InputItemPtr item(vlc_ml_get_input_item( ml, m.i_id ), false);
return vlc::playlist::Media(item.get(), options);
});
}
}
// A specific item has been asked to be added to the playlist
void MediaLib::addToPlaylist(const MLItemId & itemId, const QStringList &options)
static void convertQUrlToPlaylistMedias(QUrl mrl, const QStringList& options, QVector<vlc::playlist::Media>& medias)
{
QVector<vlc::playlist::Media> medias;
convertMLItemToPlaylistMedias(itemId, options, medias);
if (!medias.empty())
m_intf->p_mainPlaylistController->append(medias, false);
vlc::playlist::Media media{ mrl.toString(QUrl::None), mrl.fileName(), options };
medias.push_back(media);
}
void MediaLib::addToPlaylist(const QVariantList& itemIdList, const QStringList &options)
static void convertQStringToPlaylistMedias(QString mrl, const QStringList& options, QVector<vlc::playlist::Media>& medias)
{
vlc::playlist::Media media{ mrl, mrl, options };
medias.push_back(media);
}
static void convertQVariantListToPlaylistMedias(vlc_medialibrary_t* ml, QVariantList itemIdList, const QStringList& options, QVector<vlc::playlist::Media>& medias)
{
for (const QVariant& varValue: itemIdList)
{
if (varValue.canConvert<QUrl>())
{
auto mrl = varValue.value<QUrl>();
addToPlaylist(mrl, options);
convertQUrlToPlaylistMedias(mrl, options, medias);
}
else if (varValue.canConvert<QString>())
{
auto mrl = varValue.value<QString>();
addToPlaylist(mrl, options);
convertQStringToPlaylistMedias(mrl, options, medias);
}
else if (varValue.canConvert<MLItemId>())
{
MLItemId itemId = varValue.value<MLItemId>();
addToPlaylist(itemId, options);
convertMLItemToPlaylistMedias(ml, itemId, options, medias);
}
// else ignore
}
}
void MediaLib::addToPlaylist(const QString& mrl, const QStringList &options)
{
QVector<vlc::playlist::Media> medias;
convertQStringToPlaylistMedias(mrl, options, medias);
m_intf->p_mainPlaylistController->append(medias, false);
}
void MediaLib::addToPlaylist(const QUrl& mrl, const QStringList &options)
{
QVector<vlc::playlist::Media> medias;
convertQUrlToPlaylistMedias(mrl, options, medias);
m_intf->p_mainPlaylistController->append(medias, false);
}
// A specific item has been asked to be added to the playlist
void MediaLib::addToPlaylist(const MLItemId & itemId, const QStringList &options)
{
struct Context {
QVector<vlc::playlist::Media> medias;
};
runOnMLThread<Context>(this,
//ML thread
[itemId, options]
(vlc_medialibrary_t* ml, Context& ctx){
convertMLItemToPlaylistMedias(ml, itemId, options, ctx.medias);
},
//UI thread
[this](quint64, Context& ctx){
if (!ctx.medias.empty())
m_intf->p_mainPlaylistController->append(ctx.medias, false);
});
}
void MediaLib::addToPlaylist(const QVariantList& itemIdList, const QStringList &options)
{
struct Context {
QVector<vlc::playlist::Media> medias;
};
runOnMLThread<Context>(this,
//ML thread
[itemIdList, options]
(vlc_medialibrary_t* ml, Context& ctx)
{
convertQVariantListToPlaylistMedias(ml, itemIdList, options, ctx.medias);
},
//UI thread
[this](quint64, Context& ctx){
if (!ctx.medias.empty())
m_intf->p_mainPlaylistController->append(ctx.medias, false);
});
}
// A specific item has been asked to be played,
// so it's added to the playlist and played
void MediaLib::addAndPlay(const MLItemId & itemId, const QStringList &options )
{
QVector<vlc::playlist::Media> medias;
convertMLItemToPlaylistMedias(itemId, options, medias);
if (!medias.empty())
m_intf->p_mainPlaylistController->append(medias, true);
struct Context {
QVector<vlc::playlist::Media> medias;
};
runOnMLThread<Context>(this,
//ML thread
[itemId, options]
(vlc_medialibrary_t* ml, Context& ctx)
{
convertMLItemToPlaylistMedias(ml, itemId, options, ctx.medias);
},
//UI thread
[this](quint64, Context& ctx){
if (!ctx.medias.empty())
m_intf->p_mainPlaylistController->append(ctx.medias, true);
});
}
void MediaLib::addAndPlay(const QString& mrl, const QStringList &options)
......@@ -136,108 +222,136 @@ void MediaLib::addAndPlay(const QUrl& mrl, const QStringList &options)
void MediaLib::addAndPlay(const QVariantList& itemIdList, const QStringList &options)
{
bool b_start = true;
for (const QVariant& varValue: itemIdList)
struct Context {
QVector<vlc::playlist::Media> medias;
};
runOnMLThread<Context>(this,
//ML thread
[itemIdList, options]
(vlc_medialibrary_t* ml, Context& ctx)
{
if (varValue.canConvert<QUrl>())
{
auto mrl = varValue.value<QUrl>();
if (b_start)
addAndPlay(mrl, options);
else
addToPlaylist(mrl, options);
}
if (varValue.canConvert<QString>())
{
auto mrl = varValue.value<QString>();
if (b_start)
addAndPlay(mrl, options);
else
addToPlaylist(mrl, options);
}
else if (varValue.canConvert<MLItemId>())
{
MLItemId itemId = varValue.value<MLItemId>();
if (b_start)
addAndPlay(itemId, options);
else
addToPlaylist(itemId, options);
} else {
continue;
}
b_start = false;
}
convertQVariantListToPlaylistMedias(ml, itemIdList, options, ctx.medias);
},
//UI thread
[this](quint64, Context& ctx){
if (!ctx.medias.empty())
m_intf->p_mainPlaylistController->append(ctx.medias, true);
});
}
void MediaLib::insertIntoPlaylist(const size_t index, const QVariantList &itemIds, const QStringList &options)
void MediaLib::insertIntoPlaylist(const size_t index, const QVariantList &itemIdList, const QStringList &options)
{
QVector<vlc::playlist::Media> medias;
for ( const auto &id : itemIds )
struct Context {
QVector<vlc::playlist::Media> medias;
};
runOnMLThread<Context>(this,
//ML thread
[itemIdList, options]
(vlc_medialibrary_t* ml, Context& ctx )
{
if (!id.canConvert<MLItemId>())
continue;
const MLItemId itemId = id.value<MLItemId>();
convertMLItemToPlaylistMedias(itemId, options, medias);
}
if (!medias.isEmpty())
m_intf->p_mainPlaylistController->insert( index, medias );
convertQVariantListToPlaylistMedias(ml, itemIdList, options, ctx.medias);
},
//UI thread
[this, index]
(quint64, Context& ctx) {
if (!ctx.medias.isEmpty())
m_intf->p_mainPlaylistController->insert( index, ctx.medias );
});
}
void MediaLib::reload()
{
/* m_threadPool.start(lambda) is only supported since Qt 5.15 */
struct Task : QRunnable {
vlc_medialibrary_t *m_ml;
Task(vlc_medialibrary_t *ml) : m_ml(ml) {}
void run() override
{
vlc_ml_reload_folder(m_ml, nullptr);
}
};
m_threadPool.start(new Task(m_ml));
runOnMLThread(this,
//ML thread
[](vlc_medialibrary_t* ml){
vlc_ml_reload_folder(ml, nullptr);
});
}
QVariantList MediaLib::mlInputItem(const MLItemId mlId)
void MediaLib::mlInputItem(const QVariantList& variantList, QJSValue callback)
{
QVariantList items;
// NOTE: When we have a parent it's a collection of media(s).
if (mlId.type == VLC_ML_PARENT_UNKNOWN)
if (!callback.isCallable()) // invalid argument
{
QmlInputItem input(vlc_ml_get_input_item(m_ml, mlId.id), false);
msg_Warn(m_intf, "callback is not callbable");
return;
}
items.append(QVariant::fromValue(input));
std::vector<MLItemId> mlIdList;
for (const auto& variant : variantList)
{
if (variant.canConvert<MLItemId>())
mlIdList.push_back(variant.value<MLItemId>());
}
else
struct Ctx {
std::vector<QmlInputItem> items;
};
if (mlIdList.empty())
{
ml_unique_ptr<vlc_ml_media_list_t> list;
//call the callback with and empty list
auto jsEngine = qjsEngine(this);
if (!jsEngine)
return;
auto jsArray = jsEngine->newArray(0);
callback.call({jsArray});
return;
}
vlc_ml_query_params_t query;
runOnMLThread<Ctx>(this,
//ML thread
[mlIdList](vlc_medialibrary_t* ml, Ctx& ctx){
for (auto mlId : mlIdList)
{
// NOTE: When we have a parent it's a collection of media(s).
if (mlId.type == VLC_ML_PARENT_UNKNOWN)
{
ctx.items.emplace_back(vlc_ml_get_input_item(ml, mlId.id), false);
}
else
{
ml_unique_ptr<vlc_ml_media_list_t> list;
memset(&query, 0, sizeof(vlc_ml_query_params_t));
vlc_ml_query_params_t query;
memset(&query, 0, sizeof(vlc_ml_query_params_t));
list.reset(vlc_ml_list_media_of(m_ml, &query, mlId.type, mlId.id));
list.reset(vlc_ml_list_media_of(ml, &query, mlId.type, mlId.id));
if (list == nullptr)
return {};
if (list == nullptr)
continue;
for (const vlc_ml_media_t & media : ml_range_iterate<vlc_ml_media_t>(list))
{
QmlInputItem input(vlc_ml_get_input_item(m_ml, media.i_id), false);
for (const vlc_ml_media_t & media : ml_range_iterate<vlc_ml_media_t>(list))
ctx.items.emplace_back(vlc_ml_get_input_item(ml, media.i_id), false);
}
}
},
//UI thread
[this, callback](quint64, Ctx& ctx) mutable
{
auto jsEngine = qjsEngine(this);
if (!jsEngine)
return;
auto jsArray = jsEngine->newArray(ctx.items.size());
items.append(QVariant::fromValue(input));
int i = 0;
for (const auto& inputItem : ctx.items)
{
jsArray.setProperty(i, jsEngine->toScriptValue(inputItem));
i++;
}
}
return items;
callback.call({jsArray});
});
}
vlc_ml_event_callback_t* MediaLib::registerEventListener( void (*callback)(void*, const vlc_ml_event_t*), void* data)
{
return vlc_ml_event_register_callback( m_ml , callback , data ) ;
}
vlc_medialibrary_t* MediaLib::vlcMl()
void MediaLib::unregisterEventListener(vlc_ml_event_callback_t* cb)
{
return vlc_ml_instance_get( m_intf );
vlc_ml_event_unregister_callback( m_ml , cb );
}
void MediaLib::onMediaLibraryEvent( void* data, const vlc_ml_event_t* event )
......@@ -294,3 +408,103 @@ void MediaLib::onMediaLibraryEvent( void* data, const vlc_ml_event_t* event )
break;
}
}
quint64 MediaLib::runOnMLThread(const QObject* obj,
std::function< void(vlc_medialibrary_t* ml)> mlCb,
std::function< void()> uiCb,
const char* queue)
{
struct NoCtx{};
return runOnMLThread<NoCtx>(obj,
[mlCb](vlc_medialibrary_t* ml, NoCtx&){
mlCb(ml);
},
[uiCb](quint64, NoCtx&){
uiCb();
},
queue);
}
quint64 MediaLib::runOnMLThread(const QObject* obj,
std::function< void(vlc_medialibrary_t* ml)> mlCb,
std::function< void(quint64)> uiCb, const char* queue)
{
struct NoCtx{};
return runOnMLThread<NoCtx>(obj,
[mlCb](vlc_medialibrary_t* ml, NoCtx&){
mlCb(ml);
},
[uiCb](quint64 requestId, NoCtx&){
uiCb(requestId);
},
queue);
}
quint64 MediaLib::runOnMLThread(const QObject* obj,
std::function< void(vlc_medialibrary_t* ml)> mlCb,
const char* queue)
{
struct NoCtx{};
return runOnMLThread<NoCtx>(obj,
[mlCb](vlc_medialibrary_t* ml, NoCtx&){
mlCb(ml);
},
[](quint64, NoCtx&){
},
queue);
}
void MediaLib::cancelMLTask(const QObject* object, quint64 taskId)
{
assert(taskId != 0);
auto task = m_runningTasks.value(taskId, nullptr);
if (!task)
return;
task->cancel();
bool removed = m_mlThreadPool.tryTake(task);
if (removed)
delete task;
m_runningTasks.remove(taskId);
m_objectTasks.remove(object, taskId);
}
void MediaLib::runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status)
{
if (m_shuttingDown)
{
if (m_runningTasks.contains(target))
{
m_runningTasks.remove(target);
m_objectTasks.remove(object, target);
}
if (m_runningTasks.empty())
deleteLater();
}
else if (m_runningTasks.contains(target))
{
if (status == ML_TASK_STATUS_SUCCEED)
runner->runUICallback();
m_runningTasks.remove(target);
m_objectTasks.remove(object, target);
}
runner->deleteLater();
}
void MediaLib::runOnMLThreadTargetDestroyed(QObject * object)
{
if (m_objectTasks.contains(object))
{
for (auto taskId : m_objectTasks.values(object))
{
auto task = m_runningTasks.value(taskId, nullptr);
assert(task);
bool removed = m_mlThreadPool.tryTake(task);
if (removed)
delete task;
m_runningTasks.remove(taskId);
}
m_objectTasks.remove(object);
}
}
......@@ -33,6 +33,7 @@
#include <memory>
#include "qt.hpp"
#include "mlthreadpool.hpp"
#include "mlqmltypes.hpp"
#include "util/qmlinputitem.hpp"
......@@ -43,18 +44,30 @@ class Media;
}
}
struct vlc_medialibrary_t;
class RunOnMLThreadBaseRunner;
class MediaLib : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(bool discoveryPending READ discoveryPending NOTIFY discoveryPendingChanged FINAL)
Q_PROPERTY(int parsingProgress READ parsingProgress NOTIFY parsingProgressChanged FINAL)
Q_PROPERTY(QString discoveryEntryPoint READ discoveryEntryPoint NOTIFY discoveryEntryPointChanged FINAL)
Q_PROPERTY(bool idle READ idle NOTIFY idleChanged FINAL)
enum MLTaskStatus {
ML_TASK_STATUS_SUCCEED,
ML_TASK_STATUS_CANCELED
};
public:
MediaLib(qt_intf_t* _intf, QObject* _parent = nullptr );
void destroy();
Q_INVOKABLE void addToPlaylist(const MLItemId &itemId, const QStringList &options = {});
Q_INVOKABLE void addToPlaylist(const QString& mrl, const QStringList &options = {});
Q_INVOKABLE void addToPlaylist(const QUrl& mrl, const QStringList &options = {});
......@@ -68,16 +81,109 @@ public:
Q_INVOKABLE void reload();
Q_INVOKABLE QVariantList mlInputItem(MLItemId mlId);
Q_INVOKABLE void mlInputItem(const QVariantList& variantList, QJSValue callback);
inline bool idle() const { return m_idle; }
inline int discoveryPending() const { return m_discoveryPending; }
inline QString discoveryEntryPoint() const { return m_discoveryEntryPoint; }
inline int parsingProgress() const { return m_parsingProgress; }
vlc_medialibrary_t* vlcMl();
vlc_ml_event_callback_t* registerEventListener(void (*callback)(void*, const vlc_ml_event_t*), void* data);
void unregisterEventListener(vlc_ml_event_callback_t*);
/**
* this function allows to run lambdas on the ML thread,
* this should be used to perform operation that requires to call vlc_ml_xxx functions
* as vlc_ml_xxx should *not* be called from the UI thread
*
* @param Ctx is the type of context, this context is created by the runner and passed sequentially to
* the ML callback then to UI callback, the ML callback may modify its properties as a way to pass
* data to the UI callback
*
* @param obj: this is the instance of the calling QObject, we ensure that this object is still live when
* calling the ui callback, if this object gets destroyed the task is canceled
*
* @param mlCb, this callback is executed in a background thread (thread pool), it takes as arguments
* the instance of the medialibrary, and a context, the context can be modified to pass data to the
* UI callback
*
* @param uiCB, this callback is executed on the Qt thread, it takes as first argument the
* id of the tasks, and as second argument the context that was created for the ML callback
*
* @param queue, this allows to specify if the task must be exectuted on a specific queue, if nullptr
* task may be run by any thread in the threadpool. this is useful if you want to ensure that tasks must
* be executed in order.
*
* @warning the ml callback **SHOULD NOT** capture the obj object or point to its properties by reference,
* as the obj object may potentially be destroyed when the functio is called
*
* the ui callback **MAY** capture the object obj as we will ensure that the object still exists before calling
* the callback
*
* the uiCb **MAY NOT** be called if the object obj is released earlier
*
* anything stored int the context Ctx **MUST** auto release when the context is destroyed
*
* sample usage:
*
* @code
* struct Ctx {
* bool result;
* Data mydata;
* };
* runOnMLThread<Ctx>(this,
* //run on ML thread
* [](vlc_medialibrary_t* ml, Ctx& ctx){
* ctx.result = vlc_ml_xxx(ml, &ctx.mydata):
* },
* //run on UI thread
* [this](quint64 taskId, Ctx& ctx) {
* if (ctx.result)
* emit dataUpdated(ctx.mydata);
* })
* @endcode
*/
template<typename Ctx>
quint64 runOnMLThread(const QObject* obj,
std::function< void(vlc_medialibrary_t* ml, Ctx& ctx)> mlCb,
std::function< void(quint64 taskId, Ctx& ctx)> uiCB,
const char* queue = nullptr);
/**
* same as runOnMLThread<Ctx> when no context passing is required
*/
quint64 runOnMLThread(const QObject* obj,
std::function< void(vlc_medialibrary_t* ml)> mlCb,
std::function< void()> uiCB,
const char* queue = nullptr);
/**
* same as runOnMLThread<Ctx> when no context passing is required
*/
quint64 runOnMLThread(const QObject* obj,
std::function< void(vlc_medialibrary_t* ml)> mlCb,
std::function< void(quint64 taskId)> uiCB,
const char* queue = nullptr);
QThreadPool &threadPool() { return m_threadPool; }
/**
* same as runOnMLThread<Ctx> when no ui callback is required
*/
quint64 runOnMLThread(const QObject* obj,
std::function< void(vlc_medialibrary_t* ml)> mlCb,
const char* queue = nullptr);
/**
* @brief cancelMLTask, explicitly cancel a running task
*
* @param object the object passed to runOnMLThread
* @param taskId the id of the task to cancel
*
* @note there is warranty whether the ML callback will be run or not.
* The UI callback won't be run unless the task is already terminated.
*
* this must be called from the Qt thread.
*/
void cancelMLTask(const QObject* object, quint64 taskId);
signals:
void discoveryStarted();
......@@ -88,11 +194,13 @@ signals:
void idleChanged();
private:
//use the destroy function
~MediaLib();
static void onMediaLibraryEvent( void* data, const vlc_ml_event_t* event );
void convertMLItemToPlaylistMedias(
const MLItemId & itemId, const QStringList &options,
QVector<vlc::playlist::Media>& mediasOut);
private slots:
void runOnMLThreadDone(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status);
void runOnMLThreadTargetDestroyed(QObject * object);
private:
qt_intf_t* m_intf;
......@@ -106,5 +214,91 @@ private:
vlc_medialibrary_t* m_ml;
std::unique_ptr<vlc_ml_event_callback_t, std::function<void(vlc_ml_event_callback_t*)>> m_event_cb;
QThreadPool m_threadPool;
MLThreadPool m_mlThreadPool;
/* run on ml thread properties */
bool m_shuttingDown = false;
quint64 m_taskId = 1;
QMap<quint64, RunOnMLThreadBaseRunner*> m_runningTasks;
QMultiMap<const QObject*, quint64> m_objectTasks;
};
class RunOnMLThreadBaseRunner : public QObject, public QRunnable
{
Q_OBJECT
public:
virtual void runUICallback() = 0;
virtual void cancel() = 0;
signals:
void done(RunOnMLThreadBaseRunner* runner, quint64 target, const QObject* object, int status);
};
template<typename Ctx>
class RunOnMLThreadRunner : public RunOnMLThreadBaseRunner {
public:
RunOnMLThreadRunner(
quint64 taskId,
const QObject* obj,
std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun,
std::function<void (quint64, Ctx&)> uiFun,
vlc_medialibrary_t* ml
)
: RunOnMLThreadBaseRunner()
, m_taskId(taskId)
, m_obj(obj)
, m_mlFun(mlFun)
, m_uiFun(uiFun)
, m_ml(ml)
{
setAutoDelete(false);
}
void run()
{
if (m_canceled)
{
emit done(this, m_taskId, m_obj, MediaLib::ML_TASK_STATUS_CANCELED);
return;
}
m_mlFun(m_ml, m_ctx);
emit done(this, m_taskId, m_obj, MediaLib::ML_TASK_STATUS_SUCCEED);
}
//called from UI thread
void runUICallback() override
{
m_uiFun(m_taskId, m_ctx);
}
void cancel() override
{
m_canceled = true;
}
private:
std::atomic_bool m_canceled {false};
quint64 m_taskId;
Ctx m_ctx; //default constructed
const QObject* m_obj = nullptr;
std::function<void (vlc_medialibrary_t*, Ctx&)> m_mlFun;
std::function<void (quint64, Ctx&)> m_uiFun;
vlc_medialibrary_t* m_ml = nullptr;
};
template<typename Ctx>
quint64 MediaLib::runOnMLThread(const QObject* obj,
std::function<void (vlc_medialibrary_t*, Ctx&)> mlFun,
std::function<void (quint64 taskId, Ctx&)> uiFun,
const char* queue)
{
if (m_shuttingDown)
return 0;
auto taskId = m_taskId++;
auto runnable = new RunOnMLThreadRunner<Ctx>(taskId, obj, mlFun, uiFun, m_ml);
connect(runnable, &RunOnMLThreadBaseRunner::done, this, &MediaLib::runOnMLThreadDone);
connect(obj, &QObject::destroyed, this, &MediaLib::runOnMLThreadTargetDestroyed);
m_runningTasks.insert(taskId, runnable);
m_objectTasks.insert(obj, taskId);
m_mlThreadPool.start(runnable, queue);
return taskId;
}
......@@ -18,10 +18,9 @@
#include <cassert>
#include "mlalbum.hpp"
MLAlbum::MLAlbum(vlc_medialibrary_t* _ml, const vlc_ml_album_t *_data, QObject *_parent)
MLAlbum::MLAlbum(const vlc_ml_album_t *_data, QObject *_parent)
: QObject( _parent )
, MLItem ( MLItemId( _data->i_id, VLC_ML_PARENT_ALBUM ) )
, m_ml ( _ml )
, m_title ( QString::fromUtf8( _data->psz_title ) )
, m_releaseYear ( _data->i_year )
, m_shortSummary( QString::fromUtf8( _data->psz_summary ) )
......@@ -31,7 +30,6 @@ MLAlbum::MLAlbum(vlc_medialibrary_t* _ml, const vlc_ml_album_t *_data, QObject *
, m_duration ( _data->i_duration )
{
assert( _data );
assert( _ml );
}
QString MLAlbum::getTitle() const
......
......@@ -36,7 +36,7 @@ class MLAlbum : public QObject, public MLItem
Q_OBJECT
public:
MLAlbum(vlc_medialibrary_t* _ml, const vlc_ml_album_t *_data, QObject *_parent = nullptr);
MLAlbum(const vlc_ml_album_t *_data, QObject *_parent = nullptr);
QString getTitle() const;
unsigned int getReleaseYear() const;
......@@ -51,8 +51,6 @@ public:
Q_INVOKABLE QString getPresInfo() const;
private:
vlc_medialibrary_t* m_ml;
QString m_title;
unsigned int m_releaseYear;
QString m_shortSummary;
......
......@@ -83,11 +83,6 @@ void MLAlbumModel::onVlcMlEvent(const MLEvent &event)
MLBaseModel::onVlcMlEvent( event );
}
void MLAlbumModel::thumbnailUpdated(int idx)
{
emit dataChanged(index(idx), index(idx), {ALBUM_COVER});
}
vlc_ml_sorting_criteria_t MLAlbumModel::roleToCriteria(int role) const
{
switch (role)
......@@ -143,31 +138,31 @@ MLAlbumModel::createLoader() const
return new Loader(*this);
}
size_t MLAlbumModel::Loader::count() const
size_t MLAlbumModel::Loader::count(vlc_medialibrary_t* ml) const
{
MLQueryParams params = getParams();
auto queryParams = params.toCQueryParams();
if ( m_parent.id <= 0 )
return vlc_ml_count_albums(m_ml, &queryParams);
return vlc_ml_count_albums_of(m_ml, &queryParams, m_parent.type, m_parent.id);
return vlc_ml_count_albums(ml, &queryParams);
return vlc_ml_count_albums_of(ml, &queryParams, m_parent.type, m_parent.id);
}
std::vector<std::unique_ptr<MLItem>>
MLAlbumModel::Loader::load(size_t index, size_t count) const
MLAlbumModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count) const
{
MLQueryParams params = getParams(index, count);
auto queryParams = params.toCQueryParams();
ml_unique_ptr<vlc_ml_album_list_t> album_list;
if ( m_parent.id <= 0 )
album_list.reset( vlc_ml_list_albums(m_ml, &queryParams) );
album_list.reset( vlc_ml_list_albums(ml, &queryParams) );
else
album_list.reset( vlc_ml_list_albums_of(m_ml, &queryParams, m_parent.type, m_parent.id ) );
album_list.reset( vlc_ml_list_albums_of(ml, &queryParams, m_parent.type, m_parent.id ) );
if ( album_list == nullptr )
return {};
std::vector<std::unique_ptr<MLItem>> res;
for( const vlc_ml_album_t& album: ml_range_iterate<vlc_ml_album_t>( album_list ) )
res.emplace_back( std::make_unique<MLAlbum>( m_ml, &album ) );
res.emplace_back( std::make_unique<MLAlbum>( &album ) );
return res;
}
......@@ -65,15 +65,14 @@ private:
vlc_ml_sorting_criteria_t nameToCriteria(QByteArray name) const override;
QByteArray criteriaToName(vlc_ml_sorting_criteria_t criteria) const override;
virtual void onVlcMlEvent( const MLEvent &event ) override;
void thumbnailUpdated(int idx) override;
static QHash<QByteArray, vlc_ml_sorting_criteria_t> M_names_to_criteria;
struct Loader : public BaseLoader
{
Loader(const MLAlbumModel &model) : BaseLoader(model) {}
size_t count() const override;
std::vector<std::unique_ptr<MLItem>> load(size_t index, size_t count) const override;
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;
};
};
......
......@@ -148,18 +148,18 @@ MLAlbumTrackModel::createLoader() const
return new Loader(*this);
}
size_t MLAlbumTrackModel::Loader::count() const
size_t MLAlbumTrackModel::Loader::count(vlc_medialibrary_t* ml) const
{
MLQueryParams params = getParams();
auto queryParams = params.toCQueryParams();
if ( m_parent.id <= 0 )
return vlc_ml_count_audio_media(m_ml, &queryParams);
return vlc_ml_count_media_of(m_ml, &queryParams, m_parent.type, m_parent.id );
return vlc_ml_count_audio_media(ml, &queryParams);
return vlc_ml_count_media_of(ml, &queryParams, m_parent.type, m_parent.id );
}
std::vector<std::unique_ptr<MLItem>>
MLAlbumTrackModel::Loader::load(size_t index, size_t count) const
MLAlbumTrackModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count) const
{
MLQueryParams params = getParams(index, count);
auto queryParams = params.toCQueryParams();
......@@ -167,13 +167,13 @@ MLAlbumTrackModel::Loader::load(size_t index, size_t count) const
ml_unique_ptr<vlc_ml_media_list_t> media_list;
if ( m_parent.id <= 0 )
media_list.reset( vlc_ml_list_audio_media(m_ml, &queryParams) );
media_list.reset( vlc_ml_list_audio_media(ml, &queryParams) );
else
media_list.reset( vlc_ml_list_media_of(m_ml, &queryParams, m_parent.type, m_parent.id ) );
media_list.reset( vlc_ml_list_media_of(ml, &queryParams, m_parent.type, m_parent.id ) );
if ( media_list == nullptr )
return {};
std::vector<std::unique_ptr<MLItem>> res;
for( const vlc_ml_media_t& media: ml_range_iterate<vlc_ml_media_t>( media_list ) )
res.emplace_back( std::make_unique<MLAlbumTrack>( m_ml, &media ) );
res.emplace_back( std::make_unique<MLAlbumTrack>( ml, &media ) );
return res;
}
......@@ -71,8 +71,8 @@ private:
struct Loader : public BaseLoader
{
Loader(const MLAlbumTrackModel &model) : BaseLoader(model) {}
size_t count() const override;
std::vector<std::unique_ptr<MLItem>> load(size_t index, size_t count) const override;
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;
};
};
#endif // MLTRACKMODEL_HPP
......@@ -102,38 +102,33 @@ void MLArtistModel::onVlcMlEvent(const MLEvent &event)
MLBaseModel::onVlcMlEvent(event);
}
void MLArtistModel::thumbnailUpdated(int idx)
{
emit dataChanged(index(idx), index(idx), {ARTIST_COVER});
}
ListCacheLoader<std::unique_ptr<MLItem>> *
MLArtistModel::createLoader() const
{
return new Loader(*this);
}
size_t MLArtistModel::Loader::count() const
size_t MLArtistModel::Loader::count(vlc_medialibrary_t* ml) const
{
MLQueryParams params = getParams();
auto queryParams = params.toCQueryParams();
if ( m_parent.id <= 0 )
return vlc_ml_count_artists(m_ml, &queryParams, false);
return vlc_ml_count_artists_of(m_ml, &queryParams, m_parent.type, m_parent.id );
return vlc_ml_count_artists(ml, &queryParams, false);
return vlc_ml_count_artists_of(ml, &queryParams, m_parent.type, m_parent.id );
}
std::vector<std::unique_ptr<MLItem>>
MLArtistModel::Loader::load(size_t index, size_t count) const
MLArtistModel::Loader::load(vlc_medialibrary_t* ml, size_t index, size_t count) const
{
MLQueryParams params = getParams(index, count);
auto queryParams = params.toCQueryParams();
ml_unique_ptr<vlc_ml_artist_list_t> artist_list;
if ( m_parent.id <= 0 )
artist_list.reset( vlc_ml_list_artists(m_ml, &queryParams, false) );
artist_list.reset( vlc_ml_list_artists(ml, &queryParams, false) );
else
artist_list.reset( vlc_ml_list_artist_of(m_ml, &queryParams, m_parent.type, m_parent.id) );
artist_list.reset( vlc_ml_list_artist_of(ml, &queryParams, m_parent.type, m_parent.id) );
if ( artist_list == nullptr )
return {};
std::vector<std::unique_ptr<MLItem>> res;
......
......@@ -57,15 +57,14 @@ private:
vlc_ml_sorting_criteria_t nameToCriteria(QByteArray name) const override;
QByteArray criteriaToName(vlc_ml_sorting_criteria_t criteria) const override;
virtual void onVlcMlEvent(const MLEvent &event) override;
void thumbnailUpdated(int idx) override;
static QHash<QByteArray, vlc_ml_sorting_criteria_t> M_names_to_criteria;
struct Loader : public BaseLoader
{
Loader(const MLArtistModel &model) : BaseLoader(model) {}
size_t count() const override;
std::vector<std::unique_ptr<MLItem>> load(size_t index, size_t count) const override;
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;
};
};
......
......@@ -20,7 +20,7 @@
#include "medialib.hpp"
#include <vlc_cxx_helpers.hpp>
#include "util/listcache.hpp"
#include "mllistcache.hpp"
#include "util/qmlinputitem.hpp"
// MediaLibrary includes
......@@ -29,70 +29,13 @@
#include "util/asynctask.hpp"
class BulkTaskLoader : public AsyncTask<std::vector<std::unique_ptr<MLItem>>>
{
public:
BulkTaskLoader(QSharedPointer<ListCacheLoader<std::unique_ptr<MLItem>>> loader, QVector<int> indexes)
: m_loader(loader)
, m_indexes {indexes}
{
}
std::vector<std::unique_ptr<MLItem>> execute() override
{
if (m_indexes.isEmpty())
return {};
auto sortedIndexes = m_indexes;
std::sort(sortedIndexes.begin(), sortedIndexes.end());
struct Range
{
int low, high; // [low, high] (all inclusive)
};
QVector<Range> ranges;
ranges.push_back(Range {sortedIndexes[0], sortedIndexes[0]});
const int MAX_DIFFERENCE = 4;
for (const auto index : sortedIndexes)
{
if ((index - ranges.back().high) < MAX_DIFFERENCE)
ranges.back().high = index;
else
ranges.push_back(Range {index, index});
}
std::vector<std::unique_ptr<MLItem>> r(m_indexes.size());
for (const auto range : ranges)
{
auto data = m_loader->load(range.low, range.high - range.low + 1);
for (int i = 0; i < m_indexes.size(); ++i)
{
const auto targetIndex = m_indexes[i];
if (targetIndex >= range.low && targetIndex <= range.high)
{
r.at(i) = std::move(data.at(targetIndex - range.low));
}
}
}
return r;
}
private:
QSharedPointer<ListCacheLoader<std::unique_ptr<MLItem>>> m_loader;
QVector<int> m_indexes;
};
static constexpr ssize_t COUNT_UNINITIALIZED =
ListCache<std::unique_ptr<MLItem>>::COUNT_UNINITIALIZED;
static constexpr ssize_t COUNT_UNINITIALIZED = MLListCache::COUNT_UNINITIALIZED;
MLBaseModel::MLBaseModel(QObject *parent)
: QAbstractListModel(parent)
, m_ml_event_handle( nullptr, [this](vlc_ml_event_callback_t* cb ) {
assert( m_ml != nullptr );
vlc_ml_event_unregister_callback( m_ml, cb );
assert( m_mediaLib != nullptr );
m_mediaLib->unregisterEventListener( cb );
})
{
connect( this, &MLBaseModel::resetRequested, this, &MLBaseModel::onResetRequested );
......@@ -137,64 +80,82 @@ void MLBaseModel::getData(const QModelIndexList &indexes, QJSValue callback)
return;
QVector<int> indx;
std::transform(indexes.begin(), indexes.end(), std::back_inserter(indx), [](const auto &index)
{
std::transform(indexes.begin(), indexes.end(), std::back_inserter(indx),
[](const auto &index) {
return index.row();
});
TaskHandle<BulkTaskLoader> loader(new BulkTaskLoader(QSharedPointer<ListCacheLoader<std::unique_ptr<MLItem>>>(createLoader()), indx));
connect(loader.get(), &BaseAsyncTask::result, this, [this, callback, indx]() mutable
{
auto loader = (BulkTaskLoader *)sender();
auto freeSender = [this, &loader]()
{
m_externalLoaders.erase(std::find_if(std::begin(m_externalLoaders), std::end(m_externalLoaders), [&](auto &v)
{
if (v.get() != loader)
return false;
QSharedPointer<ListCacheLoader<std::unique_ptr<MLItem>>> loader{ createLoader() };
struct Ctx {
std::vector<std::unique_ptr<MLItem>> items;
};
m_mediaLib->runOnMLThread<Ctx>(this,
//ML thread
[loader, indx](vlc_medialibrary_t* ml, Ctx& ctx){
if (indx.isEmpty())
return;
v.release();
loader->deleteLater();
loader = nullptr;
return true;
}));
auto sortedIndexes = indx;
std::sort(sortedIndexes.begin(), sortedIndexes.end());
assert(!loader);
struct Range
{
int low, high; // [low, high] (all inclusive)
};
QVector<Range> ranges;
ranges.push_back(Range {sortedIndexes[0], sortedIndexes[0]});
const int MAX_DIFFERENCE = 4;
for (const auto index : sortedIndexes)
{
if ((index - ranges.back().high) < MAX_DIFFERENCE)
ranges.back().high = index;
else
ranges.push_back(Range {index, index});
}
ctx.items.resize(indx.size());
for (const auto range : ranges)
{
auto data = loader->load(ml, range.low, range.high - range.low + 1);
for (int i = 0; i < indx.size(); ++i)
{
const auto targetIndex = indx[i];
if (targetIndex >= range.low && targetIndex <= range.high)
{
ctx.items.at(i) = std::move(data.at(targetIndex - range.low));
}
}
}
},
//UI thread
[this, indxSize = indx.size(), callback]
(quint64, Ctx& ctx) mutable
{
auto jsEngine = qjsEngine(this);
if (!jsEngine)
{
freeSender();
return;
}
const auto loadedItems = loader->takeResult();
assert((int)loadedItems.size() == indx.size());
assert((int)ctx.items.size() == indxSize);
const QHash<int, QByteArray> roles = roleNames();
auto jsArray = jsEngine->newArray(loadedItems.size());
auto jsArray = jsEngine->newArray(indxSize);
for (size_t i = 0; i < loadedItems.size(); ++i)
for (int i = 0; i < indxSize; ++i)
{
const auto &item = loadedItems[i];
const auto &item = ctx.items[i];
QMap<QString, QVariant> dataDict;
for (int role: roles.keys())
{
if (item) // item may fail to load
if (item) // item may fail to load
for (int role: roles.keys())
dataDict[roles[role]] = itemRoleData(item.get(), role);
}
jsArray.setProperty(i, qjsEngine(this)->toScriptValue(dataDict));
jsArray.setProperty(i, jsEngine->toScriptValue(dataDict));
}
callback.call({jsArray});
freeSender();
});
loader->start(*QThreadPool::globalInstance());
m_externalLoaders.push_back(std::move(loader));
}
QVariant MLBaseModel::data(const QModelIndex &index, int role) const
......@@ -257,12 +218,22 @@ void MLBaseModel::onVlcMlEvent(const MLEvent &event)
if (stotal == COUNT_UNINITIALIZED)
break;
int index = 0;
int row = 0;
/* Only consider items available locally in cache */
const auto item = findInCache(event.media_thumbnail_generated.i_media_id, &index);
MLItemId itemId{event.media_thumbnail_generated.i_media_id, VLC_ML_PARENT_UNKNOWN};
MLItem* item = findInCache(itemId, &row);
if (item)
thumbnailUpdated(index);
{
vlc_ml_thumbnail_status_t status = VLC_ML_THUMBNAIL_STATUS_FAILURE;
QString mrl;
if (event.media_thumbnail_generated.b_success)
{
QString thumbnail = qfu(event.media_thumbnail_generated.psz_mrl);
status = event.media_thumbnail_generated.i_status;
}
thumbnailUpdated(index(row), item, mrl, status);
}
}
break;
}
......@@ -286,8 +257,10 @@ QString MLBaseModel::getFirstSymbol(QString str)
void MLBaseModel::onVlcMlEvent(void* data, const vlc_ml_event_t* event)
{
auto self = static_cast<MLBaseModel*>(data);
QMetaObject::invokeMethod(self, [self, event = MLEvent(event)] {
self->onVlcMlEvent(event);
//MLEvent is not copiable, but lambda needs to be copiable
auto mlEvent = std::make_shared<MLEvent>(event);
QMetaObject::invokeMethod(self, [self, mlEvent] () mutable {
self->onVlcMlEvent(*mlEvent);
});
}
......@@ -322,10 +295,9 @@ MediaLib* MLBaseModel::ml() const
void MLBaseModel::setMl(MediaLib* medialib)
{
assert(medialib);
m_ml = medialib->vlcMl();
m_mediaLib = medialib;
if ( m_ml_event_handle == nullptr )
m_ml_event_handle.reset( vlc_ml_event_register_callback( m_ml, onVlcMlEvent, this ) );
m_ml_event_handle.reset( m_mediaLib->registerEventListener(onVlcMlEvent, this ) );
}
const QString& MLBaseModel::searchPattern() const
......@@ -449,56 +421,6 @@ QVariantList MLBaseModel::getIdsForIndexes(const QVariantList & indexes) const
//-------------------------------------------------------------------------------------------------
/* Q_INVOKABLE virtual */
QVariantList MLBaseModel::getItemsForIndexes(const QModelIndexList & indexes) const
{
assert(m_ml);
QVariantList items;
vlc_ml_query_params_t query;
memset(&query, 0, sizeof(vlc_ml_query_params_t));
for (const QModelIndex & index : indexes)
{
MLItem * item = this->item(index.row());
if (item == nullptr)
continue;
MLItemId itemId = item->getId();
// NOTE: When we have a parent it's a collection of media(s).
if (itemId.type == VLC_ML_PARENT_UNKNOWN)
{
QmlInputItem input(vlc_ml_get_input_item(m_ml, itemId.id), false);
items.append(QVariant::fromValue(input));
}
else
{
ml_unique_ptr<vlc_ml_media_list_t> list;
list.reset(vlc_ml_list_media_of(m_ml, &query, itemId.type, itemId.id));
if (list == nullptr)
continue;
for (const vlc_ml_media_t & media : ml_range_iterate<vlc_ml_media_t>(list))
{
QmlInputItem input(vlc_ml_get_input_item(m_ml, media.i_id), false);
items.append(QVariant::fromValue(input));
}
}
}
return items;
}
//-------------------------------------------------------------------------------------------------
unsigned MLBaseModel::getCount() const
{
if (!m_mediaLib)
......@@ -514,14 +436,13 @@ void MLBaseModel::validateCache() const
if (m_cache)
return;
auto &threadPool = m_mediaLib->threadPool();
auto loader = createLoader();
m_cache.reset(new ListCache<std::unique_ptr<MLItem>>(threadPool, loader));
connect(&*m_cache, &BaseListCache::localSizeAboutToBeChanged,
m_cache.reset(new MLListCache(m_mediaLib, loader));
connect(&*m_cache, &MLListCache::localSizeAboutToBeChanged,
this, &MLBaseModel::onLocalSizeAboutToBeChanged);
connect(&*m_cache, &BaseListCache::localSizeChanged,
connect(&*m_cache, &MLListCache::localSizeChanged,
this, &MLBaseModel::onLocalSizeChanged);
connect(&*m_cache, &BaseListCache::localDataChanged,
connect(&*m_cache, &MLListCache::localDataChanged,
this, &MLBaseModel::onLocalDataChanged);
m_cache->initCount();
......@@ -571,11 +492,11 @@ MLItem *MLBaseModel::itemCache(int signedidx) const
return item->get();
}
MLItem *MLBaseModel::findInCache(const int id, int *index) const
MLItem *MLBaseModel::findInCache(const MLItemId& id, int *index) const
{
const auto item = m_cache->find([id](const auto &item)
{
return item->getId().id == id;
return item->getId() == id;
}, index);
return item ? item->get() : nullptr;
......@@ -583,10 +504,9 @@ MLItem *MLBaseModel::findInCache(const int id, int *index) const
//-------------------------------------------------------------------------------------------------
MLBaseModel::BaseLoader::BaseLoader(vlc_medialibrary_t *ml, MLItemId parent, QString searchPattern,
MLBaseModel::BaseLoader::BaseLoader(MLItemId parent, QString searchPattern,
vlc_ml_sorting_criteria_t sort, bool sort_desc)
: m_ml(ml)
, m_parent(parent)
: m_parent(parent)
, m_searchPattern(searchPattern)
, m_sort(sort)
, m_sort_desc(sort_desc)
......@@ -594,7 +514,7 @@ MLBaseModel::BaseLoader::BaseLoader(vlc_medialibrary_t *ml, MLItemId parent, QSt
}
MLBaseModel::BaseLoader::BaseLoader(const MLBaseModel &model)
: BaseLoader(model.m_ml, model.m_parent, model.m_search_pattern, model.m_sort, model.m_sort_desc)
: BaseLoader(model.m_parent, model.m_search_pattern, model.m_sort, model.m_sort_desc)
{
}
......
......@@ -34,16 +34,11 @@
#include <memory>
#include "mlevent.hpp"
#include "mlqueryparams.hpp"
#include "util/asynctask.hpp"
#include "util/listcacheloader.hpp"
template <typename T>
class ListCache;
class MLListCache;
class MediaLib;
class BulkTaskLoader;
class MLBaseModel : public QAbstractListModel
{
Q_OBJECT
......@@ -75,8 +70,6 @@ public: // Interface
Q_INVOKABLE virtual QVariantList getIdsForIndexes(const QVariantList & indexes) const;
Q_INVOKABLE virtual QVariantList getIdsForIndexes(const QModelIndexList & indexes) const;
Q_INVOKABLE virtual QVariantList getItemsForIndexes(const QModelIndexList & indexes) const;
Q_INVOKABLE QMap<QString, QVariant> getDataAt(const QModelIndex & index);
Q_INVOKABLE QMap<QString, QVariant> getDataAt(int idx);
......@@ -122,25 +115,24 @@ protected:
// NOTE: This is faster because it only returns items available in cache.
MLItem *itemCache(int signedidx) const;
MLItem *findInCache(int id, int *index = nullptr) const;
MLItem *findInCache(const MLItemId& id, int *index) const;
virtual void onVlcMlEvent( const MLEvent &event );
virtual ListCacheLoader<std::unique_ptr<MLItem>> *createLoader() const = 0;
virtual void thumbnailUpdated( int ) {}
virtual void thumbnailUpdated(const QModelIndex& , MLItem* , const QString& , vlc_ml_thumbnail_status_t ) {}
/* Data loader for the cache */
struct BaseLoader : public ListCacheLoader<std::unique_ptr<MLItem>>
{
BaseLoader(vlc_medialibrary_t *ml, MLItemId parent, QString searchPattern,
BaseLoader(MLItemId parent, QString searchPattern,
vlc_ml_sorting_criteria_t sort, bool sort_desc);
BaseLoader(const MLBaseModel &model);
MLQueryParams getParams(size_t index = 0, size_t count = 0) const;
protected:
vlc_medialibrary_t *m_ml;
MLItemId m_parent;
QString m_searchPattern;
vlc_ml_sorting_criteria_t m_sort;
......@@ -164,13 +156,12 @@ public:
void setSortCriteria(const QString& criteria);
void unsetSortCriteria();
int rowCount(const QModelIndex &parent = {}) const;
int rowCount(const QModelIndex &parent = {}) const override;
virtual unsigned int getCount() const;
protected:
MLItemId m_parent;
vlc_medialibrary_t* m_ml = nullptr;
MediaLib* m_mediaLib = nullptr;
QString m_search_pattern;
vlc_ml_sorting_criteria_t m_sort = VLC_ML_SORTING_DEFAULT;
......@@ -180,8 +171,7 @@ protected:
std::function<void(vlc_ml_event_callback_t*)>> m_ml_event_handle;
bool m_need_reset = false;
mutable std::unique_ptr<ListCache<std::unique_ptr<MLItem>>> m_cache;
std::vector<TaskHandle<BulkTaskLoader>> m_externalLoaders;
mutable std::unique_ptr<MLListCache> m_cache;
};
#endif // MLBASEMODEL_HPP
......@@ -25,18 +25,15 @@
#include <vlc_cxx_helpers.hpp>
#include "medialib.hpp"
#include "mlhelper.hpp"
MLBookmarkModel::MLBookmarkModel( vlc_medialibrary_t *ml, vlc_player_t *player,
MLBookmarkModel::MLBookmarkModel( MediaLib* medialib, vlc_player_t *player,
QObject *parent )
: QAbstractListModel( parent )
, m_ml( ml )
, m_mediaLib( medialib )
, m_player( player )
, m_listener( nullptr )
, m_currentItem( nullptr, &input_item_Release )
, m_currentMediaId( 0 )
, m_sort( VLC_ML_SORTING_INSERTIONDATE )
, m_desc( false )
{
static const vlc_player_cbs cbs {
&onCurrentMediaChanged,
......@@ -74,16 +71,22 @@ MLBookmarkModel::MLBookmarkModel( vlc_medialibrary_t *ml, vlc_player_t *player,
nullptr,
nullptr,
};
QString uri;
{
vlc_player_locker lock{ m_player };
vlc::threads::mutex_locker selflock{ m_mutex };
m_listener = vlc_player_AddListener( m_player, &cbs, this );
if ( m_listener == nullptr )
throw std::bad_alloc{};
auto currentItem = vlc_player_GetCurrentMedia( m_player );
m_currentItem = vlc::wrap_cptr( currentItem ? input_item_Hold( currentItem ) : nullptr,
&input_item_Release );
if (m_currentItem)
{
uri = m_currentItem->psz_uri;
}
}
refresh( false );
updateMediaId(0, uri);
}
MLBookmarkModel::~MLBookmarkModel()
......@@ -95,7 +98,7 @@ MLBookmarkModel::~MLBookmarkModel()
QVariant MLBookmarkModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() || index.row() < 0 ||
m_bookmarks == nullptr ||
!m_bookmarks ||
(uint32_t)index.row() >= m_bookmarks->i_nb_items )
{
return QVariant{};
......@@ -129,24 +132,59 @@ bool MLBookmarkModel::setData(const QModelIndex &index, const QVariant &value, i
return false;
if ( value.canConvert<QString>() == false )
return false;
size_t row = index.row();
bool updateName = (index.column() == 0);
assert( index.column() == 0 || index.column() == 2 );
assert( (size_t)index.row() < m_bookmarks->i_nb_items );
auto& b = m_bookmarks->p_items[index.row()];
if ( ! m_bookmarks || row >= m_bookmarks->i_nb_items )
return false;
auto& b = m_bookmarks->p_items[row];
auto str = value.toString();
if ( index.column() == 0 )
struct Ctx {
bool updateSucceed;
};
m_mediaLib->runOnMLThread<Ctx>(this,
//ML thread
[mediaId = b.i_media_id, bookmarkTime = b.i_time, updateName, str]
(vlc_medialibrary_t* ml, Ctx& ctx)
{
if ( vlc_ml_media_update_bookmark( m_ml, b.i_media_id, b.i_time,
qtu( str ), nullptr ) != VLC_SUCCESS )
return false;
free( b.psz_name );
b.psz_name = strdup( qtu( str ) );
return true;
}
if ( vlc_ml_media_update_bookmark( m_ml, b.i_media_id, b.i_time,
nullptr, qtu( str ) ) != VLC_SUCCESS )
return false;
free( b.psz_description );
b.psz_description = strdup( qtu( str ) );
int ret;
if ( updateName )
ret = vlc_ml_media_update_bookmark( ml, mediaId, bookmarkTime, qtu( str ), nullptr );
else
ret = vlc_ml_media_update_bookmark( ml, mediaId, bookmarkTime, nullptr, qtu( str ) );
ctx.updateSucceed = (ret == VLC_SUCCESS);
},
//UI thread
[this, updateName, mediaId = m_currentMediaId, row, str]
(quint64, Ctx& ctx)
{
if (!ctx.updateSucceed)
return;
if (m_currentMediaId != mediaId)
return;
auto& b = m_bookmarks->p_items[row];
if (updateName)
{
free( b.psz_name );
b.psz_name = strdup( qtu( str ) );
}
else
{
free( b.psz_description );
b.psz_description = strdup( qtu( str ) );
}
if (updateName)
emit dataChanged(this->index(row, 0), this->index(row, 0), {Qt::DisplayRole});
else
emit dataChanged(this->index(row, 2), this->index(row, 2), {Qt::DisplayRole});
});
return true;
}
......@@ -202,8 +240,6 @@ QVariant MLBookmarkModel::headerData( int section, Qt::Orientation orientation,
void MLBookmarkModel::sort( int column, Qt::SortOrder order )
{
vlc::threads::mutex_locker lock{ m_mutex };
switch ( column )
{
case 0:
......@@ -222,62 +258,82 @@ void MLBookmarkModel::sort( int column, Qt::SortOrder order )
break;
}
m_desc = order == Qt::DescendingOrder ? true : false;
refresh( false );
refresh( MLBOOKMARKMODEL_REFRESH );
}
void MLBookmarkModel::add()
{
vlc_tick_t currentTime;
{
vlc_player_locker lock{ m_player };
currentTime = vlc_player_GetTime( m_player );
}
{
vlc::threads::mutex_locker lock{ m_mutex };
if ( m_currentItem == nullptr )
return;
if ( m_currentMediaId == 0 )
{
auto mlMedia = vlc_ml_get_media_by_mrl( m_ml, m_currentItem->psz_uri );
if ( mlMedia == nullptr )
return;
m_currentMediaId = mlMedia->i_id;
}
}
vlc_ml_media_add_bookmark( m_ml, m_currentMediaId,
MS_FROM_VLC_TICK( currentTime ) );
refresh( false );
if (m_currentMediaId == 0)
return;
m_mediaLib->runOnMLThread(this,
//ML thread
[mediaId = m_currentMediaId, currentTime](vlc_medialibrary_t* ml){
vlc_ml_media_add_bookmark( ml, mediaId, MS_FROM_VLC_TICK( currentTime ) );
},
//UI thread
[this](){
refresh( MLBOOKMARKMODEL_REFRESH );
});
}
void MLBookmarkModel::remove( const QModelIndexList &indexes )
{
int64_t mediaId;
{
vlc::threads::mutex_locker lock{ m_mutex };
mediaId = m_currentMediaId;
}
if (m_currentMediaId == 0)
return;
std::vector<int64_t> bookmarkTimeList;
for ( const auto& i : indexes )
{
if ( i.isValid() == false || (size_t)i.row() >= m_bookmarks->i_nb_items )
continue;
auto& b = m_bookmarks->p_items[i.row()];
vlc_ml_media_remove_bookmark( m_ml, mediaId, b.i_time );
bookmarkTimeList.push_back(b.i_time);
}
refresh( false );
m_mediaLib->runOnMLThread(this,
//ML thread
[mediaId = m_currentMediaId, bookmarkTimeList]
(vlc_medialibrary_t* ml)
{
for (int64_t bookmarkTime : bookmarkTimeList)
vlc_ml_media_remove_bookmark(ml, mediaId, bookmarkTime);
},
//UI thread
[this](){
refresh( MLBOOKMARKMODEL_REFRESH );
});
}
void MLBookmarkModel::clear()
{
int64_t mediaId;
if (m_currentMediaId == 0)
return;
m_mediaLib->runOnMLThread(this,
//ML thread
[mediaId = m_currentMediaId](vlc_medialibrary_t* ml)
{
vlc::threads::mutex_locker lock{ m_mutex };
mediaId = m_currentMediaId;
}
beginResetModel();
vlc_ml_media_remove_all_bookmarks( m_ml, mediaId );
m_bookmarks.reset();
endResetModel();
vlc_ml_media_remove_all_bookmarks( ml, mediaId );
},
//UI thread
[this, mediaId = m_currentMediaId]()
{
if (mediaId == m_currentMediaId)
{
beginResetModel();
m_bookmarks.reset();
endResetModel();
}
});
}
void MLBookmarkModel::select(const QModelIndex &index)
......@@ -292,15 +348,31 @@ void MLBookmarkModel::select(const QModelIndex &index)
void MLBookmarkModel::onCurrentMediaChanged( vlc_player_t*, input_item_t* media,
void *data )
{
//Player thread
auto self = static_cast<MLBookmarkModel*>( data );
QmlInputItem item(media, true);
QString mediaUri;
uint64_t revision;
{
vlc::threads::mutex_locker lock{ self->m_mutex };
self->m_currentItem.reset( media ? input_item_Hold( media ) : nullptr );
if ( media == nullptr )
self->m_currentMediaId = 0;
revision = ++self->m_revision;
if (media)
mediaUri = media->psz_uri;
}
self->refresh( false );
//UI Thread
QMetaObject::invokeMethod(self,
[self, revision, mediaUri]()
{
{
vlc::threads::mutex_locker lock{ self->m_mutex };
//did we start playing a new media in between
if (self->m_revision != revision)
return;
}
self->updateMediaId(revision, mediaUri);
});
}
void MLBookmarkModel::onPlaybackStateChanged( vlc_player_t *, vlc_player_state state,
......@@ -308,36 +380,96 @@ void MLBookmarkModel::onPlaybackStateChanged( vlc_player_t *, vlc_player_state s
{
auto self = static_cast<MLBookmarkModel*>( data );
if ( state == VLC_PLAYER_STATE_STARTED )
self->refresh( false );
else if ( state == VLC_PLAYER_STATE_STOPPING )
self->refresh( true );
QMetaObject::invokeMethod(self, [self, state](){
if ( state == VLC_PLAYER_STATE_STARTED )
self->refresh( MLBOOKMARKMODEL_REFRESH );
else if ( state == VLC_PLAYER_STATE_STOPPING )
self->refresh( MLBOOKMARKMODEL_CLEAR );
});
}
void MLBookmarkModel::refresh( bool forceClear )
void MLBookmarkModel::updateMediaId(uint64_t revision, const QString mediaUri)
{
callAsync([this, forceClear]() {
vlc::threads::mutex_locker lock( m_mutex );
if (mediaUri.isEmpty())
{
refresh(MLBOOKMARKMODEL_CLEAR);
return;
}
//retrieve the media id in the medialib
struct Ctx{
uint64_t newMLid = 0;
BookmarkListPtr newBookmarks;
};
m_mediaLib->runOnMLThread<Ctx>(this,
//ML thread
[mediaUri, sort = m_sort, desc = m_desc](vlc_medialibrary_t* ml, Ctx& ctx){
if ( forceClear == false && m_currentMediaId == 0 && m_currentItem != nullptr )
auto mlMedia = vlc_ml_get_media_by_mrl( ml, qtu(mediaUri) );
if ( mlMedia != nullptr )
{
auto mlMedia = vlc_ml_get_media_by_mrl( m_ml, m_currentItem->psz_uri );
if ( mlMedia != nullptr )
{
m_currentMediaId = mlMedia->i_id;
vlc_ml_release( mlMedia );
}
ctx.newMLid = mlMedia->i_id;
vlc_ml_release( mlMedia );
}
beginResetModel();
if ( m_currentMediaId == 0 || forceClear == true )
m_bookmarks.reset();
else
vlc_ml_query_params_t params{};
params.i_sort = sort;
params.b_desc = desc;
ctx.newBookmarks.reset( vlc_ml_list_media_bookmarks( ml, &params, ctx.newMLid ) );
},
//UI thread
[this, revision](quint64, Ctx& ctx) {
bool valid;
{
vlc_ml_query_params_t params{};
params.i_sort = m_sort;
params.b_desc = m_desc;
m_bookmarks.reset( vlc_ml_list_media_bookmarks( m_ml, &params, m_currentMediaId ) );
vlc::threads::mutex_locker lock{ m_mutex };
//did we start playing a new media in between
valid = (m_revision == revision);
}
if (valid)
{
beginResetModel();
m_bookmarks = std::move(ctx.newBookmarks);
m_currentMediaId = ctx.newMLid;
endResetModel();
}
endResetModel();
});
}
void MLBookmarkModel::refresh(MLBookmarkModel::RefreshOperation forceClear )
{
if (m_currentMediaId == 0 || forceClear == MLBOOKMARKMODEL_CLEAR)
{
beginResetModel();
m_bookmarks.reset();
endResetModel();
}
else
{
uint64_t mediaId = m_currentMediaId;
struct Ctx
{
BookmarkListPtr newBookmarks;
};
m_mediaLib->runOnMLThread<Ctx>(this,
//ML thread
[mediaId, sort = m_sort, desc = m_desc]
(vlc_medialibrary_t* ml, Ctx& ctx) {
vlc_ml_query_params_t params{};
params.i_sort = sort;
params.b_desc = desc;
ctx.newBookmarks.reset( vlc_ml_list_media_bookmarks( ml, &params, mediaId ) );
},
//UI thread
[this, mediaId](quint64, Ctx& ctx)
{
beginResetModel();
if (m_currentMediaId == mediaId)
m_bookmarks = std::move(ctx.newBookmarks);
else
m_bookmarks.reset();
endResetModel();
});
}
}
......@@ -30,10 +30,12 @@
#include "mlhelper.hpp"
class MediaLib;
class MLBookmarkModel : public QAbstractListModel
{
public:
MLBookmarkModel( vlc_medialibrary_t* ml, vlc_player_t* player, QObject* parent );
MLBookmarkModel( MediaLib* medialib, vlc_player_t* player, QObject* parent );
virtual ~MLBookmarkModel();
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole ) const override;
......@@ -58,36 +60,36 @@ private:
static void onPlaybackStateChanged( vlc_player_t* player, vlc_player_state state,
void* data );
void refresh( bool forceClear );
template <typename Fun>
void callAsync(Fun&& fun)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QMetaObject::invokeMethod(this, std::forward<Fun>(fun), Qt::QueuedConnection, nullptr);
#else
QObject src;
QObject::connect(&src, &QObject::destroyed, this, std::forward<Fun>(fun), Qt::QueuedConnection);
#endif
}
void updateMediaId(uint64_t revision, const QString mediaUri);
enum RefreshOperation {
MLBOOKMARKMODEL_REFRESH,
MLBOOKMARKMODEL_CLEAR,
};
void refresh( RefreshOperation forceClear );
private:
using BookmarkListPtr = ml_unique_ptr<vlc_ml_bookmark_list_t>;
using InputItemPtr = std::unique_ptr<input_item_t, decltype(&input_item_Release)>;
vlc_medialibrary_t* m_ml;
vlc_player_t* m_player;
MediaLib* m_mediaLib = nullptr;
vlc_player_t* m_player = nullptr;
vlc_player_listener_id* m_listener = nullptr;
// Assume to be only used from the GUI thread
BookmarkListPtr m_bookmarks;
vlc_player_listener_id* m_listener;
uint64_t m_currentMediaId = 0;
vlc::threads::mutex m_mutex;
// current item & media id can be accessed by any thread and therefor
//avoid starting two beginReset simultaneously
unsigned m_countPendingReset = 0;
mutable vlc::threads::mutex m_mutex;
uint64_t m_revision = 0;
// current item & media id can be accessed by any thread and therefore
// must be accessed with m_mutex held
InputItemPtr m_currentItem;
int64_t m_currentMediaId;
vlc_ml_sorting_criteria_t m_sort;
bool m_desc;
vlc_ml_sorting_criteria_t m_sort = VLC_ML_SORTING_INSERTIONDATE;
bool m_desc = false;
};
#endif // MLBOOKMARKMODEL_HPP
......@@ -52,7 +52,10 @@ struct MLEvent
struct
{
int64_t i_media_id;
vlc_ml_thumbnail_size_t i_size;
bool b_success;
vlc_ml_thumbnail_status_t i_status;
char* psz_mrl;
} media_thumbnail_generated;
};
......@@ -105,9 +108,44 @@ struct MLEvent
background_idle_changed.b_idle = event->background_idle_changed.b_idle;
break;
case VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED:
{
vlc_ml_thumbnail_size_t thumbnailSize = event->media_thumbnail_generated.i_size;
const vlc_ml_thumbnail_t& thumbnail = event->media_thumbnail_generated.p_media->thumbnails[thumbnailSize];
const char* mrl = thumbnail.psz_mrl;
media_thumbnail_generated.i_media_id = event->media_thumbnail_generated.p_media->i_id;
media_thumbnail_generated.b_success = event->media_thumbnail_generated.b_success;
media_thumbnail_generated.i_size = thumbnailSize;
media_thumbnail_generated.i_status = thumbnail.i_status;
if (media_thumbnail_generated.b_success && mrl)
media_thumbnail_generated.psz_mrl = strdup(mrl);
else
media_thumbnail_generated.psz_mrl = nullptr;
break;
}
}
}
~MLEvent()
{
switch (i_type)
{
case VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED:
{
if (media_thumbnail_generated.psz_mrl)
free(media_thumbnail_generated.psz_mrl);
break;
}
default:
break;
}
}
//allow move
MLEvent(MLEvent&&) = default;
MLEvent& operator=(MLEvent&&) = default;
//forbid copy
MLEvent(MLEvent const&) = delete;
MLEvent& operator=(MLEvent const&) = delete;
};