Skip to content

Commit 77d9479

Browse files
committed
Fix MobyGames scraper
1 parent 6ae4d0c commit 77d9479

4 files changed

Lines changed: 40 additions & 45 deletions

File tree

docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ humans](https://keepachangelog.com).
2222
is now optional. It gets added automagically when no other query keyword is
2323
applied.
2424
- Updated: `mamemap.csv` for MAME 0.275
25+
- Fixed: (#150) Enable MobyGames scraper usage with personal API key. Thanks,
26+
@risalt and to the constructive support at MobyGames.
2527
- Fixed: (#122) Updated ZX-Spectrum scraper module. It uses now the site
2628
`zxinfo.dk` and a web API. The API provides at least the same information as
2729
the defunct search on `worldofspectrum.org`. Check out the added `--query=`

docs/SCRAPINGMODULES.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,9 @@ The database also supports many non-Amiga platforms, but there's no doubt that A
144144
- Shortname: _`mobygames`_
145145
- Type: _Online_
146146
- Website: _[www.mobygames.com](https://www.mobygames.com)_
147-
- Type: _File name search based_
147+
- Type: _File name_ or _Moby Games ID_ search based
148148
- User credential support: _None required_
149-
- API request limit: _1 request per 10 seconds_
150-
- Rom limit per run: _35_
149+
- API request limit: _1 request per 5 seconds (Hobbyist subscription)_
151150
- Thread limit: _1_
152151
- Platform support: _[List](https://www.mobygames.com/browse/games)_ or see `mobygames_platforms.json` sibling to your `config.ini`
153152
- Media support: _`cover`, `screenshot`_
@@ -157,9 +156,7 @@ The database also supports many non-Amiga platforms, but there's no doubt that A
157156

158157
MobyGames. What can I say. If you haven't heard about this database before you've been missing out. It's one of the best and oldest games databases on the internet. You'll probably come across references to MobyGames on other sites when searching for retro games. There's a reason for that - it's that good.
159158

160-
There's a caveat to the module as it has quite strong restrictions for the number of requests that are allowed at any given time. This restriction is global for the entire Skyscraper user base, which means that it might quit on you if other users are currently scraping from it. For this reason it has been strongly limited inside of Skyscraper by forcing a maximum number of rom scrapings per run.
161-
162-
Please use this module sparingly. And only ever use it to scrape those last few roms you can't get any data for using any of the other sources.
159+
There's a caveat to the module as it requires a subscription to get an API key, but you get well curated game information especially for hard to find titles. Examine the possible options: https://www.mobygames.com/api/subscribe/. Once you have obtained an API key add it to the [userCreds](CONFIGINI.md#usercreds) configuration (without any colon) in the [mobygames] INI section.
163160

164161
### IGDB
165162

@@ -168,7 +165,7 @@ Please use this module sparingly. And only ever use it to scrape those last few
168165
- Shortname: _`igdb`_
169166
- Type: _Online_
170167
- Website: _[www.igdb.com](https://www.igdb.com)_
171-
- Type: _File name search based_
168+
- Type: _File name_ or _IGDB Game Id_ search based
172169
- User credential support: _Yes, free private API client-id and secret-key required! Read more below_
173170
- API request limit: _A maximum of 4 requests per seconds is allowed_
174171
- Thread limit: _4 (each being limited to 1 request per second)_
@@ -202,7 +199,7 @@ Substitute CLIENTID and SECRETKEY with your own details. And that's it, you shou
202199
- Shortname: _`zxinfo`_, _`worldofspectrum`_, _`wos`_
203200
- Type: _Online_
204201
- Website: _[zxinfo.dk](https://zxinfo.dk)_
205-
- Type: _File name search based_
202+
- Type: _File name search_, _Game Id search_ or _Game hash search_
206203
- User credential support: _None required_
207204
- API request limit: _None_
208205
- Thread limit: _None_
@@ -249,7 +246,7 @@ Read a thorough description of this module [here](IMPORT.md).
249246
- Shortname: _`gamebase`_
250247
- Type: _Local_
251248
- Website: _[about the format](https://www.bu22.com/wiki/home)_
252-
- Type: _Exact filename, title or CRC match, for filename and title wildcards * and ? can be applied anywhere_
249+
- Type: _filename, title or CRC match, for filename and title wildcards '*' and '?' can be applied anywhere_
253250
- User credential support: _None required_
254251
- API request limit: _None_
255252
- Thread limit: 1

src/mobygames.cpp

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include <QJsonArray>
3333
#include <QRandomGenerator>
34+
#include <QStringBuilder>
3435
#include <cstdio>
3536

3637
static inline QMap<QString, QString> mobyRegionMap() {
@@ -56,7 +57,7 @@ static inline QMap<QString, QString> mobyRegionMap() {
5657
MobyGames::MobyGames(Settings *config, QSharedPointer<NetManager> manager)
5758
: AbstractScraper(config, manager, MatchType::MATCH_MANY) {
5859
connect(&limitTimer, &QTimer::timeout, &limiter, &QEventLoop::quit);
59-
limitTimer.setInterval(10000); // 10 second request limit
60+
limitTimer.setInterval(5000); // 5 second request limit (Hobbyist API)
6061
limitTimer.setSingleShot(false);
6162
limitTimer.start();
6263

@@ -82,24 +83,15 @@ void MobyGames::getSearchResults(QList<GameEntry> &gameEntries,
8283

8384
printf("Waiting as advised by MobyGames api restrictions...\n");
8485
limiter.exec();
85-
bool ok;
86-
int queryGameId = searchName.toInt(&ok);
87-
QString req;
88-
if (ok) {
89-
req = QString(searchUrlPre + "?api_key=" +
90-
StrTools::unMagic(
91-
"175;229;170;189;188;202;211;117;164;165;185;209;164;"
92-
"234;180;155;199;209;224;231;193;190;173;175") +
93-
"&id=" + QString::number(queryGameId));
86+
QString req = QString(searchUrlPre % "?api_key=" % config->password);
87+
bool isMobyGameId;
88+
int queryGameId = searchName.toInt(&isMobyGameId);
89+
if (isMobyGameId) {
90+
req = req % "&id=" % QString::number(queryGameId);
9491
} else {
95-
req = QString(searchUrlPre + "?api_key=" +
96-
StrTools::unMagic(
97-
"175;229;170;189;188;202;211;117;164;165;185;209;164;"
98-
"234;180;155;199;209;224;231;193;190;173;175") +
99-
"&title=" + searchName +
100-
(platformId == -1
101-
? ""
102-
: "&platform=" + QString::number(platformId)));
92+
req = req % "&title=" % searchName %
93+
(platformId == -1 ? ""
94+
: "&platform=" + QString::number(platformId));
10395
queryGameId = 0;
10496
}
10597
qDebug() << "Request: " << req;
@@ -113,10 +105,8 @@ void MobyGames::getSearchResults(QList<GameEntry> &gameEntries,
113105
}
114106

115107
if (jsonDoc.object()["code"].toInt() == 429) {
116-
printf(
117-
"\033[1;31mToo many requests! This is probably because some other "
118-
"Skyscraper user is currently using the 'mobygames' module. Please "
119-
"wait a while and try again.\n\nNow quitting...\033[0m\n");
108+
printf("\033[1;31mToo many requests! Please wait a while and try "
109+
"again.\n\nNow quitting...\033[0m\n");
120110
reqRemaining = 0;
121111
}
122112

@@ -135,11 +125,9 @@ void MobyGames::getSearchResults(QList<GameEntry> &gameEntries,
135125
while (!jsonPlatforms.isEmpty()) {
136126
QJsonObject jsonPlatform = jsonPlatforms.first().toObject();
137127
int gamePlafId = jsonPlatform["platform_id"].toInt();
138-
game.url = searchUrlPre + "/" + game.id + "/platforms/" +
139-
QString::number(gamePlafId) + "?api_key=" +
140-
StrTools::unMagic(
141-
"175;229;170;189;188;202;211;117;164;165;185;209;"
142-
"164;234;180;155;199;209;224;231;193;190;173;175");
128+
game.url = searchUrlPre % "/" % game.id % "/platforms/" %
129+
QString::number(gamePlafId) % "?api_key=" %
130+
config->password;
143131
game.releaseDate = jsonPlatform["first_release_date"].toString();
144132
game.platform = jsonPlatform["platform_name"].toString();
145133
bool matchPlafId = gamePlafId == platformId;
@@ -281,7 +269,7 @@ void MobyGames::getCover(GameEntry &game) {
281269
fflush(stdout);
282270
limiter.exec();
283271
QString req = QString(
284-
game.url.left(game.url.indexOf("?api_key=")) + "/covers" +
272+
game.url.left(game.url.indexOf("?api_key=")) % "/covers" %
285273
game.url.mid(game.url.indexOf("?api_key="),
286274
game.url.length() - game.url.indexOf("?api_key=")));
287275
qDebug() << "Covers request" << req;
@@ -372,7 +360,7 @@ void MobyGames::getScreenshot(GameEntry &game) {
372360
fflush(stdout);
373361
limiter.exec();
374362
netComm->request(
375-
game.url.left(game.url.indexOf("?api_key=")) + "/screenshots" +
363+
game.url.left(game.url.indexOf("?api_key=")) % "/screenshots" %
376364
game.url.mid(game.url.indexOf("?api_key="),
377365
game.url.length() - game.url.indexOf("?api_key=")));
378366
q.exec();

src/skyscraper.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,8 @@ void Skyscraper::loadConfig(const QCommandLineParser &parser) {
943943
}
944944

945945
if (config.importFolder.isEmpty()) {
946-
config.importFolder = Config::getSkyFolder(Config::SkyFolderType::IMPORT);
946+
config.importFolder =
947+
Config::getSkyFolder(Config::SkyFolderType::IMPORT);
947948
}
948949
// If platform subfolder exists for import path, use it
949950
QDir importFolder(config.importFolder);
@@ -1040,11 +1041,14 @@ void Skyscraper::loadConfig(const QCommandLineParser &parser) {
10401041
config.refresh = true;
10411042
}
10421043

1043-
if (!config.userCreds.isEmpty() && config.userCreds.contains(":")) {
1044+
if (!config.userCreds.isEmpty()) {
10441045
QList<QString> userCreds = config.userCreds.split(":");
10451046
if (userCreds.length() == 2) {
10461047
config.user = userCreds.at(0);
10471048
config.password = userCreds.at(1);
1049+
} else if (userCreds.length() == 1) {
1050+
// API key
1051+
config.password = userCreds.at(0);
10481052
}
10491053
}
10501054

@@ -1129,13 +1133,17 @@ void Skyscraper::prepareScraping() {
11291133
prepareIgdb(netComm, q);
11301134
} else if (config.scraper == "mobygames" && config.threads != 1) {
11311135
printf(
1132-
"\033[1;33mForcing 1 thread to accomodate limits in MobyGames "
1136+
"\033[1;33mForcing one thread to accomodate limits in MobyGames "
11331137
"scraping module. Also be aware that MobyGames has a request limit "
1134-
"of 360 requests per hour for the entire Skyscraper user base. So "
1135-
"if someone else is currently using it, it will quit.\033[0m\n\n");
1136-
// Don't change these! This limit was set by request from Mobygames
1138+
"of 720 requests per hour for a Hobbyist subscription.\033[0m\n\n");
11371139
config.threads = 1;
1138-
config.romLimit = 35;
1140+
if (config.password.isEmpty()) {
1141+
printf("The MobyGames scraping module requires an API key to "
1142+
"work. Read more about that here: "
1143+
"'https://gemba.github.io/skyscraper/"
1144+
"SCRAPINGMODULES#mobygames'\n");
1145+
exit(1);
1146+
}
11391147
} else if (config.scraper == "screenscraper") {
11401148
prepareScreenscraper(netComm, q);
11411149
} else if (config.scraper == "gamebase") {

0 commit comments

Comments
 (0)