harbour-sailfin/core/src/viewmodel/playbackmanager.cpp

363 lines
11 KiB
C++

/*
* Sailfin: a Jellyfin client written using Qt
* Copyright (C) 2021-2022 Chris Josten and the Sailfin Contributors.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "JellyfinQt/viewmodel/playbackmanager.h"
#include "JellyfinQt/apimodel.h"
#include <JellyfinQt/dto/playstatecommand.h>
#include <JellyfinQt/dto/playstaterequest.h>
// #include "JellyfinQt/DTO/dto.h"
#include <JellyfinQt/dto/useritemdatadto.h>
#include <JellyfinQt/model/playbackmanager.h>
#include <JellyfinQt/viewmodel/settings.h>
#include <utility>
namespace Jellyfin {
class ItemModel;
namespace DTO {
using UserData = UserItemDataDto;
}
namespace ViewModel {
Q_LOGGING_CATEGORY(playbackManager, "jellyfin.viewmodel.playbackmanager")
class PlaybackManagerPrivate {
Q_DECLARE_PUBLIC(PlaybackManager);
public:
explicit PlaybackManagerPrivate(PlaybackManager *q);
PlaybackManager *q_ptr = nullptr;
ApiClient *m_apiClient = nullptr;
Model::PlaybackManager *m_impl = nullptr;
/// The currently played item that will be shown in the GUI
ViewModel::Item *m_displayItem = nullptr;
/// The currently played queue that will be shown in the GUI
ViewModel::Playlist *m_displayQueue = nullptr;
bool m_handlePlaystateCommands;
};
PlaybackManagerPrivate::PlaybackManagerPrivate(PlaybackManager *q)
: q_ptr(q),
m_impl(new Model::LocalPlaybackManager(q)),
m_displayItem(new ViewModel::Item(q)),
m_displayQueue(new ViewModel::Playlist(m_impl->queue())) {
}
// PlaybackManager
PlaybackManager::PlaybackManager(QObject *parent)
: QObject(parent) {
QScopedPointer<PlaybackManagerPrivate> foo(new PlaybackManagerPrivate(this));
d_ptr.swap(foo);
Q_D(PlaybackManager);
// Set up connections.
connect(d->m_impl, &Model::PlaybackManager::positionChanged, this, &PlaybackManager::positionChanged);
connect(d->m_impl, &Model::PlaybackManager::durationChanged, this, &PlaybackManager::durationChanged);
connect(d->m_impl, &Model::PlaybackManager::hasNextChanged, this, &PlaybackManager::hasNextChanged);
connect(d->m_impl, &Model::PlaybackManager::hasPreviousChanged, this, &PlaybackManager::hasPreviousChanged);
connect(d->m_impl, &Model::PlaybackManager::seekableChanged, this, &PlaybackManager::seekableChanged);
connect(d->m_impl, &Model::PlaybackManager::queueIndexChanged, this, &PlaybackManager::queueIndexChanged);
connect(d->m_impl, &Model::PlaybackManager::itemChanged, this, &PlaybackManager::mediaPlayerItemChanged);
connect(d->m_impl, &Model::PlaybackManager::playbackStateChanged, this, &PlaybackManager::playbackStateChanged);
if (auto localImp = qobject_cast<Model::LocalPlaybackManager*>(d->m_impl)) {
connect(localImp, &Model::LocalPlaybackManager::streamUrlChanged, this, [this](const QUrl& newUrl){
this->streamUrlChanged(newUrl.toString());
});
connect(localImp, &Model::LocalPlaybackManager::playMethodChanged, this, &PlaybackManager::playMethodChanged);
}
connect(d->m_impl, &Model::PlaybackManager::mediaStatusChanged, this, &PlaybackManager::mediaStatusChanged);
}
PlaybackManager::~PlaybackManager() {
}
void PlaybackManager::setApiClient(ApiClient *apiClient) {
Q_D(PlaybackManager);
if (d->m_apiClient != nullptr) {
disconnect(d->m_apiClient->eventbus(), &EventBus::playstateCommandReceived, this, &PlaybackManager::handlePlaystateRequest);
}
if (!d->m_displayItem->data().isNull()) {
d->m_displayItem->data()->setApiClient(apiClient);
}
d->m_apiClient = apiClient;
d->m_impl->setApiClient(apiClient);
if (d->m_apiClient != nullptr) {
connect(d->m_apiClient->eventbus(), &EventBus::playstateCommandReceived, this, &PlaybackManager::handlePlaystateRequest);
}
}
bool PlaybackManager::resumePlayback() const {
const Q_D(PlaybackManager);
return d->m_impl->resumePlayback();
}
void PlaybackManager::setResumePlayback(bool newResumePlayback) {
Q_D(PlaybackManager);
return d->m_impl->setResumePlayback(newResumePlayback);
}
int PlaybackManager::audioIndex() const {
const Q_D(PlaybackManager);
return d->m_impl->audioIndex();
}
void PlaybackManager::setAudioIndex(int newAudioIndex){
Q_D(PlaybackManager);
d->m_impl->setAudioIndex(newAudioIndex);
}
int PlaybackManager::subtitleIndex() const {
const Q_D(PlaybackManager);
return d->m_impl->subtitleIndex();
}
void PlaybackManager::setSubtitleIndex(int newSubtitleIndex){
Q_D(PlaybackManager);
d->m_impl->setSubtitleIndex(newSubtitleIndex);
}
ViewModel::Item *PlaybackManager::item() const {
const Q_D(PlaybackManager);
return d->m_displayItem;
}
QSharedPointer<Model::Item> PlaybackManager::dataItem() const {
const Q_D(PlaybackManager);
return d->m_displayItem->data();
}
ApiClient * PlaybackManager::apiClient() const {
const Q_D(PlaybackManager);
return d->m_apiClient;
}
QString PlaybackManager::streamUrl() const {
const Q_D(PlaybackManager);
if (Model::LocalPlaybackManager *lpm = qobject_cast<Model::LocalPlaybackManager *>(d->m_impl)) {
return lpm->streamUrl().toString();
} else {
return QStringLiteral("<not playing back locally>");
}
}
PlayMethod PlaybackManager::playMethod() const {
const Q_D(PlaybackManager);
if (Model::LocalPlaybackManager *lpm = qobject_cast<Model::LocalPlaybackManager *>(d->m_impl)) {
return lpm->playMethod();
} else {
return PlayMethod::EnumNotSet;
}
}
Model::MediaStatus PlaybackManager::mediaStatus() const {
const Q_D(PlaybackManager);
return d->m_impl->mediaStatus();
}
qint64 PlaybackManager::position() const {
const Q_D(PlaybackManager);
return d->m_impl->position();
}
qint64 PlaybackManager::duration() const {
const Q_D(PlaybackManager);
return d->m_impl->duration();
}
ViewModel::Playlist *PlaybackManager::queue() const {
const Q_D(PlaybackManager);
return d->m_displayQueue;
}
int PlaybackManager::queueIndex() const {
const Q_D(PlaybackManager);
return d->m_impl->queueIndex();
}
bool PlaybackManager::hasNext() const {
const Q_D(PlaybackManager);
return d->m_impl->hasNext();
}
bool PlaybackManager::hasPrevious() const {
const Q_D(PlaybackManager);
return d->m_impl->hasPrevious();
}
QObject* PlaybackManager::mediaObject() const {
const Q_D(PlaybackManager);
if (auto localPb = qobject_cast<Model::LocalPlaybackManager*>(d->m_impl)) {
return localPb->player()->videoOutputSource();
} else {
return nullptr;
}
}
Model::PlayerState PlaybackManager::playbackState() const {
const Q_D(PlaybackManager);
return d->m_impl->playbackState();
}
bool PlaybackManager::hasVideo() const {
const Q_D(PlaybackManager);
return d->m_impl->hasVideo();
}
bool PlaybackManager::seekable() const {
const Q_D(PlaybackManager);
return d->m_impl->seekable();
}
bool PlaybackManager::handlePlaystateCommands() const {
const Q_D(PlaybackManager);
return d->m_handlePlaystateCommands;
}
void PlaybackManager::setHandlePlaystateCommands(bool newHandlePlaystateCommands) {
Q_D(PlaybackManager);
d->m_handlePlaystateCommands = newHandlePlaystateCommands;
emit handlePlaystateCommandsChanged(newHandlePlaystateCommands);
}
QMediaPlayer::Error PlaybackManager::error() const {
return QMediaPlayer::NoError;
}
QString PlaybackManager::errorString() const {
const Q_D(PlaybackManager);
return d->m_impl->errorString();
}
void PlaybackManager::mediaPlayerItemChanged() {
Q_D(PlaybackManager);
d->m_displayItem->setData(d->m_impl->currentItem());
emit itemChanged();
}
void PlaybackManager::playItem(Item *item) {
playItem(item->data());
}
void PlaybackManager::playItem(QSharedPointer<Model::Item> item) {
Q_D(PlaybackManager);
d->m_impl->playItem(item);
}
void PlaybackManager::playItemId(const QString &id) {
Q_D(PlaybackManager);
d->m_impl->playItemId(id);
}
void PlaybackManager::playItemInList(ItemModel *playlist, int index) {
Q_D(PlaybackManager);
d->m_impl->playItemInList(playlist->toList(), index);
}
void PlaybackManager::skipToItemIndex(int index) {
Q_D(PlaybackManager);
d->m_impl->goTo(index);
}
void PlaybackManager::play() {
Q_D(PlaybackManager);
d->m_impl->play();
}
void PlaybackManager::pause() {
Q_D(PlaybackManager);
return d->m_impl->pause();
}
void PlaybackManager::seek(qint64 pos) {
Q_D(PlaybackManager);
d->m_impl->seek(pos);
emit seeked(pos);
}
void PlaybackManager::stop() {
Q_D(PlaybackManager);
d->m_impl->stop();
}
void PlaybackManager::next() {
Q_D(PlaybackManager);
d->m_impl->next();
}
void PlaybackManager::previous() {
Q_D(PlaybackManager);
d->m_impl->previous();
}
void PlaybackManager::handlePlaystateRequest(const DTO::PlaystateRequest &request) {
//if (!m_handlePlaystateCommands) return;
switch(request.command()) {
case DTO::PlaystateCommand::Pause:
pause();
break;
case DTO::PlaystateCommand::PlayPause:
if (playbackState() != Model::PlayerState::Playing) {
play();
} else {
pause();
}
break;
case DTO::PlaystateCommand::Unpause:
play();
break;
case DTO::PlaystateCommand::Stop:
stop();
break;
case DTO::PlaystateCommand::NextTrack:
next();
break;
case DTO::PlaystateCommand::PreviousTrack:
previous();
break;
case DTO::PlaystateCommand::Seek:
if (request.seekPositionTicksNull()) {
qCWarning(playbackManager) << "Received seek command without position argument";
} else {
seek(request.seekPositionTicks().value() / MS_TICK_FACTOR);
}
break;
default:
qCDebug(playbackManager) << "Unhandled PlaystateCommand: " << request.command();
break;
}
}
void PlaybackManager::componentComplete() {
Q_D(PlaybackManager);
if (d->m_apiClient == nullptr) qCWarning(playbackManager) << "No ApiClient set for PlaybackManager";
m_qmlIsParsingComponent = false;
}
} // NS ViewModel
} // NS Jellyfin