| 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 |  |