Import: Avoid re-importing sources

Skip sources that already have imported games and avoid importing
platforms that are disabled in settings.

Remove imported games when a source is disabled so the library stays in
sync with user preferences.
This commit is contained in:
Marco Allegretti 2026-01-24 13:53:12 +01:00
parent 9c3c0e1dfd
commit 3357e48cc7

View file

@ -30,6 +30,116 @@ App::App(QObject *parent)
, m_config(new Config(this)) , m_config(new Config(this))
{ {
loadLibrary(); loadLibrary();
if (!m_config->importSteam()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Steam")) > 0) {
saveLibrary();
}
}
if (!m_config->importLutris()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Lutris")) > 0) {
saveLibrary();
}
}
if (!m_config->importHeroic()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Heroic")) > 0) {
saveLibrary();
}
}
if (!m_config->importDesktop()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Desktop")) > 0) {
saveLibrary();
}
}
if (!m_config->importBottles()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Bottles")) > 0) {
saveLibrary();
}
}
if (!m_config->importFlatpak()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Flatpak")) > 0) {
saveLibrary();
}
}
if (!m_config->importItch()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("itch.io")) > 0) {
saveLibrary();
}
}
if (!m_config->importLegendary()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Legendary")) > 0) {
saveLibrary();
}
}
if (!m_config->importRetroArch()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("RetroArch")) > 0) {
saveLibrary();
}
}
connect(m_config, &Config::importSteamChanged, this, [this]() {
if (!m_config->importSteam()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Steam")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importLutrisChanged, this, [this]() {
if (!m_config->importLutris()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Lutris")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importHeroicChanged, this, [this]() {
if (!m_config->importHeroic()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Heroic")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importDesktopChanged, this, [this]() {
if (!m_config->importDesktop()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Desktop")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importBottlesChanged, this, [this]() {
if (!m_config->importBottles()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Bottles")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importFlatpakChanged, this, [this]() {
if (!m_config->importFlatpak()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Flatpak")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importItchChanged, this, [this]() {
if (!m_config->importItch()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("itch.io")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importLegendaryChanged, this, [this]() {
if (!m_config->importLegendary()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("Legendary")) > 0) {
saveLibrary();
}
}
});
connect(m_config, &Config::importRetroArchChanged, this, [this]() {
if (!m_config->importRetroArch()) {
if (m_gameModel->removeByPlatformPrefix(QStringLiteral("RetroArch")) > 0) {
saveLibrary();
}
}
});
} }
App *App::instance() App *App::instance()
@ -98,237 +208,352 @@ void App::importAllGames()
if (m_importing) if (m_importing)
return; return;
const bool anyEnabled = m_config->importSteam() || m_config->importLutris() || m_config->importHeroic() || m_config->importDesktop()
|| m_config->importBottles() || m_config->importFlatpak() || m_config->importItch() || m_config->importLegendary() || m_config->importRetroArch();
const bool doSteam = m_config->importSteam() && !m_gameModel->hasPlatformPrefix(QStringLiteral("Steam"));
const bool doLutris = m_config->importLutris() && !m_gameModel->hasPlatformPrefix(QStringLiteral("Lutris"));
const bool doHeroic = m_config->importHeroic() && !m_gameModel->hasPlatformPrefix(QStringLiteral("Heroic"));
const bool doDesktop = m_config->importDesktop() && !m_gameModel->hasPlatformPrefix(QStringLiteral("Desktop"));
const bool doBottles = m_config->importBottles() && !m_gameModel->hasPlatformPrefix(QStringLiteral("Bottles"));
const bool doFlatpak = m_config->importFlatpak() && !m_gameModel->hasPlatformPrefix(QStringLiteral("Flatpak"));
const bool doItch = m_config->importItch() && !m_gameModel->hasPlatformPrefix(QStringLiteral("itch.io"));
const bool doLegendary = m_config->importLegendary() && !m_gameModel->hasPlatformPrefix(QStringLiteral("Legendary"));
const bool doRetroArch = m_config->importRetroArch() && !m_gameModel->hasPlatformPrefix(QStringLiteral("RetroArch"));
if (!anyEnabled) {
setImportStatus(tr("No import sources enabled"));
Q_EMIT importCompleted(0);
return;
}
if (!(doSteam || doLutris || doHeroic || doDesktop || doBottles || doFlatpak || doItch || doLegendary || doRetroArch)) {
setImportStatus(tr("All enabled sources already imported"));
Q_EMIT importCompleted(0);
return;
}
setImporting(true); setImporting(true);
setImportStatus(tr("Importing games...")); setImportStatus(tr("Importing games..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this, doSteam, doLutris, doHeroic, doDesktop, doBottles, doFlatpak, doItch, doLegendary, doRetroArch]() {
int totalCount = 0; int totalCount = 0;
// Import from Steam // Import from Steam
QMetaObject::invokeMethod( if (doSteam) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning Steam library...")); [this]() {
}, setImportStatus(tr("Scanning Steam library..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
SteamImporter steamImporter; SteamImporter steamImporter;
QList<Game *> steamGames = steamImporter.importGames(); QList<Game *> steamGames = steamImporter.importGames();
for (Game *game : steamGames) { for (Game *game : steamGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, steamGames]() {
if (!m_config->importSteam()) {
for (Game *game : steamGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : steamGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += steamGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, steamGames]() {
for (Game *game : steamGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += steamGames.count();
// Import from Lutris // Import from Lutris
QMetaObject::invokeMethod( if (doLutris) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning Lutris library...")); [this]() {
}, setImportStatus(tr("Scanning Lutris library..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
LutrisImporter lutrisImporter; LutrisImporter lutrisImporter;
QList<Game *> lutrisGames = lutrisImporter.importGames(); QList<Game *> lutrisGames = lutrisImporter.importGames();
for (Game *game : lutrisGames) { for (Game *game : lutrisGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, lutrisGames]() {
if (!m_config->importLutris()) {
for (Game *game : lutrisGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : lutrisGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += lutrisGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, lutrisGames]() {
for (Game *game : lutrisGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += lutrisGames.count();
// Import from Heroic // Import from Heroic
QMetaObject::invokeMethod( if (doHeroic) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning Heroic library...")); [this]() {
}, setImportStatus(tr("Scanning Heroic library..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
HeroicImporter heroicImporter; HeroicImporter heroicImporter;
QList<Game *> heroicGames = heroicImporter.importGames(); QList<Game *> heroicGames = heroicImporter.importGames();
for (Game *game : heroicGames) { for (Game *game : heroicGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, heroicGames]() {
if (!m_config->importHeroic()) {
for (Game *game : heroicGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : heroicGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += heroicGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, heroicGames]() {
for (Game *game : heroicGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += heroicGames.count();
// Import from Desktop entries // Import from Desktop entries
QMetaObject::invokeMethod( if (doDesktop) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning desktop entries...")); [this]() {
}, setImportStatus(tr("Scanning desktop entries..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
DesktopImporter desktopImporter; DesktopImporter desktopImporter;
QList<Game *> desktopGames = desktopImporter.importGames(); QList<Game *> desktopGames = desktopImporter.importGames();
for (Game *game : desktopGames) { for (Game *game : desktopGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, desktopGames]() {
if (!m_config->importDesktop()) {
for (Game *game : desktopGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : desktopGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += desktopGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, desktopGames]() {
for (Game *game : desktopGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += desktopGames.count();
// Import from Bottles // Import from Bottles
QMetaObject::invokeMethod( if (doBottles) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning Bottles...")); [this]() {
}, setImportStatus(tr("Scanning Bottles..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
BottlesImporter bottlesImporter; BottlesImporter bottlesImporter;
QList<Game *> bottlesGames = bottlesImporter.importGames(); QList<Game *> bottlesGames = bottlesImporter.importGames();
for (Game *game : bottlesGames) { for (Game *game : bottlesGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, bottlesGames]() {
if (!m_config->importBottles()) {
for (Game *game : bottlesGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : bottlesGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += bottlesGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, bottlesGames]() {
for (Game *game : bottlesGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += bottlesGames.count();
// Import from Flatpak // Import from Flatpak
QMetaObject::invokeMethod( if (doFlatpak) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning Flatpak games...")); [this]() {
}, setImportStatus(tr("Scanning Flatpak games..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
FlatpakImporter flatpakImporter; FlatpakImporter flatpakImporter;
QList<Game *> flatpakGames = flatpakImporter.importGames(); QList<Game *> flatpakGames = flatpakImporter.importGames();
for (Game *game : flatpakGames) { for (Game *game : flatpakGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, flatpakGames]() {
if (!m_config->importFlatpak()) {
for (Game *game : flatpakGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : flatpakGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += flatpakGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, flatpakGames]() {
for (Game *game : flatpakGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += flatpakGames.count();
// Import from itch.io // Import from itch.io
QMetaObject::invokeMethod( if (doItch) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning itch.io library...")); [this]() {
}, setImportStatus(tr("Scanning itch.io library..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
ItchImporter itchImporter; ItchImporter itchImporter;
QList<Game *> itchGames = itchImporter.importGames(); QList<Game *> itchGames = itchImporter.importGames();
for (Game *game : itchGames) { for (Game *game : itchGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, itchGames]() {
if (!m_config->importItch()) {
for (Game *game : itchGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : itchGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += itchGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, itchGames]() {
for (Game *game : itchGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += itchGames.count();
// Import from Legendary // Import from Legendary
QMetaObject::invokeMethod( if (doLegendary) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning Legendary library...")); [this]() {
}, setImportStatus(tr("Scanning Legendary library..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
LegendaryImporter legendaryImporter; LegendaryImporter legendaryImporter;
QList<Game *> legendaryGames = legendaryImporter.importGames(); QList<Game *> legendaryGames = legendaryImporter.importGames();
for (Game *game : legendaryGames) { for (Game *game : legendaryGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, legendaryGames]() {
if (!m_config->importLegendary()) {
for (Game *game : legendaryGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : legendaryGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += legendaryGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, legendaryGames]() {
for (Game *game : legendaryGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += legendaryGames.count();
// Import from RetroArch // Import from RetroArch
QMetaObject::invokeMethod( if (doRetroArch) {
this, QMetaObject::invokeMethod(
[this]() { this,
setImportStatus(tr("Scanning RetroArch playlists...")); [this]() {
}, setImportStatus(tr("Scanning RetroArch playlists..."));
Qt::QueuedConnection); },
Qt::QueuedConnection);
RetroArchImporter retroArchImporter; RetroArchImporter retroArchImporter;
QList<Game *> retroArchGames = retroArchImporter.importGames(); QList<Game *> retroArchGames = retroArchImporter.importGames();
for (Game *game : retroArchGames) { for (Game *game : retroArchGames) {
game->moveToThread(this->thread()); game->moveToThread(this->thread());
game->setParent(nullptr); game->setParent(nullptr);
}
QMetaObject::invokeMethod(
this,
[this, retroArchGames]() {
if (!m_config->importRetroArch()) {
for (Game *game : retroArchGames) {
if (game) {
game->deleteLater();
}
}
return;
}
for (Game *game : retroArchGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += retroArchGames.count();
} }
QMetaObject::invokeMethod(
this,
[this, retroArchGames]() {
for (Game *game : retroArchGames) {
m_gameModel->addGame(game);
}
},
Qt::QueuedConnection);
totalCount += retroArchGames.count();
// Complete // Complete
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
this, this,
@ -350,7 +575,7 @@ void App::importFromSteam()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning Steam library...")); setImportStatus(tr("Scanning Steam library..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
SteamImporter importer; SteamImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -382,7 +607,7 @@ void App::importFromLutris()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning Lutris library...")); setImportStatus(tr("Scanning Lutris library..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
LutrisImporter importer; LutrisImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -414,7 +639,7 @@ void App::importFromHeroic()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning Heroic library...")); setImportStatus(tr("Scanning Heroic library..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
HeroicImporter importer; HeroicImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -446,7 +671,7 @@ void App::importFromDesktop()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning desktop entries...")); setImportStatus(tr("Scanning desktop entries..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
DesktopImporter importer; DesktopImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -478,7 +703,7 @@ void App::importFromBottles()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning Bottles...")); setImportStatus(tr("Scanning Bottles..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
BottlesImporter importer; BottlesImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -510,7 +735,7 @@ void App::importFromFlatpak()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning Flatpak games...")); setImportStatus(tr("Scanning Flatpak games..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
FlatpakImporter importer; FlatpakImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -542,7 +767,7 @@ void App::importFromItch()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning itch.io library...")); setImportStatus(tr("Scanning itch.io library..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
ItchImporter importer; ItchImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -574,7 +799,7 @@ void App::importFromLegendary()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning Legendary library...")); setImportStatus(tr("Scanning Legendary library..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
LegendaryImporter importer; LegendaryImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();
@ -606,7 +831,7 @@ void App::importFromRetroArch()
setImporting(true); setImporting(true);
setImportStatus(tr("Scanning RetroArch playlists...")); setImportStatus(tr("Scanning RetroArch playlists..."));
QtConcurrent::run([this]() { [[maybe_unused]] auto future = QtConcurrent::run([this]() {
RetroArchImporter importer; RetroArchImporter importer;
QList<Game *> games = importer.importGames(); QList<Game *> games = importer.importGames();