/* * 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 #include // #include "JellyfinQt/DTO/dto.h" #include #include #include #include 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 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(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 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(d->m_impl)) { return lpm->streamUrl().toString(); } else { return QStringLiteral(""); } } PlayMethod PlaybackManager::playMethod() const { const Q_D(PlaybackManager); if (Model::LocalPlaybackManager *lpm = qobject_cast(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(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 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