1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2024-11-22 09:15:18 +00:00

Fix a few PlaybackManager bugs

The following bugs should have been fixed:

* The PlaybackManager no longer starts playing again after
  PlaybackManager.stop() has been called.
* The PlaybackManager will no longer get into an invalid state when
  next() is called many times fast.
* The PlaybackManager now exposes its error information when the
  PlaybackUrl could not be fetched.
* The PlaybackManager will keep a playbackState of Playing as long
  as it is not stopped and while in a playlist. Previously, it would
  stop and start everytime the next item got loaded.
This commit is contained in:
Chris Josten 2021-09-09 22:16:39 +02:00
parent caf72af999
commit 7c21eb425d
No known key found for this signature in database
GPG key ID: A69C050E9FD9FF6A
5 changed files with 129 additions and 22 deletions

View file

@ -116,12 +116,12 @@ public:
bool hasPrevious() const { return m_queue->hasPrevious(); } bool hasPrevious() const { return m_queue->hasPrevious(); }
// Current media player related property getters // Current media player related property getters
QMediaPlayer::State playbackState() const { return m_mediaPlayer->state()/*m_playbackState*/; } QMediaPlayer::State playbackState() const { return m_playbackState; }
QMediaPlayer::MediaStatus mediaStatus() const { return m_mediaPlayer->mediaStatus(); } QMediaPlayer::MediaStatus mediaStatus() const { return m_mediaPlayer->mediaStatus(); }
bool hasVideo() const { return m_mediaPlayer->isVideoAvailable(); } bool hasVideo() const { return m_mediaPlayer->isVideoAvailable(); }
bool seekable() const { return m_mediaPlayer->isSeekable(); } bool seekable() const { return m_mediaPlayer->isSeekable(); }
QMediaPlayer::Error error () const { return m_mediaPlayer->error(); } QMediaPlayer::Error error () const;
QString errorString() const { return m_mediaPlayer->errorString(); } QString errorString() const;
signals: signals:
void itemChanged(ViewModel::Item *newItemId); void itemChanged(ViewModel::Item *newItemId);
void streamUrlChanged(const QString &newStreamUrl); void streamUrlChanged(const QString &newStreamUrl);
@ -148,23 +148,34 @@ signals:
void hasPreviousChanged(bool newHasPrevious); void hasPreviousChanged(bool newHasPrevious);
public slots: public slots:
/** /**
* @brief playItem Replaces the current queue and plays the item with the given id. * @brief playItem Replaces the current queue and plays the given item.
* *
* This will construct the Jellyfin::Item internally * This will construct the Jellyfin::Item internally
* and delete it later. * and delete it later.
* @param item The item to play. * @param item The item to play.
*/ */
void playItem(Item *item); void playItem(Item *item);
void playItem(QSharedPointer<Model::Item> item);
/**
* @brief playItem Replaces the current queue and plays the item with the given id.
*
* This will construct the Jellyfin::Item internally
* and delete it later.
* @param item The itemId to play.
*/
void playItemId(const QString &itemId);
void playItemInList(ItemModel *itemList, int index); void playItemInList(ItemModel *itemList, int index);
/** /**
* @brief skipToItemIndex Skips to an item in the current playlist * @brief skipToItemIndex Skips to an item in the current playlist
* @param index The index to skip to * @param index The index to skip to
*/ */
void skipToItemIndex(int index); void skipToItemIndex(int index);
void play() { m_mediaPlayer->play(); } void play();
void pause() { m_mediaPlayer->pause(); } void pause() { m_mediaPlayer->pause(); setPlaybackState(QMediaPlayer::PausedState); }
void seek(qint64 pos) { m_mediaPlayer->setPosition(pos); } void seek(qint64 pos);
void stop() { m_mediaPlayer->stop(); } void stop();
/** /**
* @brief previous Play the previous track in the current playlist. * @brief previous Play the previous track in the current playlist.
@ -219,6 +230,8 @@ private:
QString m_nextStreamUrl; QString m_nextStreamUrl;
QString m_playSessionId; QString m_playSessionId;
QString m_nextPlaySessionId; QString m_nextPlaySessionId;
QString m_errorString;
QMediaPlayer::Error m_error = QMediaPlayer::NoError;
/// The index of the mediastreams of the to-be-played item containing the audio /// The index of the mediastreams of the to-be-played item containing the audio
int m_audioIndex = 0; int m_audioIndex = 0;
/// The index of the mediastreams of the to-be-played item containing subtitles /// The index of the mediastreams of the to-be-played item containing subtitles
@ -236,8 +249,10 @@ private:
bool m_autoOpen = false; bool m_autoOpen = false;
QMediaPlayer::State m_oldState = QMediaPlayer::StoppedState; QMediaPlayer::State m_oldState = QMediaPlayer::StoppedState;
PlayMethod m_playMethod = PlayMethod::Transcode; /// State of the playbackManager. While the internal media player stops after a
/// song has ended, this will not do so.
QMediaPlayer::State m_playbackState = QMediaPlayer::StoppedState; QMediaPlayer::State m_playbackState = QMediaPlayer::StoppedState;
PlayMethod m_playMethod = PlayMethod::Transcode;
/// Pointer to the current media player. /// Pointer to the current media player.
QMediaPlayer *m_mediaPlayer = nullptr; QMediaPlayer *m_mediaPlayer = nullptr;

View file

@ -53,7 +53,7 @@ bool DeviceProfile::supportsMp3VideoAudio() {
} }
int DeviceProfile::maxStreamingBitrate() { int DeviceProfile::maxStreamingBitrate() {
return 5000000; return 10 * 1024 * 1024;
} }
DTO::DeviceProfile DeviceProfile::generateProfile() { DTO::DeviceProfile DeviceProfile::generateProfile() {

View file

@ -48,7 +48,7 @@ void Playlist::previous() {
m_currentItem.clear(); m_currentItem.clear();
} }
int nextItem = m_shuffler->nextItem(); int nextItem = m_shuffler->nextItem();
if (nextItem) { if (nextItem >= 0) {
m_nextItem = m_list[m_shuffler->nextItem()]; m_nextItem = m_list[m_shuffler->nextItem()];
} else { } else {
m_nextItem.clear(); m_nextItem.clear();

View file

@ -80,7 +80,7 @@ int NoShuffle::nextIndex() const {
return (m_index + 1) % m_playlist->listSize(); return (m_index + 1) % m_playlist->listSize();
} else { } else {
if (m_index + 1 >= m_playlist->listSize()) { if (m_index + 1 >= m_playlist->listSize()) {
return -1; return 0;
} else { } else {
return m_index + 1; return m_index + 1;
} }

View file

@ -23,6 +23,7 @@
#include "JellyfinQt/loader/http/mediainfo.h" #include "JellyfinQt/loader/http/mediainfo.h"
// #include "JellyfinQt/DTO/dto.h" // #include "JellyfinQt/DTO/dto.h"
#include <JellyfinQt/loader/http/userlibrary.h>
#include <JellyfinQt/dto/useritemdatadto.h> #include <JellyfinQt/dto/useritemdatadto.h>
#include <JellyfinQt/viewmodel/settings.h> #include <JellyfinQt/viewmodel/settings.h>
#include <utility> #include <utility>
@ -98,6 +99,25 @@ void PlaybackManager::setItem(QSharedPointer<Model::Item> newItem) {
if (shouldFetchStreamUrl) { if (shouldFetchStreamUrl) {
setStreamUrl(QUrl()); setStreamUrl(QUrl());
requestItemUrl(m_item); requestItemUrl(m_item);
} else {
setStreamUrl(m_nextStreamUrl);
m_mediaPlayer->play();
}
}
QMediaPlayer::Error PlaybackManager::error() const {
if (m_error != QMediaPlayer::NoError) {
return m_error;
} else {
return m_mediaPlayer->error();
}
}
QString PlaybackManager::errorString() const {
if (!m_errorString.isEmpty()) {
return m_errorString;
} else {
return m_mediaPlayer->errorString();
} }
} }
@ -138,7 +158,6 @@ void PlaybackManager::mediaPlayerStateChanged(QMediaPlayer::State newState) {
// We've stopped playing the media. Post a stop signal. // We've stopped playing the media. Post a stop signal.
m_updateTimer.stop(); m_updateTimer.stop();
postPlaybackInfo(Stopped); postPlaybackInfo(Stopped);
setPlaybackState(QMediaPlayer::StoppedState);
} else { } else {
postPlaybackInfo(Progress); postPlaybackInfo(Progress);
} }
@ -148,9 +167,9 @@ void PlaybackManager::mediaPlayerStateChanged(QMediaPlayer::State newState) {
void PlaybackManager::mediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus newStatus) { void PlaybackManager::mediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus newStatus) {
emit mediaStatusChanged(newStatus); emit mediaStatusChanged(newStatus);
if (m_playbackState == QMediaPlayer::StoppedState) return;
if (newStatus == QMediaPlayer::LoadedMedia) { if (newStatus == QMediaPlayer::LoadedMedia) {
m_mediaPlayer->play(); m_mediaPlayer->play();
setPlaybackState(playbackState());
if (m_resumePlayback) { if (m_resumePlayback) {
qDebug() << "Resuming playback by seeking to " << (m_resumePosition / MS_TICK_FACTOR); qDebug() << "Resuming playback by seeking to " << (m_resumePosition / MS_TICK_FACTOR);
m_mediaPlayer->setPosition(m_resumePosition / MS_TICK_FACTOR); m_mediaPlayer->setPosition(m_resumePosition / MS_TICK_FACTOR);
@ -158,6 +177,9 @@ void PlaybackManager::mediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus ne
} else if (newStatus == QMediaPlayer::EndOfMedia) { } else if (newStatus == QMediaPlayer::EndOfMedia) {
if (m_queue->hasNext() && m_queue->totalSize() > 1) { if (m_queue->hasNext() && m_queue->totalSize() > 1) {
next(); next();
} else {
// End of the playlist
setPlaybackState(QMediaPlayer::StoppedState);
} }
} }
} }
@ -180,11 +202,34 @@ void PlaybackManager::updatePlaybackInfo() {
} }
void PlaybackManager::playItem(Item *item) { void PlaybackManager::playItem(Item *item) {
this->playItem(item->data());
}
void PlaybackManager::playItem(QSharedPointer<Model::Item> item) {
m_queue->clearList(); m_queue->clearList();
m_queue->appendToList(item->data()); m_queue->appendToList(item);
setItem(item->data()); setItem(item);
emit hasNextChanged(m_queue->hasNext()); emit hasNextChanged(m_queue->hasNext());
emit hasPreviousChanged(m_queue->hasPrevious()); emit hasPreviousChanged(m_queue->hasPrevious());
setPlaybackState(QMediaPlayer::PlayingState);
}
void PlaybackManager::playItemId(const QString &id) {
Jellyfin::Loader::HTTP::GetItemLoader *loader = new Jellyfin::Loader::HTTP::GetItemLoader(m_apiClient);
connect(loader, &Support::LoaderBase::error, this, [loader]() {
// TODO: error handling
loader->deleteLater();
});
connect(loader, &Support::LoaderBase::ready, this, [this, loader](){
this->playItem(QSharedPointer<Model::Item>::create(loader->result()));
loader->deleteLater();
});
Jellyfin::Loader::GetItemParams params;
params.setUserId(m_apiClient->userId());
params.setItemId(id);
loader->setParameters(params);
loader->load();
} }
void PlaybackManager::playItemInList(ItemModel *playlist, int index) { void PlaybackManager::playItemInList(ItemModel *playlist, int index) {
@ -196,6 +241,7 @@ void PlaybackManager::playItemInList(ItemModel *playlist, int index) {
setItem(playlist->itemAt(index)); setItem(playlist->itemAt(index));
emit hasNextChanged(m_queue->hasNext()); emit hasNextChanged(m_queue->hasNext());
emit hasPreviousChanged(m_queue->hasPrevious()); emit hasPreviousChanged(m_queue->hasPrevious());
setPlaybackState(QMediaPlayer::PlayingState);
} }
void PlaybackManager::skipToItemIndex(int index) { void PlaybackManager::skipToItemIndex(int index) {
@ -214,19 +260,32 @@ void PlaybackManager::skipToItemIndex(int index) {
emit hasPreviousChanged(m_queue->hasPrevious()); emit hasPreviousChanged(m_queue->hasPrevious());
} }
void PlaybackManager::play() {
m_mediaPlayer->play();
if (m_queue->totalSize() != 0) {
setPlaybackState(QMediaPlayer::PlayingState);
}
}
void PlaybackManager::next() { void PlaybackManager::next() {
m_mediaPlayer->stop(); m_mediaPlayer->stop();
m_mediaPlayer->setMedia(QMediaContent()); m_mediaPlayer->setMedia(QMediaContent());
if (m_nextItem.isNull()) { if (m_nextItem.isNull() || !m_queue->nextItem()->sameAs(*m_nextItem)) {
setItem(m_queue->nextItem()); setItem(m_queue->nextItem());
m_nextStreamUrl = QString();
m_queue->next(); m_queue->next();
m_nextItem.clear(); m_nextItem.clear();
} else { } else {
m_item = m_nextItem; m_item = m_nextItem;
m_streamUrl = m_nextStreamUrl;
m_nextItem.clear();
m_nextStreamUrl = QString();
m_queue->next();
setItem(m_nextItem); setItem(m_nextItem);
} }
m_mediaPlayer->play();
emit hasNextChanged(m_queue->hasNext()); emit hasNextChanged(m_queue->hasNext());
emit hasPreviousChanged(m_queue->hasPrevious()); emit hasPreviousChanged(m_queue->hasPrevious());
} }
@ -234,17 +293,31 @@ void PlaybackManager::next() {
void PlaybackManager::previous() { void PlaybackManager::previous() {
m_mediaPlayer->stop(); m_mediaPlayer->stop();
m_mediaPlayer->setPosition(0); m_mediaPlayer->setPosition(0);
m_nextStreamUrl = m_streamUrl;
m_item.clear();
m_streamUrl = QString(); m_streamUrl = QString();
m_nextItem = m_item;
m_nextStreamUrl = m_streamUrl;
m_nextItem = m_queue->nextItem();
m_queue->previous(); m_queue->previous();
setItem(m_queue->currentItem()); setItem(m_queue->currentItem());
m_mediaPlayer->play();
emit hasNextChanged(m_queue->hasNext()); emit hasNextChanged(m_queue->hasNext());
emit hasPreviousChanged(m_queue->hasPrevious()); emit hasPreviousChanged(m_queue->hasPrevious());
} }
void PlaybackManager::stop() {
setPlaybackState(QMediaPlayer::StoppedState);
m_queue->clearList();
m_mediaPlayer->stop();
}
void PlaybackManager::seek(qint64 pos) {
m_mediaPlayer->setPosition(pos);
postPlaybackInfo(Progress);
}
void PlaybackManager::postPlaybackInfo(PlaybackInfoType type) { void PlaybackManager::postPlaybackInfo(PlaybackInfoType type) {
QJsonObject root; QJsonObject root;
@ -411,7 +484,7 @@ void PlaybackManager::handlePlaybackInfoResponse(QString itemId, QString mediaTy
} }
if (resultingUrl.isEmpty()) { if (resultingUrl.isEmpty()) {
qWarning() << "Could not find suitable media source for item " << itemId; qWarning() << "Could not find suitable media source for item " << itemId;
onItemErrorReceived(itemId, tr("Cannot fetch stream URL")); onItemErrorReceived(itemId, tr("Could not find a suitable media source."));
} else { } else {
emit playMethodChanged(playMethod); emit playMethodChanged(playMethod);
onItemUrlReceived(itemId, resultingUrl, playSession, playMethod); onItemUrlReceived(itemId, resultingUrl, playSession, playMethod);
@ -428,6 +501,18 @@ void PlaybackManager::onItemUrlReceived(const QString &itemId, const QUrl &url,
m_playMethod = playMethod; m_playMethod = playMethod;
setStreamUrl(url); setStreamUrl(url);
emit playMethodChanged(m_playMethod); emit playMethodChanged(m_playMethod);
// Clear the error string if it is currently set
if (!m_errorString.isEmpty()) {
m_errorString.clear();
emit errorStringChanged(m_errorString);
}
if (m_error != QMediaPlayer::NoError) {
m_error = QMediaPlayer::NoError;
emit errorChanged(error());
}
m_mediaPlayer->setMedia(QMediaContent(url)); m_mediaPlayer->setMedia(QMediaContent(url));
m_mediaPlayer->play(); m_mediaPlayer->play();
} else { } else {
@ -441,6 +526,13 @@ void PlaybackManager::onItemErrorReceived(const QString &itemId, const QString &
Q_UNUSED(itemId) Q_UNUSED(itemId)
Q_UNUSED(errorString) Q_UNUSED(errorString)
qWarning() << "Error while fetching streaming url for " << itemId << ": " << errorString; qWarning() << "Error while fetching streaming url for " << itemId << ": " << errorString;
if (!m_item.isNull() && m_item->jellyfinId() == itemId) {
setStreamUrl(QUrl());
m_error = QMediaPlayer::ResourceError;
emit errorChanged(error());
m_errorString = errorString;
emit errorStringChanged(errorString);
}
} }
} // NS ViewModel } // NS ViewModel