|
|
|
|
Quelle networkmediamodel.cpp
Sprache: C
|
|
/*****************************************************************************
* Copyright (C) 2019 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 "networkmediamodel.hpp"
#include "mediatreelistener.hpp"
#include "maininterface/mainctx.hpp"
#include "medialibrary/medialib.hpp"
#include "medialibrary/mlmedia.hpp"
#include "medialibrary/mlmediastore.hpp"
#include "util/locallistbasemodel.hpp"
#include "playlist/media.hpp"
#include "playlist/playlist_controller.hpp"
#include <unordered_set>
#include <QSemaphore>
#include <QDateTime>
#include <QTimeZone>
#include <QJSValue>
#include <QQmlEngine>
namespace {
//use the same queue as in mlfoldermodel
static const char* const ML_FOLDER_ADD_QUEUE = "ML_FOLDER_ADD_QUEUE";
struct NetworkMediaItem : public NetworkBaseItem
{
QString uri;
bool indexed;
bool canBeIndexed;
NetworkTreeItem tree;
qint64 fileSize;
QDateTime fileModified;
MLMedia media;
};
using NetworkMediaItemPtr = std::shared_ptr<NetworkMediaItem>;
inline bool isADir(const NetworkMediaItemPtr& x)
{
return (x->type == NetworkMediaModel::ItemType::TYPE_DIRECTORY);
}
int compareMediaDuration(const NetworkMediaItemPtr &l
, const NetworkMediaItemPtr &r)
{
const bool lHasMedia = l->media.valid();
const bool rHasMedia = r->media.valid();
if (lHasMedia != rHasMedia) return lHasMedia ? 1 : - 1;
if (!lHasMedia && !rHasMedia) return 0;
const auto &lmedia = l->media.duration();
const auto &rmedia = r->media.duration();
if (lmedia == rmedia) return 0;
return lmedia > rmedia ? 1 : -1;
}
}
// ListCache specialisation
template<>
bool ListCache<NetworkMediaItemPtr>::compareItems(const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b)
{
//just compare the pointers here
return a == b;
}
// NetworkMediaModelPrivate
class NetworkMediaModelPrivate
: public LocalListBaseModelPrivate<NetworkMediaItemPtr>
{
Q_DECLARE_PUBLIC(NetworkMediaModel)
public:
NetworkMediaModelPrivate(NetworkMediaModel* pub)
: LocalListBaseModelPrivate<NetworkMediaItemPtr>(pub)
, m_preparseSem(1), m_parserReq(NULL)
{}
public:
const NetworkMediaItem* getItemForRow(int row) const
{
const NetworkMediaItemPtr* ref = item(row);
if (ref)
return ref->get();
return nullptr;
}
void mediaUpdated(const QString &mrl, const MLMedia &media)
{
if (!m_items.contains(mrl))
return;
//contruct by copy, don't mutate items in m_items,
//we want the change to be notified
auto newItem = std::make_shared<NetworkMediaItem>(*m_items[mrl]);
newItem->media = media;
m_items[mrl] = newItem;
++m_revision;
invalidateCache();
}
void setIntexedState(const QString &uri, bool indexed)
{
auto itr = m_items.find(uri);
if (itr == m_items.end())
return;
auto &itemPtr = *itr;
if (itemPtr->indexed == indexed)
return;
//contruct by copy, don't mutate items in m_items,
//we want the change to be notified
auto newItem = std::make_shared<NetworkMediaItem>(*itemPtr);
newItem->indexed = indexed;
itemPtr = newItem;
++m_revision;
invalidateCache();
}
void removeItem(const SharedInputItem& node, const std::vector<SharedInputItem>& itemsList)
{
if (node != m_treeItem.media)
return;
for (auto p_item : itemsList)
{
QUrl itemUri = QUrl::fromEncoded(p_item->psz_uri);
auto it = std::find_if(
m_items.begin(), m_items.end(),
[&](const NetworkMediaItemPtr& i) {
return QString::compare( qfu(p_item->psz_name), i->name, Qt::CaseInsensitive ) == 0 &&
itemUri.scheme() == i->mainMrl.scheme();
});
if ( it == m_items.end() )
continue;
if (m_MLMedias && (*it)->media.valid())
m_MLMedias->remove((*it)->media.getId());
m_items.erase( it );
}
// notify updates
++m_revision;
invalidateCache();
}
void refreshMediaList(
NetworkTreeItem& treeItem,
std::vector<SharedInputItem> children,
bool clear )
{
Q_Q(NetworkMediaModel);
if (clear)
{
m_items.clear();
if (m_MLMedias)
m_MLMedias->clear();
}
std::vector<NetworkMediaItem> items;
for (const auto& inputItem: children)
{
auto item = std::make_shared<NetworkMediaItem>();
item->name = inputItem->psz_name;
item->protocol = "";
item->indexed = false;
item->type = static_cast<NetworkMediaModel::ItemType>(inputItem->i_type);
item->uri = QString(inputItem->psz_uri);
item->mainMrl = (item->type == NetworkMediaModel::TYPE_DIRECTORY || item->type == NetworkMediaModel::TYPE_NODE) ?
QUrl::fromEncoded(QByteArray(inputItem->psz_uri).append('/')) :
QUrl::fromEncoded(inputItem->psz_uri);
item->canBeIndexed = canBeIndexed( item->mainMrl , item->type );
input_item_t* intputItemRaw = inputItem.get();
char* str = input_item_GetArtworkURL(intputItemRaw);
if (str)
{
item->artwork = QString::fromUtf8(str);
free(str);
}
str = input_item_GetInfo(intputItemRaw, ".stat", "size");
if (str)
{
item->fileSize = QString(str).toLongLong();
free(str);
}
else
item->fileSize = 0;
str = input_item_GetInfo(intputItemRaw, ".stat", "mtime");
if (str)
{
bool ok;
qint64 time = QString(str).toLongLong(&ok);
free(str);
if (ok)
item->fileModified = QDateTime::fromSecsSinceEpoch(time, QTimeZone::systemTimeZone());
}
item->tree = NetworkTreeItem(treeItem, inputItem );
if ( m_mediaLib && item->canBeIndexed)
{
struct Ctx {
bool succeed;
bool isIndexed;
};
const QString uri = item->uri;
m_mediaLib->runOnMLThread<Ctx>(q,
//ML thread
[uri](vlc_medialibrary_t* ml, Ctx& ctx){
// Medialibrary requires folders uri to be terminated with '/'
const QString mlURI = uri + "/";
auto ret = vlc_ml_is_indexed( ml, qtu( mlURI ), &ctx.isIndexed );
ctx.succeed = (ret == VLC_SUCCESS);
},
//UI thread
[this, uri](quint64, Ctx& ctx){
if (!ctx.succeed)
return;
setIntexedState(uri, ctx.isIndexed);
});
}
m_items[item->uri] = item;
if (m_MLMedias && (item->type == NetworkMediaModel::TYPE_FILE))
{
m_MLMedias->insert(item->uri);
}
}
++m_revision;
invalidateCache();
}
bool canBeIndexed(const QUrl& url , NetworkMediaModel::ItemType itemType )
{
return m_mediaLib
&& static_cast<input_item_type_e>(itemType) != ITEM_TYPE_FILE
&& (url.scheme() == "smb" || url.scheme() == "ftp" || url.scheme() == "file");
}
//BaseModelPrivateT interface implementation
public:
LocalListCacheLoader<NetworkMediaItemPtr>::ItemCompare getSortFunction() const override
{
if (m_sortCriteria == "mrl" )
{
if (m_sortOrder == Qt::SortOrder::DescendingOrder)
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return QString::compare(a->mainMrl.toString(), b->mainMrl.toString()) > 0;
};
else
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return QString::compare(a->mainMrl.toString(), b->mainMrl.toString()) < 0;
};
}
else if (m_sortCriteria == "fileSizeRaw64" )
{
if (m_sortOrder == Qt::SortOrder::DescendingOrder)
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return a->fileSize < b->fileSize;
};
else
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return a->fileSize > b->fileSize;
};
}
else if (m_sortCriteria == "fileModified")
{
if (m_sortOrder == Qt::SortOrder::DescendingOrder)
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return a->fileModified < b->fileModified;
};
else
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return a->fileModified > b->fileModified;
};
}
else if (m_sortCriteria == "duration")
{
if (m_sortOrder == Qt::SortOrder::DescendingOrder)
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool
{
if(isADir(a) != isADir(b)) return isADir(a);
return compareMediaDuration(a, b) > 0;
};
else
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool
{
if(isADir(a) != isADir(b)) return isADir(a);
return compareMediaDuration(a, b) < 0;
};
}
else // m_sortCriteria == "name"
{
if (m_sortOrder == Qt::SortOrder::DescendingOrder)
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return QString::compare(a->name, b->name, Qt::CaseInsensitive) > 0;
};
else
return [](const NetworkMediaItemPtr& a, const NetworkMediaItemPtr& b) -> bool {
if(isADir(a) != isADir(b)) return isADir(a);
return QString::compare(a->name, b->name, Qt::CaseInsensitive) < 0;
};
}
}
bool initializeModel() override
{
Q_Q(NetworkMediaModel);
if (!m_ctx || !m_hasTree || m_qmlInitializing)
return false;
auto parser = m_ctx->getNetworkPreparser();
if (unlikely(parser == NULL))
return false;
m_listener.reset();
m_items.clear();
if (!m_treeItem)
return false;
auto& tree = m_treeItem.tree;
auto l = std::make_unique<MediaTreeListener>(tree,
std::make_unique<NetworkMediaModelPrivate::ListenerCb>(q) );
if ( l->listener == nullptr )
return false;
if (m_treeItem.media)
{
m_name = m_treeItem.media->psz_name;
emit q->nameChanged();
m_url = QUrl::fromEncoded( QByteArray{ m_treeItem.media->psz_uri }.append( '/' ) );
emit q->urlChanged();
m_type = static_cast<NetworkMediaModel::ItemType>(m_treeItem.media->i_type);
emit q->typeChanged();
m_canBeIndexed = canBeIndexed( m_url, m_type );
emit q->canBeIndexedChanged();
if (m_mediaLib)
{
auto uri = QByteArray(m_treeItem.media->psz_uri).append('/');
struct Ctx {
bool succeed;
bool isIndexed;
};
m_mediaLib->runOnMLThread<Ctx>(q,
//ML thread
[uri](vlc_medialibrary_t* ml, Ctx& ctx){
auto ret = vlc_ml_is_indexed( ml, uri.constData(), &ctx.isIndexed );
ctx.succeed = (ret == VLC_SUCCESS);
},
//ML thread
[this](quint64,Ctx& ctx){
Q_Q(NetworkMediaModel);
if (!ctx.succeed)
return;
m_indexed = ctx.isIndexed;
emit q->isIndexedChanged();
});
}
}
{
input_item_node_t* mediaNode = nullptr;
input_item_node_t* parent = nullptr;
std::vector<SharedInputItem> itemList;
/* vlc_preparser_Cancel can call the callback from this thread so the mutex
must be unlocked */
if (m_parserReq != NULL)
vlc_preparser_Cancel( parser, m_parserReq );
{
MediaTreeLocker lock{tree};
m_path = {QVariant::fromValue(PathNode(m_treeItem, m_name))};
if (vlc_media_tree_Find( tree.get(), m_treeItem.media.get(), &mediaNode, &parent))
{
itemList.reserve(mediaNode->i_children);
for (int i = 0; i < mediaNode->i_children; i++)
itemList.emplace_back(mediaNode->pp_children[i]->p_item);
while (parent && parent->p_item) {
m_path.push_front(QVariant::fromValue(PathNode(
NetworkTreeItem(m_treeItem, SharedInputItem{parent->p_item}),
parent->p_item->psz_name)));
input_item_node_t *node = nullptr;
input_item_node_t *grandParent = nullptr;
if (!vlc_media_tree_Find( tree.get(), parent->p_item, &node, &grandParent)) {
break;
}
parent = grandParent;
}
}
}
if (!itemList.empty())
refreshMediaList( m_treeItem, std::move( itemList ), true );
emit q->pathChanged();
}
m_preparseSem.acquire();
if (m_parserReq != NULL)
{
vlc_preparser_req_Release( m_parserReq );
m_parserReq = NULL;
}
m_parserReq = vlc_media_tree_Preparse( tree.get(), parser, m_treeItem.media.get() );
m_listener = std::move( l );
return true;
}
// LocalListCacheLoader::ModelSource interface implementation
protected:
std::vector<NetworkMediaItemPtr> getModelData(const QString& pattern) const override
{
std::vector<NetworkMediaItemPtr> items;
items.reserve(m_items.size() / 2);
for (const auto &item : m_items)
{
if (item->name.contains(pattern, Qt::CaseInsensitive))
items.push_back(item);
}
return items;
}
public:
MediaLib* m_mediaLib;
bool m_hasTree = false;
QSemaphore m_preparseSem;
std::unique_ptr<MediaTreeListener> m_listener;
QHash<QString, NetworkMediaItemPtr> m_items;
std::unique_ptr<MLMediaStore> m_MLMedias;
//properties of the current node
QString m_name;
QUrl m_url;
NetworkMediaModel::ItemType m_type = NetworkMediaModel::ItemType::TYPE_UNKNOWN;
bool m_indexed = false;
bool m_canBeIndexed = false;
MainCtx* m_ctx = nullptr;
NetworkTreeItem m_treeItem;
QVariantList m_path;
struct ListenerCb;
private:
vlc_preparser_req *m_parserReq;
};
// NetworkMediaModel::ListenerCb implementation
struct NetworkMediaModelPrivate::ListenerCb : public MediaTreeListener::MediaTreeListenerCb {
ListenerCb(NetworkMediaModel *model) : model(model) {}
void onItemCleared( MediaTreePtr tree, input_item_node_t* node ) override;
void onItemAdded( MediaTreePtr tree, input_item_node_t* parent, input_item_node_t *const children[], size_t count ) override;
void onItemRemoved( MediaTreePtr tree, input_item_node_t * node, input_item_node_t *const children[], size_t count ) override;
void onItemPreparseEnded( MediaTreePtr tree, input_item_node_t* node, int status ) override;
NetworkMediaModel *model;
};
// NetworkMediaModel implementation
NetworkMediaModel::NetworkMediaModel( QObject* parent )
: NetworkBaseModel( new NetworkMediaModelPrivate(this), parent )
{
}
NetworkMediaModel::~NetworkMediaModel()
{
Q_D(NetworkMediaModel);
//this can only be acquired from UI thread
if (!d->m_preparseSem.tryAcquire())
{
auto parser = d->m_ctx->getNetworkPreparser();
if (likely(parser != NULL))
{
if (d->m_parserReq != NULL)
vlc_preparser_Cancel( parser, d->m_parserReq );
//wait for the callback call on cancel
d->m_preparseSem.acquire();
}
}
if (d->m_parserReq != NULL)
{
vlc_preparser_req_Release( d->m_parserReq );
d->m_parserReq = NULL;
}
}
QVariant NetworkMediaModel::data( const QModelIndex& index, int role ) const
{
Q_D(const NetworkMediaModel);
if (!d->m_ctx)
return {};
const NetworkMediaItem* item = d->getItemForRow(index.row());
if (!item)
return {};
switch ( role )
{
case NETWORK_INDEXED:
return item->indexed;
case NETWORK_CANINDEX:
return item->canBeIndexed;
case NETWORK_TREE:
return QVariant::fromValue( item->tree );
case NETWORK_BASE_ARTWORK:
{
if (!item->artwork.isEmpty())
return item->artwork;
/// XXX: request medialibrary for thumbnail if not available??
const MLMedia &media = item->media;
if (media.valid())
{
const QString bannerCover = media.bannerCover();
return !bannerCover.isEmpty() ? bannerCover : media.smallCover();
}
return {};
}
case NETWORK_FILE_SIZE:
return item->fileSize;
case NETWORK_FILE_MODIFIED:
return item->fileModified;
case NETWORK_MEDIA:
return item->media.valid()
? QVariant::fromValue(item->media)
: QVariant {};
case NETWORK_MEDIA_PROGRESS:
{
if (item->media.valid())
return item->media.progress();
return {};
}
case NETWORK_MEDIA_DURATION:
{
if (item->media.valid())
{
const VLCDuration duration = item->media.duration();
return !duration.valid() ? QVariant {} : QVariant::fromValue(duration);
}
return {};
}
case URL:
return item->uri;
default:
return basedata(*item, role);
}
}
QHash<int, QByteArray> NetworkMediaModel::roleNames() const
{
auto roles = NetworkBaseModel::roleNames();
roles[NETWORK_INDEXED] = QByteArrayLiteral("indexed");
roles[NETWORK_CANINDEX] = QByteArrayLiteral("can_index");
roles[NETWORK_TREE] = QByteArrayLiteral("tree");
roles[NETWORK_FILE_SIZE] = QByteArrayLiteral("fileSizeRaw64");
roles[NETWORK_FILE_MODIFIED] = QByteArrayLiteral("fileModified");
roles[NETWORK_MEDIA] = QByteArrayLiteral("media");
roles[NETWORK_MEDIA_PROGRESS] = QByteArrayLiteral("progress");
roles[NETWORK_MEDIA_DURATION] = QByteArrayLiteral("duration");
roles[URL] = QByteArrayLiteral("url");
return roles;
}
Qt::ItemFlags NetworkMediaModel::flags( const QModelIndex& idx ) const
{
return QAbstractListModel::flags( idx ) | Qt::ItemIsEditable;
}
bool NetworkMediaModel::setData( const QModelIndex& idx, const QVariant& value, int role )
{
Q_D(NetworkMediaModel);
if (!d->m_mediaLib)
return false;
if ( role != NETWORK_INDEXED )
return false;
bool enabled = value.toBool();
const NetworkMediaItemPtr* ref = d->item(idx.row());
if (!ref)
return false;
//local reference
NetworkMediaItemPtr item = *ref;
if ( item->indexed == enabled )
return true;
struct Ctx
{
bool succeed;
};
const QString uri = item->uri;
d->m_mediaLib->runOnMLThread<Ctx>(this,
//ML thread
[enabled, uri]
(vlc_medialibrary_t* ml, Ctx& ctx){
int res;
// Medialibrary requires folders uri to be terminated with '/'
const QString mlURI = uri + "/";
if ( enabled )
res = vlc_ml_add_folder( ml, qtu( mlURI ) );
else
res = vlc_ml_remove_folder( ml, qtu( mlURI ) );
ctx.succeed = res == VLC_SUCCESS;
},
//UI thread
[this, uri, enabled](qint64, Ctx& ctx){
Q_D(NetworkMediaModel);
if (!ctx.succeed)
return;
d->setIntexedState(uri, enabled);
},
ML_FOLDER_ADD_QUEUE);
return true;
}
void NetworkMediaModel::setIndexed(bool indexed)
{
Q_D(NetworkMediaModel);
if (indexed == d->m_indexed || !d->m_canBeIndexed)
return;
QString url = d->m_url.toString( QUrl::FullyEncoded );
struct Ctx {
bool success;
};
d->m_mediaLib->runOnMLThread<Ctx>(this,
//ML thread
[url, indexed](vlc_medialibrary_t* ml, Ctx& ctx)
{
int res;
if ( indexed )
res = vlc_ml_add_folder( ml, qtu( url ) );
else
res = vlc_ml_remove_folder( ml, qtu( url ) );
ctx.success = (res == VLC_SUCCESS);
},
//UI thread
[this, indexed](quint64, Ctx& ctx){
if (ctx.success)
{
d_func()->m_indexed = indexed;
emit isIndexedChanged();
}
},
ML_FOLDER_ADD_QUEUE);
}
void NetworkMediaModel::setCtx(MainCtx* ctx)
{
Q_D(NetworkMediaModel);
if (d->m_ctx == ctx)
return;
assert(ctx);
d->m_ctx = ctx;
d->m_mediaLib = ctx->getMediaLibrary();
d->m_MLMedias.reset();
if (d->m_mediaLib)
{
d->m_MLMedias.reset(new MLMediaStore(d->m_mediaLib));
connect(d->m_MLMedias.get(), &MLMediaStore::updated, this, [this](const QString &mrl, const MLMedia &media)
{
Q_D(NetworkMediaModel);
d->mediaUpdated(mrl, media);
});
}
d->initializeModel();
emit ctxChanged();
}
void NetworkMediaModel::setTree(QVariant parentTree)
{
Q_D(NetworkMediaModel);
if (parentTree.canConvert<NetworkTreeItem>())
d->m_treeItem = parentTree.value<NetworkTreeItem>();
else
d->m_treeItem = NetworkTreeItem();
d->m_hasTree = true;
d->initializeModel();
emit treeChanged();
}
bool NetworkMediaModel::insertIntoPlaylist(const QModelIndexList &itemIdList, const ssize_t playlistIndex)
{
Q_D(NetworkMediaModel);
if (!(d->m_ctx && d->m_hasTree))
return false;
QVector<vlc::playlist::Media> medias;
medias.reserve( itemIdList.size() );
for ( const QModelIndex &id : itemIdList )
{
if ( !id.isValid() )
continue;
const NetworkMediaItem* item = d->getItemForRow(id.row());
if (!item)
continue;
medias.append( vlc::playlist::Media {item->tree.media.get()} );
}
if (medias.isEmpty())
return false;
d->m_ctx->getIntf()->p_mainPlaylistController->insert(playlistIndex, medias, false);
return true;
}
bool NetworkMediaModel::addToPlaylist(const int index)
{
Q_D(NetworkMediaModel);
if (!(d->m_ctx && d->m_hasTree))
return false;
const NetworkMediaItem* item = d->getItemForRow(index);
if (!item)
return false;
vlc::playlist::Media media{ item->tree.media.get() };
d->m_ctx->getIntf()->p_mainPlaylistController->append( QVector<vlc::playlist::Media>{ media }, false);
return true;
}
bool NetworkMediaModel::addToPlaylist(const QVariantList &itemIdList)
{
bool ret = false;
for (const QVariant& varValue: itemIdList)
{
int index = -1;
if (varValue.canConvert<int>())
index = varValue.value<int>();
else if (varValue.canConvert<QModelIndex>())
index = varValue.value<QModelIndex>().row();
else
continue;
ret |= addToPlaylist(index);
}
return ret;
}
bool NetworkMediaModel::addToPlaylist(const QModelIndexList &itemIdList)
{
bool ret = false;
for (const QModelIndex& index: itemIdList)
{
if (!index.isValid())
continue;
ret |= addToPlaylist(index.row());
}
return ret;
}
bool NetworkMediaModel::addAndPlay(int index)
{
Q_D(NetworkMediaModel);
if (!(d->m_ctx && d->m_hasTree))
return false;
const NetworkMediaItem* item = d->getItemForRow(index);
if (!item)
return false;
vlc::playlist::Media media{ item->tree.media.get() };
d->m_ctx->getIntf()->p_mainPlaylistController->append( QVector<vlc::playlist::Media>{ media }, true);
return true;
}
bool NetworkMediaModel::addAndPlay(const QVariantList& itemIdList)
{
bool ret = false;
for (const QVariant& varValue: itemIdList)
{
int index = -1;
if (varValue.canConvert<int>())
index = varValue.value<int>();
else if (varValue.canConvert<QModelIndex>())
index = varValue.value<QModelIndex>().row();
else
continue;
if (!ret)
ret |= addAndPlay(index);
else
ret |= addToPlaylist(index);
}
return ret;
}
bool NetworkMediaModel::addAndPlay(const QModelIndexList& itemIdList)
{
bool ret = false;
for (const QModelIndex& index: itemIdList)
{
if (!index.isValid())
continue;
if (!ret)
ret |= addAndPlay(index.row());
else
ret |= addToPlaylist(index.row());
}
return ret;
}
/* Q_INVOKABLE */
void NetworkMediaModel::getItemsForIndexes(const QModelIndexList & indexes, QJSValue callback)
{
if (!callback.isCallable())
return;
getItemsForIndexes(indexes, [this, callback](const QVariantList& items) mutable {
auto engine = qjsEngine(this);
if (engine)
callback.call({engine->toScriptValue(items)});
});
}
void NetworkMediaModel::getItemsForIndexes(const QModelIndexList & indexes, std::function<void(const QVariantList&)> callback)
{
Q_D(NetworkMediaModel);
if (!callback)
return;
if (indexes.isEmpty())
{
callback(QVariantList());
return;
}
int maxIndex = 0;
for (const QModelIndex & modelIndex : indexes)
{
if (modelIndex.row() > maxIndex)
maxIndex = modelIndex.row();
}
// check if we have loaded all items we need in the cache
unsigned int loaded = getLoadedCount();
unsigned int maximum = getMaximumCount();
unsigned int needed = static_cast<unsigned int>(maxIndex) + 1;
if (loaded >= needed || loaded >= maximum)
{
QVariantList items;
for (const QModelIndex & modelIndex : indexes)
{
const NetworkMediaItem* item = d->getItemForRow(modelIndex.row());
if (!item)
continue;
const NetworkTreeItem & tree = item->tree;
items.append(QVariant::fromValue(SharedInputItem(tree.media.get(), true)));
}
callback(items);
return;
}
// item() internally calls refer() to trigger cache loading for all items till the max index
d->item(maxIndex);
connect(this, &NetworkMediaModel::dataChanged, this, [this, indexes, callback]() {
getItemsForIndexes(indexes, callback);
}, Qt::SingleShotConnection);
}
MainCtx* NetworkMediaModel::getCtx() const {
Q_D(const NetworkMediaModel);
return d->m_ctx;
}
QVariant NetworkMediaModel::getTree() const {
Q_D(const NetworkMediaModel);
return QVariant::fromValue( d->m_treeItem);
}
QVariantList NetworkMediaModel::getPath() const {
Q_D(const NetworkMediaModel);
return d->m_path;
}
QString NetworkMediaModel::getName() const {
Q_D(const NetworkMediaModel);
return d->m_name;
}
QUrl NetworkMediaModel::getUrl() const {
Q_D(const NetworkMediaModel);
return d->m_url;
}
NetworkMediaModel::ItemType NetworkMediaModel::getType() const {
Q_D(const NetworkMediaModel);
return d->m_type;
}
bool NetworkMediaModel::isIndexed() const {
Q_D(const NetworkMediaModel);
return d->m_indexed;
}
bool NetworkMediaModel::canBeIndexed() const {
Q_D(const NetworkMediaModel);
return d->m_canBeIndexed;
}
void NetworkMediaModelPrivate::ListenerCb::onItemCleared( MediaTreePtr tree, input_item_node_t* node)
{
SharedInputItem p_node { node->p_item };
QMetaObject::invokeMethod(model, [model=model, p_node = std::move(p_node), tree = std::move(tree)]() {
NetworkMediaModelPrivate* d = model->d_func();
if (p_node != d->m_treeItem.media)
return;
input_item_node_t *res;
input_item_node_t *parent;
// XXX is tree == m_treeItem.tree?
{
MediaTreeLocker lock{ d->m_treeItem.tree };
bool found = vlc_media_tree_Find( d->m_treeItem.tree.get(), d->m_treeItem.media.get(),
&res, &parent );
if (!found)
return;
}
std::vector<SharedInputItem> itemList;
itemList.reserve( static_cast<size_t>(res->i_children) );
for (int i = 0; i < res->i_children; i++)
itemList.emplace_back(res->pp_children[i]->p_item);
model->d_func()->refreshMediaList( d->m_treeItem, std::move( itemList ), true );
}, Qt::QueuedConnection);
}
void NetworkMediaModelPrivate::ListenerCb::onItemAdded( MediaTreePtr tree, input_item_node_t* parent,
input_item_node_t *const children[],
size_t count )
{
SharedInputItem p_parent { parent->p_item };
std::vector<SharedInputItem> itemList;
itemList.reserve( count );
for (size_t i = 0; i < count; i++)
itemList.emplace_back(children[i]->p_item);
QMetaObject::invokeMethod(model, [model=model, p_parent = std::move(p_parent), tree = std::move(tree), itemList=std::move(itemList)]() {
NetworkMediaModelPrivate* d = model->d_func();
if ( p_parent == d->m_treeItem.media )
d->refreshMediaList( d->m_treeItem , std::move( itemList ), false );
}, Qt::QueuedConnection);
}
void NetworkMediaModelPrivate::ListenerCb::onItemRemoved( MediaTreePtr, input_item_node_t * node,
input_item_node_t *const children[],
size_t count )
{
std::vector<SharedInputItem> itemList;
itemList.reserve( count );
for ( auto i = 0u; i < count; ++i )
itemList.emplace_back( children[i]->p_item );
SharedInputItem p_node { node->p_item };
QMetaObject::invokeMethod(
model,
[model=model, p_node=std::move(p_node), itemList=std::move(itemList)]() {
model->d_func()->removeItem(p_node, itemList);
}, Qt::QueuedConnection);
}
void NetworkMediaModelPrivate::ListenerCb::onItemPreparseEnded(MediaTreePtr, input_item_node_t* node, int )
{
model->d_func()->m_preparseSem.release();
SharedInputItem p_node { node->p_item };
QMetaObject::invokeMethod(model, [model=model, p_node=std::move(p_node)]() {
if (p_node != model->d_func()->m_treeItem.media)
return;
model->d_func()->m_loading = false;
emit model->loadingChanged();
});
}
¤ Dauer der Verarbeitung: 0.6 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104856 |
| #Domains=417797 |
|
|