1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-09-04 01:42:44 +00:00

WIP: logic rewrite

WIP: adding loaders
This commit is contained in:
Chris Josten 2021-03-24 20:04:03 +01:00
parent b9b08ab384
commit 2360b261f7
1769 changed files with 124903 additions and 1963 deletions

View file

@ -0,0 +1,197 @@
/*
* Sailfin: a Jellyfin client written using Qt
* Copyright (C) 2021 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
*/
#ifndef JELLYFIN_VIEWMODEL_ITEM_H
#define JELLYFIN_VIEWMODEL_ITEM_H
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QDateTime>
#include <QDebug>
#include <QList>
#include <QObject>
#include <QSharedPointer>
#include <QUuid>
#include <optional>
#include <cmath>
/*#include "dto.h"
#include "mediastream.h"
#include "namedguidpair.h"
#include "userdata.h"*/
#include "../loader/requesttypes.h"
#include "../model/item.h"
#include "loader.h"
namespace Jellyfin {
namespace ViewModel {
class Item : public QObject {
Q_OBJECT
public:
Q_INVOKABLE explicit Item(QSharedPointer<Model::Item> data = QSharedPointer<Model::Item>(),
QObject *parent = nullptr);
// Please keep the order of the properties the same as in the file linked above.
Q_PROPERTY(QUuid jellyfinId READ jellyfinId NOTIFY jellyfinIdChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString originalTitle READ originalTitle NOTIFY originalTitleChanged)
Q_PROPERTY(QString serverId READ serverId NOTIFY serverIdChanged)
Q_PROPERTY(QString etag READ etag NOTIFY etagChanged)
Q_PROPERTY(QString sourceType READ sourceType NOTIFY sourceTypeChanged)
Q_PROPERTY(QString playlistItemId READ playlistItemId NOTIFY playlistItemIdChanged)
Q_PROPERTY(QDateTime dateCreated READ dateCreated NOTIFY dateCreatedChanged)
Q_PROPERTY(QDateTime dateLastMediaAdded READ dateLastMediaAdded NOTIFY dateLastMediaAddedChanged)
Q_PROPERTY(QString extraType READ extraType NOTIFY extraTypeChanged)
Q_PROPERTY(int airsBeforeSeasonNumber READ airsBeforeSeasonNumber NOTIFY airsBeforeSeasonNumberChanged)
Q_PROPERTY(int airsAfterSeasonNumber READ airsAfterSeasonNumber NOTIFY airsAfterSeasonNumberChanged)
Q_PROPERTY(int airsBeforeEpisodeNumber READ airsBeforeEpisodeNumber NOTIFY airsBeforeEpisodeNumberChanged)
/*Q_PROPERTY(bool canDelete READ canDelete NOTIFY canDeleteChanged)
Q_PROPERTY(bool canDownload READ canDownload NOTIFY canDownloadChanged)
Q_PROPERTY(bool hasSubtitles READ hasSubtitles NOTIFY hasSubtitlesChanged)
Q_PROPERTY(QString preferredMetadataLanguage READ preferredMetadataLanguage NOTIFY preferredMetadataLanguageChanged)
Q_PROPERTY(QString preferredMetadataCountryCode READ preferredMetadataCountryCode NOTIFY preferredMetadataCountryCodeChanged)
Q_PROPERTY(bool supportsSync READ supportsSync NOTIFY supportsSyncChanged)
Q_PROPERTY(QString container READ container NOTIFY containerChanged)
Q_PROPERTY(QString sortName READ sortName NOTIFY sortNameChanged)
Q_PROPERTY(QString forcedSortName READ forcedSortName NOTIFY forcedSortNameChanged)
//SKIP: Video3DFormat
Q_PROPERTY(QDateTime premiereData READ premiereDate NOTIFY premiereDateChanged)
//SKIP: ExternalUrls
//SKIP: MediaSources
Q_PROPERTY(float criticRating READ criticRating NOTIFY criticRatingChanged)
Q_PROPERTY(QStringList productionLocations READ productionLocations NOTIFY productionLocationsChanged)
// Handpicked, important ones
Q_PROPERTY(qint64 runTimeTicks READ runTimeTicks NOTIFY runTimeTicksChanged)
Q_PROPERTY(QString overview READ overview NOTIFY overviewChanged)
Q_PROPERTY(int productionYear READ productionYear NOTIFY productionYearChanged)
Q_PROPERTY(int indexNumber READ indexNumber NOTIFY indexNumberChanged)
Q_PROPERTY(int indexNumberEnd READ indexNumberEnd NOTIFY indexNumberEndChanged)
Q_PROPERTY(bool isFolder READ isFolder NOTIFY isFolderChanged)
Q_PROPERTY(QString type READ type NOTIFY typeChanged)
Q_PROPERTY(QString parentBackdropItemId READ parentBackdropItemId NOTIFY parentBackdropItemIdChanged)
Q_PROPERTY(QStringList parentBackdropImageTags READ parentBackdropImageTags NOTIFY parentBackdropImageTagsChanged)
Q_PROPERTY(UserData *userData READ userData NOTIFY userDataChanged)
Q_PROPERTY(int recursiveItemCount READ recursiveItemCount NOTIFY recursiveItemCountChanged)
Q_PROPERTY(int childCount READ childCount NOTIFY childCountChanged)
Q_PROPERTY(QString albumArtist READ albumArtist NOTIFY albumArtistChanged)
Q_PROPERTY(QVariantList albumArtists READ albumArtists NOTIFY albumArtistsChanged)
Q_PROPERTY(QString seriesName READ seriesName NOTIFY seriesNameChanged)
Q_PROPERTY(QString seasonName READ seasonName NOTIFY seasonNameChanged)
Q_PROPERTY(QList<MediaStream *> __list__mediaStreams MEMBER __list__m_mediaStreams NOTIFY mediaStreamsChanged)
Q_PROPERTY(QVariantList mediaStreams READ mediaStreams NOTIFY mediaStreamsChanged STORED false)
Q_PROPERTY(QStringList artists READ artists NOTIFY artistsChanged)
// Why is this a QJsonObject? Well, because I couldn't be bothered to implement the deserialisations of
// a QHash at the moment.
Q_PROPERTY(QJsonObject imageTags READ imageTags NOTIFY imageTagsChanged)
Q_PROPERTY(QStringList backdropImageTags READ backdropImageTags NOTIFY backdropImageTagsChanged)
Q_PROPERTY(QJsonObject imageBlurHashes READ imageBlurHashes NOTIFY imageBlurHashesChanged)
Q_PROPERTY(QString mediaType READ mediaType READ mediaType NOTIFY mediaTypeChanged)
Q_PROPERTY(int width READ width NOTIFY widthChanged)
Q_PROPERTY(int height READ height NOTIFY heightChanged)*/
QUuid jellyfinId() const { return m_data->jellyfinId(); }
QString name() const { return m_data->name(); }
QString originalTitle() const { return m_data->originalTitle(); }
QString serverId() const { return m_data->serverId(); }
QString etag() const { return m_data->etag(); }
QString sourceType() const { return m_data->sourceType(); }
QString playlistItemId() const { return m_data->playlistItemId(); }
QDateTime dateCreated() const { return m_data->dateCreated(); }
QDateTime dateLastMediaAdded() const { return m_data->dateLastMediaAdded(); }
QString extraType() const { return m_data->extraType(); }
int airsBeforeSeasonNumber() const { return m_data->airsBeforeSeasonNumber().value_or(0); }
int airsAfterSeasonNumber() const { return m_data->airsAfterSeasonNumber().value_or(999); }
int airsBeforeEpisodeNumber() const { return m_data->airsBeforeEpisodeNumber().value_or(0); }
QSharedPointer<Model::Item> data() const { return m_data; }
void setData(QSharedPointer<Model::Item> newData);
signals:
void jellyfinIdChanged(const QUuid &newId);
void nameChanged(const QString &newName);
void originalTitleChanged(const QString &newOriginalTitle);
void serverIdChanged(const QString &newServerId);
void etagChanged(const QString &newEtag);
void sourceTypeChanged(const QString &sourceType);
void playlistItemIdChanged(const QString &playlistItemIdChanged);
void dateCreatedChanged(QDateTime newDateCreatedChanged);
void dateLastMediaAddedChanged(QDateTime newDateLastMediaAdded);
void extraTypeChanged(const QString &newExtraType);
void airsBeforeSeasonNumberChanged(int newAirsBeforeSeasonNumber);
void airsAfterSeasonNumberChanged(int newAirsAfterSeasonNumber);
void airsBeforeEpisodeNumberChanged(int newAirsAfterEpisodeNumber);
bool canDeleteChanged(bool newCanDelete);
void canDownloadChanged(bool newCanDownload);
void hasSubtitlesChanged(bool newHasSubtitles);
void preferredMetadataLanguageChanged(const QString &newPreferredMetadataLanguage);
void preferredMetadataCountryCodeChanged(const QString &newPreferredMetadataCountryCode);
void supportsSyncChanged(bool newSupportsSync);
void containerChanged(const QString &newContainer);
void sortNameChanged(const QString &newSortName);
void forcedSortNameChanged(const QString &newForcedSortName);
void premiereDateChanged(QDateTime newPremiereDate);
void criticRatingChanged(float newCriticRating);
void productionLocationsChanged(QStringList newProductionLocations);
// Handpicked, important ones
void runTimeTicksChanged(qint64 newRunTimeTicks);
void overviewChanged(const QString &newOverview);
void productionYearChanged(int newProductionYear);
void indexNumberChanged(int newIndexNumber);
void indexNumberEndChanged(int newIndexNumberEnd);
void isFolderChanged(bool newIsFolder);
void typeChanged(const QString &newType);
void parentBackdropItemIdChanged();
void parentBackdropImageTagsChanged();
//void userDataChanged(UserData *newUserData);
void recursiveItemCountChanged(int newRecursiveItemCount);
void childCountChanged(int newChildCount);
void albumArtistChanged(const QString &newAlbumArtist);
//void albumArtistsChanged(NameGuidPair *newAlbumArtists);
void seriesNameChanged(const QString &newSeriesName);
void seasonNameChanged(const QString &newSeasonName);
void mediaStreamsChanged(/*const QList<MediaStream *> &newMediaStreams*/);
void artistsChanged(const QStringList &newArtists);
void imageTagsChanged();
void backdropImageTagsChanged();
void imageBlurHashesChanged();
void mediaTypeChanged(const QString &newMediaType);
void widthChanged(int newWidth);
void heightChanged(int newHeight);
protected:
QSharedPointer<Model::Item> m_data;
};
class ItemLoader : Loader<Item, DTO::BaseItemDto, Jellyfin::Loader::GetItemsByUserIdParams> {
Q_OBJECT
public:
explicit ItemLoader(QObject *parent = nullptr);
};
} // NS ViewModel
} // NS Jellyfin
#endif // JELLYFIN_VIEWMODEL_ITEM_H

View file

@ -0,0 +1,211 @@
/*
* Sailfin: a Jellyfin client written using Qt
* Copyright (C) 2021 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
*/
#ifndef JELLYFIN_DTO_DTO
#define JELLYFIN_DTO_DTO
#include <optional>
#include <QFuture>
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrent>
#include "../support/loader.h"
namespace Jellyfin {
namespace ViewModel {
/**
* @brief An "interface" for a remote data source
*
* This class is basically a base class for JSON data that can be fetched from over the network.
* Subclasses should reimplement reload and call setStatus to update the QML part of the code
* appropiatly.
*/
class LoaderBase : public QObject, public QQmlParserStatus {
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
public:
enum Status {
/// The data is unitialized and not loading either.
Uninitialised,
/// The data is being loaded over the network
Loading,
/// The data is ready, the properties in this object are up to date.
Ready,
/// An error has occurred while loading the data. See error() for more details.
Error
};
Q_ENUM(Status)
explicit LoaderBase(QObject *parent = nullptr) : QObject(parent) {}
LoaderBase(ApiClient *apiClient, QObject *parent = nullptr)
: QObject(parent), m_apiClient(apiClient) {}
Q_PROPERTY(ApiClient *apiClient MEMBER m_apiClient WRITE setApiClient NOTIFY apiClientChanged STORED false)
Q_PROPERTY(Status status READ status NOTIFY statusChanged STORED false)
Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged STORED false)
Q_PROPERTY(bool autoReload MEMBER m_autoReload NOTIFY autoReloadChanged)
Status status() const { return m_status; }
QString errorString() const { return m_errorString; }
void setApiClient(ApiClient *newApiClient);
void setExtraFields(const QStringList &extraFields);
signals:
void statusChanged(Status newStatus);
void apiClientChanged(ApiClient *newApiClient);
void errorStringChanged(QString newErrorString);
void autoReloadChanged(bool newAutoReload);
void viewModelChanged();
/**
* @brief Convenience signal for status == RemoteData.Ready.
*/
void ready();
public slots:
/**
* @brief Overload this method to reimplement the fetching mechanism to
* populate the RemoteData with data from the server.
*
* The default implementation makes a GET request to getDataUrl() and parses the resulting JSON,
* which should be enough for most cases. Consider overriding getDataUrl() and
* canRelaod() if possible. Manual overrides need to make sure that
* they're calling setStatus(Status), setError(QNetworkReply::NetworkError) and
* setErrorString() to let the QML side know what this thing is up to.
*/
virtual void reload() {};
protected:
/**
* @brief Subclasses should implement this to determine if they can
* load data from the server.
*
* Usage cases include checking if the
* required properties, such as the item id are set.
*/
virtual bool canReload() const;
void setStatus(Status newStatus);
void setError(QNetworkReply::NetworkError error);
void setErrorString(const QString &newErrorString);
void classBegin() override {
m_isParsing = true;
}
void componentComplete() override {
m_isParsing = false;
if (canReload()) {
reload();
}
}
ApiClient *m_apiClient = nullptr;
protected:
// Returns true if this class is instantiated within QML and is still being parsed.
bool isQmlParsing() const { return m_isParsing; }
void emitDataChanged();
private:
Status m_status = Uninitialised;
QString m_errorString;
bool m_autoReload = true;
bool m_isParsing = false;
};
/**
* Class representing data from a remote source, meant to be instantiated from QML.
*
* Subclasses should do the following:
* - initialize m_loader with an appropiate loader
* - create properties that map to the parameters.
* - override canReload()
*/
template <class T, class R, class P>
class Loader : public LoaderBase {
using RFutureWatcher = QFutureWatcher<std::optional<R>>;
public:
Loader(QObject *parent = nullptr)
: LoaderBase(parent),
m_futureWatcher(new RFutureWatcher) {
m_dataViewModel = new T(this);
connect(m_futureWatcher, &RFutureWatcher::finished, this, &Loader<T, R, P>::updateData());
}
Loader(ApiClient *apiClient, QObject *parent = nullptr)
: LoaderBase(apiClient, parent),
m_futureWatcher(new QFutureWatcher<std::optional<R>>) {
m_dataViewModel = new T(this);
}
T *dataViewModel() const { return m_dataViewModel; }
void reload() override {
setStatus(Loading);
QFuture<std::optional<R>> future = QtConcurrent::run(this, &Loader<T, R, P>::invokeLoader, m_parameters);
m_futureWatcher->setFuture(future);
}
protected:
T* m_dataViewModel;
P m_parameters;
/**
* @brief Subclasses should initialize this to a loader that actually loads stuff.
*/
Support::Loader<R, P> m_loader;
private:
QFutureWatcher<std::optional<R>> *m_futureWatcher;
/**
* @brief Callback for QtConcurrent::run()
* @param self Pointer to this class
* @param parameters Parameters to forward to the loader
* @return empty optional if an error occured, otherwise the result.
*/
std::optional<R> invokeLoader(P parameters) {
try {
return this->m_loader.load(parameters);
} catch (Support::LoadException e) {
this->setErrorString(QString(e.what()));
return std::nullopt;
}
}
/**
* @brief Updates the data when finished.
*/
void updateData() {
std::optional<R> newData = m_futureWatcher->result();
if (newData.has_value()) {
if (newData.sameAs(*m_dataViewModel->data())) {
// Replace the data the model holds
m_dataViewModel->data()->replaceData(*newData);
} else {
// Replace the model
m_dataViewModel->setData(QSharedPointer<T>::create(*newData, m_apiClient));
}
setStatus(Ready);
emitDataChanged();
} else {
setStatus(Error);
}
}
};
void registerRemoteTypes(const char *uri);
} // NS ViewModel
} // NS Jellyfin
#endif // JELLYFIN_DTO_DTO_H

View file

@ -0,0 +1,231 @@
/*
* Sailfin: a Jellyfin client written using Qt
* Copyright (C) 2021 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
*/
#ifndef JELLYFIN_VIEWMODEL_PLAYBACKMANAGER_H
#define JELLYFIN_VIEWMODEL_PLAYBACKMANAGER_H
#include <QAbstractItemModel>
#include <QJsonArray>
#include <QJsonObject>
#include <QFuture>
#include <QObject>
#include <QtGlobal>
#include <QUrlQuery>
#include <QVariant>
#include <QtMultimedia/QMediaPlayer>
#include <QtMultimedia/QMediaPlaylist>
#include <functional>
#include "../dto/baseitemdto.h"
#include "../support/jsonconv.h"
#include "../viewmodel/item.h"
#include "../apiclient.h"
namespace Jellyfin {
// Forward declaration of Jellyfin::ApiClient found in jellyfinapiclient.h
class ApiClient;
class ItemModel;
class RemoteItem;
namespace ViewModel {
/**
* @brief The PlaybackManager class manages the playback of Jellyfin items. It fetches streams based on Jellyfin items, posts
* the current playback state to the Jellyfin Server, contains the actual media player and so on.
*
* The PlaybackManager actually keeps two mediaPlayers, m_mediaPlayer1 and m_mediaPlayer2. When one is playing, the other is
* preloading the next item in the queue. The current media player is pointed to by m_mediaPlayer.
*/
class PlaybackManager : public QObject, public QQmlParserStatus {
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
public:
enum PlayMethod {
Transcode,
Stream,
DirectPlay
};
Q_ENUM(PlayMethod)
using FetchCallback = std::function<void(QUrl &&, PlayMethod)>;
explicit PlaybackManager(QObject *parent = nullptr);
Q_PROPERTY(ApiClient *apiClient MEMBER m_apiClient WRITE setApiClient)
Q_PROPERTY(QString streamUrl READ streamUrl NOTIFY streamUrlChanged)
Q_PROPERTY(bool autoOpen MEMBER m_autoOpen NOTIFY autoOpenChanged)
Q_PROPERTY(int audioIndex MEMBER m_audioIndex NOTIFY audioIndexChanged)
Q_PROPERTY(int subtitleIndex MEMBER m_subtitleIndex NOTIFY subtitleIndexChanged)
Q_PROPERTY(bool resumePlayback MEMBER m_resumePlayback NOTIFY resumePlaybackChanged)
Q_PROPERTY(PlayMethod playMethod READ playMethod NOTIFY playMethodChanged)
// Current Item and queue informatoion
Q_PROPERTY(Model::Item *item READ item NOTIFY itemChanged)
Q_PROPERTY(QAbstractItemModel *queue READ queue NOTIFY queueChanged)
Q_PROPERTY(int queueIndex READ queueIndex NOTIFY queueIndexChanged)
// Current media player related property getters
Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
Q_PROPERTY(QMediaPlayer::Error error READ error NOTIFY errorChanged)
Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged)
Q_PROPERTY(QObject* mediaObject READ mediaObject NOTIFY mediaObjectChanged)
Q_PROPERTY(QMediaPlayer::MediaStatus mediaStatus READ mediaStatus NOTIFY mediaStatusChanged)
Q_PROPERTY(QMediaPlayer::State playbackState READ playbackState NOTIFY playbackStateChanged)
Q_PROPERTY(qint64 position READ position NOTIFY positionChanged)
Model::Item *item() const { return m_item.data(); }
void setApiClient(ApiClient *apiClient);
QString streamUrl() const { return m_streamUrl; }
PlayMethod playMethod() const { return m_playMethod; }
QObject *mediaObject() const { return m_mediaPlayer; }
qint64 position() const { return m_mediaPlayer->position(); }
qint64 duration() const { return m_mediaPlayer->duration(); }
ItemModel *queue() const { return m_queue; }
int queueIndex() const { return m_queueIndex; }
// Current media player related property getters
QMediaPlayer::State playbackState() const { return m_playbackState; }
QMediaPlayer::MediaStatus mediaStatus() const { return m_mediaPlayer->mediaStatus(); }
bool hasVideo() const { return m_mediaPlayer->isVideoAvailable(); }
QMediaPlayer::Error error () const { return m_mediaPlayer->error(); }
QString errorString() const { return m_mediaPlayer->errorString(); }
signals:
void itemChanged(BaseItemDto *newItemId);
void streamUrlChanged(const QString &newStreamUrl);
void autoOpenChanged(bool autoOpen);
void audioIndexChanged(int audioIndex);
void subtitleIndexChanged(int subtitleIndex);
void mediaPlayerChanged(QObject *newMediaPlayer);
void resumePlaybackChanged(bool newResumePlayback);
void playMethodChanged(PlayMethod newPlayMethod);
// Current media player related property signals
void mediaObjectChanged(QObject *newMediaObject);
void positionChanged(qint64 newPosition);
void durationChanged(qint64 newDuration);
void queueChanged(ItemModel *newQue);
void queueIndexChanged(int newIndex);
void playbackStateChanged(QMediaPlayer::State newState);
void mediaStatusChanged(QMediaPlayer::MediaStatus newMediaStatus);
void hasVideoChanged(bool newHasVideo);
void errorChanged(QMediaPlayer::Error newError);
void errorStringChanged(const QString &newErrorString);
public slots:
/**
* @brief playItem Plays the item with the given id. This will construct the Jellyfin::Item internally
* and delete it later.
* @param itemId The id of the item to play.
*/
void playItem(const QString &itemId);
void playItemInList(ItemModel *itemList, int index);
void play() { m_mediaPlayer->play(); }
void pause() { m_mediaPlayer->pause(); }
void seek(qint64 pos) { m_mediaPlayer->setPosition(pos); }
void stop() { m_mediaPlayer->stop(); }
/**
* @brief previous Play the previous track in the current playlist.
*/
void previous();
/**
* @brief next Play the next track in the current playlist.
*/
void next();
private slots:
void mediaPlayerStateChanged(QMediaPlayer::State newState);
void mediaPlayerPositionChanged(qint64 position);
void mediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus newStatus);
void mediaPlayerError(QMediaPlayer::Error error);
/**
* @brief updatePlaybackInfo Updates the Jellyfin server with the current playback progress etc.
*/
void updatePlaybackInfo();
private:
QTimer m_updateTimer;
ApiClient *m_apiClient = nullptr;
QSharedPointer<Model::Item> m_item;
QString m_streamUrl;
QString m_playSessionId;
int m_audioIndex = 0;
int m_subtitleIndex = -1;
qint64 m_resumePosition = 0;
qint64 m_oldPosition = 0;
qint64 m_stopPosition = 0;
QMediaPlayer::State m_oldState = QMediaPlayer::StoppedState;
PlayMethod m_playMethod = Transcode;
QMediaPlayer::State m_playbackState = QMediaPlayer::StoppedState;
// Pointer to the current media player.
QMediaPlayer *m_mediaPlayer = nullptr;
QMediaPlayer *m_mediaPlayer1;
QMediaPlayer *m_mediaPlayer2;
ItemModel *m_queue = nullptr;
int m_queueIndex = 0;
bool m_resumePlayback = true;
void setItem(ViewModel::Item *newItem);
void swapMediaPlayer();
bool m_qmlIsParsingComponent = false;
/**
* @brief Whether to automatically open the livestream of the item;
*/
bool m_autoOpen = false;
/**
* @brief Retrieves the URL of the stream to open.
*/
void fetchStreamUrl(const Model::Item *item, bool autoOpen, const FetchCallback &callback);
void fetchAndSetStreamUrl(const Model::Item *item);
void setStreamUrl(const QString &streamUrl);
void setPlaybackState(QMediaPlayer::State newState);
Model::Item *nextItem();
void setQueue(ItemModel *itemModel);
// Factor to multiply with when converting from milliseconds to ticks.
const static int MS_TICK_FACTOR = 10000;
enum PlaybackInfoType { Started, Stopped, Progress };
/**
* @brief Posts the playback information
*/
void postPlaybackInfo(PlaybackInfoType type);
void classBegin() override {
m_qmlIsParsingComponent = true;
}
void componentComplete() override;
};
} // NS ViewModel
} // NS Jellyfin
#endif // JELLYFIN_VIEWMODEL_PLAYBACKMANAGER_H