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 (8)
Showing
with 381 additions and 198 deletions
......@@ -210,6 +210,8 @@ libqt_plugin_la_SOURCES = \
gui/qt/medialibrary/mlplaylistmedia.hpp \
gui/qt/medialibrary/mlplaylistmodel.cpp \
gui/qt/medialibrary/mlplaylistmodel.hpp \
gui/qt/medialibrary/thumbnailcollector.hpp \
gui/qt/medialibrary/thumbnailcollector.cpp \
gui/qt/menus/custom_menus.cpp \
gui/qt/menus/custom_menus.hpp \
gui/qt/menus/qml_menu_wrapper.cpp \
......@@ -416,6 +418,7 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/medialibrary/mlplaylistmodel.moc.cpp \
gui/qt/medialibrary/mlvideofoldersmodel.moc.cpp \
gui/qt/medialibrary/mlvideogroupsmodel.moc.cpp \
gui/qt/medialibrary/thumbnailcollector.moc.cpp \
gui/qt/menus/custom_menus.moc.cpp \
gui/qt/menus/qml_menu_wrapper.moc.cpp \
gui/qt/menus/menus.moc.cpp \
......
......@@ -39,6 +39,7 @@
class MLListCache;
class MediaLib;
class MLItemCover;
class CoverGenerator;
class MLBaseModel : public QAbstractListModel
{
......@@ -195,7 +196,9 @@ protected:
std::shared_ptr<BaseLoader> m_itemLoader;
private: // Friends
friend QString getVideoListCover(const MLBaseModel*, MLItemCover*, int, int, int);
friend QString createGroupMediaCover(const MLBaseModel* model, MLItemCover* parent
, int role
, const std::shared_ptr<CoverGenerator> generator);
};
#endif // MLBASEMODEL_HPP
......@@ -38,6 +38,29 @@ static const int MLGENREMODEL_COVER_BLUR = 4;
//-------------------------------------------------------------------------------------------------
namespace
{
QStringList getGenreMediaThumbnails(vlc_medialibrary_t* p_ml, const int count, const int64_t id)
{
QStringList thumbnails;
vlc_ml_query_params_t params;
memset(&params, 0, sizeof(vlc_ml_query_params_t));
// NOTE: We retrieve twice the count to maximize our chances to get a valid thumbnail.
params.i_nbResults = count * 2;
ml_unique_ptr<vlc_ml_album_list_t> list(vlc_ml_list_genre_albums(p_ml, &params, id));
thumbnailCopy(ml_range_iterate<vlc_ml_album_t>(list), std::back_inserter(thumbnails), count);
return thumbnails;
}
}
QHash<QByteArray, vlc_ml_sorting_criteria_t> MLGenreModel::M_names_to_criteria = {
{"title", VLC_ML_SORTING_ALPHA}
};
......@@ -154,7 +177,7 @@ QString MLGenreModel::getCover(MLGenre * genre) const
[genreId, coverDefault = m_coverDefault]
(vlc_medialibrary_t* ml, Context& ctx)
{
CoverGenerator generator{ml, genreId};
CoverGenerator generator {genreId};
generator.setSize(QSize(MLGENREMODEL_COVER_WIDTH,
MLGENREMODEL_COVER_HEIGHT));
......@@ -172,7 +195,8 @@ QString MLGenreModel::getCover(MLGenre * genre) const
if (generator.cachedFileAvailable())
ctx.cover = generator.cachedFileURL();
else
ctx.cover = generator.execute();
ctx.cover = generator.execute(getGenreMediaThumbnails(ml, MLGENREMODEL_COVER_COUNTX * MLGENREMODEL_COVER_COUNTY, genreId.id));
vlc_ml_media_set_genre_thumbnail(ml, genreId.id, qtu(ctx.cover), VLC_ML_THUMBNAIL_SMALL);
},
//UI thread
......
......@@ -21,6 +21,42 @@
// MediaLibrary includes
#include "mlbasemodel.hpp"
#include "mlitemcover.hpp"
#include "thumbnailcollector.hpp"
namespace
{
struct ThumbnailList
{
QSet<int64_t> toGenerate;
QStringList existing;
};
ThumbnailList extractChildMediaThumbnailsOrIDs(vlc_medialibrary_t *p_ml, const int count, const MLItemId &itemID)
{
ThumbnailList result;
vlc_ml_query_params_t params {};
params.i_nbResults = count;
ml_unique_ptr<vlc_ml_media_list_t> list(vlc_ml_list_media_of(p_ml, &params, itemID.type, itemID.id));
for (const auto &media : ml_range_iterate<vlc_ml_media_t>(list))
{
const bool isThumbnailAvailable = (media.thumbnails[VLC_ML_THUMBNAIL_SMALL].i_status == VLC_ML_THUMBNAIL_STATUS_AVAILABLE);
if (isThumbnailAvailable)
{
result.existing.push_back(toValidLocalFile(media.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl));
} else if (media.i_type == VLC_ML_MEDIA_TYPE_VIDEO)
{
result.toGenerate.insert(media.i_id);
}
}
return result;
}
}
QString MsToString( int64_t time , bool doShort )
{
......@@ -47,58 +83,93 @@ QString MsToString( int64_t time , bool doShort )
}
QString getVideoListCover( const MLBaseModel* model, MLItemCover* item, int width, int height,
int role )
QStringList extractMediaThumbnails(vlc_medialibrary_t *p_ml, const int count, const MLItemId &itemID)
{
// NOTE: We retrieve twice the count to maximize our chances to get a valid thumbnail.
return extractChildMediaThumbnailsOrIDs(p_ml, count * 2, itemID).existing;
}
QString createGroupMediaCover(const MLBaseModel* model, MLItemCover* parent
, int role
, const std::shared_ptr<CoverGenerator> generator)
{
QString cover = item->getCover();
QString cover = parent->getCover();
// NOTE: Making sure we're not already generating a cover.
if (cover.isNull() == false || item->hasGenerator())
if (cover.isNull() == false || parent->hasGenerator())
return cover;
MLItemId itemId = item->getId();
struct Context { QString cover; };
item->setGenerator(true);
model->ml()->runOnMLThread<Context>(model,
//ML thread
[itemId, width, height]
(vlc_medialibrary_t * ml, Context & ctx)
{
CoverGenerator generator{ ml, itemId };
generator.setSize(QSize(width, height));
if (generator->cachedFileAvailable())
return generator->cachedFileURL();
generator.setDefaultThumbnail(":/noart_videoCover.svg");
MLItemId itemId = parent->getId();
parent->setGenerator(true);
if (generator.cachedFileAvailable())
ctx.cover = generator.cachedFileURL();
else
ctx.cover = generator.execute();
},
//UI Thread
[model, itemId, role]
(quint64, Context & ctx)
const auto generateCover = [=](const QStringList &childCovers)
{
int row;
// NOTE: We want to avoid calling 'MLBaseModel::item' for performance issues.
auto item = static_cast<MLItemCover *>(model->findInCache(itemId, &row));
if (!item)
return;
item->setCover(ctx.cover);
item->setGenerator(false);
QModelIndex modelIndex = model->index(row);
//we're running in a callback
emit const_cast<MLBaseModel *>(model)->dataChanged(modelIndex, modelIndex, { role });
});
struct Context { QString cover; };
model->ml()->runOnMLThread<Context>(model,
//ML thread
[generator, childCovers]
(vlc_medialibrary_t * , Context & ctx)
{
ctx.cover = generator->execute(childCovers);
},
//UI Thread
[model, itemId, role]
(quint64, Context & ctx)
{
int row;
// NOTE: We want to avoid calling 'MLBaseModel::item' for performance issues.
auto item = static_cast<MLItemCover *>(model->findInCache(itemId, &row));
if (!item)
return;
item->setCover(ctx.cover);
item->setGenerator(false);
QModelIndex modelIndex = model->index(row);
emit const_cast<MLBaseModel *>(model)->dataChanged(modelIndex, modelIndex, { role });
}
);
};
model->ml()->runOnMLThread<ThumbnailList>(model,
//ML thread (get child thumbnails or ids)
[itemId, generator](vlc_medialibrary_t *p_ml, ThumbnailList &ctx)
{
ctx = extractChildMediaThumbnailsOrIDs(p_ml, generator->requiredNoOfThumbnails(), itemId);
}
//UI Thread
, [=](quint64, ThumbnailList & ctx)
{
if (ctx.toGenerate.empty())
{
generateCover(ctx.existing);
return;
}
// request child thumbnail generation, when finished generate the cover
auto collector = new ThumbnailCollector(const_cast<MLBaseModel *>(model));
QObject::connect(collector, &ThumbnailCollector::finished, model, [=]()
{
const auto thumbnails = ctx.existing + collector->allGenerated().values();
generateCover(thumbnails);
collector->deleteLater();
});
collector->start(model->ml(), ctx.toGenerate);
}
);
return cover;
}
QString toValidLocalFile(const char *mrl)
{
QUrl url(mrl);
return url.isLocalFile() ? url.toLocalFile() : QString {};
}
......@@ -31,6 +31,8 @@
// Forward declarations
class MLBaseModel;
class MLItemCover;
class MLItemId;
class CoverGenerator;
template<typename T>
class MLDeleter
......@@ -75,9 +77,33 @@ MLListRange<T> ml_range_iterate(L& list)
return MLListRange<T>{ list->p_items, list->p_items + list->i_nb_items };
}
QString toValidLocalFile(const char *mrl);
template <typename T, typename O>
void thumbnailCopy(const MLListRange<T> &list, O dst, const int max)
{
int count = 0;
for (const auto &item : list)
{
if (item.thumbnails[VLC_ML_THUMBNAIL_SMALL].i_status != VLC_ML_THUMBNAIL_STATUS_AVAILABLE)
continue;
const auto path = toValidLocalFile(item.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl);
if (path.isEmpty())
continue;
*dst++ = path;
++count;
if (count == max)
return;
}
}
QString MsToString( int64_t time, bool doShort = false );
QString getVideoListCover( const MLBaseModel* model, MLItemCover* item, int width, int height,
int role );
QStringList extractMediaThumbnails(vlc_medialibrary_t *p_ml, const int count, const MLItemId &itemID);
QString createGroupMediaCover(const MLBaseModel* model, MLItemCover* parent
, int role, const std::shared_ptr<CoverGenerator> generator);
#endif // MLHELPER_HPP
......@@ -33,12 +33,16 @@
//-------------------------------------------------------------------------------------------------
// Static variables
// NOTE: We multiply by 2 to cover most dpi settings.
static const int MLPLAYLISTMODEL_COVER_WIDTH = 512 * 2; // 16 / 10 ratio
static const int MLPLAYLISTMODEL_COVER_HEIGHT = 320 * 2;
namespace {
// NOTE: We multiply by 2 to cover most dpi settings.
const int MLPLAYLISTMODEL_COVER_WIDTH = 512 * 2; // 16 / 10 ratio
const int MLPLAYLISTMODEL_COVER_HEIGHT = 320 * 2;
const int PLAYLIST_COVERX = 2;
const int PLAYLIST_COVERY = 2;
void appendMediaIntoPlaylist(vlc_medialibrary_t* ml, int64_t playlistId, const std::vector<MLItemId>& itemList)
{
vlc_ml_query_params_t query;
......@@ -296,54 +300,21 @@ void MLPlaylistListModel::endTransaction()
QString MLPlaylistListModel::getCover(MLPlaylist * playlist) const
{
QString cover = playlist->getCover();
// NOTE: Making sure we're not already generating a cover.
if (cover.isNull() == false || playlist->hasGenerator())
return cover;
MLItemId playlistId = playlist->getId();
struct Context{
QString cover;
};
playlist->setGenerator(true);
m_mediaLib->runOnMLThread<Context>(this,
//ML thread
[playlistId, coverSize = m_coverSize, coverDefault = m_coverDefault, coverPrefix = m_coverPrefix]
(vlc_medialibrary_t* ml, Context& ctx)
{
CoverGenerator generator{ml, playlistId};
auto generator = std::make_shared<CoverGenerator>(playlist->getId());
generator.setSize(coverSize);
generator->setCountX(PLAYLIST_COVERX);
generator->setCountY(PLAYLIST_COVERY);
if (!coverDefault.isEmpty())
generator.setDefaultThumbnail(coverDefault);
generator->setSize(m_coverSize);
generator.setPrefix(coverPrefix);
if (!m_coverDefault.isEmpty())
generator->setDefaultThumbnail(m_coverDefault);
if (generator.cachedFileAvailable())
ctx.cover = generator.cachedFileURL();
else
ctx.cover = generator.execute();
},
//UI thread
[this, playlistId]
(quint64, Context& ctx)
{
int row;
// NOTE: We want to avoid calling 'MLBaseModel::item' for performance issues.
auto playlist = static_cast<MLPlaylist *>(findInCache(playlistId, &row));
if (!playlist)
return;
playlist->setCover(ctx.cover);
playlist->setGenerator(false);
//we're running in a callback
QModelIndex modelIndex = index(row);
emit const_cast<MLPlaylistListModel*>(this)->dataChanged(modelIndex, modelIndex, { PLAYLIST_THUMBNAIL });
});
generator->setPrefix(m_coverPrefix);
return cover;
return createGroupMediaCover(this, playlist
, PLAYLIST_THUMBNAIL
, generator);
}
//-------------------------------------------------------------------------------------------------
......
......@@ -340,11 +340,13 @@ QVariant MLPlaylistModel::itemRoleData(MLItem *item, int role) const /* override
{
vlc_ml_thumbnail_status_t status;
QString thumbnail = media->getThumbnail(&status);
if (media->getType() == VLC_ML_MEDIA_TYPE_AUDIO
&& (status == VLC_ML_THUMBNAIL_STATUS_MISSING || status == VLC_ML_THUMBNAIL_STATUS_FAILURE))
if ((media->getType() == VLC_ML_MEDIA_TYPE_VIDEO)
&& (status == VLC_ML_THUMBNAIL_STATUS_MISSING
|| status == VLC_ML_THUMBNAIL_STATUS_FAILURE))
{
generateThumbnail(item->getId());
}
return QVariant::fromValue(thumbnail);
}
case MEDIA_DURATION:
......
......@@ -85,8 +85,13 @@ QVariant MLVideoFoldersModel::itemRoleData(MLItem * item, const int role) const
case FOLDER_TITLE:
return QVariant::fromValue(folder->getTitle());
case FOLDER_THUMBNAIL:
return getVideoListCover(this, folder, MLVIDEOFOLDERSMODEL_COVER_WIDTH,
MLVIDEOFOLDERSMODEL_COVER_HEIGHT, FOLDER_THUMBNAIL);
{
auto generator = std::make_shared<CoverGenerator>(folder->getId());
generator->setSize(QSize(MLVIDEOFOLDERSMODEL_COVER_WIDTH, MLVIDEOFOLDERSMODEL_COVER_HEIGHT));
generator->setDefaultThumbnail(":/noart_videoCover.svg");
return createGroupMediaCover(this, folder, FOLDER_THUMBNAIL, generator);
}
case FOLDER_DURATION:
return QVariant::fromValue(folder->getDuration());
case FOLDER_COUNT:
......
......@@ -92,8 +92,15 @@ QVariant MLVideoGroupsModel::itemRoleData(MLItem * item, const int role) const /
case VIDEO_TITLE:
return QVariant::fromValue(group->getTitle());
case VIDEO_THUMBNAIL:
return getVideoListCover(this, group, MLVIDEOGROUPSMODEL_COVER_WIDTH,
MLVIDEOGROUPSMODEL_COVER_HEIGHT, VIDEO_THUMBNAIL);
{
auto generator = std::make_shared<CoverGenerator>(group->getId());
generator->setSize(QSize(MLVIDEOGROUPSMODEL_COVER_WIDTH, MLVIDEOGROUPSMODEL_COVER_HEIGHT));
generator->setDefaultThumbnail(":/noart_videoCover.svg");
return createGroupMediaCover(this, group
, VIDEO_THUMBNAIL
, generator);
}
case VIDEO_DURATION:
return QVariant::fromValue(group->getDuration());
case GROUP_IS_VIDEO:
......
/*****************************************************************************
* 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.
*****************************************************************************/
#include "thumbnailcollector.hpp"
#include "medialibrary/medialib.hpp"
#include "medialibrary/mlhelper.hpp"
ThumbnailCollector::ThumbnailCollector(QObject *parent)
: QObject(parent)
, m_ml_event_handle(nullptr, [this](vlc_ml_event_callback_t *cb)
{
assert(m_ml);
QMutexLocker lock(&m_mut);
m_ml->unregisterEventListener(cb);
})
{
}
void ThumbnailCollector::start(MediaLib *ml, const QSet<int64_t> &mlIds)
{
assert(!m_ml); // class object is only one time usable
m_ml = ml;
m_ml_event_handle.reset(ml->registerEventListener(&onVlcMLEvent, this));
m_pending = mlIds;
m_ml->runOnMLThread(this, [ids = mlIds](vlc_medialibrary_t* ml)
{
for (const auto id : ids)
vlc_ml_media_generate_thumbnail(ml, id, VLC_ML_THUMBNAIL_SMALL, 512, 320, .15);
});
}
void ThumbnailCollector::onVlcMLEvent(void *data, const vlc_ml_event_t *event)
{
static const auto mediaID = [](const vlc_ml_event_t *event)
{
return (event->i_type == VLC_ML_EVENT_MEDIA_DELETED)
? event->deletion.i_entity_id
: event->media_thumbnail_generated.p_media->i_id;
};
if ((event->i_type != VLC_ML_EVENT_MEDIA_DELETED)
&& (event->i_type != VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED))
return;
const auto self = reinterpret_cast<ThumbnailCollector *>(data);
QMutexLocker lock(&self->m_mut);
const auto id = mediaID(event);
if (!self->m_pending.contains(id))
return;
self->m_pending.remove(id);
if (event->i_type == VLC_ML_EVENT_MEDIA_THUMBNAIL_GENERATED)
{
const auto media = event->media_thumbnail_generated.p_media;
const auto url = toValidLocalFile(media->thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl);
if (event->media_thumbnail_generated.b_success && !url.isEmpty()) {
self->m_thumbnails.insert(id, url);
} else {
qDebug("thumbnail generation failed, id: %ld, url: '%s'", id, qUtf8Printable(url));
}
}
if (self->m_pending.empty())
emit self->finished();
}
/*****************************************************************************
* 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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 THUMBNAILCOLLECTOR_HPP
#define THUMBNAILCOLLECTOR_HPP
#include <QObject>
#include <QHash>
#include <QSet>
#include <QMutex>
#include <memory>
#include <functional>
class MediaLib;
struct vlc_medialibrary_t;
struct vlc_ml_event_callback_t;
struct vlc_ml_event_t;
class ThumbnailCollector : public QObject
{
Q_OBJECT
public:
ThumbnailCollector(QObject *parent = nullptr);
void start(MediaLib *ml, const QSet<int64_t> &mlIds);
QHash<int64_t, QString> allGenerated() { return m_thumbnails; }
signals:
void finished();
private:
static void onVlcMLEvent(void *data, const vlc_ml_event_t *event);
QMutex m_mut;
MediaLib *m_ml {};
std::unique_ptr<vlc_ml_event_callback_t,
std::function<void(vlc_ml_event_callback_t*)>> m_ml_event_handle {};
QSet<int64_t> m_pending;
QHash<int64_t, QString> m_thumbnails;
};
#endif // THUMBNAILCOLLECTOR_HPP
......@@ -53,9 +53,8 @@ static const QString COVERGENERATOR_DEFAULT = ":/noart_albumCover.svg";
// Ctor / dtor
//-------------------------------------------------------------------------------------------------
CoverGenerator::CoverGenerator(vlc_medialibrary_t * ml, const MLItemId & itemId)
: m_ml(ml)
, m_id(itemId)
CoverGenerator::CoverGenerator(const MLItemId & itemId)
: m_id(itemId)
, m_countX(COVERGENERATOR_COUNT)
, m_countY(COVERGENERATOR_COUNT)
, m_split(Divide)
......@@ -114,6 +113,11 @@ void CoverGenerator::setPrefix(const QString & prefix)
m_prefix = prefix;
}
int CoverGenerator::requiredNoOfThumbnails() const
{
return m_countX * m_countY;
}
bool CoverGenerator::cachedFileAvailable() const
{
return QFile::exists(fileName());
......@@ -138,31 +142,20 @@ QString CoverGenerator::fileName() const
// QRunnable implementation
//-------------------------------------------------------------------------------------------------
QString CoverGenerator::execute() /* override */
QString CoverGenerator::execute(QStringList thumbnails) const
{
QDir dir(config_GetUserDir(VLC_CACHE_DIR) + COVERGENERATOR_STORAGE);
dir.mkpath(dir.absolutePath());
vlc_ml_parent_type type = m_id.type;
int64_t id = m_id.id;
QString fileName = this->fileName();
if (dir.exists(fileName))
{
return QUrl::fromLocalFile(fileName).toString();
}
QStringList thumbnails;
int count = m_countX * m_countY;
if (type == VLC_ML_PARENT_GENRE)
thumbnails = getGenre(count, id);
else
thumbnails = getMedias(count, id, type);
int countX;
int countY;
......@@ -233,7 +226,7 @@ QString CoverGenerator::execute() /* override */
//-------------------------------------------------------------------------------------------------
void CoverGenerator::draw(QPainter & painter,
const QStringList & fileNames, int countX, int countY)
const QStringList & fileNames, int countX, int countY) const
{
int count = fileNames.count();
......@@ -268,7 +261,7 @@ void CoverGenerator::draw(QPainter & painter,
}
}
void CoverGenerator::drawImage(QPainter & painter, const QString & fileName, const QRect & target)
void CoverGenerator::drawImage(QPainter & painter, const QString & fileName, const QRect & target) const
{
//QFile expect the :/ instead of qrc:// for resources files
const QUrl fileURL {fileName};
......@@ -329,7 +322,7 @@ void CoverGenerator::drawImage(QPainter & painter, const QString & fileName, con
//-------------------------------------------------------------------------------------------------
void CoverGenerator::blur(QImage& image)
void CoverGenerator::blur(QImage& image) const
{
if (Q_LIKELY(&qt_blurImage))
{
......@@ -390,71 +383,3 @@ QString CoverGenerator::getPrefix(vlc_ml_parent_type type) const
return "unknown";
}
}
//-------------------------------------------------------------------------------------------------
QStringList CoverGenerator::getGenre(int count, int64_t id) const
{
QStringList thumbnails;
vlc_ml_query_params_t params;
memset(&params, 0, sizeof(vlc_ml_query_params_t));
// NOTE: We retrieve twice the count to maximize our chances to get a valid thumbnail.
params.i_nbResults = count * 2;
ml_unique_ptr<vlc_ml_album_list_t> list(vlc_ml_list_genre_albums(m_ml, &params, id));
for (const vlc_ml_album_t & album : ml_range_iterate<vlc_ml_album_t>(list))
{
if (album.thumbnails[VLC_ML_THUMBNAIL_SMALL].i_status != VLC_ML_THUMBNAIL_STATUS_AVAILABLE)
continue;
QUrl url(album.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl);
// NOTE: We only want local files to compose the cover.
if (url.isLocalFile() == false)
continue;
thumbnails.append(url.toLocalFile());
if (thumbnails.count() == count)
return thumbnails;
}
return thumbnails;
}
QStringList CoverGenerator::getMedias(int count, int64_t id, vlc_ml_parent_type type) const
{
QStringList thumbnails;
vlc_ml_query_params_t params;
memset(&params, 0, sizeof(vlc_ml_query_params_t));
// NOTE: We retrieve twice the count to maximize our chances to get a valid thumbnail.
params.i_nbResults = count * 2;
ml_unique_ptr<vlc_ml_media_list_t> list(vlc_ml_list_media_of(m_ml, &params, type, id));
for (const vlc_ml_media_t & media : ml_range_iterate<vlc_ml_media_t>(list))
{
if (media.thumbnails[VLC_ML_THUMBNAIL_SMALL].i_status != VLC_ML_THUMBNAIL_STATUS_AVAILABLE)
continue;
QUrl url(media.thumbnails[VLC_ML_THUMBNAIL_SMALL].psz_mrl);
// NOTE: We only want local files to compose the cover.
if (url.isLocalFile() == false)
continue;
thumbnails.append(url.toLocalFile());
if (thumbnails.count() == count)
return thumbnails;
}
return thumbnails;
}
......@@ -48,7 +48,7 @@ public: // Enums
};
public:
CoverGenerator(vlc_medialibrary_t * ml, const MLItemId & itemId);
CoverGenerator(const MLItemId & itemId);
public: // Interface
MLItemId getId();
......@@ -72,30 +72,26 @@ public: // Interface
// NOTE: This lets us enforce a specific prefix for the cover fileName.
void setPrefix(const QString & prefix);
int requiredNoOfThumbnails() const;
bool cachedFileAvailable() const;
QString cachedFileURL() const;
public:
QString execute();
QString execute(QStringList thumbnails) const;
private: // Functions
QString fileName() const;
void draw(QPainter & painter, const QStringList & fileNames, int countX, int countY);
void draw(QPainter & painter, const QStringList & fileNames, int countX, int countY) const;
void drawImage(QPainter & painter, const QString & fileName, const QRect & rect);
void drawImage(QPainter & painter, const QString & fileName, const QRect & rect) const;
void blur(QImage &image);
void blur(QImage &image) const;
QString getPrefix(vlc_ml_parent_type type) const;
QStringList getMedias(int count, int64_t id, vlc_ml_parent_type type) const;
QStringList getGenre (int count, int64_t id) const;
private:
vlc_medialibrary_t * m_ml;
MLItemId m_id;
QSize m_size;
......