move MediaPlayer from CES2017
[apps/mediaplayer.git] / app / playlistwithmetadata.cpp
1 /*
2  * Copyright (C) 2016 The Qt Company Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "playlistwithmetadata.h"
18
19 #include <QtCore/QDebug>
20 #include <QtCore/QBuffer>
21 #include <QtGui/QImage>
22 #include <QtGui/QImageWriter>
23 #include <QtMultimedia/QMediaPlayer>
24 #include <QtMultimedia/QMediaMetaData>
25
26 class PlaylistWithMetadata::Private
27 {
28 public:
29     Private(PlaylistWithMetadata *parent);
30
31     void disconnect();
32     void connect();
33
34 private:
35     void loadMetadata(int row);
36
37 private:
38     PlaylistWithMetadata *q;
39
40 public:
41     QAbstractListModel *source;
42     QList<QMetaObject::Connection> connections;
43     QList<QUrl> urls;
44     QHash<QUrl, QString> title;
45     QHash<QUrl, QString> artist;
46     QHash<QUrl, QUrl> coverArt;
47     QHash<QUrl, qint64> duration;
48     QHash<QUrl, QMediaPlayer *> players;
49 };
50
51 PlaylistWithMetadata::Private::Private(PlaylistWithMetadata *parent)
52     : q(parent)
53     , source(nullptr)
54 {
55 }
56
57 void PlaylistWithMetadata::Private::disconnect()
58 {
59     if (source) {
60         for (const auto &connection : connections)
61             q->disconnect(connection);
62         connections.clear();
63     }
64 }
65
66 void PlaylistWithMetadata::Private::connect()
67 {
68     if (source) {
69         connections.append(q->connect(source, &QAbstractListModel::rowsAboutToBeInserted, [&](const QModelIndex &parent, int first, int last) {
70             Q_UNUSED(parent)
71             q->beginInsertRows(QModelIndex(), first, last);
72         }));
73         connections.append(q->connect(source, &QAbstractListModel::rowsInserted, [&](const QModelIndex &parent, int first, int last) {
74             Q_UNUSED(parent)
75             for (int i = first; i <= last; i++) {
76                 loadMetadata(i);
77             }
78             q->endInsertRows();
79         }));
80
81         int count = source->rowCount();
82         if (count > 0) {
83             q->beginInsertRows(QModelIndex(), 0, count);
84             for (int i = 0; i < count; i++) {
85                 loadMetadata(i);
86             }
87             q->endInsertRows();
88         }
89     }
90 }
91
92 void PlaylistWithMetadata::Private::loadMetadata(int row)
93 {
94     QUrl url = source->data(source->index(row), Qt::UserRole + 1).toUrl();
95     QMediaPlayer *player = new QMediaPlayer(q);
96     urls.append(url);
97     players.insert(url, player);
98     q->connect(player, &QMediaPlayer::mediaStatusChanged, [this, url](QMediaPlayer::MediaStatus mediaStatus) {
99         switch (mediaStatus) {
100         case QMediaPlayer::NoMedia:
101         case QMediaPlayer::LoadedMedia: {
102             QMediaPlayer *player = players.take(url);
103             title.insert(url, player->metaData(QMediaMetaData::Title).toString());
104             artist.insert(url, player->metaData(QMediaMetaData::ContributingArtist).toString());
105             QVariant coverArtImage = player->metaData(QMediaMetaData::CoverArtImage);
106             if (coverArtImage.type() == QVariant::Image) {
107                 QImage image = coverArtImage.value<QImage>();
108                 QByteArray data;
109                 QBuffer buffer(&data);
110                 buffer.open(QBuffer::WriteOnly);
111                 QImageWriter png(&buffer, "png");
112                 if (png.write(image)) {
113                     buffer.close();
114                     coverArt.insert(url, QUrl(QStringLiteral("data:image/png;base64,") + data.toBase64()));
115                 }
116             }
117             duration.insert(url, player->duration());
118             QModelIndex index = q->index(urls.indexOf(url));
119             q->dataChanged(index, index, QVector<int>() << TitleRole << ArtistRole << CoverArtRole << DurationRole);
120             player->deleteLater();
121             break; }
122         default:
123             break;
124         }
125
126     });
127     player->setMedia(url);
128 }
129
130 PlaylistWithMetadata::PlaylistWithMetadata(QObject *parent)
131     : QAbstractListModel(parent)
132     , d(new Private(this))
133 {
134 }
135
136 PlaylistWithMetadata::~PlaylistWithMetadata()
137 {
138     delete d;
139 }
140
141 int PlaylistWithMetadata::rowCount(const QModelIndex &parent) const
142 {
143     int ret = 0;
144     if (parent.isValid())
145         return ret;
146     if (d->source)
147         ret = d->source->rowCount(QModelIndex());
148     return ret;
149 }
150
151 QVariant PlaylistWithMetadata::data(const QModelIndex &index, int role) const
152 {
153     QVariant ret;
154     if (!index.isValid())
155         return ret;
156     int row = index.row();
157     if (row < 0 || rowCount() <= row)
158         return ret;
159     QUrl url = d->urls.at(row);
160     switch (role) {
161     case TitleRole:
162         ret = d->title.value(url);
163         break;
164     case ArtistRole:
165         ret = d->artist.value(url);
166         break;
167     case CoverArtRole:
168         ret = d->coverArt.value(url);
169         break;
170     case SourceRole:
171         ret = url;
172         break;
173     case DurationRole:
174         ret = d->duration.value(url);
175         break;
176     default:
177         qWarning() << role;
178     }
179
180     return ret;
181 }
182
183 QHash<int, QByteArray> PlaylistWithMetadata::roleNames() const
184 {
185     return {
186         {TitleRole, "title"},
187         {ArtistRole, "artist"},
188         {CoverArtRole, "coverArt"},
189         {SourceRole, "source"},
190         {DurationRole, "duration"}
191     };
192 }
193
194 QAbstractListModel *PlaylistWithMetadata::source() const
195 {
196     return d->source;
197 }
198
199 void PlaylistWithMetadata::setSource(QAbstractListModel *source)
200 {
201     if (d->source == source) return;
202     d->disconnect();
203     d->source = source;
204     d->connect();
205     emit sourceChanged(source);
206 }