File indexing completed on 2024-04-28 04:49:05

0001 /*
0002    SPDX-FileCopyrightText: 2015 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003 
0004    SPDX-License-Identifier: LGPL-3.0-or-later
0005  */
0006 
0007 #include "upnpcontentdirectorymodel.h"
0008 
0009 #include "musiclistenersmanager.h"
0010 
0011 #include "upnpLogging.h"
0012 
0013 #include "didlparser.h"
0014 #include "upnpcontrolcontentdirectory.h"
0015 #include "upnpdiscoverallmusic.h"
0016 
0017 #include <UpnpDeviceDescription>
0018 #include <UpnpServiceDescription>
0019 #include <UpnpActionDescription>
0020 
0021 
0022 #include <QHash>
0023 #include <QString>
0024 #include <QUrl>
0025 
0026 class UpnpContentDirectoryModelPrivate
0027 {
0028 public:
0029 
0030     UpnpControlContentDirectory *mContentDirectory;
0031 
0032     DidlParser mDidlParser;
0033 
0034     QString mParentId = QStringLiteral("0");
0035 
0036     QString mBrowseFlag = QStringLiteral("BrowseDirectChildren");
0037 
0038     QString mFilter = QStringLiteral("*");
0039 
0040     QString mSortCriteria;
0041 
0042     quintptr mLastInternalId;
0043 
0044     QHash<QString, quintptr> mUpnpIds;
0045 
0046     QHash<quintptr, QVector<quintptr> > mChilds;
0047 
0048     QHash<quintptr, DataTypes::UpnpTrackDataType> mAllTrackData;
0049 
0050     int mCurrentUpdateId;
0051 
0052     bool mUseLocalIcons = false;
0053 
0054     bool mIsBusy = false;
0055 
0056 };
0057 
0058 UpnpContentDirectoryModel::UpnpContentDirectoryModel(QObject *parent)
0059     : QAbstractItemModel(parent), d(new UpnpContentDirectoryModelPrivate)
0060 {
0061     d->mContentDirectory = nullptr;
0062 
0063     connect(&d->mDidlParser, &DidlParser::isDataValidChanged, this, &UpnpContentDirectoryModel::contentChanged);
0064 }
0065 
0066 UpnpContentDirectoryModel::~UpnpContentDirectoryModel()
0067 = default;
0068 
0069 int UpnpContentDirectoryModel::rowCount(const QModelIndex &parent) const
0070 {
0071     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::rowCount" << parent;
0072 
0073     int result = 0;
0074 
0075     auto currentInternalId = parent.internalId();
0076 
0077     if (!parent.isValid()) {
0078         currentInternalId = d->mUpnpIds[parentId()];
0079     }
0080 
0081     if (!d->mChilds.contains(currentInternalId)) {
0082         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::rowCount" << parent << currentInternalId << "unknown child" << result;
0083 
0084         if (d->mAllTrackData.contains(currentInternalId)) {
0085             result = d->mAllTrackData[currentInternalId][DataTypes::ChildCountRole].toInt();
0086 
0087             qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::rowCount" << parent << currentInternalId << result;
0088         }
0089 
0090         return result;
0091     }
0092 
0093     result = d->mChilds[currentInternalId].size();
0094 
0095     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::rowCount" << parent << currentInternalId << result;
0096 
0097     return result;
0098 }
0099 
0100 QHash<int, QByteArray> UpnpContentDirectoryModel::roleNames() const
0101 {
0102     auto roles = QAbstractItemModel::roleNames();
0103 
0104     roles[static_cast<int>(DataTypes::TitleRole)] = "title";
0105     roles[static_cast<int>(DataTypes::SecondaryTextRole)] = "secondaryText";
0106     roles[static_cast<int>(DataTypes::ImageUrlRole)] = "imageUrl";
0107     roles[static_cast<int>(DataTypes::DatabaseIdRole)] = "databaseId";
0108     roles[static_cast<int>(DataTypes::ElementTypeRole)] = "dataType";
0109     roles[static_cast<int>(DataTypes::ResourceRole)] = "url";
0110 
0111     roles[static_cast<int>(DataTypes::ArtistRole)] = "artist";
0112     roles[static_cast<int>(DataTypes::AllArtistsRole)] = "allArtists";
0113     roles[static_cast<int>(DataTypes::HighestTrackRating)] = "highestTrackRating";
0114     roles[static_cast<int>(DataTypes::GenreRole)] = "genre";
0115 
0116     roles[static_cast<int>(DataTypes::AlbumRole)] = "album";
0117     roles[static_cast<int>(DataTypes::AlbumArtistRole)] = "albumArtist";
0118     roles[static_cast<int>(DataTypes::DurationRole)] = "duration";
0119     roles[static_cast<int>(DataTypes::TrackNumberRole)] = "trackNumber";
0120     roles[static_cast<int>(DataTypes::DiscNumberRole)] = "discNumber";
0121     roles[static_cast<int>(DataTypes::RatingRole)] = "rating";
0122     roles[static_cast<int>(DataTypes::IsSingleDiscAlbumRole)] = "isSingleDiscAlbum";
0123     roles[static_cast<int>(DataTypes::FullDataRole)] = "fullData";
0124     roles[static_cast<int>(DataTypes::HasModelChildrenRole)] = "hasModelChildren";
0125 
0126     return roles;
0127 }
0128 
0129 Qt::ItemFlags UpnpContentDirectoryModel::flags(const QModelIndex &index) const
0130 {
0131     if (!index.isValid()) {
0132         return Qt::NoItemFlags;
0133     }
0134 
0135     return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
0136 }
0137 
0138 QVariant UpnpContentDirectoryModel::data(const QModelIndex &index, int role) const
0139 {
0140     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::data" << index << role;
0141 
0142     QVariant result;
0143 
0144     if (!index.isValid()) {
0145         return result;
0146     }
0147 
0148     if (index.column() != 0) {
0149         return result;
0150     }
0151 
0152     if (index.row() < 0) {
0153         return result;
0154     }
0155 
0156     if (!d->mAllTrackData.contains(index.internalId())) {
0157         return result;
0158     }
0159 
0160     auto convertedRole = static_cast<DataTypes::ColumnsRoles>(role);
0161 
0162     switch(role)
0163     {
0164     case Qt::DisplayRole:
0165         result = d->mAllTrackData[index.internalId()][DataTypes::TitleRole];
0166         break;
0167     case DataTypes::ElementTypeRole:
0168     case DataTypes::IdRole:
0169     case DataTypes::ParentIdRole:
0170     case DataTypes::TitleRole:
0171     case DataTypes::DurationRole:
0172     case DataTypes::ArtistRole:
0173     case DataTypes::AlbumRole:
0174         result = d->mAllTrackData[index.internalId()][convertedRole];
0175         break;
0176     case DataTypes::RatingRole:
0177         result = 0;
0178         break;
0179     case DataTypes::ResourceRole:
0180         result = QUrl{};
0181         break;
0182     case DataTypes::ImageUrlRole:
0183         switch (d->mAllTrackData[index.internalId()][DataTypes::ElementTypeRole].value<ElisaUtils::PlayListEntryType>())
0184         {
0185         case ElisaUtils::Album:
0186             if (!d->mAllTrackData[index.internalId()][DataTypes::ImageUrlRole].toString().isEmpty()) {
0187                 result = d->mAllTrackData[index.internalId()][DataTypes::ImageUrlRole].toUrl();
0188             } else {
0189                 if (d->mUseLocalIcons) {
0190                     result = QUrl(QStringLiteral("qrc:/media-default-album.svg"));
0191                 } else {
0192                     result = QUrl(QStringLiteral("image://icon/media-default-album"));
0193                 }
0194             }
0195             break;
0196         case ElisaUtils::Container:
0197         case ElisaUtils::UpnpMediaServer:
0198             if (!d->mAllTrackData[index.internalId()][DataTypes::ImageUrlRole].toString().isEmpty()) {
0199                 result = d->mAllTrackData[index.internalId()][DataTypes::ImageUrlRole].toUrl();
0200             } else {
0201                 if (d->mUseLocalIcons) {
0202                     return QUrl(QStringLiteral("qrc:/folder.svg"));
0203                 } else {
0204                     return QUrl(QStringLiteral("image://icon/folder"));
0205                 }
0206             }
0207             break;
0208         case ElisaUtils::Track:
0209             result = d->mAllTrackData[index.internalId()][DataTypes::ImageUrlRole];
0210             break;
0211         case ElisaUtils::Artist:
0212         case ElisaUtils::Composer:
0213         case ElisaUtils::FileName:
0214         case ElisaUtils::Genre:
0215         case ElisaUtils::Lyricist:
0216         case ElisaUtils::Radio:
0217         case ElisaUtils::Unknown:
0218             break;
0219         }
0220         break;
0221     case DataTypes::HasModelChildrenRole:
0222         result = rowCount(index);
0223         break;
0224     case DataTypes::FullDataRole:
0225         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::data" << d->mAllTrackData[index.internalId()];
0226         result = QVariant::fromValue(static_cast<DataTypes::MusicDataType>(d->mAllTrackData[index.internalId()]));
0227         break;
0228     }
0229 
0230     return result;
0231 }
0232 
0233 QModelIndex UpnpContentDirectoryModel::index(int row, int column, const QModelIndex &parent) const
0234 {
0235     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::index" << row << column << parent;
0236 
0237     QModelIndex result;
0238 
0239     auto currentInternalId = parent.internalId();
0240 
0241     if (!parent.isValid()) {
0242         currentInternalId = d->mUpnpIds[parentId()];
0243     }
0244 
0245     if (!d->mChilds.contains(currentInternalId)) {
0246         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::index" << row << column << parent << result;
0247 
0248         return result;
0249     }
0250 
0251     if (row < 0 || row >= d->mChilds[currentInternalId].size()) {
0252         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::index" << row << column << parent << result;
0253 
0254         return result;
0255     }
0256 
0257     if (column != 0) {
0258         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::index" << row << column << parent << result;
0259 
0260         return result;
0261     }
0262 
0263     result = createIndex(row, column, d->mChilds[currentInternalId][row]);
0264 
0265     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::index" << row << column << parent << result;
0266 
0267     return result;
0268 }
0269 
0270 QModelIndex UpnpContentDirectoryModel::parent(const QModelIndex &child) const
0271 {
0272     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::parent" << child;
0273 
0274     QModelIndex result;
0275 
0276     // child is valid
0277     if (!child.isValid()) {
0278         return result;
0279     }
0280 
0281     // data knows child internal id
0282     if (!d->mAllTrackData.contains(child.internalId())) {
0283         return result;
0284     }
0285 
0286     const auto &childData = d->mAllTrackData[child.internalId()];
0287 
0288     // child data has upnp id of parent
0289     if (!childData.contains(DataTypes::ParentIdRole)) {
0290         return result;
0291     }
0292 
0293     const auto &parentStringId = childData[DataTypes::ParentIdRole].toString();
0294 
0295     // upnp ids has the internal id of parent
0296     if (!d->mUpnpIds.contains(parentStringId)) {
0297         return result;
0298     }
0299 
0300     // special case if we are already at top of model
0301     if (parentStringId == QLatin1Char('0')) {
0302         return result;
0303     }
0304 
0305     auto parentInternalId = d->mUpnpIds[parentStringId];
0306 
0307     // data knows parent internal id
0308     if (!d->mAllTrackData.contains(parentInternalId)) {
0309         return result;
0310     }
0311 
0312     const auto &parentData = d->mAllTrackData[parentInternalId];
0313 
0314     // parent data has upnp id of parent
0315     if (!parentData.contains(DataTypes::ParentIdRole)) {
0316         return result;
0317     }
0318 
0319     const auto &grandParentStringId = parentData[DataTypes::ParentIdRole].toString();
0320 
0321     // upnp ids has the internal id of grand parent
0322     if (!d->mUpnpIds.contains(grandParentStringId)) {
0323         return result;
0324     }
0325 
0326     auto grandParentInternalId = d->mUpnpIds[grandParentStringId];
0327 
0328     // childs of grand parent are known
0329     if (!d->mChilds.contains(grandParentInternalId)) {
0330         return result;
0331     }
0332 
0333     const auto &allUncles = d->mChilds[grandParentInternalId];
0334 
0335     // look for my parent
0336     for (int i = 0; i < allUncles.size(); ++i) {
0337         if (allUncles[i] == parentInternalId) {
0338             result = createIndex(i, 0, parentInternalId);
0339             break;
0340         }
0341     }
0342 
0343     return result;
0344 }
0345 
0346 int UpnpContentDirectoryModel::columnCount(const QModelIndex &parent) const
0347 {
0348     Q_UNUSED(parent)
0349 
0350     return 1;
0351 }
0352 
0353 bool UpnpContentDirectoryModel::canFetchMore(const QModelIndex &parent) const
0354 {
0355     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::canFetchMore" << parent;
0356 
0357     bool result = false;
0358 
0359     auto parentInternalId = parent.internalId();
0360 
0361     if (!parent.isValid()) {
0362         parentInternalId = d->mUpnpIds[parentId()];
0363     }
0364 
0365     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::canFetchMore" << parent
0366                                << parentInternalId << !d->mChilds.contains(parentInternalId)
0367                                <<  d->mChilds[parentInternalId].isEmpty();
0368 
0369     result = !d->mChilds.contains(parentInternalId) ||  d->mChilds[parentInternalId].isEmpty();
0370 
0371     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::canFetchMore" << parent << result;
0372 
0373     return result;
0374 }
0375 
0376 void UpnpContentDirectoryModel::fetchMore(const QModelIndex &parent)
0377 {
0378     if (d->mIsBusy) {
0379         return;
0380     }
0381 
0382     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::fetchMore" << parent;
0383 
0384     d->mIsBusy = true;
0385     Q_EMIT isBusyChanged();
0386 
0387     if (!d->mContentDirectory) {
0388         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::fetchMore" << parent << "no content directory";
0389 
0390         d->mIsBusy = false;
0391         Q_EMIT isBusyChanged();
0392 
0393         return;
0394     }
0395 
0396     auto parentInternalId = parent.internalId();
0397 
0398     if (!parent.isValid()) {
0399         parentInternalId = d->mUpnpIds[parentId()];
0400     }
0401 
0402     if (!d->mAllTrackData.contains(parentInternalId)) {
0403         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::fetchMore" << parent << "no parent internal id";
0404 
0405         d->mIsBusy = false;
0406         Q_EMIT isBusyChanged();
0407 
0408         return;
0409     }
0410 
0411     if (!d->mAllTrackData[parentInternalId].contains(DataTypes::IdRole)) {
0412         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::fetchMore" << parent << "no id role";
0413 
0414         d->mIsBusy = false;
0415         Q_EMIT isBusyChanged();
0416 
0417         return;
0418     }
0419 
0420     if (parentId().isEmpty()) {
0421         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::fetchMore" << parent << d->mAllTrackData[parentInternalId][DataTypes::IdRole].toString();
0422 
0423         d->mDidlParser.setParentId(d->mAllTrackData[parentInternalId][DataTypes::IdRole].toString());
0424     } else {
0425         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::fetchMore" << parent << parentId();
0426 
0427         d->mDidlParser.setParentId(parentId());
0428     }
0429     d->mDidlParser.browse();
0430 }
0431 
0432 const QString &UpnpContentDirectoryModel::parentId() const
0433 {
0434     return d->mParentId;
0435 }
0436 
0437 const QString &UpnpContentDirectoryModel::browseFlag() const
0438 {
0439     return d->mBrowseFlag;
0440 }
0441 
0442 void UpnpContentDirectoryModel::setBrowseFlag(const QString &flag)
0443 {
0444     d->mBrowseFlag = flag;
0445     Q_EMIT browseFlagChanged();
0446 }
0447 
0448 const QString &UpnpContentDirectoryModel::filter() const
0449 {
0450     return d->mFilter;
0451 }
0452 
0453 void UpnpContentDirectoryModel::setFilter(const QString &flag)
0454 {
0455     d->mFilter = flag;
0456     Q_EMIT filterChanged();
0457 }
0458 
0459 const QString &UpnpContentDirectoryModel::sortCriteria() const
0460 {
0461     return d->mSortCriteria;
0462 }
0463 
0464 void UpnpContentDirectoryModel::setSortCriteria(const QString &criteria)
0465 {
0466     d->mSortCriteria = criteria;
0467     Q_EMIT sortCriteriaChanged();
0468 }
0469 
0470 UpnpControlContentDirectory *UpnpContentDirectoryModel::contentDirectory() const
0471 {
0472     return d->mContentDirectory;
0473 }
0474 
0475 void UpnpContentDirectoryModel::setContentDirectory(UpnpControlContentDirectory *directory)
0476 {
0477     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::setContentDirectory" << directory;
0478     if (directory) {
0479         beginResetModel();
0480     }
0481     if (d->mContentDirectory) {
0482     }
0483 
0484     d->mContentDirectory = directory;
0485 
0486     if (!d->mContentDirectory) {
0487         Q_EMIT contentDirectoryChanged();
0488         return;
0489     }
0490 
0491     d->mDidlParser.setContentDirectory(d->mContentDirectory);
0492     d->mDidlParser.setBrowseFlag(browseFlag());
0493     d->mDidlParser.setFilter(filter());
0494     d->mDidlParser.setSortCriteria(sortCriteria());
0495     d->mDidlParser.setParentId(parentId());
0496 
0497     endResetModel();
0498 
0499     Q_EMIT contentDirectoryChanged();
0500 }
0501 
0502 bool UpnpContentDirectoryModel::useLocalIcons() const
0503 {
0504     return d->mUseLocalIcons;
0505 }
0506 
0507 void UpnpContentDirectoryModel::setUseLocalIcons(bool value)
0508 {
0509     d->mUseLocalIcons = value;
0510     Q_EMIT useLocalIconsChanged();
0511 }
0512 
0513 bool UpnpContentDirectoryModel::isBusy() const
0514 {
0515     return d->mIsBusy;
0516 }
0517 
0518 void UpnpContentDirectoryModel::initializeByData(MusicListenersManager *manager, DatabaseInterface *database,
0519                                                  ElisaUtils::PlayListEntryType modelType, ElisaUtils::FilterType filter,
0520                                                  const DataTypes::DataType &dataFilter)
0521 {
0522     Q_UNUSED(database)
0523     Q_UNUSED(modelType)
0524     Q_UNUSED(filter)
0525 
0526     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::initializeByData" << modelType << filter << dataFilter << dataFilter[DataTypes::UUIDRole].toString() << dataFilter[DataTypes::IdRole].toString();
0527 
0528     if (manager && manager->ssdpEngine() && manager->upnpServiceDiscovery()) {
0529         auto *newContentDirectory = new UpnpControlContentDirectory;
0530 
0531         const auto &deviceDescription = manager->upnpServiceDiscovery()->deviceDescriptionByUdn(dataFilter[DataTypes::UUIDRole].toString());
0532         newContentDirectory->setDescription(deviceDescription.serviceById(QStringLiteral("urn:upnp-org:serviceId:ContentDirectory")));
0533         setParentId(dataFilter[DataTypes::IdRole].toString());
0534         setContentDirectory(newContentDirectory);
0535         d->mDidlParser.setDeviceUUID(dataFilter[DataTypes::UUIDRole].toString());
0536     }
0537 }
0538 
0539 void UpnpContentDirectoryModel::setParentId(QString parentId)
0540 {
0541     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::setParentId" << parentId;
0542 
0543     if (d->mParentId == parentId) {
0544         return;
0545     }
0546 
0547     if (parentId.isEmpty()) {
0548         parentId = QStringLiteral("0");
0549     }
0550 
0551     d->mParentId = std::move(parentId);
0552     d->mDidlParser.setParentId(d->mParentId);
0553 
0554     Q_EMIT parentIdChanged();
0555 
0556     beginResetModel();
0557     d->mLastInternalId = 1;
0558 
0559     d->mUpnpIds[d->mParentId] = d->mLastInternalId;
0560 
0561     d->mChilds[d->mLastInternalId] = QVector<quintptr>();
0562 
0563     d->mAllTrackData[d->mLastInternalId] = {{DataTypes::IdRole, d->mParentId},
0564                                             {DataTypes::ElementTypeRole, QVariant::fromValue(ElisaUtils::Container)},
0565                                             {DataTypes::TitleRole, QStringLiteral("Root")},};
0566 
0567     ++d->mLastInternalId;
0568 
0569     d->mCurrentUpdateId = -1;
0570     endResetModel();
0571 }
0572 
0573 QModelIndex UpnpContentDirectoryModel::indexFromInternalId(quintptr internalId) const
0574 {
0575     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::indexFromInternalId" << internalId;
0576 
0577     QModelIndex result;
0578 
0579     if (internalId == 1) {
0580         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::indexFromInternalId" << internalId << result;
0581 
0582         return result;
0583     }
0584 
0585     // data knows child internal id
0586     if (!d->mAllTrackData.contains(internalId)) {
0587         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::indexFromInternalId" << internalId << "unknown children id" << result;
0588 
0589         return result;
0590     }
0591 
0592     const auto &childData = d->mAllTrackData[internalId];
0593 
0594     // child data has upnp id of parent
0595     if (!childData.contains(DataTypes::ParentIdRole)) {
0596         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::indexFromInternalId" << internalId << "unknown parent" << result;
0597 
0598         return result;
0599     }
0600 
0601     const auto &parentStringId = childData[DataTypes::ParentIdRole].toString();
0602 
0603     // upnp ids has the internal id of parent
0604     if (!d->mUpnpIds.contains(parentStringId)) {
0605         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::indexFromInternalId" << internalId << "unknown parent from ids list" << result;
0606 
0607         return result;
0608     }
0609 
0610     auto parentInternalId = d->mUpnpIds[parentStringId];
0611 
0612     // childs of parent are known
0613     if (!d->mChilds.contains(parentInternalId)) {
0614         qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::indexFromInternalId" << internalId << "unknown parent id" << result;
0615 
0616         return result;
0617     }
0618 
0619     const auto &allUncles = d->mChilds[parentInternalId];
0620 
0621     // look for my parent
0622     for (int i = 0; i < allUncles.size(); ++i) {
0623         if (allUncles[i] == internalId) {
0624             result = createIndex(i, 0, internalId);
0625             break;
0626         }
0627     }
0628 
0629     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::indexFromInternalId" << internalId << result;
0630 
0631     return result;
0632 }
0633 
0634 void UpnpContentDirectoryModel::contentChanged(const QString &parentId)
0635 {
0636     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::contentChanged" << parentId;
0637 
0638     auto parentInternalId = d->mUpnpIds[parentId];
0639 
0640     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::contentChanged" << parentId
0641                                << parentInternalId
0642                                << indexFromInternalId(parentInternalId)
0643                                << 0 << d->mDidlParser.newMusicTracks().size() - 1;
0644     beginInsertRows(indexFromInternalId(parentInternalId), 0, d->mDidlParser.newMusicTracks().size() - 1);
0645 
0646     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::contentChanged" << parentId << parentInternalId;
0647 
0648     for(const auto &oneUpnpTrack : std::as_const(d->mDidlParser.newMusicTracks())) {
0649         d->mAllTrackData[d->mLastInternalId] = oneUpnpTrack;
0650         d->mUpnpIds[oneUpnpTrack[DataTypes::IdRole].toString()] = d->mLastInternalId;
0651         d->mChilds[parentInternalId].push_back(d->mLastInternalId);
0652         ++d->mLastInternalId;
0653     }
0654 
0655     qCDebug(orgKdeElisaUpnp()) << "UpnpContentDirectoryModel::contentChanged" << parentId << d->mChilds[parentInternalId].size();
0656 
0657     endInsertRows();
0658 
0659     d->mIsBusy = false;
0660     Q_EMIT isBusyChanged();
0661 }
0662 
0663 #include "moc_upnpcontentdirectorymodel.cpp"