mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-02-09 13:03:09 +00:00
Import: Improve Heroic cover handling
Prefer cached cover images when available and import sideload apps. Also accept Legendary-only setups and normalize cover metadata from the Heroic configuration.
This commit is contained in:
parent
0897aa8769
commit
9c3c0e1dfd
2 changed files with 415 additions and 32 deletions
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "heroicimporter.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
|
|
@ -27,7 +28,7 @@ QString HeroicImporter::platformId() const
|
|||
|
||||
bool HeroicImporter::isAvailable() const
|
||||
{
|
||||
return directoryExists(findHeroicConfigPath());
|
||||
return directoryExists(findHeroicConfigPath()) || directoryExists(findLegendaryConfigPath());
|
||||
}
|
||||
|
||||
QString HeroicImporter::findHeroicConfigPath() const
|
||||
|
|
@ -44,17 +45,158 @@ QString HeroicImporter::findHeroicConfigPath() const
|
|||
return {};
|
||||
}
|
||||
|
||||
QString HeroicImporter::findLegendaryConfigPath() const
|
||||
{
|
||||
QStringList possiblePaths = {expandPath(QStringLiteral("~/.config/legendary")),
|
||||
expandPath(QStringLiteral("~/.var/app/com.heroicgameslauncher.hgl/config/legendary"))};
|
||||
|
||||
for (const QString &path : possiblePaths) {
|
||||
if (directoryExists(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QString HeroicImporter::launchCommandForApp(const QString &appName) const
|
||||
{
|
||||
if (!findExecutable(QStringLiteral("heroic")).isEmpty()) {
|
||||
return QStringLiteral("heroic --launch %1").arg(appName);
|
||||
}
|
||||
if (!findExecutable(QStringLiteral("flatpak")).isEmpty()) {
|
||||
return QStringLiteral("flatpak run com.heroicgameslauncher.hgl --launch %1").arg(appName);
|
||||
}
|
||||
return QStringLiteral("heroic --launch %1").arg(appName);
|
||||
}
|
||||
|
||||
QUrl HeroicImporter::extractCoverUrl(const QJsonObject &gameObj) const
|
||||
{
|
||||
static const QStringList stringKeys = {
|
||||
QStringLiteral("coverUrl"),
|
||||
QStringLiteral("cover_url"),
|
||||
QStringLiteral("cover"),
|
||||
QStringLiteral("image"),
|
||||
QStringLiteral("imageUrl"),
|
||||
QStringLiteral("image_url"),
|
||||
QStringLiteral("art_cover"),
|
||||
QStringLiteral("artCover"),
|
||||
QStringLiteral("art_square"),
|
||||
QStringLiteral("artSquare"),
|
||||
QStringLiteral("thumbnail"),
|
||||
};
|
||||
|
||||
for (const QString &key : stringKeys) {
|
||||
const QString urlString = gameObj.value(key).toString();
|
||||
if (!urlString.isEmpty()) {
|
||||
const QUrl url(urlString);
|
||||
if (url.isValid()) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QJsonValue keyImagesValue = gameObj.value(QStringLiteral("keyImages"));
|
||||
if (keyImagesValue.isArray()) {
|
||||
const QJsonArray keyImages = keyImagesValue.toArray();
|
||||
for (const QJsonValue &imgValue : keyImages) {
|
||||
const QJsonObject imgObj = imgValue.toObject();
|
||||
const QString type = imgObj.value(QStringLiteral("type")).toString();
|
||||
if (type == QStringLiteral("DieselGameBoxTall") || type == QStringLiteral("Thumbnail") || type == QStringLiteral("Poster")) {
|
||||
const QString urlString = imgObj.value(QStringLiteral("url")).toString();
|
||||
if (!urlString.isEmpty()) {
|
||||
const QUrl url(urlString);
|
||||
if (url.isValid()) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QJsonValue imagesValue = gameObj.value(QStringLiteral("images"));
|
||||
if (imagesValue.isArray()) {
|
||||
const QJsonArray images = imagesValue.toArray();
|
||||
for (const QJsonValue &imgValue : images) {
|
||||
const QJsonObject imgObj = imgValue.toObject();
|
||||
const QString urlString = imgObj.value(QStringLiteral("url")).toString();
|
||||
if (!urlString.isEmpty()) {
|
||||
const QUrl url(urlString);
|
||||
if (url.isValid()) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QUrl HeroicImporter::preferCachedCoverUrl(const QUrl &coverUrl) const
|
||||
{
|
||||
if (!coverUrl.isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (coverUrl.isLocalFile() || coverUrl.scheme() == QLatin1String("file")) {
|
||||
return coverUrl;
|
||||
}
|
||||
|
||||
QString urlString = coverUrl.toString();
|
||||
|
||||
// Heroic uses an imagecache:// scheme which percent-encodes the real image URL.
|
||||
if (coverUrl.scheme() == QLatin1String("imagecache")) {
|
||||
const QString prefix = QStringLiteral("imagecache://");
|
||||
if (urlString.startsWith(prefix)) {
|
||||
urlString = QUrl::fromPercentEncoding(urlString.mid(prefix.size()).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
const QUrl realUrl(urlString);
|
||||
if (!realUrl.isValid()) {
|
||||
return coverUrl;
|
||||
}
|
||||
|
||||
if (realUrl.scheme() != QLatin1String("http") && realUrl.scheme() != QLatin1String("https")) {
|
||||
return realUrl;
|
||||
}
|
||||
|
||||
const QString configPath = findHeroicConfigPath();
|
||||
if (configPath.isEmpty()) {
|
||||
return realUrl;
|
||||
}
|
||||
|
||||
const QString cacheDir = configPath + QStringLiteral("/images-cache");
|
||||
if (!QDir(cacheDir).exists()) {
|
||||
return realUrl;
|
||||
}
|
||||
|
||||
// Upstream Heroic stores cached images as sha256(url) (no extension) inside images-cache.
|
||||
const QByteArray digest = QCryptographicHash::hash(realUrl.toString().toUtf8(), QCryptographicHash::Sha256).toHex();
|
||||
const QString cachedPath = cacheDir + QStringLiteral("/") + QString::fromLatin1(digest);
|
||||
if (QFile::exists(cachedPath)) {
|
||||
return QUrl::fromLocalFile(cachedPath);
|
||||
}
|
||||
|
||||
return realUrl;
|
||||
}
|
||||
|
||||
QUrl HeroicImporter::findCoverImage(const QString &appName, const QString &store) const
|
||||
{
|
||||
QString configPath = findHeroicConfigPath();
|
||||
if (configPath.isEmpty()) {
|
||||
return {};
|
||||
configPath = findLegendaryConfigPath();
|
||||
}
|
||||
|
||||
// Check images cache
|
||||
QStringList imageDirs = {configPath + QStringLiteral("/images-cache"), configPath + QStringLiteral("/images")};
|
||||
if (!store.isEmpty()) {
|
||||
const QString storeLower = store.toLower();
|
||||
imageDirs.append(configPath + QStringLiteral("/images-cache/") + storeLower);
|
||||
imageDirs.append(configPath + QStringLiteral("/images/") + storeLower);
|
||||
}
|
||||
|
||||
QStringList extensions = {QStringLiteral(".jpg"), QStringLiteral(".png"), QStringLiteral(".webp")};
|
||||
QStringList extensions = {QStringLiteral(".jpg"), QStringLiteral(".jpeg"), QStringLiteral(".png"), QStringLiteral(".webp")};
|
||||
QStringList prefixes = {appName, appName + QStringLiteral("_cover"), appName + QStringLiteral("_library")};
|
||||
|
||||
for (const QString &imageDir : imageDirs) {
|
||||
|
|
@ -71,6 +213,16 @@ QUrl HeroicImporter::findCoverImage(const QString &appName, const QString &store
|
|||
}
|
||||
}
|
||||
|
||||
QStringList wildcardFilters;
|
||||
wildcardFilters.reserve(extensions.count());
|
||||
for (const QString &ext : extensions) {
|
||||
wildcardFilters.append(QStringLiteral("*") + appName + QStringLiteral("*") + ext);
|
||||
}
|
||||
const QStringList wildcardMatches = dir.entryList(wildcardFilters, QDir::Files);
|
||||
if (!wildcardMatches.isEmpty()) {
|
||||
return QUrl::fromLocalFile(imageDir + QStringLiteral("/") + wildcardMatches.first());
|
||||
}
|
||||
|
||||
// Also try looking in subdirectories
|
||||
QStringList subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for (const QString &subDir : subDirs) {
|
||||
|
|
@ -82,6 +234,14 @@ QUrl HeroicImporter::findCoverImage(const QString &appName, const QString &store
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDir sub(imageDir + QStringLiteral("/") + subDir);
|
||||
if (sub.exists()) {
|
||||
const QStringList subMatches = sub.entryList(wildcardFilters, QDir::Files);
|
||||
if (!subMatches.isEmpty()) {
|
||||
return QUrl::fromLocalFile(sub.absoluteFilePath(subMatches.first()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +255,179 @@ QList<Game *> HeroicImporter::importGames()
|
|||
games.append(importEpicGames());
|
||||
games.append(importGOGGames());
|
||||
games.append(importAmazonGames());
|
||||
games.append(importSideloadApps());
|
||||
|
||||
return games;
|
||||
}
|
||||
|
||||
QList<Game *> HeroicImporter::importSideloadApps()
|
||||
{
|
||||
QList<Game *> games;
|
||||
const QString configPath = findHeroicConfigPath();
|
||||
if (configPath.isEmpty()) {
|
||||
return games;
|
||||
}
|
||||
|
||||
const QString libraryPath = configPath + QStringLiteral("/sideload_apps/library.json");
|
||||
QFile libraryFile(libraryPath);
|
||||
if (!libraryFile.open(QIODevice::ReadOnly)) {
|
||||
return games;
|
||||
}
|
||||
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(libraryFile.readAll());
|
||||
|
||||
QList<QJsonObject> entries;
|
||||
if (doc.isArray()) {
|
||||
const QJsonArray arr = doc.array();
|
||||
entries.reserve(arr.size());
|
||||
for (const QJsonValue &value : arr) {
|
||||
if (value.isObject()) {
|
||||
entries.append(value.toObject());
|
||||
}
|
||||
}
|
||||
} else if (doc.isObject()) {
|
||||
const QJsonObject root = doc.object();
|
||||
const QStringList arrayKeys = {
|
||||
QStringLiteral("games"),
|
||||
QStringLiteral("apps"),
|
||||
QStringLiteral("library"),
|
||||
QStringLiteral("items"),
|
||||
};
|
||||
|
||||
bool foundArray = false;
|
||||
for (const QString &key : arrayKeys) {
|
||||
const QJsonValue v = root.value(key);
|
||||
if (v.isArray()) {
|
||||
const QJsonArray arr = v.toArray();
|
||||
entries.reserve(arr.size());
|
||||
for (const QJsonValue &item : arr) {
|
||||
if (item.isObject()) {
|
||||
entries.append(item.toObject());
|
||||
}
|
||||
}
|
||||
foundArray = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundArray) {
|
||||
// Some formats may store entries keyed by appName.
|
||||
for (auto it = root.constBegin(); it != root.constEnd(); ++it) {
|
||||
if (it.value().isObject()) {
|
||||
QJsonObject obj = it.value().toObject();
|
||||
if (!obj.contains(QStringLiteral("app_name")) && !obj.contains(QStringLiteral("appName"))) {
|
||||
obj.insert(QStringLiteral("app_name"), it.key());
|
||||
}
|
||||
entries.append(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return games;
|
||||
}
|
||||
|
||||
for (const QJsonObject &obj : entries) {
|
||||
QString appName = obj.value(QStringLiteral("app_name")).toString();
|
||||
if (appName.isEmpty()) {
|
||||
appName = obj.value(QStringLiteral("appName")).toString();
|
||||
}
|
||||
if (appName.isEmpty()) {
|
||||
appName = obj.value(QStringLiteral("id")).toString();
|
||||
}
|
||||
|
||||
QString title = obj.value(QStringLiteral("title")).toString();
|
||||
if (title.isEmpty()) {
|
||||
title = obj.value(QStringLiteral("name")).toString();
|
||||
}
|
||||
|
||||
if (appName.isEmpty() || title.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString launchCommand = obj.value(QStringLiteral("launchCommand")).toString();
|
||||
if (launchCommand.isEmpty()) {
|
||||
launchCommand = obj.value(QStringLiteral("launch_command")).toString();
|
||||
}
|
||||
if (launchCommand.isEmpty()) {
|
||||
launchCommand = obj.value(QStringLiteral("command")).toString();
|
||||
}
|
||||
if (launchCommand.isEmpty()) {
|
||||
launchCommand = obj.value(QStringLiteral("exec")).toString();
|
||||
}
|
||||
if (launchCommand.isEmpty()) {
|
||||
launchCommand = obj.value(QStringLiteral("exe")).toString();
|
||||
}
|
||||
if (launchCommand.isEmpty()) {
|
||||
launchCommand = obj.value(QStringLiteral("executable")).toString();
|
||||
}
|
||||
if (launchCommand.isEmpty()) {
|
||||
launchCommand = obj.value(QStringLiteral("selectedExe")).toString();
|
||||
}
|
||||
|
||||
QString workingDirectory = obj.value(QStringLiteral("workingDirectory")).toString();
|
||||
if (workingDirectory.isEmpty()) {
|
||||
workingDirectory = obj.value(QStringLiteral("install_path")).toString();
|
||||
}
|
||||
if (workingDirectory.isEmpty()) {
|
||||
workingDirectory = obj.value(QStringLiteral("installPath")).toString();
|
||||
}
|
||||
|
||||
bool isInstalled = obj.value(QStringLiteral("installed")).toBool(true);
|
||||
if (obj.contains(QStringLiteral("is_installed"))) {
|
||||
isInstalled = obj.value(QStringLiteral("is_installed")).toBool();
|
||||
}
|
||||
if (obj.contains(QStringLiteral("isInstalled"))) {
|
||||
isInstalled = obj.value(QStringLiteral("isInstalled")).toBool();
|
||||
}
|
||||
|
||||
Game *game = new Game(QStringLiteral("heroic-sideload-%1").arg(appName), title);
|
||||
game->setPlatform(QStringLiteral("Heroic (Sideload)"));
|
||||
game->setPlatformId(appName);
|
||||
if (!workingDirectory.isEmpty()) {
|
||||
game->setWorkingDirectory(workingDirectory);
|
||||
}
|
||||
game->setInstalled(isInstalled);
|
||||
|
||||
// We don't know if Heroic's CLI can always launch sideload apps consistently.
|
||||
// Prefer an explicit command from the library file; fallback to Heroic's CLI if absent.
|
||||
if (!launchCommand.isEmpty()) {
|
||||
game->setLaunchCommand(launchCommand);
|
||||
} else {
|
||||
game->setLaunchCommand(launchCommandForApp(appName));
|
||||
}
|
||||
|
||||
QString cover = obj.value(QStringLiteral("art_square")).toString();
|
||||
if (cover.isEmpty()) {
|
||||
cover = obj.value(QStringLiteral("artSquare")).toString();
|
||||
}
|
||||
if (cover.isEmpty()) {
|
||||
cover = obj.value(QStringLiteral("art_cover")).toString();
|
||||
}
|
||||
if (cover.isEmpty()) {
|
||||
cover = obj.value(QStringLiteral("artCover")).toString();
|
||||
}
|
||||
|
||||
QUrl coverUrl;
|
||||
if (!cover.isEmpty()) {
|
||||
if (cover.startsWith(QLatin1String("/"))) {
|
||||
coverUrl = QUrl::fromLocalFile(cover);
|
||||
} else {
|
||||
coverUrl = QUrl(cover);
|
||||
}
|
||||
} else {
|
||||
coverUrl = extractCoverUrl(obj);
|
||||
}
|
||||
|
||||
coverUrl = preferCachedCoverUrl(coverUrl);
|
||||
if (!coverUrl.isValid()) {
|
||||
coverUrl = findCoverImage(appName, QStringLiteral("sideload"));
|
||||
}
|
||||
if (coverUrl.isValid()) {
|
||||
game->setCoverUrl(coverUrl);
|
||||
}
|
||||
|
||||
games.append(game);
|
||||
}
|
||||
|
||||
return games;
|
||||
}
|
||||
|
|
@ -103,24 +436,37 @@ QList<Game *> HeroicImporter::importEpicGames()
|
|||
{
|
||||
QList<Game *> games;
|
||||
QString configPath = findHeroicConfigPath();
|
||||
QString legendaryConfigPath = findLegendaryConfigPath();
|
||||
|
||||
if (configPath.isEmpty()) {
|
||||
if (configPath.isEmpty() && legendaryConfigPath.isEmpty()) {
|
||||
return games;
|
||||
}
|
||||
|
||||
// Check for installed.json
|
||||
QString installedPath = configPath + QStringLiteral("/legendaryConfig/legendary/installed.json");
|
||||
QFile installedFile(installedPath);
|
||||
QStringList installedPaths;
|
||||
if (!legendaryConfigPath.isEmpty()) {
|
||||
installedPaths.append(legendaryConfigPath + QStringLiteral("/installed.json"));
|
||||
}
|
||||
if (!configPath.isEmpty()) {
|
||||
installedPaths.append(configPath + QStringLiteral("/legendaryConfig/legendary/installed.json"));
|
||||
installedPaths.append(configPath + QStringLiteral("/store_cache/legendary_library"));
|
||||
installedPaths.append(configPath + QStringLiteral("/store_cache/legendary_library.json"));
|
||||
installedPaths.append(configPath + QStringLiteral("/store/legendary_library"));
|
||||
installedPaths.append(configPath + QStringLiteral("/store/legendary_library.json"));
|
||||
}
|
||||
|
||||
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||||
// Try alternative path
|
||||
installedPath = configPath + QStringLiteral("/store_cache/legendary_library.json");
|
||||
installedFile.setFileName(installedPath);
|
||||
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||||
return games;
|
||||
QFile installedFile;
|
||||
for (const QString &path : installedPaths) {
|
||||
installedFile.setFileName(path);
|
||||
if (installedFile.open(QIODevice::ReadOnly)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!installedFile.isOpen()) {
|
||||
return games;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(installedFile.readAll());
|
||||
|
||||
if (doc.isObject()) {
|
||||
|
|
@ -142,7 +488,7 @@ QList<Game *> HeroicImporter::importEpicGames()
|
|||
Game *game = new Game(QStringLiteral("heroic-epic-%1").arg(appName), title);
|
||||
game->setPlatform(QStringLiteral("Heroic (Epic)"));
|
||||
game->setPlatformId(appName);
|
||||
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||||
game->setLaunchCommand(launchCommandForApp(appName));
|
||||
game->setWorkingDirectory(installPath);
|
||||
game->setInstalled(isInstalled);
|
||||
|
||||
|
|
@ -152,7 +498,10 @@ QList<Game *> HeroicImporter::importEpicGames()
|
|||
game->setDeveloper(developer);
|
||||
game->setPublisher(publisher);
|
||||
|
||||
QUrl coverUrl = findCoverImage(appName, QStringLiteral("epic"));
|
||||
QUrl coverUrl = preferCachedCoverUrl(extractCoverUrl(gameObj));
|
||||
if (!coverUrl.isValid()) {
|
||||
coverUrl = findCoverImage(appName, QStringLiteral("epic"));
|
||||
}
|
||||
if (coverUrl.isValid()) {
|
||||
game->setCoverUrl(coverUrl);
|
||||
}
|
||||
|
|
@ -180,10 +529,13 @@ QList<Game *> HeroicImporter::importEpicGames()
|
|||
Game *game = new Game(QStringLiteral("heroic-epic-%1").arg(appName), title);
|
||||
game->setPlatform(QStringLiteral("Heroic (Epic)"));
|
||||
game->setPlatformId(appName);
|
||||
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||||
game->setLaunchCommand(launchCommandForApp(appName));
|
||||
game->setInstalled(isInstalled);
|
||||
|
||||
QUrl coverUrl = findCoverImage(appName, QStringLiteral("epic"));
|
||||
QUrl coverUrl = preferCachedCoverUrl(extractCoverUrl(gameObj));
|
||||
if (!coverUrl.isValid()) {
|
||||
coverUrl = findCoverImage(appName, QStringLiteral("epic"));
|
||||
}
|
||||
if (coverUrl.isValid()) {
|
||||
game->setCoverUrl(coverUrl);
|
||||
}
|
||||
|
|
@ -206,19 +558,27 @@ QList<Game *> HeroicImporter::importGOGGames()
|
|||
return games;
|
||||
}
|
||||
|
||||
// Check for GOG installed games
|
||||
QString installedPath = configPath + QStringLiteral("/gog_store/installed.json");
|
||||
QFile installedFile(installedPath);
|
||||
// Check for GOG installed games / library cache
|
||||
QStringList installedPaths = {
|
||||
configPath + QStringLiteral("/gog_store/installed.json"),
|
||||
configPath + QStringLiteral("/store_cache/gog_library"),
|
||||
configPath + QStringLiteral("/store_cache/gog_library.json"),
|
||||
configPath + QStringLiteral("/store/gog_library"),
|
||||
configPath + QStringLiteral("/store/gog_library.json"),
|
||||
};
|
||||
|
||||
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||||
// Try library file
|
||||
installedPath = configPath + QStringLiteral("/store_cache/gog_library.json");
|
||||
installedFile.setFileName(installedPath);
|
||||
if (!installedFile.open(QIODevice::ReadOnly)) {
|
||||
return games;
|
||||
QFile installedFile;
|
||||
for (const QString &path : installedPaths) {
|
||||
installedFile.setFileName(path);
|
||||
if (installedFile.open(QIODevice::ReadOnly)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!installedFile.isOpen()) {
|
||||
return games;
|
||||
}
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(installedFile.readAll());
|
||||
|
||||
auto parseGames = [this, &games](const QJsonArray &arr) {
|
||||
|
|
@ -243,10 +603,13 @@ QList<Game *> HeroicImporter::importGOGGames()
|
|||
Game *game = new Game(QStringLiteral("heroic-gog-%1").arg(appName), title);
|
||||
game->setPlatform(QStringLiteral("Heroic (GOG)"));
|
||||
game->setPlatformId(appName);
|
||||
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||||
game->setLaunchCommand(launchCommandForApp(appName));
|
||||
game->setInstalled(isInstalled);
|
||||
|
||||
QUrl coverUrl = findCoverImage(appName, QStringLiteral("gog"));
|
||||
QUrl coverUrl = preferCachedCoverUrl(extractCoverUrl(gameObj));
|
||||
if (!coverUrl.isValid()) {
|
||||
coverUrl = findCoverImage(appName, QStringLiteral("gog"));
|
||||
}
|
||||
if (coverUrl.isValid()) {
|
||||
game->setCoverUrl(coverUrl);
|
||||
}
|
||||
|
|
@ -276,10 +639,22 @@ QList<Game *> HeroicImporter::importAmazonGames()
|
|||
return games;
|
||||
}
|
||||
|
||||
QString libraryPath = configPath + QStringLiteral("/store_cache/nile_library.json");
|
||||
QFile libraryFile(libraryPath);
|
||||
QStringList libraryPaths = {
|
||||
configPath + QStringLiteral("/store_cache/nile_library"),
|
||||
configPath + QStringLiteral("/store_cache/nile_library.json"),
|
||||
configPath + QStringLiteral("/store/nile_library"),
|
||||
configPath + QStringLiteral("/store/nile_library.json"),
|
||||
};
|
||||
|
||||
if (!libraryFile.open(QIODevice::ReadOnly)) {
|
||||
QFile libraryFile;
|
||||
for (const QString &path : libraryPaths) {
|
||||
libraryFile.setFileName(path);
|
||||
if (libraryFile.open(QIODevice::ReadOnly)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!libraryFile.isOpen()) {
|
||||
return games;
|
||||
}
|
||||
|
||||
|
|
@ -304,10 +679,13 @@ QList<Game *> HeroicImporter::importAmazonGames()
|
|||
Game *game = new Game(QStringLiteral("heroic-amazon-%1").arg(appName), title);
|
||||
game->setPlatform(QStringLiteral("Heroic (Amazon)"));
|
||||
game->setPlatformId(appName);
|
||||
game->setLaunchCommand(QStringLiteral("heroic --launch %1").arg(appName));
|
||||
game->setLaunchCommand(launchCommandForApp(appName));
|
||||
game->setInstalled(isInstalled);
|
||||
|
||||
QUrl coverUrl = findCoverImage(appName, QStringLiteral("amazon"));
|
||||
QUrl coverUrl = preferCachedCoverUrl(extractCoverUrl(gameObj));
|
||||
if (!coverUrl.isValid()) {
|
||||
coverUrl = findCoverImage(appName, QStringLiteral("amazon"));
|
||||
}
|
||||
if (coverUrl.isValid()) {
|
||||
game->setCoverUrl(coverUrl);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,13 @@ public:
|
|||
|
||||
private:
|
||||
QString findHeroicConfigPath() const;
|
||||
QString findLegendaryConfigPath() const;
|
||||
QList<Game *> importEpicGames();
|
||||
QList<Game *> importGOGGames();
|
||||
QList<Game *> importAmazonGames();
|
||||
QList<Game *> importSideloadApps();
|
||||
QUrl extractCoverUrl(const QJsonObject &gameObj) const;
|
||||
QUrl preferCachedCoverUrl(const QUrl &coverUrl) const;
|
||||
QString launchCommandForApp(const QString &appName) const;
|
||||
QUrl findCoverImage(const QString &appName, const QString &store) const;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue