diff --git a/.gitignore b/.gitignore index ec1d4fe..77cd4f7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ build-*/ # IDE files *.user CMakeLists.txt.user.* +compile_commands.json diff --git a/core/include/JellyfinQt/model/playbackmanager.h b/core/include/JellyfinQt/model/playbackmanager.h index 84c5e9a..03f6834 100644 --- a/core/include/JellyfinQt/model/playbackmanager.h +++ b/core/include/JellyfinQt/model/playbackmanager.h @@ -131,6 +131,7 @@ public: static const qint64 MS_TICK_FACTOR = 10000; protected: void setItem(QSharedPointer item); + void setQueueIndex(int index); signals: void playbackStateChanged(Jellyfin::Model::PlayerStateClass::Value newPlaybackState); diff --git a/core/include/JellyfinQt/model/remotejellyfinplayback.h b/core/include/JellyfinQt/model/remotejellyfinplayback.h index 292bbc0..835856c 100644 --- a/core/include/JellyfinQt/model/remotejellyfinplayback.h +++ b/core/include/JellyfinQt/model/remotejellyfinplayback.h @@ -75,6 +75,17 @@ private: void sendGeneralCommand(DTO::GeneralCommandType command, QJsonObject arguments = QJsonObject()); void sendCommand(Support::LoaderBase *loader); void playItemInList(const QStringList &items, int index, qint64 resumeTicks = -1); + /** + * @brief isQueueSame Checks if the items in the list are the same as in the queue + * @param items The item ids to compare to the queue + * @return True if the same, otherwise false + */ + bool isQueueSame(QList itemIds); + /** + * Updates the now playing queue, with the given items + * @param itemIds The item ids to load + */ + void updateQueue(QList itemIds); ApiClient &m_apiClient; QString m_sessionId; std::optional m_lastSessionInfo; diff --git a/core/src/model/playbackmanager.cpp b/core/src/model/playbackmanager.cpp index 5ed84e6..50a7e4c 100644 --- a/core/src/model/playbackmanager.cpp +++ b/core/src/model/playbackmanager.cpp @@ -203,6 +203,13 @@ void PlaybackManager::setItem(QSharedPointer item) { emit itemChanged(); } +void PlaybackManager::setQueueIndex(int index) +{ + Q_D(PlaybackManager); + d->m_queueIndex = index; + emit queueIndexChanged(index); +} + /***************************************************************************** * LocalPlaybackManagerPrivate * *****************************************************************************/ @@ -611,9 +618,8 @@ void LocalPlaybackManager::playItemInList(const QListm_queue->clearList(); d->m_queue->appendToList(items); d->m_queue->play(index); - d->m_queueIndex = index; - emit queueIndexChanged(d->m_queueIndex); + setQueueIndex(index); d->setItem(items.at(index)); emit hasNextChanged(d->m_queue->hasNext()); @@ -624,8 +630,7 @@ void LocalPlaybackManager::playItemInList(const QListm_queue->play(index); - d->m_queueIndex = index; - emit queueIndexChanged(index); + setQueueIndex(index); d->setItem(d->m_queue->currentItem()); emit hasNextChanged(d->m_queue->hasNext()); diff --git a/core/src/model/playlist.cpp b/core/src/model/playlist.cpp index ce9e899..cbfbbcd 100644 --- a/core/src/model/playlist.cpp +++ b/core/src/model/playlist.cpp @@ -180,17 +180,28 @@ void Playlist::reshuffle() { } void Playlist::play(int index) { + // We need to defend against indices that are outside of the playlist. + // The RemoteJellyfinPlaybackController will usually first update the now playing index + // before this playlist is updated. For example, if new playlist is bigger than + // the current and an item index out of range of the current playlist is requested. + m_shuffler->setIndex(index); if (!m_nextItemFromQueue) { int nextItemIdx = m_shuffler->nextItem(); if (nextItemIdx >= 0) { - m_nextItem = m_list[m_shuffler->nextItem()]; + m_nextItem = m_list[nextItemIdx]; } else { m_nextItem.clear(); } } - m_currentItem = m_list[m_shuffler->currentItem()]; - emit currentItemChanged(); + + if (m_shuffler->currentItem() >= 0) { + m_currentItem = m_list[m_shuffler->currentItem()]; + emit currentItemChanged(); + } else { + m_currentItem.clear(); + emit currentItemChanged(); + } } bool Playlist::playingFromQueue() const { diff --git a/core/src/model/remotejellyfinplayback.cpp b/core/src/model/remotejellyfinplayback.cpp index 6e673a7..ac45c47 100644 --- a/core/src/model/remotejellyfinplayback.cpp +++ b/core/src/model/remotejellyfinplayback.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,7 @@ namespace Jellyfin { namespace Model { + RemoteJellyfinPlayback::RemoteJellyfinPlayback(ApiClient &apiClient, QString sessionId, QObject *parent) : PlaybackManager(parent), m_apiClient(apiClient), m_sessionId(sessionId), m_positionTimer(new QTimer(this)) { setApiClient(&m_apiClient); @@ -144,7 +146,8 @@ void RemoteJellyfinPlayback::next() { } void RemoteJellyfinPlayback::goTo(int index) { - + // This is the way Jellyfin does it as well: we send the entire queue to the server. + this->playItemInList(this->queue()->queueAndList(), index); } void RemoteJellyfinPlayback::stop() { @@ -193,6 +196,24 @@ void RemoteJellyfinPlayback::onSessionInfoUpdated(const QString &sessionId, cons m_position = 0; } + + // Handle the now playing queue + if (sessionInfo.nowPlayingQueueNull()) { + queue()->clearList(); + } else { + QList sessionQueue = sessionInfo.nowPlayingQueue(); + updateQueue(sessionQueue); + + int pos = 0; + for (pos = 0; pos < sessionQueue.size(); pos++) { + if (sessionQueue[pos].playlistItemId() == sessionInfo.playlistItemId()) break; + } + this->setQueueIndex(pos); + if (this->queue()->listSize() > 0) { + this->queue()->play(pos); + } + } + emit playbackStateChanged(playbackState()); emit durationChanged(duration()); emit positionChanged(position()); @@ -261,6 +282,56 @@ void RemoteJellyfinPlayback::playItemInList(const QStringList &items, int index, sendCommand(loader); } +bool RemoteJellyfinPlayback::isQueueSame(QList itemIds) { + Playlist *queue = this->queue(); + if (itemIds.length() == queue->listSize()) { + for (int i = 0; i < itemIds.length(); i++) { + if (queue->listAt(i)->jellyfinId() != itemIds[i].jellyfinId()) { + return false; + } + } + return true; + } else { + return false; + } +} + +void RemoteJellyfinPlayback::updateQueue(QList items) { + using GetItemsLoader = Loader::HTTP::GetItemsLoader; + using GetItemsParams = Loader::GetItemsParams; + + static GetItemsLoader *loader = new GetItemsLoader(&this->m_apiClient); + + if (!isQueueSame(items)) { + // Update queue + + QStringList itemIds; + for (QueueItem queueItem : items) { + itemIds.append(queueItem.jellyfinId()); + } + + if (loader->isRunning()) { + loader->cancel(); + } + + GetItemsParams params; + params.setIds(itemIds); + loader->setParameters(params); + connect(loader, &GetItemsLoader::ready, this, [this]() { + auto queue = this->queue(); + Loader::BaseItemDtoQueryResult result = loader->result(); + QList> items; + for (Item item : result.items()) { + items.append(QSharedPointer::create(item, &this->m_apiClient)); + } + + queue->clearList(); + queue->appendToList(items); + }); + loader->load(); + } +} + } // NS Model } // NS Jellyfin diff --git a/core/src/model/shuffle.cpp b/core/src/model/shuffle.cpp index f9aa1b7..cc6d23e 100644 --- a/core/src/model/shuffle.cpp +++ b/core/src/model/shuffle.cpp @@ -80,7 +80,7 @@ int NoShuffle::nextIndex() const { return (m_index + 1) % m_playlist->listSize(); } else { if (m_index + 1 >= m_playlist->listSize()) { - return 0; + return -1; } else { return m_index + 1; } @@ -88,7 +88,11 @@ int NoShuffle::nextIndex() const { } void NoShuffle::setIndex(int i) { - m_index = i; + if (i >= 0 && i < m_playlist->listSize()) { + m_index = i; + } else { + m_index = -1; + } } // ListShuffleBase