Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/gui/qt/network/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 18 kB image not shown  

Quelle  networkdevicemodel.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 <unordered_set>
#include <QTimer>
#include <QJSValue>
#include <QQmlEngine>

#include "maininterface/mainctx.hpp"

#include "devicesourceprovider.hpp"
#include "networkmediamodel.hpp"

#include "playlist/media.hpp"
#include "playlist/playlist_controller.hpp"

#include "util/shared_input_item.hpp"
#include "vlcmediasourcewrapper.hpp"

#include "util/locallistbasemodel.hpp"

#include "vlcmediasourcewrapper.hpp"

namespace
{

//represents an entry of the model
struct NetworkDeviceItem : public NetworkBaseItem
{
    NetworkDeviceItem(
        const SharedInputItem& item,
        const SharedMediaSourceModel& source
        )
    {
        name = qfu(item->psz_name);
        mainMrl = QUrl::fromEncoded(item->psz_uri);
        protocol = mainMrl.scheme();
        type = static_cast<NetworkDeviceModel::ItemType>(item->i_type);

        id = qHash(item);
        mrls.push_back(Source{mainMrl, item, source});

        char* artworkUrl = input_item_GetArtworkURL(item.get());
        if (artworkUrl)
        {
            artwork = QString::fromUtf8(artworkUrl);
            free(artworkUrl);
        }
    }

    QString sourceDesciption() const {
        if (mrls.empty())
            return {};
        return mrls.front().source->getDescription();
    }

    NetworkTreeItem makeTreeItem() const
    {
        if (unlikely(mrls.empty()))
            return {};
        const Source& src = mrls.front();
        return NetworkTreeItem(src.source, src.item);
    }

    SharedInputItem getInputItem() const
    {
        if (unlikely(mrls.empty()))
            return {};
        const Source& src = mrls.front();
        return src.item;
    }

    void addSource(const SharedInputItem& item,
                   const SharedMediaSourceModel& source)
    {
        mrls.emplace_back(Source{
            QUrl::fromEncoded(item->psz_uri),
            item,
            source
        });
    }

    /**
     * @brief addRemoveSource
     * @param item
     * @param source
     * @return false when no sources left
     */

    bool removeSource(const SharedInputItem& item,
                   const SharedMediaSourceModel&)
    {
        mrls.erase(std::remove_if(
            mrls.begin(), mrls.end(),
            [item](const Source& source){
                return source.item == item;
            })
        );

        if (mrls.empty())
        {
            mainMrl = QUrl{};
            return false;
        }
        else
        {
            mainMrl = mrls.front().mrl;
            return true;
        }
    }


    struct Source {
        QUrl mrl;
        SharedInputItem item;
        SharedMediaSourceModel source;
    };

    size_t id;
    std::vector<Source> mrls;
};

using NetworkDeviceItemPtr =std::shared_ptr<NetworkDeviceItem>;


static inline bool operator == (const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b) noexcept
{
    return a->id == b->id
           && QString::compare(a->name, b->name, Qt::CaseInsensitive) == 0
           && QString::compare(a->protocol, b->protocol, Qt::CaseInsensitive) == 0;
}

bool itemMatchPattern(const NetworkDeviceItemPtr& a, const QString& pattern)
{
    return a->name.contains(pattern, Qt::CaseInsensitive);
}

bool ascendingMrl(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b)
{
    return (QString::compare(a->mainMrl.toString(),
                             b->mainMrl.toString(), Qt::CaseInsensitive) <= 0);
}

bool ascendingName(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b)
{
    int result = QString::compare(a->name, b->name, Qt::CaseInsensitive);

    if (result != 0)
        return (result <= 0);

    return ascendingMrl(a, b);
}

bool descendingMrl(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b)
{
    return (QString::compare(a->mainMrl.toString(),
                             b->mainMrl.toString(), Qt::CaseInsensitive) >= 0);
}

bool descendingName(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& ;b)
{
    int result = QString::compare(a->name, b->name, Qt::CaseInsensitive);

    if (result != 0)
        return (result >= 0);

    return descendingMrl(a, b);
}

}

// ListCache specialisation

template<>
bool ListCache<NetworkDeviceItemPtr>::compareItems(const NetworkDeviceItemPtr& a, const NetworkDeviceItemPtr& b)
{
    //just compare the pointers here
    return a == b;
}

// NetworkDeviceModelPrivate

using NetworkDeviceModelLoader = LocalListCacheLoader<NetworkDeviceItemPtr>;

class NetworkDeviceModelPrivate
    : public LocalListBaseModelPrivate<NetworkDeviceItemPtr>
{
    Q_DECLARE_PUBLIC(NetworkDeviceModel)
public:
    NetworkDeviceModelPrivate(NetworkDeviceModel * pub)
        : LocalListBaseModelPrivate<NetworkDeviceItemPtr>(pub)
    {}

    ~NetworkDeviceModelPrivate()
    {
        //Explicilty disconnect signals here:
        // - destructor will release m_sourcesProvider
        // - its shared MediaSourceModel will be released
        // - they will notify `mediaRemoved`, but the slot expects the shared MediaSourceModel to be valid
        // at this point we don't care about model updates
        for (auto& conn : m_sourceUpdateConnections)
            QObject::disconnect(conn);
    }

    NetworkDeviceModelLoader::ItemCompare getSortFunction() const override
    {
        if (m_sortCriteria == "mrl")
        {
            if (m_sortOrder == Qt::AscendingOrder)
                return ascendingMrl;
            else
                return descendingMrl;
        }
        else
        {
            if (m_sortOrder == Qt::AscendingOrder)
                return ascendingName;
            else
                return descendingName;
        }
    }

    bool initializeModel() override
    {
        Q_Q(NetworkDeviceModel);

        if (m_qmlInitializing || !m_ctx || m_sdSource == NetworkDeviceModel::CAT_UNDEFINED || m_sourceName.isEmpty())
            return false;

        m_items.clear();

        if (m_sourcesProvider)
            m_sourcesProvider.reset();

        m_name = QString {};
        emit q->nameChanged();

        m_sourcesProvider = std::make_unique<DeviceSourceProvider>( m_sdSource, m_sourceName, m_ctx );

        QObject::connect(m_sourcesProvider.get(), &DeviceSourceProvider::failed, q,
                [this]()
        {
            m_items.clear();

            m_revision += 1;
            invalidateCache();
        });

        QObject::connect(m_sourcesProvider.get(), &DeviceSourceProvider::nameUpdated, q,
                [this, q](QString name)
        {
            m_name = name;
            emit q->nameChanged();
        });

        for (auto conn : m_sourceUpdateConnections)
            QObject::disconnect(conn);
        m_sourceUpdateConnections.clear();

        //itemsUpdated is called only once after init.
        QObject::connect(m_sourcesProvider.get(), &DeviceSourceProvider::itemsUpdated, q,
                [this]()
        {
            for (const auto& source: m_sourcesProvider->getMediaSources())
            {
                //fill initial values
                for (const SharedInputItem& media : source->getMedias())
                    onMediaAdded(source, media);

                //don't store a strong reference in the lambda or this may leak the source
                //as the connection is destroyed when the source is destroyed but the
                //connection holds a reference
                QWeakPointer<MediaSourceModel> weakSource{source};

                auto conn = QObject::connect(
                    source.get(), &MediaSourceModel::mediaAdded,
                    q_ptr, [this, weakSource](SharedInputItem media) {
                        //source is the signal emitter, it should always exist
                        auto strongSource  = weakSource.toStrongRef();
                        assert(strongSource);
                        onMediaAdded(strongSource, media);
                    });
                m_sourceUpdateConnections.push_back(conn);

                conn = QObject::connect(
                    source.get(), &MediaSourceModel::mediaRemoved,
                    q_ptr, [this, weakSource](SharedInputItem media) {
                        auto strongSource  = weakSource.toStrongRef();
                        assert(strongSource);
                        onMediaRemoved(strongSource, media);
                    });

                m_sourceUpdateConnections.push_back(conn);
            }

            m_revision += 1;
            invalidateCache();
        });

        m_sourcesProvider->init();

        //service discovery don't notify preparse end
        m_loading = false;
        emit q->loadingChanged();

        return true;
    }

    const NetworkDeviceItem* getItemForRow(int row) const
    {
        const NetworkDeviceItemPtr* ref = item(row);
        if (ref)
            return ref->get();
        return nullptr;
    }

    void onMediaAdded(const QSharedPointer<MediaSourceModel>& mediaSource, SharedInputItem media)
    {
        std::size_t hash = qHash(media);
        auto it = std::find_if(
            m_items.begin(), m_items.end(),
            [hash](const NetworkDeviceItemPtr& item) {
                return item->id == hash;
            });
        if (it != m_items.end())
        {
            (*it)->addSource(media, mediaSource);
        }
        else
        {
            m_items.push_back(std::make_unique<NetworkDeviceItem>(media, mediaSource));
            m_revision += 1;
            invalidateCache();
        }
    }

    void onMediaRemoved(const QSharedPointer<MediaSourceModel>& mediaSource, SharedInputItem media)
    {
        std::size_t hash = qHash(media);
        auto it = std::find_if(
            m_items.begin(), m_items.end(),
            [hash](const NetworkDeviceItemPtr& item) {
                return item->id == hash;
            });
        if (it == m_items.end())
            return;

        bool needDestruction = (*it)->removeSource(media, mediaSource);
        if (needDestruction)
        {
            m_items.erase(it);
        }

        m_revision += 1;
        invalidateCache();
    }

public: //LocalListCacheLoader::ModelSource
    std::vector<NetworkDeviceItemPtr> getModelData(const QString& pattern) const override
    {
        std::vector<NetworkDeviceItemPtr> items;
        if (pattern.isEmpty())
        {
            std::copy(
                m_items.cbegin(), m_items.cend(),
                std::back_inserter(items));
        }
        else
        {
            std::copy_if(
                m_items.cbegin(), m_items.cend(),
                std::back_inserter(items),
                [&pattern](const NetworkDeviceItemPtr& item){
                    return itemMatchPattern(item, pattern);
                });
        }
        return items;
    }

public:
    std::unique_ptr<DeviceSourceProvider> m_sourcesProvider;
    std::vector<NetworkDeviceItemPtr> m_items;

    MainCtx* m_ctx = nullptr;
    NetworkDeviceModel::SDCatType m_sdSource = NetworkDeviceModel::CAT_UNDEFINED;
    QString m_sourceName; // '*' -> all sources
    QString m_name; // source long name

    std::vector<QMetaObject::Connection> m_sourceUpdateConnections;
};

NetworkDeviceModel::NetworkDeviceModel( QObject* parent )
    : NetworkDeviceModel(new NetworkDeviceModelPrivate(this), parent)
{
}

NetworkDeviceModel::NetworkDeviceModel( NetworkDeviceModelPrivate* priv, QObject* parent)
    : NetworkBaseModel(priv, parent)
{
}

QVariant NetworkDeviceModel::data( const QModelIndex& index, int role ) const
{
    Q_D(const NetworkDeviceModel);
    if (!d->m_ctx)
        return {};

    const NetworkDeviceItem* item = d->getItemForRow(index.row());
    if (!item)
        return {};

    switch ( role )
    {
        case NETWORK_SOURCE:
            return item->sourceDesciption();
        case NETWORK_TREE:
            return QVariant::fromValue(item->makeTreeItem());
        default:
            return NetworkBaseModel::basedata(*item, role);
    }
}

QHash<int, QByteArray> NetworkDeviceModel::roleNames() const
{
    QHash<int, QByteArray> roles = NetworkBaseModel::roleNames();
    roles[NETWORK_SOURCE] = QByteArrayLiteral("source");
    roles[NETWORK_TREE] = QByteArrayLiteral("tree");
    return roles;
}

void NetworkDeviceModel::setCtx(MainCtx* ctx)
{
    Q_D(NetworkDeviceModel);
    if (d->m_ctx == ctx)
        return;
    d->m_ctx = ctx;
    d->initializeModel();
    emit ctxChanged();
}

MainCtx* NetworkDeviceModel::getCtx() const
{
    Q_D(const NetworkDeviceModel);
    return d->m_ctx;
}

NetworkDeviceModel::SDCatType NetworkDeviceModel::getSdSource() const {
    Q_D(const NetworkDeviceModel);
    return d->m_sdSource;
}
QString NetworkDeviceModel::getName() const {
    Q_D(const NetworkDeviceModel);
    return d->m_name;

}
QString NetworkDeviceModel::getSourceName() const {
    Q_D(const NetworkDeviceModel);
    return d->m_sourceName;
}

void NetworkDeviceModel::setSdSource(SDCatType s)
{
    Q_D(NetworkDeviceModel);
    if (d->m_sdSource == s)
        return;
    d->m_sdSource = s;
    d->initializeModel();
    emit sdSourceChanged();
}

void NetworkDeviceModel::setSourceName(const QString& sourceName)
{
    Q_D(NetworkDeviceModel);
    if (d->m_sourceName == sourceName)
        return;
    d->m_sourceName = sourceName;
    d->initializeModel();
    emit sourceNameChanged();
}

bool NetworkDeviceModel::insertIntoPlaylist(const QModelIndexList &itemIdList, ssize_t playlistIndex)
{
    Q_D(NetworkDeviceModel);
    if (!(d->m_ctx && d->m_sdSource != CAT_MYCOMPUTER))
        return false;
    QVector<vlc::playlist::Media> medias;
    medias.reserve( itemIdList.size() );
    for ( const QModelIndex &id : itemIdList )
    {
        const NetworkDeviceItem* item = d->getItemForRow(id.row());
        if (!item)
            continue;
        medias.append( vlc::playlist::Media {item->getInputItem().get() } );
    }
    if (medias.isEmpty())
        return false;
    d->m_ctx->getIntf()->p_mainPlaylistController->insert(playlistIndex, medias, false);
    return true;
}

bool NetworkDeviceModel::addToPlaylist(int row)
{
    Q_D(NetworkDeviceModel);
    if (!(d->m_ctx && d->m_sdSource != CAT_MYCOMPUTER))
        return false;

    const NetworkDeviceItem* item = d->getItemForRow(row);
    if (!item)
        return false;

    vlc::playlist::Media media{ item->getInputItem().get() };
    d->m_ctx->getIntf()->p_mainPlaylistController->append( QVector<vlc::playlist::Media>{ media }, false);
    return true;
}

bool NetworkDeviceModel::addToPlaylist(const QVariantList &itemIdList)
{
    bool ret = false;
    for (const QVariant& varValue: itemIdList)
    {
        if (varValue.canConvert<int>())
        {
            auto index = varValue.value<int>();
            ret |= addToPlaylist(index);
        }
    }
    return ret;
}

bool NetworkDeviceModel::addToPlaylist(const QModelIndexList &itemIdList)
{
    bool ret = false;
    for (const QModelIndex& index: itemIdList)
    {
        if (!index.isValid())
            continue;
        ret |= addToPlaylist(index.row());
    }
    return ret;
}

bool NetworkDeviceModel::addAndPlay(int row)
{
    Q_D(NetworkDeviceModel);
    if (!(d->m_ctx && d->m_sdSource != CAT_MYCOMPUTER))
        return false;

    const NetworkDeviceItem* item = d->getItemForRow(row);
    if (!item)
        return false;

    vlc::playlist::Media media{ item->getInputItem().get() };
    d->m_ctx->getIntf()->p_mainPlaylistController->append( QVector<vlc::playlist::Media>{ media }, true);
    return true;
}

bool NetworkDeviceModel::addAndPlay(const QVariantList& itemIdList)
{
    bool ret = false;
    for (const QVariant& varValue: itemIdList)
    {
        if (varValue.canConvert<int>())
        {
            auto row = varValue.value<int>();
            if (!ret)
                ret |= addAndPlay(row);
            else
                ret |= addToPlaylist(row);
        }
    }
    return ret;
}

bool NetworkDeviceModel::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 NetworkDeviceModel::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 NetworkDeviceModel::getItemsForIndexes(const QModelIndexList & indexes, std::function<void(const QVariantList&)> callback)
{
    Q_D(NetworkDeviceModel);

    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
    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 NetworkDeviceItem* item = d->getItemForRow(modelIndex.row());
            if (!item)
                continue;

            items.append(QVariant::fromValue(SharedInputItem(item->getInputItem().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, &NetworkDeviceModel::dataChanged, this, [this, indexes, callback]() {
        getItemsForIndexes(indexes, callback);
    }, Qt::SingleShotConnection);
}

Messung
CHZG
8410010094

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© 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.