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:
parent
caf72af999
commit
7c21eb425d
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue