Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

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();
    });
}

Messung
CHZG
84999893

¤ Dauer der Verarbeitung: 0.6 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104856
#Domains=417797