chore: baseline usable version
This commit is contained in:
932
src/qEmbyCore/models/admin/adminmodels.h
Normal file
932
src/qEmbyCore/models/admin/adminmodels.h
Normal file
@@ -0,0 +1,932 @@
|
||||
#ifndef ADMINMODELS_H
|
||||
#define ADMINMODELS_H
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDateTime>
|
||||
#include <QMetaType>
|
||||
#include "../../qEmbyCore_global.h"
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT SystemInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString serverName;
|
||||
QString version;
|
||||
QString operatingSystem;
|
||||
QString operatingSystemName;
|
||||
QString architecture;
|
||||
QString id;
|
||||
QString localAddress;
|
||||
QString wanAddress;
|
||||
bool hasUpdateAvailable = false;
|
||||
bool supportsLibraryMonitor = false;
|
||||
bool canSelfRestart = false;
|
||||
bool canSelfUpdate = false;
|
||||
|
||||
static SystemInfo fromJson(const QJsonObject& obj) {
|
||||
SystemInfo info;
|
||||
info.serverName = obj["ServerName"].toString();
|
||||
info.version = obj["Version"].toString();
|
||||
info.operatingSystem = obj["OperatingSystem"].toString();
|
||||
info.operatingSystemName = obj["OperatingSystemDisplayName"].toString();
|
||||
info.architecture = obj["SystemArchitecture"].toString();
|
||||
info.id = obj["Id"].toString();
|
||||
info.localAddress = obj["LocalAddress"].toString();
|
||||
info.wanAddress = obj["WanAddress"].toString();
|
||||
info.hasUpdateAvailable = obj["HasUpdateAvailable"].toBool();
|
||||
info.supportsLibraryMonitor = obj["SupportsLibraryMonitor"].toBool();
|
||||
info.canSelfRestart = obj["CanSelfRestart"].toBool();
|
||||
info.canSelfUpdate = obj["CanSelfUpdate"].toBool();
|
||||
return info;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(SystemInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT SessionInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString userId;
|
||||
QString userName;
|
||||
QString client;
|
||||
QString deviceName;
|
||||
QString deviceId;
|
||||
QString applicationVersion;
|
||||
QString remoteEndPoint;
|
||||
QString lastActivityDate;
|
||||
|
||||
|
||||
QString nowPlayingItemId;
|
||||
QString nowPlayingItemName;
|
||||
QString nowPlayingItemType;
|
||||
QString nowPlayingSeriesName;
|
||||
int nowPlayingParentIndexNumber = -1;
|
||||
int nowPlayingIndexNumber = -1;
|
||||
QString nowPlayingImageTag;
|
||||
QString nowPlayingThumbTag;
|
||||
QString nowPlayingBackdropTag;
|
||||
QString nowPlayingSeriesId;
|
||||
QString nowPlayingSeriesPrimaryTag;
|
||||
QString parentThumbItemId;
|
||||
QString parentThumbImageTag;
|
||||
QString parentBackdropItemId;
|
||||
QString parentBackdropImageTag;
|
||||
qint64 nowPlayingRunTimeTicks = 0;
|
||||
|
||||
|
||||
bool isPaused = false;
|
||||
qint64 positionTicks = 0;
|
||||
QString playMethod;
|
||||
|
||||
|
||||
QString transcodingContainer;
|
||||
QString transcodingVideoCodec;
|
||||
QString transcodingAudioCodec;
|
||||
int transcodingBitrate = 0;
|
||||
double transcodingProgress = 0.0;
|
||||
|
||||
|
||||
bool isNowPlaying() const { return !nowPlayingItemId.isEmpty(); }
|
||||
|
||||
QString seasonEpisodeLabel() const {
|
||||
if (nowPlayingParentIndexNumber >= 0 && nowPlayingIndexNumber >= 0) {
|
||||
return QString("S%1:E%2")
|
||||
.arg(nowPlayingParentIndexNumber)
|
||||
.arg(nowPlayingIndexNumber);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
static QString formatTicks(qint64 ticks) {
|
||||
qint64 totalSeconds = ticks / 10000000;
|
||||
int hours = totalSeconds / 3600;
|
||||
int minutes = (totalSeconds % 3600) / 60;
|
||||
int seconds = totalSeconds % 60;
|
||||
if (hours > 0)
|
||||
return QString("%1:%2:%3").arg(hours).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
|
||||
return QString("%1:%2").arg(minutes).arg(seconds, 2, 10, QChar('0'));
|
||||
}
|
||||
|
||||
double playbackProgress() const {
|
||||
if (nowPlayingRunTimeTicks <= 0) return 0.0;
|
||||
return static_cast<double>(positionTicks) / nowPlayingRunTimeTicks;
|
||||
}
|
||||
|
||||
static SessionInfo fromJson(const QJsonObject& obj) {
|
||||
SessionInfo session;
|
||||
session.id = obj["Id"].toString();
|
||||
session.userId = obj["UserId"].toString();
|
||||
session.userName = obj["UserName"].toString();
|
||||
session.client = obj["Client"].toString();
|
||||
session.deviceName = obj["DeviceName"].toString();
|
||||
session.deviceId = obj["DeviceId"].toString();
|
||||
session.applicationVersion = obj["ApplicationVersion"].toString();
|
||||
session.remoteEndPoint = obj["RemoteEndPoint"].toString();
|
||||
session.lastActivityDate = obj["LastActivityDate"].toString();
|
||||
|
||||
|
||||
if (obj.contains("NowPlayingItem")) {
|
||||
QJsonObject np = obj["NowPlayingItem"].toObject();
|
||||
session.nowPlayingItemId = np["Id"].toString();
|
||||
session.nowPlayingItemName = np["Name"].toString();
|
||||
session.nowPlayingItemType = np["Type"].toString();
|
||||
session.nowPlayingSeriesName = np["SeriesName"].toString();
|
||||
session.nowPlayingParentIndexNumber = np["ParentIndexNumber"].toInt(-1);
|
||||
session.nowPlayingIndexNumber = np["IndexNumber"].toInt(-1);
|
||||
session.nowPlayingRunTimeTicks = np["RunTimeTicks"].toVariant().toLongLong();
|
||||
|
||||
|
||||
QJsonObject imgTags = np["ImageTags"].toObject();
|
||||
session.nowPlayingImageTag = imgTags["Primary"].toString();
|
||||
session.nowPlayingThumbTag = imgTags["Thumb"].toString();
|
||||
|
||||
|
||||
QJsonArray bdTags = np["BackdropImageTags"].toArray();
|
||||
if (!bdTags.isEmpty()) {
|
||||
session.nowPlayingBackdropTag = bdTags.first().toString();
|
||||
}
|
||||
|
||||
|
||||
session.nowPlayingSeriesId = np["SeriesId"].toString();
|
||||
session.nowPlayingSeriesPrimaryTag = np["SeriesPrimaryImageTag"].toString();
|
||||
|
||||
|
||||
session.parentThumbItemId = np["ParentThumbItemId"].toString();
|
||||
session.parentThumbImageTag = np["ParentThumbImageTag"].toString();
|
||||
session.parentBackdropItemId = np["ParentBackdropItemId"].toString();
|
||||
QJsonArray parentBdTags = np["ParentBackdropImageTags"].toArray();
|
||||
if (!parentBdTags.isEmpty()) {
|
||||
session.parentBackdropImageTag = parentBdTags.first().toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("PlayState")) {
|
||||
QJsonObject ps = obj["PlayState"].toObject();
|
||||
session.isPaused = ps["IsPaused"].toBool();
|
||||
session.positionTicks = ps["PositionTicks"].toVariant().toLongLong();
|
||||
session.playMethod = ps["PlayMethod"].toString();
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("TranscodingInfo")) {
|
||||
QJsonObject ti = obj["TranscodingInfo"].toObject();
|
||||
session.transcodingContainer = ti["Container"].toString();
|
||||
session.transcodingVideoCodec = ti["VideoCodec"].toString();
|
||||
session.transcodingAudioCodec = ti["AudioCodec"].toString();
|
||||
session.transcodingBitrate = ti["Bitrate"].toInt();
|
||||
session.transcodingProgress = ti["CompletionPercentage"].toDouble();
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(SessionInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT UserInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
QString serverIdRaw;
|
||||
bool isAdministrator = false;
|
||||
bool isDisabled = false;
|
||||
bool hasPassword = false;
|
||||
QString lastLoginDate;
|
||||
QString lastActivityDate;
|
||||
QString primaryImageTag;
|
||||
|
||||
|
||||
bool enableAllFolders = true;
|
||||
|
||||
static UserInfo fromJson(const QJsonObject& obj) {
|
||||
UserInfo user;
|
||||
user.id = obj["Id"].toString();
|
||||
user.name = obj["Name"].toString();
|
||||
user.serverIdRaw = obj["ServerId"].toString();
|
||||
user.hasPassword = obj["HasPassword"].toBool();
|
||||
user.lastLoginDate = obj["LastLoginDate"].toString();
|
||||
user.lastActivityDate = obj["LastActivityDate"].toString();
|
||||
user.primaryImageTag = obj["PrimaryImageTag"].toString();
|
||||
|
||||
|
||||
if (obj.contains("Policy")) {
|
||||
QJsonObject policy = obj["Policy"].toObject();
|
||||
user.isAdministrator = policy["IsAdministrator"].toBool();
|
||||
user.isDisabled = policy["IsDisabled"].toBool();
|
||||
user.enableAllFolders = policy["EnableAllFolders"].toBool();
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(UserInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT AdminMediaSubFolderInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString guid;
|
||||
QString path;
|
||||
bool isUserAccessConfigurable = true;
|
||||
|
||||
static AdminMediaSubFolderInfo fromJson(const QJsonObject& obj) {
|
||||
AdminMediaSubFolderInfo folder;
|
||||
folder.id = obj["Id"].toString();
|
||||
folder.guid = obj["Guid"].toString();
|
||||
folder.path = obj["Path"].toString();
|
||||
folder.isUserAccessConfigurable =
|
||||
obj["IsUserAccessConfigurable"].toBool(true);
|
||||
return folder;
|
||||
}
|
||||
|
||||
QString effectiveId() const {
|
||||
return !guid.trimmed().isEmpty() ? guid.trimmed() : id.trimmed();
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(AdminMediaSubFolderInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT AdminMediaFolderInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
|
||||
static AdminMediaFolderInfo fromJson(const QJsonObject& obj) {
|
||||
AdminMediaFolderInfo folder;
|
||||
folder.id = obj["Guid"].toString();
|
||||
if (folder.id.isEmpty()) {
|
||||
folder.id = obj["Id"].toString();
|
||||
}
|
||||
folder.name = obj["Name"].toString();
|
||||
return folder;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(AdminMediaFolderInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT SelectableMediaFolderInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
bool isUserAccessConfigurable = true;
|
||||
QList<AdminMediaSubFolderInfo> subFolders;
|
||||
|
||||
static SelectableMediaFolderInfo fromJson(const QJsonObject& obj) {
|
||||
SelectableMediaFolderInfo folder;
|
||||
folder.id = obj["Guid"].toString();
|
||||
if (folder.id.isEmpty()) {
|
||||
folder.id = obj["Id"].toString();
|
||||
}
|
||||
folder.name = obj["Name"].toString();
|
||||
folder.isUserAccessConfigurable =
|
||||
obj["IsUserAccessConfigurable"].toBool(true);
|
||||
const QJsonArray subFolderArray = obj["SubFolders"].toArray();
|
||||
for (const auto& value : subFolderArray) {
|
||||
folder.subFolders.append(
|
||||
AdminMediaSubFolderInfo::fromJson(value.toObject()));
|
||||
}
|
||||
return folder;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(SelectableMediaFolderInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT AdminChannelInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
|
||||
static AdminChannelInfo fromJson(const QJsonObject& obj) {
|
||||
AdminChannelInfo channel;
|
||||
channel.id = obj["Guid"].toString();
|
||||
if (channel.id.isEmpty()) {
|
||||
channel.id = obj["Id"].toString();
|
||||
}
|
||||
channel.name = obj["Name"].toString();
|
||||
return channel;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(AdminChannelInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT AdminDeviceInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
QString appName;
|
||||
|
||||
static AdminDeviceInfo fromJson(const QJsonObject& obj) {
|
||||
AdminDeviceInfo device;
|
||||
device.id = obj["ReportedDeviceId"].toString();
|
||||
if (device.id.isEmpty()) {
|
||||
device.id = obj["Id"].toString();
|
||||
}
|
||||
device.name = obj["Name"].toString();
|
||||
if (device.name.isEmpty()) {
|
||||
device.name = obj["DeviceName"].toString();
|
||||
}
|
||||
device.appName = obj["AppName"].toString();
|
||||
return device;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(AdminDeviceInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT AdminAuthProviderInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
|
||||
static AdminAuthProviderInfo fromJson(const QJsonObject& obj) {
|
||||
AdminAuthProviderInfo provider;
|
||||
provider.id = obj["Id"].toString();
|
||||
provider.name = obj["Name"].toString();
|
||||
return provider;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(AdminAuthProviderInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT AdminFeatureInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
|
||||
static AdminFeatureInfo fromJson(const QJsonObject& obj) {
|
||||
AdminFeatureInfo feature;
|
||||
feature.id = obj["Id"].toString();
|
||||
feature.name = obj["Name"].toString();
|
||||
return feature;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(AdminFeatureInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT AdminParentalRatingInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString name;
|
||||
int value = 0;
|
||||
|
||||
static AdminParentalRatingInfo fromJson(const QJsonObject& obj) {
|
||||
AdminParentalRatingInfo rating;
|
||||
rating.name = obj["Name"].toString();
|
||||
rating.value = obj["Value"].toInt();
|
||||
return rating;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(AdminParentalRatingInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT LocalizationCulture {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString name;
|
||||
QString displayName;
|
||||
QString twoLetterISOLanguageName;
|
||||
QString threeLetterISOLanguageName;
|
||||
QStringList threeLetterISOLanguageNames;
|
||||
QStringList twoLetterISOLanguageNames;
|
||||
|
||||
static LocalizationCulture fromJson(const QJsonObject& obj) {
|
||||
LocalizationCulture culture;
|
||||
culture.name = obj["Name"].toString();
|
||||
culture.displayName = obj["DisplayName"].toString();
|
||||
culture.twoLetterISOLanguageName =
|
||||
obj["TwoLetterISOLanguageName"].toString();
|
||||
culture.threeLetterISOLanguageName =
|
||||
obj["ThreeLetterISOLanguageName"].toString();
|
||||
|
||||
const QJsonArray threeLetterNames =
|
||||
obj["ThreeLetterISOLanguageNames"].toArray();
|
||||
for (const auto& value : threeLetterNames) {
|
||||
const QString text = value.toString().trimmed();
|
||||
if (!text.isEmpty() &&
|
||||
!culture.threeLetterISOLanguageNames.contains(text)) {
|
||||
culture.threeLetterISOLanguageNames.append(text);
|
||||
}
|
||||
}
|
||||
|
||||
const QJsonArray twoLetterNames =
|
||||
obj["TwoLetterISOLanguageNames"].toArray();
|
||||
for (const auto& value : twoLetterNames) {
|
||||
const QString text = value.toString().trimmed();
|
||||
if (!text.isEmpty() &&
|
||||
!culture.twoLetterISOLanguageNames.contains(text)) {
|
||||
culture.twoLetterISOLanguageNames.append(text);
|
||||
}
|
||||
}
|
||||
|
||||
return culture;
|
||||
}
|
||||
|
||||
QString canonicalCode() const {
|
||||
if (!twoLetterISOLanguageName.trimmed().isEmpty()) {
|
||||
return twoLetterISOLanguageName.trimmed();
|
||||
}
|
||||
if (!twoLetterISOLanguageNames.isEmpty()) {
|
||||
return twoLetterISOLanguageNames.first().trimmed();
|
||||
}
|
||||
if (!threeLetterISOLanguageName.trimmed().isEmpty()) {
|
||||
return threeLetterISOLanguageName.trimmed();
|
||||
}
|
||||
if (!threeLetterISOLanguageNames.isEmpty()) {
|
||||
return threeLetterISOLanguageNames.first().trimmed();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString visibleName() const {
|
||||
if (!displayName.trimmed().isEmpty()) {
|
||||
return displayName.trimmed();
|
||||
}
|
||||
return name.trimmed();
|
||||
}
|
||||
|
||||
QStringList allCodes() const {
|
||||
QStringList codes;
|
||||
auto appendUnique = [&codes](const QString& value) {
|
||||
const QString trimmed = value.trimmed();
|
||||
if (!trimmed.isEmpty() && !codes.contains(trimmed, Qt::CaseInsensitive)) {
|
||||
codes.append(trimmed);
|
||||
}
|
||||
};
|
||||
|
||||
appendUnique(twoLetterISOLanguageName);
|
||||
appendUnique(threeLetterISOLanguageName);
|
||||
for (const QString& value : twoLetterISOLanguageNames) {
|
||||
appendUnique(value);
|
||||
}
|
||||
for (const QString& value : threeLetterISOLanguageNames) {
|
||||
appendUnique(value);
|
||||
}
|
||||
return codes;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(LocalizationCulture)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT LocalizationCountry {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString name;
|
||||
QString displayName;
|
||||
QString englishName;
|
||||
QString twoLetterISORegionName;
|
||||
QString threeLetterISORegionName;
|
||||
|
||||
static LocalizationCountry fromJson(const QJsonObject& obj) {
|
||||
LocalizationCountry country;
|
||||
country.name = obj["Name"].toString();
|
||||
country.displayName = obj["DisplayName"].toString();
|
||||
country.englishName = obj["EnglishName"].toString();
|
||||
country.twoLetterISORegionName =
|
||||
obj["TwoLetterISORegionName"].toString();
|
||||
country.threeLetterISORegionName =
|
||||
obj["ThreeLetterISORegionName"].toString();
|
||||
return country;
|
||||
}
|
||||
|
||||
QString canonicalCode() const {
|
||||
if (!twoLetterISORegionName.trimmed().isEmpty()) {
|
||||
return twoLetterISORegionName.trimmed();
|
||||
}
|
||||
if (!name.trimmed().isEmpty()) {
|
||||
return name.trimmed();
|
||||
}
|
||||
if (!threeLetterISORegionName.trimmed().isEmpty()) {
|
||||
return threeLetterISORegionName.trimmed();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString visibleName() const {
|
||||
if (!displayName.trimmed().isEmpty()) {
|
||||
return displayName.trimmed();
|
||||
}
|
||||
if (!englishName.trimmed().isEmpty()) {
|
||||
return englishName.trimmed();
|
||||
}
|
||||
return name.trimmed();
|
||||
}
|
||||
|
||||
QStringList allCodes() const {
|
||||
QStringList codes;
|
||||
auto appendUnique = [&codes](const QString& value) {
|
||||
const QString trimmed = value.trimmed();
|
||||
if (!trimmed.isEmpty() && !codes.contains(trimmed, Qt::CaseInsensitive)) {
|
||||
codes.append(trimmed);
|
||||
}
|
||||
};
|
||||
|
||||
appendUnique(name);
|
||||
appendUnique(twoLetterISORegionName);
|
||||
appendUnique(threeLetterISORegionName);
|
||||
return codes;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(LocalizationCountry)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT VirtualFolder {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
QString itemId;
|
||||
QString guid;
|
||||
QString collectionType;
|
||||
QStringList locations;
|
||||
QString primaryImageItemId;
|
||||
QJsonObject libraryOptions;
|
||||
|
||||
static VirtualFolder fromJson(const QJsonObject& obj) {
|
||||
VirtualFolder folder;
|
||||
folder.id = obj["Id"].toString();
|
||||
folder.name = obj["Name"].toString();
|
||||
folder.itemId = obj["ItemId"].toString();
|
||||
folder.guid = obj["Guid"].toString();
|
||||
folder.collectionType = obj["CollectionType"].toString();
|
||||
folder.primaryImageItemId = obj["PrimaryImageItemId"].toString();
|
||||
folder.libraryOptions = obj["LibraryOptions"].toObject();
|
||||
|
||||
if (folder.id.isEmpty()) {
|
||||
folder.id = folder.itemId;
|
||||
}
|
||||
if (folder.itemId.isEmpty()) {
|
||||
folder.itemId = folder.id;
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("Locations")) {
|
||||
QJsonArray locs = obj["Locations"].toArray();
|
||||
for (const auto& loc : locs) {
|
||||
folder.locations.append(loc.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
QString virtualFolderId() const {
|
||||
return id.isEmpty() ? itemId : id;
|
||||
}
|
||||
|
||||
|
||||
QString collectionTypeDisplayName() const {
|
||||
if (collectionType == "movies") return QObject::tr("Movies");
|
||||
if (collectionType == "tvshows") return QObject::tr("TV Shows");
|
||||
if (collectionType == "music") return QObject::tr("Music");
|
||||
if (collectionType == "homevideos") return QObject::tr("Home Videos");
|
||||
if (collectionType == "audiobooks" || collectionType == "books")
|
||||
return QObject::tr("Books");
|
||||
if (collectionType == "musicvideos") return QObject::tr("Music Videos");
|
||||
if (collectionType == "boxsets") return QObject::tr("Collections");
|
||||
if (collectionType == "playlists") return QObject::tr("Playlists");
|
||||
if (collectionType.isEmpty()) return QObject::tr("Mixed");
|
||||
return collectionType;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(VirtualFolder)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT ItemImageInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString imageType;
|
||||
int imageIndex = -1;
|
||||
QString path;
|
||||
QString fileName;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
qint64 size = 0;
|
||||
|
||||
static ItemImageInfo fromJson(const QJsonObject& obj) {
|
||||
ItemImageInfo info;
|
||||
info.imageType = obj.value(QStringLiteral("ImageType")).toString();
|
||||
if (info.imageType.isEmpty()) {
|
||||
info.imageType = obj.value(QStringLiteral("Type")).toString();
|
||||
}
|
||||
info.imageIndex = obj.value(QStringLiteral("ImageIndex")).toInt(-1);
|
||||
info.path = obj.value(QStringLiteral("Path")).toString();
|
||||
info.fileName = obj.value(QStringLiteral("Filename")).toString();
|
||||
info.width = obj.value(QStringLiteral("Width")).toInt();
|
||||
info.height = obj.value(QStringLiteral("Height")).toInt();
|
||||
info.size = obj.value(QStringLiteral("Size")).toVariant().toLongLong();
|
||||
return info;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(ItemImageInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT RemoteSearchResult {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString name;
|
||||
int productionYear = 0;
|
||||
int indexNumber = -1;
|
||||
int indexNumberEnd = -1;
|
||||
int parentIndexNumber = -1;
|
||||
QString premiereDate;
|
||||
QString imageUrl;
|
||||
QString searchProviderName;
|
||||
QString overview;
|
||||
QString albumArtistName;
|
||||
QStringList artistNames;
|
||||
QJsonObject providerIds;
|
||||
QJsonObject rawData;
|
||||
|
||||
QString providerIdSummary() const {
|
||||
QStringList pairs;
|
||||
const QStringList keys = providerIds.keys();
|
||||
for (const QString& key : keys) {
|
||||
const QString value = providerIds.value(key).toString().trimmed();
|
||||
if (!key.trimmed().isEmpty() && !value.isEmpty()) {
|
||||
pairs.append(QStringLiteral("%1=%2").arg(key.trimmed(), value));
|
||||
}
|
||||
}
|
||||
return pairs.join(QStringLiteral(", "));
|
||||
}
|
||||
|
||||
QString secondaryText() const {
|
||||
QStringList parts;
|
||||
|
||||
if (productionYear > 0) {
|
||||
parts.append(QString::number(productionYear));
|
||||
}
|
||||
|
||||
if (parentIndexNumber >= 0 && indexNumber >= 0) {
|
||||
parts.append(QStringLiteral("S%1E%2")
|
||||
.arg(parentIndexNumber, 2, 10, QChar('0'))
|
||||
.arg(indexNumber, 2, 10, QChar('0')));
|
||||
} else if (indexNumber >= 0) {
|
||||
parts.append(QStringLiteral("E%1").arg(indexNumber));
|
||||
}
|
||||
|
||||
if (!searchProviderName.trimmed().isEmpty()) {
|
||||
parts.append(searchProviderName.trimmed());
|
||||
}
|
||||
|
||||
return parts.join(QStringLiteral(" | "));
|
||||
}
|
||||
|
||||
QJsonObject toJson() const {
|
||||
if (!rawData.isEmpty()) {
|
||||
return rawData;
|
||||
}
|
||||
|
||||
QJsonObject obj;
|
||||
obj.insert(QStringLiteral("Name"), name);
|
||||
obj.insert(QStringLiteral("ProductionYear"), productionYear);
|
||||
obj.insert(QStringLiteral("IndexNumber"), indexNumber);
|
||||
obj.insert(QStringLiteral("IndexNumberEnd"), indexNumberEnd);
|
||||
obj.insert(QStringLiteral("ParentIndexNumber"), parentIndexNumber);
|
||||
obj.insert(QStringLiteral("PremiereDate"), premiereDate);
|
||||
obj.insert(QStringLiteral("ImageUrl"), imageUrl);
|
||||
obj.insert(QStringLiteral("SearchProviderName"), searchProviderName);
|
||||
obj.insert(QStringLiteral("Overview"), overview);
|
||||
obj.insert(QStringLiteral("ProviderIds"), providerIds);
|
||||
if (!albumArtistName.trimmed().isEmpty()) {
|
||||
QJsonObject albumArtist;
|
||||
albumArtist.insert(QStringLiteral("Name"), albumArtistName.trimmed());
|
||||
obj.insert(QStringLiteral("AlbumArtist"), albumArtist);
|
||||
}
|
||||
if (!artistNames.isEmpty()) {
|
||||
QJsonArray artists;
|
||||
for (const QString& artistName : artistNames) {
|
||||
if (artistName.trimmed().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonObject artist;
|
||||
artist.insert(QStringLiteral("Name"), artistName.trimmed());
|
||||
artists.append(artist);
|
||||
}
|
||||
obj.insert(QStringLiteral("Artists"), artists);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static RemoteSearchResult fromJson(const QJsonObject& obj) {
|
||||
RemoteSearchResult result;
|
||||
result.name = obj.value(QStringLiteral("Name")).toString();
|
||||
result.productionYear =
|
||||
obj.value(QStringLiteral("ProductionYear")).toInt();
|
||||
result.indexNumber = obj.value(QStringLiteral("IndexNumber")).toInt(-1);
|
||||
result.indexNumberEnd =
|
||||
obj.value(QStringLiteral("IndexNumberEnd")).toInt(-1);
|
||||
result.parentIndexNumber =
|
||||
obj.value(QStringLiteral("ParentIndexNumber")).toInt(-1);
|
||||
result.premiereDate = obj.value(QStringLiteral("PremiereDate")).toString();
|
||||
result.imageUrl = obj.value(QStringLiteral("ImageUrl")).toString();
|
||||
result.searchProviderName =
|
||||
obj.value(QStringLiteral("SearchProviderName")).toString();
|
||||
result.overview = obj.value(QStringLiteral("Overview")).toString();
|
||||
result.providerIds = obj.value(QStringLiteral("ProviderIds")).toObject();
|
||||
result.rawData = obj;
|
||||
|
||||
const QJsonValue albumArtistValue = obj.value(QStringLiteral("AlbumArtist"));
|
||||
if (albumArtistValue.isObject()) {
|
||||
result.albumArtistName =
|
||||
albumArtistValue.toObject().value(QStringLiteral("Name")).toString();
|
||||
} else if (albumArtistValue.isString()) {
|
||||
result.albumArtistName = albumArtistValue.toString();
|
||||
}
|
||||
|
||||
const QJsonArray artists = obj.value(QStringLiteral("Artists")).toArray();
|
||||
for (const QJsonValue& artistValue : artists) {
|
||||
if (artistValue.isObject()) {
|
||||
const QString artistName =
|
||||
artistValue.toObject().value(QStringLiteral("Name")).toString().trimmed();
|
||||
if (!artistName.isEmpty()) {
|
||||
result.artistNames.append(artistName);
|
||||
}
|
||||
} else if (artistValue.isString()) {
|
||||
const QString artistName = artistValue.toString().trimmed();
|
||||
if (!artistName.isEmpty()) {
|
||||
result.artistNames.append(artistName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(RemoteSearchResult)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT ScheduledTaskInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
QString description;
|
||||
QString category;
|
||||
QString state;
|
||||
double currentProgressPercentage = 0.0;
|
||||
QJsonArray triggers;
|
||||
bool isHidden = false;
|
||||
QString key;
|
||||
|
||||
|
||||
QString lastExecutionResult;
|
||||
QString lastStartTime;
|
||||
QString lastEndTime;
|
||||
|
||||
bool isRunning() const { return state == "Running"; }
|
||||
bool hasTriggers() const { return !triggers.isEmpty(); }
|
||||
|
||||
static ScheduledTaskInfo fromJson(const QJsonObject& obj) {
|
||||
ScheduledTaskInfo task;
|
||||
task.id = obj["Id"].toString();
|
||||
task.name = obj["Name"].toString();
|
||||
task.description = obj["Description"].toString();
|
||||
task.category = obj["Category"].toString();
|
||||
task.state = obj["State"].toString();
|
||||
task.currentProgressPercentage = obj["CurrentProgressPercentage"].toDouble();
|
||||
task.triggers = obj["Triggers"].toArray();
|
||||
task.isHidden = obj["IsHidden"].toBool(false);
|
||||
task.key = obj["Key"].toString();
|
||||
|
||||
|
||||
if (obj.contains("LastExecutionResult")) {
|
||||
QJsonObject result = obj["LastExecutionResult"].toObject();
|
||||
task.lastExecutionResult = result["Status"].toString();
|
||||
task.lastStartTime = result["StartTimeUtc"].toString();
|
||||
task.lastEndTime = result["EndTimeUtc"].toString();
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(ScheduledTaskInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT LogFileInfo {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
QString name;
|
||||
QString dateCreated;
|
||||
QString dateModified;
|
||||
long long size = 0;
|
||||
|
||||
|
||||
QString readableSize() const {
|
||||
if (size < 1024) return QString("%1 B").arg(size);
|
||||
if (size < 1024 * 1024) return QString("%1 KB").arg(size / 1024.0, 0, 'f', 1);
|
||||
return QString("%1 MB").arg(size / (1024.0 * 1024.0), 0, 'f', 1);
|
||||
}
|
||||
|
||||
static LogFileInfo fromJson(const QJsonObject& obj) {
|
||||
LogFileInfo log;
|
||||
log.name = obj["Name"].toString();
|
||||
log.dateCreated = obj["DateCreated"].toString();
|
||||
log.dateModified = obj["DateModified"].toString();
|
||||
log.size = obj["Size"].toVariant().toLongLong();
|
||||
return log;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(LogFileInfo)
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT ActivityLogEntry {
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
qint64 id = 0;
|
||||
QString name;
|
||||
QString type;
|
||||
QString overview;
|
||||
QString shortOverview;
|
||||
QString date;
|
||||
QString userId;
|
||||
QString severity;
|
||||
QString itemId;
|
||||
|
||||
static ActivityLogEntry fromJson(const QJsonObject& obj) {
|
||||
ActivityLogEntry entry;
|
||||
entry.id = obj["Id"].toVariant().toLongLong();
|
||||
entry.name = obj["Name"].toString();
|
||||
entry.type = obj["Type"].toString();
|
||||
entry.overview = obj["Overview"].toString();
|
||||
entry.shortOverview = obj["ShortOverview"].toString();
|
||||
entry.date = obj["Date"].toString();
|
||||
entry.userId = obj["UserId"].toString();
|
||||
entry.severity = obj["Severity"].toString();
|
||||
entry.itemId = obj["ItemId"].toString();
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(ActivityLogEntry)
|
||||
|
||||
#endif
|
||||
194
src/qEmbyCore/models/danmaku/danmakumodels.h
Normal file
194
src/qEmbyCore/models/danmaku/danmakumodels.h
Normal file
@@ -0,0 +1,194 @@
|
||||
#ifndef DANMAKUMODELS_H
|
||||
#define DANMAKUMODELS_H
|
||||
|
||||
#include "../../qEmbyCore_global.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVariantMap>
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuServerDefinition {
|
||||
QString id;
|
||||
QString name;
|
||||
QString provider = QStringLiteral("dandanplay");
|
||||
QString baseUrl = QStringLiteral("https://api.dandanplay.net");
|
||||
QString description;
|
||||
QString appId;
|
||||
QString appSecret;
|
||||
|
||||
QString contentScope;
|
||||
bool builtIn = false;
|
||||
bool enabled = true;
|
||||
|
||||
bool isValid() const {
|
||||
return !id.trimmed().isEmpty() && !name.trimmed().isEmpty() &&
|
||||
!baseUrl.trimmed().isEmpty();
|
||||
}
|
||||
|
||||
QString displayName() const {
|
||||
return name.trimmed().isEmpty() ? baseUrl.trimmed() : name.trimmed();
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuServerDefinition)
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuProviderConfig {
|
||||
QString provider = QStringLiteral("dandanplay");
|
||||
QString baseUrl = QStringLiteral("https://api.dandanplay.net");
|
||||
QString endpointId;
|
||||
QString endpointName;
|
||||
QString appId;
|
||||
QString appSecret;
|
||||
|
||||
QString contentScope;
|
||||
bool enabled = true;
|
||||
bool withRelated = true;
|
||||
int cacheHours = 24;
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuProviderConfig)
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuMediaContext {
|
||||
QString serverId;
|
||||
QString mediaId;
|
||||
QString mediaSourceId;
|
||||
QString itemType;
|
||||
QString title;
|
||||
QString originalTitle;
|
||||
QString seriesName;
|
||||
int productionYear = 0;
|
||||
int seasonNumber = -1;
|
||||
int episodeNumber = -1;
|
||||
qint64 durationMs = 0;
|
||||
QString path;
|
||||
QVariantMap providerIds;
|
||||
|
||||
QString cacheKey() const {
|
||||
return mediaSourceId.isEmpty() ? mediaId
|
||||
: QStringLiteral("%1:%2")
|
||||
.arg(mediaId, mediaSourceId);
|
||||
}
|
||||
|
||||
bool isEpisode() const {
|
||||
return itemType.compare(QStringLiteral("Episode"),
|
||||
Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
|
||||
QString displayTitle() const {
|
||||
if (isEpisode() && !seriesName.isEmpty()) {
|
||||
return QStringLiteral("%1 S%2E%3")
|
||||
.arg(seriesName)
|
||||
.arg(seasonNumber, 2, 10, QChar('0'))
|
||||
.arg(episodeNumber, 2, 10, QChar('0'));
|
||||
}
|
||||
return title;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuMediaContext)
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuMatchCandidate {
|
||||
QString provider;
|
||||
QString cacheScope;
|
||||
QString endpointId;
|
||||
QString endpointName;
|
||||
QString targetId;
|
||||
QString title;
|
||||
QString subtitle;
|
||||
int seasonNumber = -1;
|
||||
int episodeNumber = -1;
|
||||
qint64 durationMs = 0;
|
||||
double score = 0.0;
|
||||
QString matchReason;
|
||||
int commentCount = 0;
|
||||
|
||||
bool isValid() const {
|
||||
return !provider.isEmpty() && !targetId.isEmpty();
|
||||
}
|
||||
|
||||
QString displayText() const {
|
||||
return subtitle.isEmpty() ? title
|
||||
: QStringLiteral("%1 - %2")
|
||||
.arg(subtitle, title);
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuMatchCandidate)
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuMatchResult {
|
||||
bool matched = false;
|
||||
bool cacheHit = false;
|
||||
bool manualOverride = false;
|
||||
DanmakuMatchCandidate selected;
|
||||
QList<DanmakuMatchCandidate> candidates;
|
||||
|
||||
bool needManualMatch() const {
|
||||
return !matched && !candidates.isEmpty();
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuMatchResult)
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuComment {
|
||||
qint64 timeMs = 0;
|
||||
int mode = 1;
|
||||
QColor color = QColor(Qt::white);
|
||||
int fontLevel = 25;
|
||||
QString sender;
|
||||
QString text;
|
||||
QDateTime createdAt;
|
||||
|
||||
bool isValid() const {
|
||||
return !text.trimmed().isEmpty() && timeMs >= 0;
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuComment)
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuRenderOptions {
|
||||
bool enabled = true;
|
||||
double opacity = 0.72;
|
||||
double fontScale = 1.0;
|
||||
int fontWeight = 400;
|
||||
double outlineSize = 3.0;
|
||||
double shadowOffset = 1.0;
|
||||
int areaPercent = 70;
|
||||
int density = 100;
|
||||
double speedScale = 0.5;
|
||||
int offsetMs = 0;
|
||||
bool hideScroll = false;
|
||||
bool hideTop = false;
|
||||
bool hideBottom = false;
|
||||
bool dualSubtitle = true;
|
||||
QStringList blockedKeywords;
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuRenderOptions)
|
||||
|
||||
struct QEMBYCORE_EXPORT DanmakuLoadResult {
|
||||
bool success = false;
|
||||
QString assFilePath;
|
||||
int commentCount = 0;
|
||||
QString provider;
|
||||
QString sourceServerId;
|
||||
QString sourceServerName;
|
||||
QString sourceTitle;
|
||||
bool needManualMatch = false;
|
||||
QList<DanmakuComment> comments;
|
||||
DanmakuMatchResult matchResult;
|
||||
|
||||
bool hasComments() const {
|
||||
return commentCount > 0 && hasRenderableContent();
|
||||
}
|
||||
|
||||
bool hasCommentPayload() const {
|
||||
return !comments.isEmpty();
|
||||
}
|
||||
|
||||
bool hasRenderableTrack() const {
|
||||
return success && !assFilePath.isEmpty();
|
||||
}
|
||||
|
||||
bool hasRenderableContent() const {
|
||||
return hasRenderableTrack() || hasCommentPayload();
|
||||
}
|
||||
};
|
||||
Q_DECLARE_METATYPE(DanmakuLoadResult)
|
||||
|
||||
#endif
|
||||
277
src/qEmbyCore/models/media/mediaitem.cpp
Normal file
277
src/qEmbyCore/models/media/mediaitem.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
#include "mediaitem.h"
|
||||
#include <QDateTime>
|
||||
|
||||
MediaPersonInfo MediaPersonInfo::fromJson(const QJsonObject& obj) {
|
||||
MediaPersonInfo person;
|
||||
person.id = obj["Id"].toString();
|
||||
person.name = obj["Name"].toString();
|
||||
person.role = obj["Role"].toString();
|
||||
person.type = obj["Type"].toString();
|
||||
if (obj.contains("PrimaryImageTag")) {
|
||||
person.primaryImageTag = obj["PrimaryImageTag"].toString();
|
||||
}
|
||||
return person;
|
||||
}
|
||||
|
||||
MediaStudioInfo MediaStudioInfo::fromJson(const QJsonObject& obj) {
|
||||
MediaStudioInfo studio;
|
||||
studio.name = obj["Name"].toString();
|
||||
|
||||
studio.id = obj["Id"].toVariant().toString();
|
||||
return studio;
|
||||
}
|
||||
|
||||
|
||||
MediaExternalUrlInfo MediaExternalUrlInfo::fromJson(const QJsonObject& obj) {
|
||||
MediaExternalUrlInfo urlInfo;
|
||||
urlInfo.name = obj["Name"].toString();
|
||||
urlInfo.url = obj["Url"].toString();
|
||||
return urlInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QPair<QString, QString> MediaImageInfo::bestPoster() const {
|
||||
if (!primaryTag.isEmpty()) return {primaryTag, QStringLiteral("Primary")};
|
||||
if (!thumbTag.isEmpty()) return {thumbTag, QStringLiteral("Thumb")};
|
||||
if (!backdropTag.isEmpty()) return {backdropTag, QStringLiteral("Backdrop")};
|
||||
|
||||
if (!parentPrimaryTag.isEmpty()) return {parentPrimaryTag, QStringLiteral("Primary")};
|
||||
if (!parentBackdropTag.isEmpty()) return {parentBackdropTag, QStringLiteral("Backdrop")};
|
||||
return {{}, {}};
|
||||
}
|
||||
|
||||
QPair<QString, QString> MediaImageInfo::bestThumb() const {
|
||||
if (!thumbTag.isEmpty()) return {thumbTag, QStringLiteral("Thumb")};
|
||||
if (!backdropTag.isEmpty()) return {backdropTag, QStringLiteral("Backdrop")};
|
||||
if (!primaryTag.isEmpty()) return {primaryTag, QStringLiteral("Primary")};
|
||||
|
||||
if (!parentBackdropTag.isEmpty()) return {parentBackdropTag, QStringLiteral("Backdrop")};
|
||||
if (!parentThumbTag.isEmpty()) return {parentThumbTag, QStringLiteral("Thumb")};
|
||||
if (!parentPrimaryTag.isEmpty()) return {parentPrimaryTag, QStringLiteral("Primary")};
|
||||
return {{}, {}};
|
||||
}
|
||||
|
||||
|
||||
QPair<QString, QString> MediaImageInfo::bestBackdrop() const {
|
||||
if (!backdropTag.isEmpty()) return {backdropTag, QStringLiteral("Backdrop")};
|
||||
if (!thumbTag.isEmpty()) return {thumbTag, QStringLiteral("Thumb")};
|
||||
if (!primaryTag.isEmpty()) return {primaryTag, QStringLiteral("Primary")};
|
||||
|
||||
if (!parentBackdropTag.isEmpty()) return {parentBackdropTag, QStringLiteral("Backdrop")};
|
||||
if (!parentThumbTag.isEmpty()) return {parentThumbTag, QStringLiteral("Thumb")};
|
||||
return {{}, {}};
|
||||
}
|
||||
|
||||
|
||||
bool MediaImageInfo::isParentTag(const QString& tag) const {
|
||||
if (tag.isEmpty()) return false;
|
||||
return (tag == parentPrimaryTag || tag == parentBackdropTag || tag == parentThumbTag);
|
||||
}
|
||||
|
||||
MediaImageInfo MediaImageInfo::fromJson(const QJsonObject& obj) {
|
||||
MediaImageInfo info;
|
||||
info.primaryImageAspectRatio = obj["PrimaryImageAspectRatio"].toDouble();
|
||||
|
||||
|
||||
if (obj.contains("ImageTags")) {
|
||||
QJsonObject tagsObj = obj["ImageTags"].toObject();
|
||||
if (tagsObj.contains("Primary")) info.primaryTag = tagsObj["Primary"].toString();
|
||||
if (tagsObj.contains("Thumb")) info.thumbTag = tagsObj["Thumb"].toString();
|
||||
if (tagsObj.contains("Logo")) info.logoTag = tagsObj["Logo"].toString();
|
||||
if (tagsObj.contains("Backdrop")) info.backdropTag = tagsObj["Backdrop"].toString();
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("PrimaryImageItemId")) {
|
||||
info.primaryImageItemId = obj["PrimaryImageItemId"].toString();
|
||||
}
|
||||
|
||||
if (info.primaryTag.isEmpty() && obj.contains("PrimaryImageTag")) {
|
||||
|
||||
info.primaryTag = obj["PrimaryImageTag"].toString();
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("BackdropImageTags")) {
|
||||
QJsonArray backdropTags = obj["BackdropImageTags"].toArray();
|
||||
if (!backdropTags.isEmpty()) {
|
||||
info.backdropTag = backdropTags[0].toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("SeriesPrimaryImageTag")) {
|
||||
info.parentPrimaryTag = obj["SeriesPrimaryImageTag"].toString();
|
||||
}
|
||||
if (obj.contains("ParentThumbImageTag")) {
|
||||
info.parentThumbTag = obj["ParentThumbImageTag"].toString();
|
||||
}
|
||||
if (obj.contains("ParentBackdropImageTags")) {
|
||||
QJsonArray parentBackdrops = obj["ParentBackdropImageTags"].toArray();
|
||||
if (!parentBackdrops.isEmpty()) {
|
||||
info.parentBackdropTag = parentBackdrops[0].toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.contains("SeriesId")) {
|
||||
info.parentImageItemId = obj["SeriesId"].toString();
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
MediaUserDataInfo MediaUserDataInfo::fromJson(const QJsonObject& obj) {
|
||||
MediaUserDataInfo ud;
|
||||
ud.isFavorite = obj["IsFavorite"].toBool();
|
||||
ud.playedPercentage = obj["PlayedPercentage"].toDouble();
|
||||
ud.playbackPositionTicks = obj["PlaybackPositionTicks"].toVariant().toLongLong();
|
||||
ud.played = obj["Played"].toBool();
|
||||
ud.playCount = obj["PlayCount"].toInt();
|
||||
ud.lastPlayedDate = obj["LastPlayedDate"].toString();
|
||||
return ud;
|
||||
}
|
||||
|
||||
MediaItem MediaItem::fromJson(const QJsonObject& obj) {
|
||||
MediaItem item;
|
||||
|
||||
|
||||
item.id = obj["Id"].toString();
|
||||
item.name = obj["Name"].toString();
|
||||
item.originalTitle = obj["OriginalTitle"].toString();
|
||||
item.sortName = obj["SortName"].toString();
|
||||
item.type = obj["Type"].toString();
|
||||
item.mediaType = obj["MediaType"].toString();
|
||||
item.collectionType = obj["CollectionType"].toString();
|
||||
|
||||
|
||||
item.collectionId = obj["CollectionId"].toString();
|
||||
item.collectionName = obj["CollectionName"].toString();
|
||||
item.playlistItemId = obj["PlaylistItemId"].toString();
|
||||
|
||||
|
||||
item.seriesId = obj["SeriesId"].toString();
|
||||
item.seriesName = obj["SeriesName"].toString();
|
||||
item.parentIndexNumber = obj.contains("ParentIndexNumber") ? obj["ParentIndexNumber"].toInt(-1) : -1;
|
||||
item.indexNumber = obj.contains("IndexNumber") ? obj["IndexNumber"].toInt(-1) : -1;
|
||||
if (obj.contains("ProviderIds")) {
|
||||
const QJsonObject providerIdsObj = obj["ProviderIds"].toObject();
|
||||
for (auto it = providerIdsObj.constBegin(); it != providerIdsObj.constEnd(); ++it) {
|
||||
item.providerIds.insert(it.key(), it.value().toVariant());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
item.productionYear = obj["ProductionYear"].toInt();
|
||||
item.overview = obj["Overview"].toString();
|
||||
item.officialRating = obj["OfficialRating"].toString();
|
||||
item.criticRating = obj["CriticRating"].toInt();
|
||||
item.runTimeTicks = obj["RunTimeTicks"].toVariant().toLongLong();
|
||||
item.communityRating = obj["CommunityRating"].toDouble();
|
||||
item.canDownload = obj["CanDownload"].toBool(false);
|
||||
|
||||
|
||||
if (obj.contains("PremiereDate")) {
|
||||
item.premiereDate = obj["PremiereDate"].toString();
|
||||
int tIdx = item.premiereDate.indexOf('T');
|
||||
if (tIdx > 0) item.premiereDate = item.premiereDate.left(tIdx);
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("DateCreated")) {
|
||||
QString rawDate = obj["DateCreated"].toString();
|
||||
item.dateCreatedRaw = rawDate;
|
||||
QDateTime dt = QDateTime::fromString(rawDate, Qt::ISODate);
|
||||
if (dt.isValid()) {
|
||||
item.dateCreated = dt.toLocalTime().toString("yyyy-MM-dd HH:mm");
|
||||
} else {
|
||||
|
||||
item.dateCreated = rawDate;
|
||||
int tIdx = item.dateCreated.indexOf('T');
|
||||
if (tIdx > 0 && item.dateCreated.length() >= tIdx + 6) {
|
||||
item.dateCreated = item.dateCreated.left(tIdx + 6).replace('T', ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
item.container = obj["Container"].toString();
|
||||
item.path = obj["Path"].toString();
|
||||
item.size = obj["Size"].toVariant().toLongLong();
|
||||
item.bitrate = obj["Bitrate"].toVariant().toLongLong();
|
||||
item.width = obj["Width"].toInt();
|
||||
item.height = obj["Height"].toInt();
|
||||
item.childCount = obj["ChildCount"].toInt();
|
||||
item.recursiveItemCount = obj["RecursiveItemCount"].toInt();
|
||||
|
||||
|
||||
if (obj.contains("UserData")) {
|
||||
item.userData = MediaUserDataInfo::fromJson(obj["UserData"].toObject());
|
||||
}
|
||||
|
||||
|
||||
|
||||
item.images = MediaImageInfo::fromJson(obj);
|
||||
|
||||
|
||||
|
||||
|
||||
if (obj.contains("GenreItems")) {
|
||||
QJsonArray arr = obj["GenreItems"].toArray();
|
||||
for (const auto& val : arr) {
|
||||
QString name = val.toObject()["Name"].toString();
|
||||
if (!name.isEmpty()) item.genres.append(name);
|
||||
}
|
||||
}
|
||||
if (item.genres.isEmpty() && obj.contains("Genres")) {
|
||||
QJsonArray arr = obj["Genres"].toArray();
|
||||
for (const auto& val : arr) item.genres.append(val.toString());
|
||||
}
|
||||
|
||||
if (obj.contains("Taglines")) {
|
||||
QJsonArray arr = obj["Taglines"].toArray();
|
||||
for (const auto& val : arr) item.taglines.append(val.toString());
|
||||
}
|
||||
|
||||
if (obj.contains("TagItems")) {
|
||||
QJsonArray arr = obj["TagItems"].toArray();
|
||||
for (const auto& val : arr) item.tags.append(val.toObject()["Name"].toString());
|
||||
}
|
||||
|
||||
if (obj.contains("RemoteTrailers")) {
|
||||
QJsonArray arr = obj["RemoteTrailers"].toArray();
|
||||
for (const auto& val : arr) item.remoteTrailers.append(val.toObject()["Url"].toString());
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("People")) {
|
||||
QJsonArray arr = obj["People"].toArray();
|
||||
for (const auto& val : arr) {
|
||||
item.people.append(MediaPersonInfo::fromJson(val.toObject()));
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.contains("Studios")) {
|
||||
QJsonArray arr = obj["Studios"].toArray();
|
||||
for (const auto& val : arr) {
|
||||
item.studios.append(MediaStudioInfo::fromJson(val.toObject()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (obj.contains("ExternalUrls")) {
|
||||
QJsonArray arr = obj["ExternalUrls"].toArray();
|
||||
for (const auto& val : arr) {
|
||||
item.externalUrls.append(MediaExternalUrlInfo::fromJson(val.toObject()));
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.contains("MediaSources")) {
|
||||
QJsonArray arr = obj["MediaSources"].toArray();
|
||||
for (const auto& val : arr) {
|
||||
item.mediaSources.append(MediaSourceInfo::fromJson(val.toObject()));
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
249
src/qEmbyCore/models/media/mediaitem.h
Normal file
249
src/qEmbyCore/models/media/mediaitem.h
Normal file
@@ -0,0 +1,249 @@
|
||||
#ifndef MEDIAITEM_H
|
||||
#define MEDIAITEM_H
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QStringList>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include "../../qEmbyCore_global.h"
|
||||
|
||||
|
||||
#include "playbackinfo.h"
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaPersonInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString id MEMBER id)
|
||||
Q_PROPERTY(QString name MEMBER name)
|
||||
Q_PROPERTY(QString role MEMBER role)
|
||||
Q_PROPERTY(QString type MEMBER type)
|
||||
Q_PROPERTY(QString primaryImageTag MEMBER primaryImageTag)
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
QString role;
|
||||
QString type;
|
||||
QString primaryImageTag;
|
||||
|
||||
static MediaPersonInfo fromJson(const QJsonObject& obj);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaPersonInfo)
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaStudioInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString id MEMBER id)
|
||||
Q_PROPERTY(QString name MEMBER name)
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
|
||||
static MediaStudioInfo fromJson(const QJsonObject& obj);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaStudioInfo)
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaExternalUrlInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString name MEMBER name)
|
||||
Q_PROPERTY(QString url MEMBER url)
|
||||
|
||||
public:
|
||||
QString name;
|
||||
QString url;
|
||||
|
||||
static MediaExternalUrlInfo fromJson(const QJsonObject& obj);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaExternalUrlInfo)
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaImageInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString primaryTag MEMBER primaryTag)
|
||||
Q_PROPERTY(QString thumbTag MEMBER thumbTag)
|
||||
Q_PROPERTY(QString backdropTag MEMBER backdropTag)
|
||||
Q_PROPERTY(QString logoTag MEMBER logoTag)
|
||||
Q_PROPERTY(double primaryImageAspectRatio MEMBER primaryImageAspectRatio)
|
||||
Q_PROPERTY(QString primaryImageItemId MEMBER primaryImageItemId)
|
||||
|
||||
public:
|
||||
QString primaryTag;
|
||||
QString thumbTag;
|
||||
QString backdropTag;
|
||||
QString logoTag;
|
||||
double primaryImageAspectRatio = 0.0;
|
||||
QString primaryImageItemId;
|
||||
|
||||
|
||||
QString parentPrimaryTag;
|
||||
QString parentBackdropTag;
|
||||
QString parentThumbTag;
|
||||
QString parentImageItemId;
|
||||
|
||||
|
||||
|
||||
QPair<QString, QString> bestPoster() const;
|
||||
|
||||
|
||||
|
||||
QPair<QString, QString> bestThumb() const;
|
||||
|
||||
|
||||
|
||||
QPair<QString, QString> bestBackdrop() const;
|
||||
|
||||
|
||||
bool isParentTag(const QString& tag) const;
|
||||
|
||||
static MediaImageInfo fromJson(const QJsonObject& obj);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaImageInfo)
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaUserDataInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(bool isFavorite MEMBER isFavorite)
|
||||
Q_PROPERTY(double playedPercentage MEMBER playedPercentage)
|
||||
Q_PROPERTY(long long playbackPositionTicks MEMBER playbackPositionTicks)
|
||||
Q_PROPERTY(bool played MEMBER played)
|
||||
Q_PROPERTY(int playCount MEMBER playCount)
|
||||
Q_PROPERTY(QString lastPlayedDate MEMBER lastPlayedDate)
|
||||
|
||||
public:
|
||||
bool isFavorite = false;
|
||||
double playedPercentage = 0.0;
|
||||
long long playbackPositionTicks = 0;
|
||||
bool played = false;
|
||||
int playCount = 0;
|
||||
QString lastPlayedDate;
|
||||
|
||||
static MediaUserDataInfo fromJson(const QJsonObject& obj);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaUserDataInfo)
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaItem {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString id MEMBER id)
|
||||
Q_PROPERTY(QString name MEMBER name)
|
||||
Q_PROPERTY(QString originalTitle MEMBER originalTitle)
|
||||
Q_PROPERTY(QString sortName MEMBER sortName)
|
||||
Q_PROPERTY(QString type MEMBER type)
|
||||
Q_PROPERTY(QString mediaType MEMBER mediaType)
|
||||
Q_PROPERTY(QString collectionType MEMBER collectionType)
|
||||
Q_PROPERTY(QString collectionId MEMBER collectionId)
|
||||
Q_PROPERTY(QString collectionName MEMBER collectionName)
|
||||
Q_PROPERTY(QString playlistId MEMBER playlistId)
|
||||
Q_PROPERTY(QString playlistItemId MEMBER playlistItemId)
|
||||
Q_PROPERTY(QString seriesId MEMBER seriesId)
|
||||
Q_PROPERTY(QString seriesName MEMBER seriesName)
|
||||
Q_PROPERTY(QVariantMap providerIds MEMBER providerIds)
|
||||
Q_PROPERTY(int productionYear MEMBER productionYear)
|
||||
Q_PROPERTY(QString overview MEMBER overview)
|
||||
Q_PROPERTY(QString officialRating MEMBER officialRating)
|
||||
Q_PROPERTY(QString premiereDate MEMBER premiereDate)
|
||||
Q_PROPERTY(QString dateCreated MEMBER dateCreated)
|
||||
Q_PROPERTY(QString dateCreatedRaw MEMBER dateCreatedRaw)
|
||||
Q_PROPERTY(int criticRating MEMBER criticRating)
|
||||
Q_PROPERTY(bool canDownload MEMBER canDownload)
|
||||
|
||||
|
||||
Q_PROPERTY(QString container MEMBER container)
|
||||
Q_PROPERTY(QString path MEMBER path)
|
||||
Q_PROPERTY(long long size MEMBER size)
|
||||
Q_PROPERTY(long long bitrate MEMBER bitrate)
|
||||
Q_PROPERTY(int width MEMBER width)
|
||||
Q_PROPERTY(int height MEMBER height)
|
||||
Q_PROPERTY(int partCount MEMBER partCount)
|
||||
Q_PROPERTY(int childCount MEMBER childCount)
|
||||
Q_PROPERTY(int recursiveItemCount MEMBER recursiveItemCount)
|
||||
Q_PROPERTY(int parentIndexNumber MEMBER parentIndexNumber)
|
||||
Q_PROPERTY(int indexNumber MEMBER indexNumber)
|
||||
|
||||
public:
|
||||
|
||||
QString id;
|
||||
QString name;
|
||||
QString originalTitle;
|
||||
QString sortName;
|
||||
QString type;
|
||||
QString mediaType;
|
||||
QString collectionType;
|
||||
QString collectionId;
|
||||
QString collectionName;
|
||||
QString playlistId;
|
||||
QString playlistItemId;
|
||||
QString seriesId;
|
||||
QString seriesName;
|
||||
QVariantMap providerIds;
|
||||
|
||||
|
||||
int productionYear = 0;
|
||||
QString overview;
|
||||
QString officialRating;
|
||||
QString premiereDate;
|
||||
QString dateCreated;
|
||||
QString dateCreatedRaw;
|
||||
long long runTimeTicks = 0;
|
||||
double communityRating = 0.0;
|
||||
int criticRating = 0;
|
||||
bool canDownload = false;
|
||||
|
||||
|
||||
QString container;
|
||||
QString path;
|
||||
long long size = 0;
|
||||
long long bitrate = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int partCount = 1;
|
||||
int childCount = 0;
|
||||
int recursiveItemCount = 0;
|
||||
int parentIndexNumber = -1;
|
||||
int indexNumber = -1;
|
||||
|
||||
|
||||
QStringList genres;
|
||||
QStringList tags;
|
||||
QStringList taglines;
|
||||
QStringList remoteTrailers;
|
||||
|
||||
|
||||
MediaImageInfo images;
|
||||
MediaUserDataInfo userData;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QString resumeItemId;
|
||||
MediaUserDataInfo resumeUserData;
|
||||
bool hasResumeContext = false;
|
||||
|
||||
QList<MediaPersonInfo> people;
|
||||
QList<MediaStudioInfo> studios;
|
||||
QList<MediaExternalUrlInfo> externalUrls;
|
||||
QList<MediaSourceInfo> mediaSources;
|
||||
|
||||
|
||||
bool isFavorite() const { return userData.isFavorite; }
|
||||
QString primaryImageTag() const { return images.primaryTag; }
|
||||
QString backdropImageTag() const { return images.backdropTag; }
|
||||
|
||||
|
||||
|
||||
QString getPrimaryImageId() const {
|
||||
return images.primaryImageItemId.isEmpty() ? id : images.primaryImageItemId;
|
||||
}
|
||||
|
||||
static MediaItem fromJson(const QJsonObject& obj);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaItem)
|
||||
|
||||
#endif
|
||||
119
src/qEmbyCore/models/media/playbackinfo.cpp
Normal file
119
src/qEmbyCore/models/media/playbackinfo.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "playbackinfo.h"
|
||||
#include <QJsonArray>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
namespace {
|
||||
|
||||
QDateTime parseOptionalDateTime(const QJsonObject& json, const QStringList& keys)
|
||||
{
|
||||
for (const QString& key : keys) {
|
||||
const QString rawValue = json.value(key).toString().trimmed();
|
||||
if (rawValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QDateTime parsed = QDateTime::fromString(rawValue, Qt::ISODateWithMs);
|
||||
if (!parsed.isValid()) {
|
||||
parsed = QDateTime::fromString(rawValue, Qt::ISODate);
|
||||
}
|
||||
if (parsed.isValid()) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MediaStreamInfo MediaStreamInfo::fromJson(const QJsonObject& json) {
|
||||
MediaStreamInfo info;
|
||||
info.type = json["Type"].toString();
|
||||
info.codec = json["Codec"].toString();
|
||||
info.codecTag = json["CodecTag"].toString();
|
||||
info.title = json["Title"].toString();
|
||||
info.displayTitle = json["DisplayTitle"].toString();
|
||||
info.profile = json["Profile"].toString();
|
||||
info.level = json["Level"].toInt();
|
||||
info.width = json["Width"].toInt();
|
||||
info.height = json["Height"].toInt();
|
||||
info.aspectRatio = json["AspectRatio"].toString();
|
||||
info.isInterlaced = json["IsInterlaced"].toBool();
|
||||
info.realFrameRate = json["RealFrameRate"].toDouble();
|
||||
info.bitRate = json["BitRate"].toVariant().toLongLong();
|
||||
info.bitDepth = json["BitDepth"].toInt();
|
||||
info.pixelFormat = json["PixelFormat"].toString();
|
||||
info.refFrames = json["RefFrames"].toInt();
|
||||
info.language = json["Language"].toString();
|
||||
info.channelLayout = json["ChannelLayout"].toString();
|
||||
info.channels = json["Channels"].toInt();
|
||||
info.sampleRate = json["SampleRate"].toInt();
|
||||
info.isDefault = json["IsDefault"].toBool();
|
||||
|
||||
|
||||
info.displayLanguage = json["DisplayLanguage"].toString();
|
||||
info.isForced = json["IsForced"].toBool();
|
||||
info.isHearingImpaired = json["IsHearingImpaired"].toBool();
|
||||
|
||||
|
||||
info.index = json.contains("Index") ? json["Index"].toInt() : -1;
|
||||
|
||||
info.isExternal = json["IsExternal"].toBool();
|
||||
info.deliveryMethod = json["DeliveryMethod"].toString();
|
||||
info.deliveryUrl = json["DeliveryUrl"].toString();
|
||||
info.isExternalUrl = json["IsExternalUrl"].toBool();
|
||||
info.isTextSubtitleStream = json["IsTextSubtitleStream"].toBool();
|
||||
info.supportsExternalStream = json["SupportsExternalStream"].toBool();
|
||||
info.path = json["Path"].toString();
|
||||
info.protocol = json["Protocol"].toString();
|
||||
info.extendedVideoType = json["ExtendedVideoType"].toString();
|
||||
info.extendedVideoSubType = json["ExtendedVideoSubType"].toString();
|
||||
info.attachmentSize = json["AttachmentSize"].toVariant().toLongLong();
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
MediaSourceInfo MediaSourceInfo::fromJson(const QJsonObject& json) {
|
||||
MediaSourceInfo info;
|
||||
info.id = json["Id"].toString();
|
||||
info.name = json["Name"].toString();
|
||||
info.path = json["Path"].toString();
|
||||
info.container = json["Container"].toString();
|
||||
info.directStreamUrl = json["DirectStreamUrl"].toString();
|
||||
|
||||
if (json.contains("Size")) {
|
||||
info.size = json["Size"].toVariant().toLongLong();
|
||||
}
|
||||
|
||||
if (json.contains("RunTimeTicks")) {
|
||||
info.runTimeTicks = json["RunTimeTicks"].toVariant().toLongLong();
|
||||
}
|
||||
|
||||
info.dateCreated = parseOptionalDateTime(
|
||||
json, {QStringLiteral("DateCreated"), QStringLiteral("CreatedAt")});
|
||||
info.dateModified = parseOptionalDateTime(
|
||||
json, {QStringLiteral("DateModified"), QStringLiteral("ModifiedAt"),
|
||||
QStringLiteral("LastModified"), QStringLiteral("UpdatedAt")});
|
||||
|
||||
if (json.contains("MediaStreams")) {
|
||||
QJsonArray streamsArr = json["MediaStreams"].toArray();
|
||||
for (const auto& val : streamsArr) {
|
||||
info.mediaStreams.append(MediaStreamInfo::fromJson(val.toObject()));
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
PlaybackInfo PlaybackInfo::fromJson(const QJsonObject& json) {
|
||||
PlaybackInfo info;
|
||||
info.playSessionId = json["PlaySessionId"].toString();
|
||||
|
||||
if (json.contains("MediaSources")) {
|
||||
QJsonArray sourcesArr = json["MediaSources"].toArray();
|
||||
for (const auto& val : sourcesArr) {
|
||||
info.mediaSources.append(MediaSourceInfo::fromJson(val.toObject()));
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
140
src/qEmbyCore/models/media/playbackinfo.h
Normal file
140
src/qEmbyCore/models/media/playbackinfo.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#ifndef PLAYBACKINFO_H
|
||||
#define PLAYBACKINFO_H
|
||||
|
||||
#include "../../qEmbyCore_global.h"
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QJsonObject>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaStreamInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString type MEMBER type)
|
||||
Q_PROPERTY(QString codec MEMBER codec)
|
||||
Q_PROPERTY(QString codecTag MEMBER codecTag)
|
||||
Q_PROPERTY(QString title MEMBER title)
|
||||
Q_PROPERTY(QString displayTitle MEMBER displayTitle)
|
||||
Q_PROPERTY(QString profile MEMBER profile)
|
||||
Q_PROPERTY(int level MEMBER level)
|
||||
Q_PROPERTY(int width MEMBER width)
|
||||
Q_PROPERTY(int height MEMBER height)
|
||||
Q_PROPERTY(QString aspectRatio MEMBER aspectRatio)
|
||||
Q_PROPERTY(bool isInterlaced MEMBER isInterlaced)
|
||||
Q_PROPERTY(float realFrameRate MEMBER realFrameRate)
|
||||
Q_PROPERTY(long long bitRate MEMBER bitRate)
|
||||
Q_PROPERTY(int bitDepth MEMBER bitDepth)
|
||||
Q_PROPERTY(QString pixelFormat MEMBER pixelFormat)
|
||||
Q_PROPERTY(int refFrames MEMBER refFrames)
|
||||
Q_PROPERTY(QString language MEMBER language)
|
||||
Q_PROPERTY(QString channelLayout MEMBER channelLayout)
|
||||
Q_PROPERTY(int channels MEMBER channels)
|
||||
Q_PROPERTY(int sampleRate MEMBER sampleRate)
|
||||
Q_PROPERTY(bool isDefault MEMBER isDefault)
|
||||
|
||||
|
||||
Q_PROPERTY(QString displayLanguage MEMBER displayLanguage)
|
||||
Q_PROPERTY(bool isForced MEMBER isForced)
|
||||
Q_PROPERTY(bool isHearingImpaired MEMBER isHearingImpaired)
|
||||
Q_PROPERTY(int index MEMBER index)
|
||||
Q_PROPERTY(bool isExternal MEMBER isExternal)
|
||||
Q_PROPERTY(QString deliveryMethod MEMBER deliveryMethod)
|
||||
Q_PROPERTY(QString deliveryUrl MEMBER deliveryUrl)
|
||||
Q_PROPERTY(bool isExternalUrl MEMBER isExternalUrl)
|
||||
Q_PROPERTY(bool isTextSubtitleStream MEMBER isTextSubtitleStream)
|
||||
Q_PROPERTY(bool supportsExternalStream MEMBER supportsExternalStream)
|
||||
Q_PROPERTY(QString path MEMBER path)
|
||||
Q_PROPERTY(QString protocol MEMBER protocol)
|
||||
Q_PROPERTY(QString extendedVideoType MEMBER extendedVideoType)
|
||||
Q_PROPERTY(QString extendedVideoSubType MEMBER extendedVideoSubType)
|
||||
Q_PROPERTY(long long attachmentSize MEMBER attachmentSize)
|
||||
|
||||
public:
|
||||
QString type;
|
||||
QString codec;
|
||||
QString codecTag;
|
||||
QString title;
|
||||
QString displayTitle;
|
||||
QString profile;
|
||||
int level = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
QString aspectRatio;
|
||||
bool isInterlaced = false;
|
||||
float realFrameRate = 0.0f;
|
||||
long long bitRate = 0;
|
||||
int bitDepth = 0;
|
||||
QString pixelFormat;
|
||||
int refFrames = 0;
|
||||
QString language;
|
||||
QString channelLayout;
|
||||
int channels = 0;
|
||||
int sampleRate = 0;
|
||||
bool isDefault = false;
|
||||
|
||||
|
||||
QString displayLanguage;
|
||||
bool isForced = false;
|
||||
bool isHearingImpaired = false;
|
||||
int index = -1;
|
||||
bool isExternal = false;
|
||||
QString deliveryMethod;
|
||||
QString deliveryUrl;
|
||||
bool isExternalUrl = false;
|
||||
bool isTextSubtitleStream = false;
|
||||
bool supportsExternalStream = false;
|
||||
QString path;
|
||||
QString protocol;
|
||||
QString extendedVideoType;
|
||||
QString extendedVideoSubType;
|
||||
long long attachmentSize = 0;
|
||||
|
||||
static MediaStreamInfo fromJson(const QJsonObject& json);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaStreamInfo)
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT MediaSourceInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString id MEMBER id)
|
||||
Q_PROPERTY(QString name MEMBER name)
|
||||
Q_PROPERTY(QString path MEMBER path)
|
||||
Q_PROPERTY(QString container MEMBER container)
|
||||
Q_PROPERTY(long long size MEMBER size)
|
||||
Q_PROPERTY(QString directStreamUrl MEMBER directStreamUrl)
|
||||
Q_PROPERTY(long long runTimeTicks MEMBER runTimeTicks)
|
||||
Q_PROPERTY(QDateTime dateCreated MEMBER dateCreated)
|
||||
Q_PROPERTY(QDateTime dateModified MEMBER dateModified)
|
||||
|
||||
public:
|
||||
QString id;
|
||||
QString name;
|
||||
QString path;
|
||||
QString container;
|
||||
long long size = 0;
|
||||
QString directStreamUrl;
|
||||
long long runTimeTicks = 0;
|
||||
QDateTime dateCreated;
|
||||
QDateTime dateModified;
|
||||
QList<MediaStreamInfo> mediaStreams;
|
||||
|
||||
static MediaSourceInfo fromJson(const QJsonObject& json);
|
||||
};
|
||||
Q_DECLARE_METATYPE(MediaSourceInfo)
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT PlaybackInfo {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString playSessionId MEMBER playSessionId)
|
||||
|
||||
public:
|
||||
QString playSessionId;
|
||||
QList<MediaSourceInfo> mediaSources;
|
||||
|
||||
static PlaybackInfo fromJson(const QJsonObject& json);
|
||||
};
|
||||
Q_DECLARE_METATYPE(PlaybackInfo)
|
||||
|
||||
#endif
|
||||
13
src/qEmbyCore/models/media/playerlaunchcontext.h
Normal file
13
src/qEmbyCore/models/media/playerlaunchcontext.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef PLAYERLAUNCHCONTEXT_H
|
||||
#define PLAYERLAUNCHCONTEXT_H
|
||||
|
||||
#include "mediaitem.h"
|
||||
#include "playbackinfo.h"
|
||||
|
||||
struct PlayerLaunchContext {
|
||||
MediaItem mediaItem;
|
||||
MediaSourceInfo selectedSource;
|
||||
};
|
||||
Q_DECLARE_METATYPE(PlayerLaunchContext)
|
||||
|
||||
#endif
|
||||
92
src/qEmbyCore/models/profile/proxyconfig.cpp
Normal file
92
src/qEmbyCore/models/profile/proxyconfig.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "proxyconfig.h"
|
||||
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QNetworkProxyQuery>
|
||||
#include <QString>
|
||||
|
||||
QNetworkProxy ProxyConfig::toQNetworkProxy() const {
|
||||
switch (mode) {
|
||||
case None:
|
||||
return QNetworkProxy(QNetworkProxy::NoProxy);
|
||||
|
||||
case System: {
|
||||
|
||||
const auto proxies =
|
||||
QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery());
|
||||
if (proxies.isEmpty()) {
|
||||
return QNetworkProxy(QNetworkProxy::NoProxy);
|
||||
}
|
||||
return proxies.first();
|
||||
}
|
||||
|
||||
case Custom: {
|
||||
if (host.trimmed().isEmpty() || port == 0) {
|
||||
return QNetworkProxy(QNetworkProxy::NoProxy);
|
||||
}
|
||||
const QNetworkProxy::ProxyType qtType =
|
||||
(type == Socks5) ? QNetworkProxy::Socks5Proxy
|
||||
: QNetworkProxy::HttpProxy;
|
||||
return QNetworkProxy(qtType, host.trimmed(), port,
|
||||
username, password);
|
||||
}
|
||||
}
|
||||
return QNetworkProxy(QNetworkProxy::NoProxy);
|
||||
}
|
||||
|
||||
QJsonObject ProxyConfig::toJson() const {
|
||||
QJsonObject obj;
|
||||
obj["mode"] = static_cast<int>(mode);
|
||||
obj["type"] = static_cast<int>(type);
|
||||
obj["host"] = host;
|
||||
obj["port"] = static_cast<int>(port);
|
||||
obj["username"] = username;
|
||||
obj["password"] = password;
|
||||
obj["bypassLocalhost"] = bypassLocalhost;
|
||||
return obj;
|
||||
}
|
||||
|
||||
ProxyConfig ProxyConfig::fromJson(const QJsonObject& obj) {
|
||||
ProxyConfig cfg;
|
||||
if (obj.isEmpty()) {
|
||||
return cfg;
|
||||
}
|
||||
cfg.mode = static_cast<Mode>(obj.value("mode").toInt(0));
|
||||
cfg.type = static_cast<Type>(obj.value("type").toInt(0));
|
||||
cfg.host = obj.value("host").toString();
|
||||
const int parsedPort = obj.value("port").toInt(0);
|
||||
cfg.port = (parsedPort > 0 && parsedPort <= 65535)
|
||||
? static_cast<quint16>(parsedPort)
|
||||
: 0;
|
||||
cfg.username = obj.value("username").toString();
|
||||
cfg.password = obj.value("password").toString();
|
||||
cfg.bypassLocalhost = obj.value("bypassLocalhost").toBool(true);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
QString ProxyConfig::summary() const {
|
||||
switch (mode) {
|
||||
case None:
|
||||
return QStringLiteral("none");
|
||||
case System:
|
||||
return QStringLiteral("system");
|
||||
case Custom: {
|
||||
const QString typeLabel =
|
||||
(type == Socks5) ? QStringLiteral("socks5") : QStringLiteral("http");
|
||||
const QString credLabel =
|
||||
username.isEmpty() ? QString() : QStringLiteral(" auth=***");
|
||||
return QStringLiteral("custom %1://%2:%3%4")
|
||||
.arg(typeLabel,
|
||||
host.trimmed(),
|
||||
QString::number(port),
|
||||
credLabel);
|
||||
}
|
||||
}
|
||||
return QStringLiteral("unknown");
|
||||
}
|
||||
|
||||
bool ProxyConfig::operator==(const ProxyConfig& other) const {
|
||||
return mode == other.mode && type == other.type && host == other.host &&
|
||||
port == other.port && username == other.username &&
|
||||
password == other.password &&
|
||||
bypassLocalhost == other.bypassLocalhost;
|
||||
}
|
||||
59
src/qEmbyCore/models/profile/proxyconfig.h
Normal file
59
src/qEmbyCore/models/profile/proxyconfig.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef PROXYCONFIG_H
|
||||
#define PROXYCONFIG_H
|
||||
|
||||
#include "../../qEmbyCore_global.h"
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkProxy>
|
||||
#include <QString>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct QEMBYCORE_EXPORT ProxyConfig {
|
||||
enum Mode {
|
||||
None = 0,
|
||||
System = 1,
|
||||
Custom = 2,
|
||||
};
|
||||
|
||||
enum Type {
|
||||
Http = 0,
|
||||
Socks5 = 1,
|
||||
};
|
||||
|
||||
Mode mode = None;
|
||||
Type type = Http;
|
||||
QString host;
|
||||
quint16 port = 0;
|
||||
QString username;
|
||||
QString password;
|
||||
bool bypassLocalhost = true;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QNetworkProxy toQNetworkProxy() const;
|
||||
|
||||
|
||||
QJsonObject toJson() const;
|
||||
static ProxyConfig fromJson(const QJsonObject& obj);
|
||||
|
||||
|
||||
bool isExplicit() const { return mode != None; }
|
||||
|
||||
|
||||
QString summary() const;
|
||||
|
||||
bool operator==(const ProxyConfig& other) const;
|
||||
bool operator!=(const ProxyConfig& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
#endif
|
||||
35
src/qEmbyCore/models/profile/serverprofile.h
Normal file
35
src/qEmbyCore/models/profile/serverprofile.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef SERVERPROFILE_H
|
||||
#define SERVERPROFILE_H
|
||||
|
||||
#include "proxyconfig.h"
|
||||
#include <QString>
|
||||
#include <QUuid>
|
||||
|
||||
struct ServerProfile {
|
||||
enum ServerType { Emby, Jellyfin };
|
||||
|
||||
QString id = QUuid::createUuid().toString();
|
||||
QString name;
|
||||
QString url;
|
||||
ServerType type = Emby;
|
||||
bool ignoreSslVerification = false;
|
||||
|
||||
QString userId;
|
||||
QString userName;
|
||||
QString accessToken;
|
||||
QString deviceId;
|
||||
bool isAdmin = false;
|
||||
bool canDownloadMedia = false;
|
||||
|
||||
QString iconBase64;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool useGlobalProxy = false;
|
||||
ProxyConfig proxy;
|
||||
|
||||
bool isValid() const { return !accessToken.isEmpty(); }
|
||||
};
|
||||
#endif
|
||||
370
src/qEmbyCore/models/sync/configbundle.cpp
Normal file
370
src/qEmbyCore/models/sync/configbundle.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
#include "configbundle.h"
|
||||
|
||||
#include "../../config/configstore.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonValue>
|
||||
#include <QStandardPaths>
|
||||
#include <QSysInfo>
|
||||
#include <QVariant>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
constexpr char kKSchemaVersion[] = "schemaVersion";
|
||||
constexpr char kKAppVersion[] = "appVersion";
|
||||
constexpr char kKDeviceName[] = "deviceName";
|
||||
constexpr char kKOsName[] = "osName";
|
||||
constexpr char kKOsPretty[] = "osPretty";
|
||||
constexpr char kKExportedAt[] = "exportedAt";
|
||||
|
||||
|
||||
constexpr char kKMetadata[] = "metadata";
|
||||
constexpr char kKServers[] = "servers";
|
||||
constexpr char kKConfigEntries[] = "configEntries";
|
||||
|
||||
|
||||
QString serversJsonPath()
|
||||
{
|
||||
const QString dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
return QDir(dir).filePath(QStringLiteral("servers.json"));
|
||||
}
|
||||
|
||||
QString compactProductType(QString type)
|
||||
{
|
||||
type = type.trimmed().toLower();
|
||||
if (type.isEmpty())
|
||||
{
|
||||
return QStringLiteral("OS");
|
||||
}
|
||||
if (type == QStringLiteral("ubuntu"))
|
||||
return QStringLiteral("Ubuntu");
|
||||
if (type == QStringLiteral("debian"))
|
||||
return QStringLiteral("Debian");
|
||||
if (type == QStringLiteral("arch"))
|
||||
return QStringLiteral("Arch");
|
||||
if (type == QStringLiteral("fedora"))
|
||||
return QStringLiteral("Fedora");
|
||||
if (type == QStringLiteral("centos"))
|
||||
return QStringLiteral("CentOS");
|
||||
if (type == QStringLiteral("opensuse"))
|
||||
return QStringLiteral("openSUSE");
|
||||
if (type == QStringLiteral("manjaro"))
|
||||
return QStringLiteral("Manjaro");
|
||||
if (type == QStringLiteral("rhel"))
|
||||
return QStringLiteral("RHEL");
|
||||
if (type == QStringLiteral("linux"))
|
||||
return QStringLiteral("Linux");
|
||||
type[0] = type.at(0).toUpper();
|
||||
return type;
|
||||
}
|
||||
|
||||
int windowsBuildNumber()
|
||||
{
|
||||
const QStringList parts = QSysInfo::kernelVersion().split(QLatin1Char('.'));
|
||||
bool ok = false;
|
||||
const int build = parts.size() >= 3 ? parts.at(2).toInt(&ok) : 0;
|
||||
return ok ? build : 0;
|
||||
}
|
||||
|
||||
|
||||
QString readableOsName()
|
||||
{
|
||||
const QString type = QSysInfo::productType().toLower();
|
||||
const QString version = QSysInfo::productVersion().trimmed();
|
||||
if (type == QStringLiteral("windows"))
|
||||
{
|
||||
const int build = windowsBuildNumber();
|
||||
if (build >= 22000)
|
||||
return QStringLiteral("Win11");
|
||||
if (build > 0)
|
||||
return QStringLiteral("Win10");
|
||||
return version.isEmpty() ? QStringLiteral("Windows")
|
||||
: QStringLiteral("Windows%1").arg(version);
|
||||
}
|
||||
if (type == QStringLiteral("macos") || type == QStringLiteral("osx"))
|
||||
return version.isEmpty() ? QStringLiteral("macOS")
|
||||
: QStringLiteral("macOS%1").arg(version);
|
||||
|
||||
const QString name = compactProductType(type);
|
||||
return version.isEmpty() ? name : name + version;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QJsonValue variantToJson(const QVariant &v)
|
||||
{
|
||||
if (!v.isValid() || v.isNull()) {
|
||||
return QJsonValue::Null;
|
||||
}
|
||||
|
||||
switch (static_cast<QMetaType::Type>(v.userType())) {
|
||||
case QMetaType::Bool:
|
||||
return v.toBool();
|
||||
case QMetaType::Int:
|
||||
case QMetaType::UInt:
|
||||
case QMetaType::LongLong:
|
||||
case QMetaType::ULongLong:
|
||||
return v.toLongLong();
|
||||
case QMetaType::Double:
|
||||
case QMetaType::Float:
|
||||
return v.toDouble();
|
||||
case QMetaType::QStringList: {
|
||||
QJsonArray arr;
|
||||
for (const QString &s : v.toStringList()) {
|
||||
arr.append(s);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
case QMetaType::QByteArray:
|
||||
|
||||
{
|
||||
const QByteArray ba = v.toByteArray();
|
||||
const QJsonDocument tryDoc = QJsonDocument::fromJson(ba);
|
||||
if (!tryDoc.isNull()) {
|
||||
if (tryDoc.isObject()) return tryDoc.object();
|
||||
if (tryDoc.isArray()) return tryDoc.array();
|
||||
}
|
||||
return QString::fromUtf8(ba);
|
||||
}
|
||||
case QMetaType::QString:
|
||||
default:
|
||||
return v.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
QVariant jsonToVariant(const QJsonValue &v)
|
||||
{
|
||||
switch (v.type()) {
|
||||
case QJsonValue::Bool:
|
||||
return v.toBool();
|
||||
case QJsonValue::Double: {
|
||||
const double d = v.toDouble();
|
||||
const qlonglong i = static_cast<qlonglong>(d);
|
||||
if (static_cast<double>(i) == d) {
|
||||
return QVariant::fromValue(i);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
case QJsonValue::String:
|
||||
return v.toString();
|
||||
case QJsonValue::Array:
|
||||
return QString::fromUtf8(
|
||||
QJsonDocument(v.toArray()).toJson(QJsonDocument::Compact));
|
||||
case QJsonValue::Object:
|
||||
return QString::fromUtf8(
|
||||
QJsonDocument(v.toObject()).toJson(QJsonDocument::Compact));
|
||||
case QJsonValue::Null:
|
||||
case QJsonValue::Undefined:
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QJsonObject ConfigBundleMetadata::toJson() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj.insert(QString::fromLatin1(kKSchemaVersion), schemaVersion);
|
||||
obj.insert(QString::fromLatin1(kKAppVersion), appVersion);
|
||||
obj.insert(QString::fromLatin1(kKDeviceName), deviceName);
|
||||
obj.insert(QString::fromLatin1(kKOsName), osName);
|
||||
obj.insert(QString::fromLatin1(kKOsPretty), osPretty);
|
||||
obj.insert(QString::fromLatin1(kKExportedAt), exportedAt);
|
||||
return obj;
|
||||
}
|
||||
|
||||
ConfigBundleMetadata ConfigBundleMetadata::fromJson(const QJsonObject &obj)
|
||||
{
|
||||
ConfigBundleMetadata m;
|
||||
m.schemaVersion = obj.value(QString::fromLatin1(kKSchemaVersion)).toInt(1);
|
||||
m.appVersion = obj.value(QString::fromLatin1(kKAppVersion)).toString();
|
||||
m.deviceName = obj.value(QString::fromLatin1(kKDeviceName)).toString();
|
||||
m.osName = obj.value(QString::fromLatin1(kKOsName)).toString();
|
||||
m.osPretty = obj.value(QString::fromLatin1(kKOsPretty)).toString();
|
||||
m.exportedAt = static_cast<qint64>(obj.value(QString::fromLatin1(kKExportedAt)).toDouble(0.0));
|
||||
return m;
|
||||
}
|
||||
|
||||
ConfigBundleMetadata ConfigBundleMetadata::current()
|
||||
{
|
||||
ConfigBundleMetadata m;
|
||||
m.schemaVersion = 1;
|
||||
m.appVersion = QCoreApplication::applicationVersion();
|
||||
if (m.appVersion.isEmpty()) {
|
||||
m.appVersion = QStringLiteral("dev");
|
||||
}
|
||||
m.deviceName = QSysInfo::machineHostName();
|
||||
m.osName = readableOsName();
|
||||
m.osPretty = QSysInfo::prettyProductName();
|
||||
m.exportedAt = QDateTime::currentMSecsSinceEpoch();
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const QStringList &ConfigBundle::includedNamespaces()
|
||||
{
|
||||
static const QStringList list = {
|
||||
QStringLiteral("general/"),
|
||||
QStringLiteral("network/"),
|
||||
QStringLiteral("appearance/"),
|
||||
QStringLiteral("library/"),
|
||||
QStringLiteral("player/"),
|
||||
QStringLiteral("danmaku/"),
|
||||
QStringLiteral("ext_player/"),
|
||||
QStringLiteral("search/"),
|
||||
QStringLiteral("sort/"),
|
||||
QStringLiteral("view/"),
|
||||
QStringLiteral("server/"),
|
||||
|
||||
|
||||
QStringLiteral("app/"),
|
||||
};
|
||||
return list;
|
||||
}
|
||||
|
||||
bool ConfigBundle::shouldExcludeKey(const QString &key)
|
||||
{
|
||||
|
||||
static const QStringList exactBlacklist = {
|
||||
QStringLiteral("general/last_selected_server_id"),
|
||||
QStringLiteral("app/last_selected_server_id"),
|
||||
QStringLiteral("appearance/startup_window_state"),
|
||||
QStringLiteral("ext_player/detected_list"),
|
||||
QStringLiteral("player/last_subtitle_dir"),
|
||||
};
|
||||
if (exactBlacklist.contains(key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const QStringList prefixBlacklist = {
|
||||
QStringLiteral("cache/"),
|
||||
QStringLiteral("player/external_subtitle/"),
|
||||
QStringLiteral("download/"),
|
||||
};
|
||||
for (const QString &prefix : prefixBlacklist) {
|
||||
if (key.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QJsonObject ConfigBundle::toJson() const
|
||||
{
|
||||
QJsonObject root;
|
||||
root.insert(QString::fromLatin1(kKMetadata), metadata.toJson());
|
||||
root.insert(QString::fromLatin1(kKServers), servers);
|
||||
root.insert(QString::fromLatin1(kKConfigEntries), configEntries);
|
||||
return root;
|
||||
}
|
||||
|
||||
QByteArray ConfigBundle::serialize(bool indented) const
|
||||
{
|
||||
return QJsonDocument(toJson()).toJson(indented ? QJsonDocument::Indented
|
||||
: QJsonDocument::Compact);
|
||||
}
|
||||
|
||||
ConfigBundle ConfigBundle::fromJson(const QJsonObject &obj)
|
||||
{
|
||||
ConfigBundle b;
|
||||
b.metadata = ConfigBundleMetadata::fromJson(
|
||||
obj.value(QString::fromLatin1(kKMetadata)).toObject());
|
||||
b.servers = obj.value(QString::fromLatin1(kKServers)).toArray();
|
||||
b.configEntries = obj.value(QString::fromLatin1(kKConfigEntries)).toObject();
|
||||
return b;
|
||||
}
|
||||
|
||||
std::optional<ConfigBundle> ConfigBundle::deserialize(const QByteArray &bytes)
|
||||
{
|
||||
QJsonParseError err{};
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(bytes, &err);
|
||||
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||
qWarning() << "[ConfigBundle] deserialize parse failed:" << err.errorString();
|
||||
return std::nullopt;
|
||||
}
|
||||
return fromJson(doc.object());
|
||||
}
|
||||
|
||||
|
||||
|
||||
ConfigBundle ConfigBundle::collectFromLocal()
|
||||
{
|
||||
ConfigBundle b;
|
||||
b.metadata = ConfigBundleMetadata::current();
|
||||
|
||||
|
||||
{
|
||||
QFile f(serversJsonPath());
|
||||
if (f.open(QIODevice::ReadOnly)) {
|
||||
const QByteArray bytes = f.readAll();
|
||||
f.close();
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(bytes);
|
||||
if (doc.isArray()) {
|
||||
b.servers = doc.array();
|
||||
} else {
|
||||
qWarning() << "[ConfigBundle] servers.json is not array, skipped";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "[ConfigBundle] servers.json not present at"
|
||||
<< serversJsonPath();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ConfigStore *store = ConfigStore::instance();
|
||||
if (store) {
|
||||
const QStringList allKeys = store->allKeys();
|
||||
const QStringList &nsList = includedNamespaces();
|
||||
|
||||
for (const QString &key : allKeys) {
|
||||
|
||||
bool inWhitelist = false;
|
||||
for (const QString &ns : nsList) {
|
||||
if (key.startsWith(ns)) {
|
||||
inWhitelist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inWhitelist) {
|
||||
continue;
|
||||
}
|
||||
if (shouldExcludeKey(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QVariant val = store->get<QVariant>(key);
|
||||
b.configEntries.insert(key, variantToJson(val));
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "[ConfigBundle] collectFromLocal"
|
||||
<< "| servers:" << b.servers.size()
|
||||
<< "| entries:" << b.configEntries.size()
|
||||
<< "| host:" << b.metadata.deviceName
|
||||
<< "| os:" << b.metadata.osPretty
|
||||
<< "| app:" << b.metadata.appVersion;
|
||||
return b;
|
||||
}
|
||||
80
src/qEmbyCore/models/sync/configbundle.h
Normal file
80
src/qEmbyCore/models/sync/configbundle.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef CONFIGBUNDLE_H
|
||||
#define CONFIGBUNDLE_H
|
||||
|
||||
#include "../../qEmbyCore_global.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <optional>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum class MergeStrategy
|
||||
{
|
||||
Replace = 0,
|
||||
Merge = 1,
|
||||
LocalWins = 2,
|
||||
};
|
||||
|
||||
struct QEMBYCORE_EXPORT ConfigBundleMetadata
|
||||
{
|
||||
int schemaVersion = 1;
|
||||
QString appVersion;
|
||||
QString deviceName;
|
||||
QString osName;
|
||||
QString osPretty;
|
||||
qint64 exportedAt = 0;
|
||||
|
||||
QJsonObject toJson() const;
|
||||
static ConfigBundleMetadata fromJson(const QJsonObject &obj);
|
||||
|
||||
|
||||
static ConfigBundleMetadata current();
|
||||
};
|
||||
|
||||
class QEMBYCORE_EXPORT ConfigBundle
|
||||
{
|
||||
public:
|
||||
ConfigBundleMetadata metadata;
|
||||
QJsonArray servers;
|
||||
QJsonObject configEntries;
|
||||
|
||||
|
||||
QJsonObject toJson() const;
|
||||
QByteArray serialize(bool indented = false) const;
|
||||
|
||||
static ConfigBundle fromJson(const QJsonObject &obj);
|
||||
static std::optional<ConfigBundle> deserialize(const QByteArray &bytes);
|
||||
|
||||
|
||||
|
||||
|
||||
static ConfigBundle collectFromLocal();
|
||||
|
||||
|
||||
|
||||
static const QStringList &includedNamespaces();
|
||||
|
||||
|
||||
static bool shouldExcludeKey(const QString &key);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user