Add sound manager initial source code
[staging/soundmanager.git] / sample / mediaplayer / app / playlistwithmetadata.cpp
diff --git a/sample/mediaplayer/app/playlistwithmetadata.cpp b/sample/mediaplayer/app/playlistwithmetadata.cpp
new file mode 100644 (file)
index 0000000..9e2e614
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "playlistwithmetadata.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QBuffer>
+#include <QtGui/QImage>
+#include <QtGui/QImageWriter>
+#include <QtMultimedia/QMediaPlayer>
+#include <QtMultimedia/QMediaMetaData>
+
+class PlaylistWithMetadata::Private
+{
+public:
+    Private(PlaylistWithMetadata *parent);
+
+    void disconnect();
+    void connect();
+
+private:
+    void loadMetadata(int row);
+
+private:
+    PlaylistWithMetadata *q;
+
+public:
+    QAbstractListModel *source;
+    QList<QMetaObject::Connection> connections;
+    QList<QUrl> urls;
+    QHash<QUrl, QString> title;
+    QHash<QUrl, QString> artist;
+    QHash<QUrl, QUrl> coverArt;
+    QHash<QUrl, qint64> duration;
+    QHash<QUrl, QMediaPlayer *> players;
+};
+
+PlaylistWithMetadata::Private::Private(PlaylistWithMetadata *parent)
+    : q(parent)
+    , source(nullptr)
+{
+}
+
+void PlaylistWithMetadata::Private::disconnect()
+{
+    if (source) {
+        for (const auto &connection : connections)
+            q->disconnect(connection);
+        connections.clear();
+    }
+}
+
+void PlaylistWithMetadata::Private::connect()
+{
+    if (source) {
+        connections.append(q->connect(source, &QAbstractListModel::rowsAboutToBeInserted, [&](const QModelIndex &parent, int first, int last) {
+            Q_UNUSED(parent)
+            q->beginInsertRows(QModelIndex(), first, last);
+        }));
+        connections.append(q->connect(source, &QAbstractListModel::rowsInserted, [&](const QModelIndex &parent, int first, int last) {
+            Q_UNUSED(parent)
+            for (int i = first; i <= last; i++) {
+                loadMetadata(i);
+            }
+            q->endInsertRows();
+        }));
+
+        connections.append(q->connect(source, &QAbstractListModel::rowsAboutToBeRemoved, [&](const QModelIndex &parent, int first, int last) {
+            Q_UNUSED(parent)
+            q->beginRemoveRows(QModelIndex(), first, last);
+        }));
+        connections.append(q->connect(source, &QAbstractListModel::rowsRemoved, [&](const QModelIndex &parent, int first, int last) {
+            Q_UNUSED(parent)
+            for (int i = last; i >= first; --i) {
+                QUrl url = urls.at(i);
+                urls.removeAt(i);
+
+                players.remove(url);
+                title.remove(url);
+                artist.remove(url);
+                coverArt.remove(url);
+                duration.remove(url);
+            }
+            q->endRemoveRows();
+        }));
+
+        int count = source->rowCount();
+        if (count > 0) {
+            q->beginInsertRows(QModelIndex(), 0, count);
+            for (int i = 0; i < count; i++) {
+                loadMetadata(i);
+            }
+            q->endInsertRows();
+        }
+    }
+}
+
+void PlaylistWithMetadata::Private::loadMetadata(int row)
+{
+    QUrl url = source->data(source->index(row), Qt::UserRole + 1).toUrl();
+    QMediaPlayer *player = new QMediaPlayer(q);
+    urls.append(url);
+    players.insert(url, player);
+    q->connect(player, &QMediaPlayer::mediaStatusChanged, [this, url](QMediaPlayer::MediaStatus mediaStatus) {
+        switch (mediaStatus) {
+        case QMediaPlayer::NoMedia:
+        case QMediaPlayer::LoadedMedia: {
+            QMediaPlayer *player = players.take(url);
+            title.insert(url, player->metaData(QMediaMetaData::Title).toString());
+            artist.insert(url, player->metaData(QMediaMetaData::ContributingArtist).toString());
+            QVariant coverArtImage = player->metaData(QMediaMetaData::CoverArtImage);
+            if (coverArtImage.type() == QVariant::Image) {
+                QImage image = coverArtImage.value<QImage>();
+                QByteArray data;
+                QBuffer buffer(&data);
+                buffer.open(QBuffer::WriteOnly);
+                QImageWriter png(&buffer, "png");
+                if (png.write(image)) {
+                    buffer.close();
+                    coverArt.insert(url, QUrl(QStringLiteral("data:image/png;base64,") + data.toBase64()));
+                }
+            }
+            duration.insert(url, player->duration());
+            QModelIndex index = q->index(urls.indexOf(url));
+            q->dataChanged(index, index, QVector<int>() << TitleRole << ArtistRole << CoverArtRole << DurationRole);
+            player->deleteLater();
+            break; }
+        default:
+            break;
+        }
+
+    });
+    player->setMedia(url);
+}
+
+PlaylistWithMetadata::PlaylistWithMetadata(QObject *parent)
+    : QAbstractListModel(parent)
+    , d(new Private(this))
+{
+}
+
+PlaylistWithMetadata::~PlaylistWithMetadata()
+{
+    delete d;
+}
+
+int PlaylistWithMetadata::rowCount(const QModelIndex &parent) const
+{
+    int ret = 0;
+    if (parent.isValid())
+        return ret;
+    if (d->source)
+        ret = d->source->rowCount(QModelIndex());
+    return ret;
+}
+
+QVariant PlaylistWithMetadata::data(const QModelIndex &index, int role) const
+{
+    QVariant ret;
+    if (!index.isValid())
+        return ret;
+    int row = index.row();
+    if (row < 0 || rowCount() <= row)
+        return ret;
+    QUrl url = d->urls.at(row);
+    switch (role) {
+    case TitleRole:
+        ret = d->title.value(url);
+        if (ret.toString().isEmpty())
+            ret = QVariant(url.fileName());
+        break;
+    case ArtistRole:
+        ret = d->artist.value(url);
+        break;
+    case CoverArtRole:
+        ret = d->coverArt.value(url);
+        break;
+    case SourceRole:
+        ret = url;
+        break;
+    case DurationRole:
+        ret = d->duration.value(url);
+        break;
+    default:
+        qWarning() << role;
+    }
+
+    return ret;
+}
+
+QHash<int, QByteArray> PlaylistWithMetadata::roleNames() const
+{
+    return {
+        {TitleRole, "title"},
+        {ArtistRole, "artist"},
+        {CoverArtRole, "coverArt"},
+        {SourceRole, "source"},
+        {DurationRole, "duration"}
+    };
+}
+
+QAbstractListModel *PlaylistWithMetadata::source() const
+{
+    return d->source;
+}
+
+void PlaylistWithMetadata::setSource(QAbstractListModel *source)
+{
+    if (d->source == source) return;
+    d->disconnect();
+    d->source = source;
+    d->connect();
+    emit sourceChanged(source);
+}