1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qmediaplaylist.h" |
41 | #include "qmediaplaylist_p.h" |
42 | #include "qmediaplaylistprovider_p.h" |
43 | #include "qmediaplaylistioplugin_p.h" |
44 | #include "qmedianetworkplaylistprovider_p.h" |
45 | #include "qmediaservice.h" |
46 | #include "qmediaplaylistcontrol_p.h" |
47 | #include "qmediaplayercontrol.h" |
48 | |
49 | #include <QtCore/qlist.h> |
50 | #include <QtCore/qfile.h> |
51 | #include <QtCore/qurl.h> |
52 | #include <QtCore/qcoreevent.h> |
53 | #include <QtCore/qcoreapplication.h> |
54 | |
55 | #include "qmediapluginloader_p.h" |
56 | |
57 | QT_BEGIN_NAMESPACE |
58 | |
59 | Q_GLOBAL_STATIC_WITH_ARGS(QMediaPluginLoader, playlistIOLoader, |
60 | (QMediaPlaylistIOInterface_iid, QLatin1String("playlistformats" ), Qt::CaseInsensitive)) |
61 | |
62 | static void qRegisterMediaPlaylistMetaTypes() |
63 | { |
64 | qRegisterMetaType<QMediaPlaylist::Error>(); |
65 | qRegisterMetaType<QMediaPlaylist::PlaybackMode>(); |
66 | } |
67 | |
68 | Q_CONSTRUCTOR_FUNCTION(qRegisterMediaPlaylistMetaTypes) |
69 | |
70 | |
71 | /*! |
72 | \class QMediaPlaylist |
73 | \inmodule QtMultimedia |
74 | \ingroup multimedia |
75 | \ingroup multimedia_playback |
76 | |
77 | |
78 | \brief The QMediaPlaylist class provides a list of media content to play. |
79 | |
80 | QMediaPlaylist is intended to be used with other media objects, |
81 | like QMediaPlayer. |
82 | |
83 | QMediaPlaylist allows to access the service intrinsic playlist functionality |
84 | if available, otherwise it provides the local memory playlist implementation. |
85 | |
86 | \snippet multimedia-snippets/media.cpp Movie playlist |
87 | |
88 | Depending on playlist source implementation, most of the playlist mutating |
89 | operations can be asynchronous. |
90 | |
91 | \sa QMediaContent |
92 | */ |
93 | |
94 | |
95 | /*! |
96 | \enum QMediaPlaylist::PlaybackMode |
97 | |
98 | The QMediaPlaylist::PlaybackMode describes the order items in playlist are played. |
99 | |
100 | \value CurrentItemOnce The current item is played only once. |
101 | |
102 | \value CurrentItemInLoop The current item is played repeatedly in a loop. |
103 | |
104 | \value Sequential Playback starts from the current and moves through each successive item until the last is reached and then stops. |
105 | The next item is a null item when the last one is currently playing. |
106 | |
107 | \value Loop Playback restarts at the first item after the last has finished playing. |
108 | |
109 | \value Random Play items in random order. |
110 | */ |
111 | |
112 | |
113 | |
114 | /*! |
115 | Create a new playlist object with the given \a parent. |
116 | */ |
117 | |
118 | QMediaPlaylist::QMediaPlaylist(QObject *parent) |
119 | : QObject(parent) |
120 | , d_ptr(new QMediaPlaylistPrivate) |
121 | { |
122 | Q_D(QMediaPlaylist); |
123 | |
124 | d->q_ptr = this; |
125 | d->networkPlaylistControl = new QMediaNetworkPlaylistControl(this); |
126 | |
127 | setMediaObject(nullptr); |
128 | } |
129 | |
130 | /*! |
131 | Destroys the playlist. |
132 | */ |
133 | |
134 | QMediaPlaylist::~QMediaPlaylist() |
135 | { |
136 | Q_D(QMediaPlaylist); |
137 | |
138 | if (d->mediaObject) |
139 | d->mediaObject->unbind(this); |
140 | |
141 | delete d_ptr; |
142 | } |
143 | |
144 | /*! |
145 | Returns the QMediaObject instance that this QMediaPlaylist is bound too, |
146 | or 0 otherwise. |
147 | */ |
148 | QMediaObject *QMediaPlaylist::mediaObject() const |
149 | { |
150 | return d_func()->mediaObject; |
151 | } |
152 | |
153 | /*! |
154 | \internal |
155 | If \a mediaObject is null or doesn't have an intrinsic playlist, |
156 | internal local memory playlist source will be created. |
157 | */ |
158 | bool QMediaPlaylist::setMediaObject(QMediaObject *mediaObject) |
159 | { |
160 | Q_D(QMediaPlaylist); |
161 | |
162 | if (mediaObject && mediaObject == d->mediaObject) |
163 | return true; |
164 | |
165 | QMediaService *service = mediaObject |
166 | ? mediaObject->service() : nullptr; |
167 | |
168 | QMediaPlaylistControl *newControl = nullptr; |
169 | |
170 | if (service) |
171 | newControl = qobject_cast<QMediaPlaylistControl*>(object: service->requestControl(QMediaPlaylistControl_iid)); |
172 | |
173 | if (!newControl) |
174 | newControl = d->networkPlaylistControl; |
175 | |
176 | if (d->control != newControl) { |
177 | int removedStart = -1; |
178 | int removedEnd = -1; |
179 | int insertedStart = -1; |
180 | int insertedEnd = -1; |
181 | |
182 | if (d->control) { |
183 | QMediaPlaylistProvider *playlist = d->control->playlistProvider(); |
184 | disconnect(sender: playlist, SIGNAL(loadFailed(QMediaPlaylist::Error,QString)), |
185 | receiver: this, SLOT(_q_loadFailed(QMediaPlaylist::Error,QString))); |
186 | |
187 | disconnect(sender: playlist, signal: &QMediaPlaylistProvider::mediaChanged, receiver: this, slot: &QMediaPlaylist::mediaChanged); |
188 | disconnect(sender: playlist, signal: &QMediaPlaylistProvider::mediaAboutToBeInserted, receiver: this, slot: &QMediaPlaylist::mediaAboutToBeInserted); |
189 | disconnect(sender: playlist, signal: &QMediaPlaylistProvider::mediaInserted, receiver: this, slot: &QMediaPlaylist::mediaInserted); |
190 | disconnect(sender: playlist, signal: &QMediaPlaylistProvider::mediaAboutToBeRemoved, receiver: this, slot: &QMediaPlaylist::mediaAboutToBeRemoved); |
191 | disconnect(sender: playlist, signal: &QMediaPlaylistProvider::mediaRemoved, receiver: this, slot: &QMediaPlaylist::mediaRemoved); |
192 | |
193 | disconnect(sender: playlist, signal: &QMediaPlaylistProvider::loaded, receiver: this, slot: &QMediaPlaylist::loaded); |
194 | |
195 | disconnect(sender: d->control, signal: &QMediaPlaylistControl::playbackModeChanged, |
196 | receiver: this, slot: &QMediaPlaylist::playbackModeChanged); |
197 | disconnect(sender: d->control, signal: &QMediaPlaylistControl::currentIndexChanged, |
198 | receiver: this, slot: &QMediaPlaylist::currentIndexChanged); |
199 | disconnect(sender: d->control, signal: &QMediaPlaylistControl::currentMediaChanged, |
200 | receiver: this, slot: &QMediaPlaylist::currentMediaChanged); |
201 | |
202 | // Copy playlist items, sync playback mode and sync current index between |
203 | // old control and new control |
204 | d->syncControls(oldControl: d->control, newControl, |
205 | removedStart: &removedStart, removedEnd: &removedEnd, |
206 | insertedStart: &insertedStart, insertedEnd: &insertedEnd); |
207 | |
208 | if (d->mediaObject) |
209 | d->mediaObject->service()->releaseControl(control: d->control); |
210 | } |
211 | |
212 | d->control = newControl; |
213 | QMediaPlaylistProvider *playlist = d->control->playlistProvider(); |
214 | connect(sender: playlist, SIGNAL(loadFailed(QMediaPlaylist::Error,QString)), |
215 | receiver: this, SLOT(_q_loadFailed(QMediaPlaylist::Error,QString))); |
216 | |
217 | connect(sender: playlist, signal: &QMediaPlaylistProvider::mediaChanged, receiver: this, slot: &QMediaPlaylist::mediaChanged); |
218 | connect(sender: playlist, signal: &QMediaPlaylistProvider::mediaAboutToBeInserted, receiver: this, slot: &QMediaPlaylist::mediaAboutToBeInserted); |
219 | connect(sender: playlist, signal: &QMediaPlaylistProvider::mediaInserted, receiver: this, slot: &QMediaPlaylist::mediaInserted); |
220 | connect(sender: playlist, signal: &QMediaPlaylistProvider::mediaAboutToBeRemoved, receiver: this, slot: &QMediaPlaylist::mediaAboutToBeRemoved); |
221 | connect(sender: playlist, signal: &QMediaPlaylistProvider::mediaRemoved, receiver: this, slot: &QMediaPlaylist::mediaRemoved); |
222 | |
223 | connect(sender: playlist, signal: &QMediaPlaylistProvider::loaded, receiver: this, slot: &QMediaPlaylist::loaded); |
224 | |
225 | connect(sender: d->control, signal: &QMediaPlaylistControl::playbackModeChanged, |
226 | receiver: this, slot: &QMediaPlaylist::playbackModeChanged); |
227 | connect(sender: d->control, signal: &QMediaPlaylistControl::currentIndexChanged, |
228 | receiver: this, slot: &QMediaPlaylist::currentIndexChanged); |
229 | connect(sender: d->control, signal: &QMediaPlaylistControl::currentMediaChanged, |
230 | receiver: this, slot: &QMediaPlaylist::currentMediaChanged); |
231 | |
232 | if (removedStart != -1 && removedEnd != -1) { |
233 | emit mediaAboutToBeRemoved(start: removedStart, end: removedEnd); |
234 | emit mediaRemoved(start: removedStart, end: removedEnd); |
235 | } |
236 | |
237 | if (insertedStart != -1 && insertedEnd != -1) { |
238 | emit mediaAboutToBeInserted(start: insertedStart, end: insertedEnd); |
239 | emit mediaInserted(start: insertedStart, end: insertedEnd); |
240 | } |
241 | } |
242 | |
243 | d->mediaObject = mediaObject; |
244 | |
245 | return true; |
246 | } |
247 | |
248 | /*! |
249 | \property QMediaPlaylist::playbackMode |
250 | |
251 | This property defines the order that items in the playlist are played. |
252 | |
253 | \sa QMediaPlaylist::PlaybackMode |
254 | */ |
255 | |
256 | QMediaPlaylist::PlaybackMode QMediaPlaylist::playbackMode() const |
257 | { |
258 | return d_func()->control->playbackMode(); |
259 | } |
260 | |
261 | void QMediaPlaylist::setPlaybackMode(QMediaPlaylist::PlaybackMode mode) |
262 | { |
263 | Q_D(QMediaPlaylist); |
264 | d->control->setPlaybackMode(mode); |
265 | } |
266 | |
267 | /*! |
268 | Returns position of the current media content in the playlist. |
269 | */ |
270 | int QMediaPlaylist::currentIndex() const |
271 | { |
272 | return d_func()->control->currentIndex(); |
273 | } |
274 | |
275 | /*! |
276 | Returns the current media content. |
277 | */ |
278 | |
279 | QMediaContent QMediaPlaylist::currentMedia() const |
280 | { |
281 | return d_func()->playlist()->media(index: currentIndex()); |
282 | } |
283 | |
284 | /*! |
285 | Returns the index of the item, which would be current after calling next() |
286 | \a steps times. |
287 | |
288 | Returned value depends on the size of playlist, current position |
289 | and playback mode. |
290 | |
291 | \sa QMediaPlaylist::playbackMode(), previousIndex() |
292 | */ |
293 | int QMediaPlaylist::nextIndex(int steps) const |
294 | { |
295 | return d_func()->control->nextIndex(steps); |
296 | } |
297 | |
298 | /*! |
299 | Returns the index of the item, which would be current after calling previous() |
300 | \a steps times. |
301 | |
302 | \sa QMediaPlaylist::playbackMode(), nextIndex() |
303 | */ |
304 | |
305 | int QMediaPlaylist::previousIndex(int steps) const |
306 | { |
307 | return d_func()->control->previousIndex(steps); |
308 | } |
309 | |
310 | |
311 | /*! |
312 | Returns the number of items in the playlist. |
313 | |
314 | \sa isEmpty() |
315 | */ |
316 | int QMediaPlaylist::mediaCount() const |
317 | { |
318 | return d_func()->playlist()->mediaCount(); |
319 | } |
320 | |
321 | /*! |
322 | Returns true if the playlist contains no items, otherwise returns false. |
323 | |
324 | \sa mediaCount() |
325 | */ |
326 | bool QMediaPlaylist::isEmpty() const |
327 | { |
328 | return mediaCount() == 0; |
329 | } |
330 | |
331 | /*! |
332 | Returns true if the playlist can be modified, otherwise returns false. |
333 | |
334 | \sa mediaCount() |
335 | */ |
336 | bool QMediaPlaylist::isReadOnly() const |
337 | { |
338 | return d_func()->playlist()->isReadOnly(); |
339 | } |
340 | |
341 | /*! |
342 | Returns the media content at \a index in the playlist. |
343 | */ |
344 | |
345 | QMediaContent QMediaPlaylist::media(int index) const |
346 | { |
347 | return d_func()->playlist()->media(index); |
348 | } |
349 | |
350 | /*! |
351 | Append the media \a content to the playlist. |
352 | |
353 | Returns true if the operation is successful, otherwise returns false. |
354 | */ |
355 | bool QMediaPlaylist::addMedia(const QMediaContent &content) |
356 | { |
357 | return d_func()->control->playlistProvider()->addMedia(content); |
358 | } |
359 | |
360 | /*! |
361 | Append multiple media content \a items to the playlist. |
362 | |
363 | Returns true if the operation is successful, otherwise returns false. |
364 | */ |
365 | bool QMediaPlaylist::addMedia(const QList<QMediaContent> &items) |
366 | { |
367 | return d_func()->control->playlistProvider()->addMedia(contentList: items); |
368 | } |
369 | |
370 | /*! |
371 | Insert the media \a content to the playlist at position \a pos. |
372 | |
373 | Returns true if the operation is successful, otherwise returns false. |
374 | */ |
375 | |
376 | bool QMediaPlaylist::insertMedia(int pos, const QMediaContent &content) |
377 | { |
378 | QMediaPlaylistProvider *playlist = d_func()->playlist(); |
379 | return playlist->insertMedia(index: qBound(min: 0, val: pos, max: playlist->mediaCount()), content); |
380 | } |
381 | |
382 | /*! |
383 | Insert multiple media content \a items to the playlist at position \a pos. |
384 | |
385 | Returns true if the operation is successful, otherwise returns false. |
386 | */ |
387 | |
388 | bool QMediaPlaylist::insertMedia(int pos, const QList<QMediaContent> &items) |
389 | { |
390 | QMediaPlaylistProvider *playlist = d_func()->playlist(); |
391 | return playlist->insertMedia(index: qBound(min: 0, val: pos, max: playlist->mediaCount()), content: items); |
392 | } |
393 | |
394 | /*! |
395 | Move the item from position \a from to position \a to. |
396 | |
397 | Returns true if the operation is successful, otherwise false. |
398 | |
399 | \since 5.7 |
400 | */ |
401 | bool QMediaPlaylist::moveMedia(int from, int to) |
402 | { |
403 | QMediaPlaylistProvider *playlist = d_func()->playlist(); |
404 | return playlist->moveMedia(from: qBound(min: 0, val: from, max: playlist->mediaCount()), |
405 | to: qBound(min: 0, val: to, max: playlist->mediaCount())); |
406 | } |
407 | |
408 | /*! |
409 | Remove the item from the playlist at position \a pos. |
410 | |
411 | Returns true if the operation is successful, otherwise return false. |
412 | */ |
413 | bool QMediaPlaylist::removeMedia(int pos) |
414 | { |
415 | QMediaPlaylistProvider *playlist = d_func()->playlist(); |
416 | if (pos >= 0 && pos < playlist->mediaCount()) |
417 | return playlist->removeMedia(pos); |
418 | else |
419 | return false; |
420 | } |
421 | |
422 | /*! |
423 | Remove items in the playlist from \a start to \a end inclusive. |
424 | |
425 | Returns true if the operation is successful, otherwise return false. |
426 | */ |
427 | bool QMediaPlaylist::removeMedia(int start, int end) |
428 | { |
429 | QMediaPlaylistProvider *playlist = d_func()->playlist(); |
430 | start = qMax(a: 0, b: start); |
431 | end = qMin(a: end, b: playlist->mediaCount() - 1); |
432 | if (start <= end) |
433 | return playlist->removeMedia(start, end); |
434 | else |
435 | return false; |
436 | } |
437 | |
438 | /*! |
439 | Remove all the items from the playlist. |
440 | |
441 | Returns true if the operation is successful, otherwise return false. |
442 | */ |
443 | bool QMediaPlaylist::clear() |
444 | { |
445 | Q_D(QMediaPlaylist); |
446 | return d->playlist()->clear(); |
447 | } |
448 | |
449 | bool QMediaPlaylistPrivate::readItems(QMediaPlaylistReader *reader) |
450 | { |
451 | QList<QMediaContent> items; |
452 | |
453 | while (!reader->atEnd()) |
454 | items.append(t: reader->readItem()); |
455 | |
456 | return playlist()->addMedia(contentList: items); |
457 | } |
458 | |
459 | bool QMediaPlaylistPrivate::writeItems(QMediaPlaylistWriter *writer) |
460 | { |
461 | for (int i=0; i<playlist()->mediaCount(); i++) { |
462 | if (!writer->writeItem(content: playlist()->media(index: i))) |
463 | return false; |
464 | } |
465 | writer->close(); |
466 | return true; |
467 | } |
468 | |
469 | /*! |
470 | * \internal |
471 | * Copy playlist items, sync playback mode and sync current index between old control and new control |
472 | */ |
473 | void QMediaPlaylistPrivate::syncControls(QMediaPlaylistControl *oldControl, QMediaPlaylistControl *newControl, |
474 | int *removedStart, int *removedEnd, |
475 | int *insertedStart, int *insertedEnd) |
476 | { |
477 | Q_ASSERT(oldControl != NULL && newControl != NULL); |
478 | Q_ASSERT(removedStart != NULL && removedEnd != NULL |
479 | && insertedStart != NULL && insertedEnd != NULL); |
480 | |
481 | QMediaPlaylistProvider *oldPlaylist = oldControl->playlistProvider(); |
482 | QMediaPlaylistProvider *newPlaylist = newControl->playlistProvider(); |
483 | |
484 | Q_ASSERT(oldPlaylist != NULL && newPlaylist != NULL); |
485 | |
486 | *removedStart = -1; |
487 | *removedEnd = -1; |
488 | *insertedStart = -1; |
489 | *insertedEnd = -1; |
490 | |
491 | if (newPlaylist->isReadOnly()) { |
492 | // we can't transfer the items from the old control. |
493 | // Report these items as removed. |
494 | if (oldPlaylist->mediaCount() > 0) { |
495 | *removedStart = 0; |
496 | *removedEnd = oldPlaylist->mediaCount() - 1; |
497 | } |
498 | // The new control might have some items that can't be cleared. |
499 | // Report these as inserted. |
500 | if (newPlaylist->mediaCount() > 0) { |
501 | *insertedStart = 0; |
502 | *insertedEnd = newPlaylist->mediaCount() - 1; |
503 | } |
504 | } else { |
505 | const int oldPlaylistSize = oldPlaylist->mediaCount(); |
506 | |
507 | newPlaylist->clear(); |
508 | for (int i = 0; i < oldPlaylistSize; ++i) |
509 | newPlaylist->addMedia(content: oldPlaylist->media(index: i)); |
510 | } |
511 | |
512 | newControl->setPlaybackMode(oldControl->playbackMode()); |
513 | newControl->setCurrentIndex(oldControl->currentIndex()); |
514 | } |
515 | |
516 | /*! |
517 | Load playlist using network \a request. If \a format is specified, it is used, |
518 | otherwise format is guessed from playlist name and data. |
519 | |
520 | New items are appended to playlist. |
521 | |
522 | QMediaPlaylist::loaded() signal is emitted if playlist was loaded successfully, |
523 | otherwise the playlist emits loadFailed(). |
524 | */ |
525 | void QMediaPlaylist::load(const QNetworkRequest &request, const char *format) |
526 | { |
527 | Q_D(QMediaPlaylist); |
528 | |
529 | d->error = NoError; |
530 | d->errorString.clear(); |
531 | |
532 | if (d->playlist()->load(request,format)) |
533 | return; |
534 | |
535 | if (isReadOnly()) { |
536 | d->error = AccessDeniedError; |
537 | d->errorString = tr(s: "Could not add items to read only playlist." ); |
538 | emit loadFailed(); |
539 | return; |
540 | } |
541 | |
542 | const auto keys = playlistIOLoader()->keys(); |
543 | for (QString const& key : keys) { |
544 | QMediaPlaylistIOInterface* plugin = qobject_cast<QMediaPlaylistIOInterface*>(object: playlistIOLoader()->instance(key)); |
545 | if (plugin && plugin->canRead(location: request.url(), format)) { |
546 | QMediaPlaylistReader *reader = plugin->createReader(location: request.url(), format: QByteArray(format)); |
547 | if (reader && d->readItems(reader)) { |
548 | delete reader; |
549 | emit loaded(); |
550 | return; |
551 | } |
552 | delete reader; |
553 | } |
554 | } |
555 | |
556 | d->error = FormatNotSupportedError; |
557 | d->errorString = tr(s: "Playlist format is not supported" ); |
558 | emit loadFailed(); |
559 | |
560 | return; |
561 | } |
562 | |
563 | /*! |
564 | Load playlist from \a location. If \a format is specified, it is used, |
565 | otherwise format is guessed from location name and data. |
566 | |
567 | New items are appended to playlist. |
568 | |
569 | QMediaPlaylist::loaded() signal is emitted if playlist was loaded successfully, |
570 | otherwise the playlist emits loadFailed(). |
571 | */ |
572 | |
573 | void QMediaPlaylist::load(const QUrl &location, const char *format) |
574 | { |
575 | load(request: QNetworkRequest(location), format); |
576 | } |
577 | |
578 | /*! |
579 | Load playlist from QIODevice \a device. If \a format is specified, it is used, |
580 | otherwise format is guessed from device data. |
581 | |
582 | New items are appended to playlist. |
583 | |
584 | QMediaPlaylist::loaded() signal is emitted if playlist was loaded successfully, |
585 | otherwise the playlist emits loadFailed(). |
586 | */ |
587 | void QMediaPlaylist::load(QIODevice * device, const char *format) |
588 | { |
589 | Q_D(QMediaPlaylist); |
590 | |
591 | d->error = NoError; |
592 | d->errorString.clear(); |
593 | |
594 | if (d->playlist()->load(device,format)) |
595 | return; |
596 | |
597 | if (isReadOnly()) { |
598 | d->error = AccessDeniedError; |
599 | d->errorString = tr(s: "Could not add items to read only playlist." ); |
600 | emit loadFailed(); |
601 | return; |
602 | } |
603 | |
604 | const auto keys = playlistIOLoader()->keys(); |
605 | for (QString const& key : keys) { |
606 | QMediaPlaylistIOInterface* plugin = qobject_cast<QMediaPlaylistIOInterface*>(object: playlistIOLoader()->instance(key)); |
607 | if (plugin && plugin->canRead(device,format)) { |
608 | QMediaPlaylistReader *reader = plugin->createReader(device,format: QByteArray(format)); |
609 | if (reader && d->readItems(reader)) { |
610 | delete reader; |
611 | emit loaded(); |
612 | return; |
613 | } |
614 | delete reader; |
615 | } |
616 | } |
617 | |
618 | d->error = FormatNotSupportedError; |
619 | d->errorString = tr(s: "Playlist format is not supported" ); |
620 | emit loadFailed(); |
621 | |
622 | return; |
623 | } |
624 | |
625 | /*! |
626 | Save playlist to \a location. If \a format is specified, it is used, |
627 | otherwise format is guessed from location name. |
628 | |
629 | Returns true if playlist was saved successfully, otherwise returns false. |
630 | */ |
631 | bool QMediaPlaylist::save(const QUrl &location, const char *format) |
632 | { |
633 | Q_D(QMediaPlaylist); |
634 | |
635 | d->error = NoError; |
636 | d->errorString.clear(); |
637 | |
638 | if (d->playlist()->save(location,format)) |
639 | return true; |
640 | |
641 | QFile file(location.toLocalFile()); |
642 | |
643 | if (!file.open(flags: QIODevice::WriteOnly | QIODevice::Truncate)) { |
644 | d->error = AccessDeniedError; |
645 | d->errorString = tr(s: "The file could not be accessed." ); |
646 | return false; |
647 | } |
648 | |
649 | return save(device: &file, format); |
650 | } |
651 | |
652 | /*! |
653 | Save playlist to QIODevice \a device using format \a format. |
654 | |
655 | Returns true if playlist was saved successfully, otherwise returns false. |
656 | */ |
657 | bool QMediaPlaylist::save(QIODevice * device, const char *format) |
658 | { |
659 | Q_D(QMediaPlaylist); |
660 | |
661 | d->error = NoError; |
662 | d->errorString.clear(); |
663 | |
664 | if (d->playlist()->save(device,format)) |
665 | return true; |
666 | |
667 | const auto keys = playlistIOLoader()->keys(); |
668 | for (QString const& key : keys) { |
669 | QMediaPlaylistIOInterface* plugin = qobject_cast<QMediaPlaylistIOInterface*>(object: playlistIOLoader()->instance(key)); |
670 | if (plugin && plugin->canWrite(device,format)) { |
671 | QMediaPlaylistWriter *writer = plugin->createWriter(device,format: QByteArray(format)); |
672 | if (writer && d->writeItems(writer)) { |
673 | delete writer; |
674 | return true; |
675 | } |
676 | delete writer; |
677 | } |
678 | } |
679 | |
680 | d->error = FormatNotSupportedError; |
681 | d->errorString = tr(s: "Playlist format is not supported." ); |
682 | |
683 | return false; |
684 | } |
685 | |
686 | /*! |
687 | Returns the last error condition. |
688 | */ |
689 | QMediaPlaylist::Error QMediaPlaylist::error() const |
690 | { |
691 | return d_func()->error; |
692 | } |
693 | |
694 | /*! |
695 | Returns the string describing the last error condition. |
696 | */ |
697 | QString QMediaPlaylist::errorString() const |
698 | { |
699 | return d_func()->errorString; |
700 | } |
701 | |
702 | /*! |
703 | Shuffle items in the playlist. |
704 | */ |
705 | void QMediaPlaylist::shuffle() |
706 | { |
707 | d_func()->playlist()->shuffle(); |
708 | } |
709 | |
710 | |
711 | /*! |
712 | Advance to the next media content in playlist. |
713 | */ |
714 | void QMediaPlaylist::next() |
715 | { |
716 | d_func()->control->next(); |
717 | } |
718 | |
719 | /*! |
720 | Return to the previous media content in playlist. |
721 | */ |
722 | void QMediaPlaylist::previous() |
723 | { |
724 | d_func()->control->previous(); |
725 | } |
726 | |
727 | /*! |
728 | Activate media content from playlist at position \a playlistPosition. |
729 | */ |
730 | |
731 | void QMediaPlaylist::setCurrentIndex(int playlistPosition) |
732 | { |
733 | d_func()->control->setCurrentIndex(playlistPosition); |
734 | } |
735 | |
736 | /*! |
737 | \fn void QMediaPlaylist::mediaInserted(int start, int end) |
738 | |
739 | This signal is emitted after media has been inserted into the playlist. |
740 | The new items are those between \a start and \a end inclusive. |
741 | */ |
742 | |
743 | /*! |
744 | \fn void QMediaPlaylist::mediaRemoved(int start, int end) |
745 | |
746 | This signal is emitted after media has been removed from the playlist. |
747 | The removed items are those between \a start and \a end inclusive. |
748 | */ |
749 | |
750 | /*! |
751 | \fn void QMediaPlaylist::mediaChanged(int start, int end) |
752 | |
753 | This signal is emitted after media has been changed in the playlist |
754 | between \a start and \a end positions inclusive. |
755 | */ |
756 | |
757 | /*! |
758 | \fn void QMediaPlaylist::currentIndexChanged(int position) |
759 | |
760 | Signal emitted when playlist position changed to \a position. |
761 | */ |
762 | |
763 | /*! |
764 | \fn void QMediaPlaylist::playbackModeChanged(QMediaPlaylist::PlaybackMode mode) |
765 | |
766 | Signal emitted when playback mode changed to \a mode. |
767 | */ |
768 | |
769 | /*! |
770 | \fn void QMediaPlaylist::mediaAboutToBeInserted(int start, int end) |
771 | |
772 | Signal emitted when items are to be inserted at \a start and ending at \a end. |
773 | */ |
774 | |
775 | /*! |
776 | \fn void QMediaPlaylist::mediaAboutToBeRemoved(int start, int end) |
777 | |
778 | Signal emitted when item are to be deleted at \a start and ending at \a end. |
779 | */ |
780 | |
781 | /*! |
782 | \fn void QMediaPlaylist::currentMediaChanged(const QMediaContent &content) |
783 | |
784 | Signal emitted when current media changes to \a content. |
785 | */ |
786 | |
787 | /*! |
788 | \property QMediaPlaylist::currentIndex |
789 | \brief Current position. |
790 | */ |
791 | |
792 | /*! |
793 | \property QMediaPlaylist::currentMedia |
794 | \brief Current media content. |
795 | */ |
796 | |
797 | /*! |
798 | \fn QMediaPlaylist::loaded() |
799 | |
800 | Signal emitted when playlist finished loading. |
801 | */ |
802 | |
803 | /*! |
804 | \fn QMediaPlaylist::loadFailed() |
805 | |
806 | Signal emitted if failed to load playlist. |
807 | */ |
808 | |
809 | /*! |
810 | \enum QMediaPlaylist::Error |
811 | |
812 | This enum describes the QMediaPlaylist error codes. |
813 | |
814 | \value NoError No errors. |
815 | \value FormatError Format error. |
816 | \value FormatNotSupportedError Format not supported. |
817 | \value NetworkError Network error. |
818 | \value AccessDeniedError Access denied error. |
819 | */ |
820 | |
821 | QT_END_NAMESPACE |
822 | |
823 | #include "moc_qmediaplaylist.cpp" |
824 | #include "moc_qmediaplaylist_p.cpp" |
825 | |