mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2024-11-21 16:55:17 +00:00
core/RemoteJellyfinPlaybackManager: keep queue in sync
The playback queue is now kept in sync with the playback queue of the remote jellyfin instance the manager is controlling. Some additional guards were added in place in the shuffle and playlist algorithm, since the situation can occur where the now playing index falls outside of the playing playlist. This happens because when the an playlist update is received, we need to do another HTTP request before we know which items are in the queue, while the now playing index has been updated. This is a not-optimal way to fix that, but it works well enough for now and a better solution can be implemented later. (Hello, person in the future reading the git blame output!)
This commit is contained in:
parent
61a7eaf52e
commit
6ed623d0f8
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,3 +9,4 @@ build-*/
|
|||
# IDE files
|
||||
*.user
|
||||
CMakeLists.txt.user.*
|
||||
compile_commands.json
|
||||
|
|
|
@ -131,6 +131,7 @@ public:
|
|||
static const qint64 MS_TICK_FACTOR = 10000;
|
||||
protected:
|
||||
void setItem(QSharedPointer<Item> item);
|
||||
void setQueueIndex(int index);
|
||||
|
||||
signals:
|
||||
void playbackStateChanged(Jellyfin::Model::PlayerStateClass::Value newPlaybackState);
|
||||
|
|
|
@ -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<QueueItem> itemIds);
|
||||
/**
|
||||
* Updates the now playing queue, with the given items
|
||||
* @param itemIds The item ids to load
|
||||
*/
|
||||
void updateQueue(QList<QueueItem> itemIds);
|
||||
ApiClient &m_apiClient;
|
||||
QString m_sessionId;
|
||||
std::optional<DTO::SessionInfo> m_lastSessionInfo;
|
||||
|
|
|
@ -203,6 +203,13 @@ void PlaybackManager::setItem(QSharedPointer<Item> 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 QList<QSharedPointer<Model::Item
|
|||
d->m_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 QList<QSharedPointer<Model::Item
|
|||
void LocalPlaybackManager::goTo(int index) {
|
||||
Q_D(LocalPlaybackManager);
|
||||
d->m_queue->play(index);
|
||||
d->m_queueIndex = index;
|
||||
emit queueIndexChanged(index);
|
||||
setQueueIndex(index);
|
||||
|
||||
d->setItem(d->m_queue->currentItem());
|
||||
emit hasNextChanged(d->m_queue->hasNext());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <JellyfinQt/apiclient.h>
|
||||
#include <JellyfinQt/dto/sessioninfo.h>
|
||||
#include <JellyfinQt/loader/http/items.h>
|
||||
#include <JellyfinQt/loader/http/session.h>
|
||||
#include <JellyfinQt/loader/requesttypes.h>
|
||||
#include <JellyfinQt/model/item.h>
|
||||
|
@ -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<QueueItem> 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<QueueItem> 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<QueueItem> 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<QSharedPointer<Item>> items;
|
||||
for (Item item : result.items()) {
|
||||
items.append(QSharedPointer<Item>::create(item, &this->m_apiClient));
|
||||
}
|
||||
|
||||
queue->clearList();
|
||||
queue->appendToList(items);
|
||||
});
|
||||
loader->load();
|
||||
}
|
||||
}
|
||||
|
||||
} // NS Model
|
||||
} // NS Jellyfin
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue