mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2025-09-06 02:32:44 +00:00
Discover remote sessions
Adds a way of discovering remote sessions and in Jellyfin the UI.
This commit is contained in:
parent
b1bd15f2c1
commit
b257fe60aa
20 changed files with 1051 additions and 80 deletions
|
@ -44,6 +44,7 @@ public:
|
|||
// Authentication-related variables
|
||||
QString token;
|
||||
QString baseUrl;
|
||||
QString appName;
|
||||
QString deviceName;
|
||||
QString deviceId;
|
||||
QString userId;
|
||||
|
@ -103,6 +104,21 @@ void ApiClient::setBaseUrl(const QString &url) {
|
|||
emit this->baseUrlChanged(d->baseUrl);
|
||||
}
|
||||
|
||||
const QString &ApiClient::appName() const {
|
||||
const Q_D(ApiClient);
|
||||
return d->appName;
|
||||
}
|
||||
|
||||
void ApiClient::setAppName(const QString &name) {
|
||||
Q_D(ApiClient);
|
||||
d->appName = name;
|
||||
emit appNameChanged(name);
|
||||
|
||||
if (!d->componentBeingParsed) {
|
||||
generateDeviceProfile();
|
||||
}
|
||||
}
|
||||
|
||||
const QString &ApiClient::userId() const {
|
||||
Q_D(const ApiClient);
|
||||
return d->userId;
|
||||
|
@ -220,7 +236,7 @@ void ApiClient::addBaseRequestHeaders(QNetworkRequest &request, const QString &p
|
|||
void ApiClient::addTokenHeader(QNetworkRequest &request) const {
|
||||
Q_D(const ApiClient);
|
||||
QString authentication = "MediaBrowser ";
|
||||
authentication += "Client=\"Sailfin\"";
|
||||
authentication += "Client=\"" +d->appName +"\"";
|
||||
authentication += ", Device=\"" + d->deviceName + "\"";
|
||||
authentication += ", DeviceId=\"" + d->deviceId + "\"";
|
||||
authentication += ", Version=\"" + version() + "\"";
|
||||
|
@ -425,7 +441,6 @@ QString ApiClient::downloadUrl(const QString &itemId) const {
|
|||
void ApiClient::generateDeviceProfile() {
|
||||
Q_D(ApiClient);
|
||||
QSharedPointer<DTO::DeviceProfile> deviceProfile = QSharedPointer<DTO::DeviceProfile>::create(Model::DeviceProfile::generateProfile());
|
||||
deviceProfile->setName(d->deviceName);
|
||||
deviceProfile->setJellyfinId(d->deviceId);
|
||||
deviceProfile->setFriendlyName(QSysInfo::prettyProductName());
|
||||
deviceProfile->setMaxStreamingBitrate(d->settings->maxStreamingBitRate());
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "JellyfinQt/eventbus.h"
|
||||
#include "JellyfinQt/serverdiscoverymodel.h"
|
||||
#include "JellyfinQt/websocket.h"
|
||||
#include "JellyfinQt/model/controllablesession.h"
|
||||
#include "JellyfinQt/model/player.h"
|
||||
#include "JellyfinQt/viewmodel/item.h"
|
||||
#include "JellyfinQt/viewmodel/itemmodel.h"
|
||||
|
@ -39,6 +40,7 @@
|
|||
#include "JellyfinQt/viewmodel/platformmediacontrol.h"
|
||||
#include "JellyfinQt/viewmodel/playbackmanager.h"
|
||||
#include "JellyfinQt/viewmodel/playlist.h"
|
||||
#include "JellyfinQt/viewmodel/remotedevice.h"
|
||||
#include "JellyfinQt/viewmodel/settings.h"
|
||||
#include "JellyfinQt/viewmodel/userdata.h"
|
||||
#include "JellyfinQt/viewmodel/usermodel.h"
|
||||
|
@ -66,6 +68,7 @@ void JellyfinPlugin::registerTypes(const char *uri) {
|
|||
qmlRegisterType<ViewModel::ItemModel>(uri, 1, 0, "ItemModel");
|
||||
qmlRegisterType<ViewModel::UserModel>(uri, 1, 0, "UserModel");
|
||||
qmlRegisterUncreatableType<ViewModel::Playlist>(uri, 1, 0, "Playlist", "Available via PlaybackManager");
|
||||
qmlRegisterType<ViewModel::RemoteDeviceList>(uri, 1, 0, "RemoteDeviceList");
|
||||
|
||||
// Loaders
|
||||
qmlRegisterUncreatableType<ViewModel::LoaderBase>(uri, 1, 0, "LoaderBase", "Use one of its subclasses");
|
||||
|
@ -90,6 +93,7 @@ void JellyfinPlugin::registerTypes(const char *uri) {
|
|||
qmlRegisterUncreatableType<Jellyfin::ViewModel::NowPlayingSection>(uri, 1, 0, "NowPlayingSection", "Is an enum");
|
||||
qmlRegisterUncreatableType<Jellyfin::Model::PlayerStateClass>(uri, 1, 0, "PlayerState", "Is an enum");
|
||||
qmlRegisterUncreatableType<Jellyfin::Model::MediaStatusClass>(uri, 1, 0, "MediaStatus", "Is an enum");
|
||||
qmlRegisterUncreatableType<Jellyfin::Model::DeviceTypeClass>(uri, 1, 0, "DeviceType", "Is an enum");
|
||||
|
||||
qRegisterMetaType<Jellyfin::DTO::PlayMethodClass::Value>();
|
||||
}
|
||||
|
|
132
core/src/model/controllablesession.cpp
Normal file
132
core/src/model/controllablesession.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include "JellyfinQt/model/controllablesession.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "JellyfinQt/loader/http/session.h"
|
||||
#include "JellyfinQt/loader/requesttypes.h"
|
||||
#include <JellyfinQt/model/playbackmanager.h>
|
||||
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace Model {
|
||||
|
||||
ControllableSession::ControllableSession(QObject *parent)
|
||||
: QObject(parent) {}
|
||||
|
||||
// LocalSession
|
||||
LocalSession::LocalSession(ApiClient &apiClient, QObject *parent)
|
||||
: ControllableSession(parent), m_apiClient(apiClient) {}
|
||||
|
||||
QString LocalSession::id() const {
|
||||
return m_apiClient.deviceId();
|
||||
}
|
||||
|
||||
QString LocalSession::appName() const {
|
||||
return m_apiClient.appName();
|
||||
}
|
||||
|
||||
QString LocalSession::name() const {
|
||||
//: Shown in a list of devices to indicate that media should be played on this device
|
||||
return tr("This device");
|
||||
}
|
||||
|
||||
DeviceType LocalSession::deviceType() const {
|
||||
return DeviceType::Unknown;
|
||||
}
|
||||
|
||||
QString LocalSession::userName() const {
|
||||
return m_apiClient.userId();
|
||||
}
|
||||
|
||||
PlaybackManager *LocalSession::createPlaybackManager() const {
|
||||
return new LocalPlaybackManager();
|
||||
}
|
||||
|
||||
// ControllableJellyfinSession
|
||||
ControllableJellyfinSession::ControllableJellyfinSession(const QSharedPointer<DTO::SessionInfo> info, QObject *parent)
|
||||
: ControllableSession(parent),
|
||||
m_data(info) {}
|
||||
|
||||
QString ControllableJellyfinSession::id() const {
|
||||
return m_data->jellyfinId();
|
||||
}
|
||||
|
||||
QString ControllableJellyfinSession::appName() const {
|
||||
return m_data->client();
|
||||
}
|
||||
|
||||
QString ControllableJellyfinSession::name() const {
|
||||
return m_data->deviceName();
|
||||
}
|
||||
|
||||
DeviceType ControllableJellyfinSession::deviceType() const {
|
||||
return DeviceType::Unknown;
|
||||
}
|
||||
|
||||
QString ControllableJellyfinSession::userName() const {
|
||||
return m_data->userName();
|
||||
}
|
||||
|
||||
PlaybackManager * ControllableJellyfinSession::createPlaybackManager() const {
|
||||
// TODO: implement
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RemoteSessionScanner::RemoteSessionScanner(QObject *parent)
|
||||
: QObject(parent) {}
|
||||
|
||||
using GetSessionsLoader = Loader::HTTP::GetSessionsLoader;
|
||||
class RemoteJellyfinSessionScannerPrivate {
|
||||
public:
|
||||
RemoteJellyfinSessionScannerPrivate(ApiClient *apiClient)
|
||||
: apiClient(apiClient) {
|
||||
};
|
||||
|
||||
ApiClient *apiClient;
|
||||
GetSessionsLoader *loader = nullptr;
|
||||
};
|
||||
|
||||
|
||||
RemoteJellyfinSessionScanner::RemoteJellyfinSessionScanner(ApiClient *apiClient, QObject *parent)
|
||||
: RemoteSessionScanner(parent),
|
||||
d_ptr(new RemoteJellyfinSessionScannerPrivate(apiClient)) {
|
||||
}
|
||||
|
||||
RemoteJellyfinSessionScanner::~RemoteJellyfinSessionScanner() {}
|
||||
|
||||
void RemoteJellyfinSessionScanner::startScanning() {
|
||||
Q_D(RemoteJellyfinSessionScanner);
|
||||
if (d->loader != nullptr) return;
|
||||
|
||||
emit resetSessions();
|
||||
emit sessionFound(new LocalSession(*d->apiClient));
|
||||
|
||||
Loader::GetSessionsParams params;
|
||||
params.setControllableByUserId(d->apiClient->userId());
|
||||
d->loader = new GetSessionsLoader(d->apiClient);
|
||||
d->loader->setParameters(params);
|
||||
connect(d->loader, &Loader::HTTP::GetSessionsLoader::ready, this, [this, d]() {
|
||||
if (d->loader == nullptr) return;
|
||||
QList<DTO::SessionInfo> sessions = d->loader->result();
|
||||
|
||||
for(auto it = sessions.begin(); it != sessions.end(); it++) {
|
||||
|
||||
// Skip this device
|
||||
if (it->jellyfinId() == d->apiClient->deviceId()) continue;
|
||||
|
||||
emit sessionFound(new ControllableJellyfinSession(QSharedPointer<DTO::SessionInfo>::create(*it)));
|
||||
}
|
||||
});
|
||||
d->loader->load();
|
||||
}
|
||||
|
||||
void RemoteJellyfinSessionScanner::stopScanning() {
|
||||
Q_D(RemoteJellyfinSessionScanner);
|
||||
if (d->loader != nullptr) {
|
||||
d->loader->deleteLater();
|
||||
d->loader = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // NS Model
|
||||
} // NS Jellyfin
|
|
@ -89,7 +89,7 @@ QtMultimediaPlayerPrivate::QtMultimediaPlayerPrivate(QtMultimediaPlayer *q)
|
|||
q->connect(m_mediaPlayer, &QMediaPlayer::seekableChanged, q, &QtMultimediaPlayer::seekableChanged);
|
||||
q->connect(m_mediaPlayer, &QMediaPlayer::audioAvailableChanged, q, &QtMultimediaPlayer::hasAudioChanged);
|
||||
q->connect(m_mediaPlayer, &QMediaPlayer::videoAvailableChanged, q, &QtMultimediaPlayer::hasVideoChanged);
|
||||
q->connect(m_mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), q, SLOT(errorStringChanged));
|
||||
//q->connect(m_mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), q, SLOT(errorStringChanged(QString)));
|
||||
if (m_mediaStreamsControl != nullptr) {
|
||||
q->connect(m_mediaStreamsControl, &QMediaStreamsControl::streamsChanged, q, [this](){
|
||||
qCDebug(player) << m_mediaStreamsControl->streamCount() << " streams in the medi source";
|
||||
|
|
132
core/src/model/remotejellyfinplayback.cpp
Normal file
132
core/src/model/remotejellyfinplayback.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Sailfin: a Jellyfin client written using Qt
|
||||
* Copyright (C) 2023 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/model/remotejellyfinplayback.h>
|
||||
|
||||
#include <JellyfinQt/apiclient.h>
|
||||
#include <JellyfinQt/loader/http/session.h>
|
||||
#include <JellyfinQt/loader/requesttypes.h>
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace Model {
|
||||
|
||||
RemoteJellyfinPlayback::RemoteJellyfinPlayback(ApiClient &apiClient, QObject *parent)
|
||||
: PlaybackManager(parent), m_apiClient(apiClient) {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::swap(PlaybackManager &other) {
|
||||
|
||||
}
|
||||
|
||||
PlayerState RemoteJellyfinPlayback::playbackState() const {
|
||||
|
||||
}
|
||||
|
||||
MediaStatus RemoteJellyfinPlayback::mediaStatus() const {
|
||||
|
||||
}
|
||||
|
||||
bool RemoteJellyfinPlayback::hasNext() const {
|
||||
|
||||
}
|
||||
|
||||
bool RemoteJellyfinPlayback::hasPrevious() const {
|
||||
|
||||
}
|
||||
|
||||
PlaybackManagerError RemoteJellyfinPlayback::error() const {
|
||||
|
||||
}
|
||||
|
||||
const QString &RemoteJellyfinPlayback::errorString() const {
|
||||
|
||||
}
|
||||
|
||||
qint64 RemoteJellyfinPlayback::position() const {
|
||||
|
||||
}
|
||||
|
||||
qint64 RemoteJellyfinPlayback::duration() const {
|
||||
|
||||
}
|
||||
|
||||
bool RemoteJellyfinPlayback::seekable() const {
|
||||
|
||||
}
|
||||
|
||||
bool RemoteJellyfinPlayback::hasAudio() const {
|
||||
|
||||
}
|
||||
|
||||
bool RemoteJellyfinPlayback::hasVideo() const {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::playItem(QSharedPointer<Item> item) {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::playItemInList(const QList<QSharedPointer<Item> > &items, int index) {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::pause() {
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::play() {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::playItemId(const QString &id) {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::previous() {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::next() {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::goTo(int index) {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::stop() {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::seek(qint64 pos) {
|
||||
|
||||
}
|
||||
|
||||
void RemoteJellyfinPlayback::sendGeneralCommand(DTO::GeneralCommandType command, QJsonObject arguments) {
|
||||
Loader::SendFullGeneralCommandParams params;
|
||||
QSharedPointer<DTO::GeneralCommand> fullCommand = QSharedPointer<DTO::GeneralCommand>::create(command, m_apiClient.userId());
|
||||
fullCommand->setArguments(arguments);
|
||||
// FIXME: send command
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // NS Model
|
||||
} // NS Jellyfin
|
||||
|
|
@ -23,10 +23,13 @@
|
|||
#include <JellyfinQt/dto/playstatecommand.h>
|
||||
#include <JellyfinQt/dto/playstaterequest.h>
|
||||
|
||||
// #include "JellyfinQt/DTO/dto.h"
|
||||
#include <JellyfinQt/dto/useritemdatadto.h>
|
||||
#include <JellyfinQt/model/controllablesession.h>
|
||||
#include <JellyfinQt/model/playbackmanager.h>
|
||||
#include <JellyfinQt/viewmodel/settings.h>
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Jellyfin {
|
||||
|
@ -47,7 +50,8 @@ public:
|
|||
PlaybackManager *q_ptr = nullptr;
|
||||
|
||||
ApiClient *m_apiClient = nullptr;
|
||||
Model::PlaybackManager *m_impl = nullptr;
|
||||
QSharedPointer<Model::ControllableSession> m_session;
|
||||
QScopedPointer<Model::PlaybackManager> m_impl;
|
||||
|
||||
/// The currently played item that will be shown in the GUI
|
||||
ViewModel::Item *m_displayItem = nullptr;
|
||||
|
@ -59,6 +63,7 @@ public:
|
|||
|
||||
PlaybackManagerPrivate::PlaybackManagerPrivate(PlaybackManager *q)
|
||||
: q_ptr(q),
|
||||
m_session(nullptr),
|
||||
m_impl(new Model::LocalPlaybackManager(q)),
|
||||
m_displayItem(new ViewModel::Item(q)),
|
||||
m_displayQueue(new ViewModel::Playlist(m_impl->queue())) {
|
||||
|
@ -73,21 +78,22 @@ PlaybackManager::PlaybackManager(QObject *parent)
|
|||
|
||||
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(d->m_impl.data(), &Model::PlaybackManager::positionChanged, this, &PlaybackManager::positionChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::durationChanged, this, &PlaybackManager::durationChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::hasNextChanged, this, &PlaybackManager::hasNextChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::hasPreviousChanged, this, &PlaybackManager::hasPreviousChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::seekableChanged, this, &PlaybackManager::seekableChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::queueIndexChanged, this, &PlaybackManager::queueIndexChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::itemChanged, this, &PlaybackManager::mediaPlayerItemChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::playbackStateChanged, this, &PlaybackManager::playbackStateChanged);
|
||||
|
||||
if (auto localImp = qobject_cast<Model::LocalPlaybackManager*>(d->m_impl.data())) {
|
||||
connect(localImp, &Model::LocalPlaybackManager::streamUrlChanged, this, [this](const QUrl& newUrl){
|
||||
this->streamUrlChanged(newUrl.toString());
|
||||
emit this->streamUrlChanged(newUrl.toString());
|
||||
});
|
||||
connect(localImp, &Model::LocalPlaybackManager::playMethodChanged, this, &PlaybackManager::playMethodChanged);
|
||||
}
|
||||
connect(d->m_impl, &Model::PlaybackManager::mediaStatusChanged, this, &PlaybackManager::mediaStatusChanged);
|
||||
connect(d->m_impl.data(), &Model::PlaybackManager::mediaStatusChanged, this, &PlaybackManager::mediaStatusChanged);
|
||||
}
|
||||
|
||||
PlaybackManager::~PlaybackManager() {
|
||||
|
@ -107,6 +113,10 @@ void PlaybackManager::setApiClient(ApiClient *apiClient) {
|
|||
d->m_impl->setApiClient(apiClient);
|
||||
|
||||
if (d->m_apiClient != nullptr) {
|
||||
// Set the session to a new LocalSession in case it hasn't been set yet.
|
||||
if (d->m_session.isNull()) {
|
||||
setControllingSession(QSharedPointer<Model::LocalSession>::create(*apiClient, this));
|
||||
}
|
||||
connect(d->m_apiClient->eventbus(), &EventBus::playstateCommandReceived, this, &PlaybackManager::handlePlaystateRequest);
|
||||
}
|
||||
}
|
||||
|
@ -155,9 +165,42 @@ ApiClient * PlaybackManager::apiClient() const {
|
|||
return d->m_apiClient;
|
||||
}
|
||||
|
||||
QSharedPointer<Model::ControllableSession> PlaybackManager::controllingSession() const {
|
||||
const Q_D(PlaybackManager);
|
||||
return d->m_session;
|
||||
}
|
||||
|
||||
void PlaybackManager::setControllingSession(QSharedPointer<Model::ControllableSession> session) {
|
||||
Q_D(PlaybackManager);
|
||||
|
||||
qCDebug(playbackManager()) << "Now controlling session " << session->name();
|
||||
session->setParent(this);
|
||||
d->m_session.swap(session);
|
||||
// TODO: swap out playback manager
|
||||
emit controllingSessionChanged();
|
||||
emit controllingSessionIdChanged();
|
||||
emit controllingSessionNameChanged();
|
||||
emit controllingSessionLocalChanged();
|
||||
}
|
||||
|
||||
QString PlaybackManager::controllingSessionId() const {
|
||||
const Q_D(PlaybackManager);
|
||||
return d->m_session->id();
|
||||
}
|
||||
|
||||
QString PlaybackManager::controllingSessionName() const {
|
||||
const Q_D(PlaybackManager);
|
||||
return d->m_session->name();
|
||||
}
|
||||
|
||||
bool PlaybackManager::controllingSessionLocal() const {
|
||||
const Q_D(PlaybackManager);
|
||||
return qobject_cast<Model::LocalPlaybackManager *>(d->m_impl.data()) != nullptr;
|
||||
}
|
||||
|
||||
QString PlaybackManager::streamUrl() const {
|
||||
const Q_D(PlaybackManager);
|
||||
if (Model::LocalPlaybackManager *lpm = qobject_cast<Model::LocalPlaybackManager *>(d->m_impl)) {
|
||||
if (Model::LocalPlaybackManager *lpm = qobject_cast<Model::LocalPlaybackManager *>(d->m_impl.data())) {
|
||||
return lpm->streamUrl().toString();
|
||||
} else {
|
||||
return QStringLiteral("<not playing back locally>");
|
||||
|
@ -166,7 +209,7 @@ QString PlaybackManager::streamUrl() const {
|
|||
|
||||
PlayMethod PlaybackManager::playMethod() const {
|
||||
const Q_D(PlaybackManager);
|
||||
if (Model::LocalPlaybackManager *lpm = qobject_cast<Model::LocalPlaybackManager *>(d->m_impl)) {
|
||||
if (Model::LocalPlaybackManager *lpm = qobject_cast<Model::LocalPlaybackManager *>(d->m_impl.data())) {
|
||||
return lpm->playMethod();
|
||||
} else {
|
||||
return PlayMethod::EnumNotSet;
|
||||
|
@ -210,7 +253,7 @@ bool PlaybackManager::hasPrevious() const {
|
|||
|
||||
QObject* PlaybackManager::mediaObject() const {
|
||||
const Q_D(PlaybackManager);
|
||||
if (auto localPb = qobject_cast<Model::LocalPlaybackManager*>(d->m_impl)) {
|
||||
if (auto localPb = qobject_cast<Model::LocalPlaybackManager*>(d->m_impl.data())) {
|
||||
return localPb->player()->videoOutputSource();
|
||||
} else {
|
||||
return nullptr;
|
||||
|
|
152
core/src/viewmodel/remotedevice.cpp
Normal file
152
core/src/viewmodel/remotedevice.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Sailfin: a Jellyfin client written using Qt
|
||||
* Copyright (C) 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/remotedevice.h>
|
||||
|
||||
#include <JellyfinQt/model/controllablesession.h>
|
||||
#include <JellyfinQt/viewmodel/playbackmanager.h>
|
||||
|
||||
namespace Jellyfin {
|
||||
namespace ViewModel {
|
||||
|
||||
RemoteDeviceList::RemoteDeviceList(QObject *parent)
|
||||
: QAbstractListModel(parent) {}
|
||||
|
||||
void RemoteDeviceList::classBegin() {}
|
||||
void RemoteDeviceList::componentComplete() {
|
||||
m_componentComplete = true;
|
||||
if (m_apiClient != nullptr) {
|
||||
setApiClient(m_apiClient);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteDeviceList::setApiClient(ApiClient *apiClient) {
|
||||
if (m_apiClient != nullptr) {
|
||||
for (auto it = m_scanners.begin(); it != m_scanners.end(); it++) {
|
||||
disconnect(*it, &Model::RemoteSessionScanner::sessionFound, this, &RemoteDeviceList::onSessionFound);
|
||||
disconnect(*it, &Model::RemoteSessionScanner::sessionLost, this, &RemoteDeviceList::onSessionLost);
|
||||
disconnect(*it, &Model::RemoteSessionScanner::resetSessions, this, &RemoteDeviceList::onSessionsReset);
|
||||
}
|
||||
for (auto it = m_sessions.begin(); it != m_sessions.end(); it++) {
|
||||
it->first->stopScanning();
|
||||
it->first->deleteLater();
|
||||
it->second->deleteLater();
|
||||
}
|
||||
m_scanners.clear();
|
||||
m_sessions.clear();
|
||||
}
|
||||
m_apiClient = apiClient;
|
||||
emit apiClientChanged();
|
||||
if (!m_componentComplete) return;
|
||||
|
||||
m_scanners.append(new Model::RemoteJellyfinSessionScanner(m_apiClient, this));
|
||||
for (auto it = m_scanners.begin(); it != m_scanners.end(); it++) {
|
||||
connect(*it, &Model::RemoteSessionScanner::sessionFound, this, &RemoteDeviceList::onSessionFound);
|
||||
connect(*it, &Model::RemoteSessionScanner::sessionLost, this, &RemoteDeviceList::onSessionLost);
|
||||
connect(*it, &Model::RemoteSessionScanner::resetSessions, this, &RemoteDeviceList::onSessionsReset);
|
||||
}
|
||||
}
|
||||
|
||||
int RemoteDeviceList::rowCount(const QModelIndex &parent) const {
|
||||
return m_sessions.size();
|
||||
}
|
||||
|
||||
QVariant RemoteDeviceList::data(const QModelIndex &index, int role) const {
|
||||
int row = index.row();
|
||||
if (!index.isValid() || row < 0 || row > rowCount()) return QVariant();
|
||||
const QSharedPointer<Model::ControllableSession> session = m_sessions.at(row).second;
|
||||
if (session.isNull()) return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case RoleNames::jellyfinId:
|
||||
return session->id();
|
||||
case RoleNames::name:
|
||||
return session->name();
|
||||
case RoleNames::deviceName:
|
||||
return session->appName();
|
||||
case RoleNames::deviceType:
|
||||
return QVariant::fromValue<Model::DeviceType>(session->deviceType());
|
||||
case RoleNames::userName:
|
||||
return session->userName();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteDeviceList::activateSession(PlaybackManager *manager, int index) {
|
||||
manager->setControllingSession(m_sessions.at(index).second);
|
||||
}
|
||||
|
||||
void RemoteDeviceList::setScanning(bool scanning) {
|
||||
if (scanning == m_scanning) return;
|
||||
m_scanning = scanning;
|
||||
emit scanningChanged();
|
||||
|
||||
if (scanning) {
|
||||
for (auto it = m_scanners.begin(); it != m_scanners.end(); it++) {
|
||||
(*it)->startScanning();
|
||||
}
|
||||
} else {
|
||||
for (auto it = m_scanners.begin(); it != m_scanners.end(); it++) {
|
||||
(*it)->stopScanning();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteDeviceList::onSessionFound(Model::ControllableSession *session) {
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_sessions.append(std::make_pair(qobject_cast<Model::RemoteSessionScanner *>(sender()), QSharedPointer<Model::ControllableSession>(session)));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void RemoteDeviceList::onSessionLost(QString sessionId) {
|
||||
Model::RemoteSessionScanner *scanner = qobject_cast<Model::RemoteSessionScanner *>(sender());
|
||||
for (int i = 0; i < m_sessions.size(); i++) {
|
||||
auto row = m_sessions.at(i);
|
||||
if (row.first == scanner && row.second->name() == sessionId) {
|
||||
beginRemoveRows(QModelIndex(), i, i);
|
||||
m_sessions.removeAt(i);
|
||||
if (row.second->parent() == this) {
|
||||
row.second->deleteLater();
|
||||
}
|
||||
endRemoveRows();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteDeviceList::onSessionsReset() {
|
||||
Model::RemoteSessionScanner *scanner = qobject_cast<Model::RemoteSessionScanner *>(sender());
|
||||
for (int i = 0; i < m_sessions.size(); i++) {
|
||||
auto row = m_sessions.at(i);
|
||||
if (row.first == scanner) {
|
||||
beginRemoveRows(QModelIndex(), i, i);
|
||||
m_sessions.removeAt(i);
|
||||
if (row.second->parent() == this) {
|
||||
row.second->deleteLater();
|
||||
}
|
||||
endRemoveRows();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // NS Model
|
||||
} // NS Jellyfin
|
Loading…
Add table
Add a link
Reference in a new issue