mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 06:46:00 +00:00
Add tests for mobygames, sgdb and ssfr service adapter
add ssfr and sgdb service test
This commit is contained in:
@@ -5,6 +5,7 @@ from collections.abc import AsyncIterator, Collection
|
||||
from typing import Literal, cast
|
||||
|
||||
import aiohttp
|
||||
import aiohttp.client_exceptions
|
||||
import yarl
|
||||
from adapters.services.steamgriddb_types import (
|
||||
SGDBDimension,
|
||||
@@ -58,8 +59,10 @@ class SteamGridDBService:
|
||||
)
|
||||
res.raise_for_status()
|
||||
return await res.json()
|
||||
except aiohttp.ClientResponseError as exc:
|
||||
except aiohttp.client_exceptions.ClientResponseError as exc:
|
||||
print(f"Request failed with status {exc.status} for URL: {url}")
|
||||
if exc.status == http.HTTPStatus.UNAUTHORIZED:
|
||||
print("Invalid API key or unauthorized access.")
|
||||
raise SGDBInvalidAPIKeyException from exc
|
||||
# Log the error and return an empty dict if the request fails with a different code
|
||||
log.error(exc)
|
||||
@@ -112,7 +115,8 @@ class SteamGridDBService:
|
||||
if page_number is not None:
|
||||
params["page"] = [str(page_number)]
|
||||
|
||||
url = self.url.joinpath("grids/game", str(game_id)).with_query(**params)
|
||||
base_url = self.url.joinpath("grids/game", str(game_id))
|
||||
url = base_url.with_query(**params) if params else base_url
|
||||
response = await self._request(str(url))
|
||||
if not response:
|
||||
return SGDBGridList(
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?id=999999&format=normal
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"code":401,"error":"Authorization required","message":"You must have
|
||||
an API key to use this resource."}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b9278fab3544f-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- "105"
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:37:23 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=IREPYBRjyxV9xuhHi5Vquq%2FEtCBarJCxOMSPPc%2FGTKYYv28m%2BfL5Qalu0oL%2FpFSaMGeRRsi7lXj5fkybujCYJKBmg49O%2B26HoYlHirpw"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Vary:
|
||||
- Cookie
|
||||
WWW-Authenticate:
|
||||
- Basic realm="MobyGames API key (no password)"
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
version: 1
|
||||
@@ -0,0 +1,59 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?format=brief&limit=3
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"games":[{"game_id":1,"moby_url":"https://www.mobygames.com/game/1/the-x-files-game/","title":"The
|
||||
X-Files Game"},{"game_id":2,"moby_url":"https://www.mobygames.com/game/2/who-framed-roger-rabbit/","title":"Who
|
||||
Framed Roger Rabbit"},{"game_id":3,"moby_url":"https://www.mobygames.com/game/3/wing-commander/","title":"Wing
|
||||
Commander"}]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b8a9f79ca36a0-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:32:01 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "314"
|
||||
Ratelimit-Reset:
|
||||
- "1679"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=mcmYDbCrD3jPELg2NLm%2BjYkbXdj33QDYigpwtKqeA5jslmTS%2Fn%2BWLh1BovEFmT7XNA%2F6KZdRdXLFbX1dInGwdHUy8bZo%2B9MNYZPzOogS"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,55 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?format=id&limit=5
|
||||
response:
|
||||
body:
|
||||
string: '{"games":[1,2,3,4,5]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b8aced955a202-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:32:09 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "311"
|
||||
Ratelimit-Reset:
|
||||
- "1671"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=7WqR9E5idJevIDcTjt%2F9tbyCEpUuxCRu1EYJXEDPq9H8sAY2WpKuB%2FNsY6GRPrwcG34ZFlJsQUGFSRSDPUWMyR2IqhDl5NLRLIK35js%2B"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,118 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?format=normal&limit=2
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"games":[{"alternate_titles":[{"description":"Finnish title","title":"Salaiset
|
||||
Kansiot"},{"description":"PSX title","title":"The X Files"},{"description":"Spanish
|
||||
title","title":"The X-Files: Expediente X - El Juego"},{"description":"French
|
||||
title","title":"The X-Files: Le Jeu"}],"description":"<p>As an extension of
|
||||
one of the most long-running television series of all time, <em>The X-Files</em>,
|
||||
play through the eyes of Special Agent Craig Willmore, a new FBI field investigator
|
||||
brought in to locate missing agents Fox Mulder and Dana Scully whose last
|
||||
location was the Everett, Washington, area. In this \"movie quality\" video
|
||||
production, characters are played by the actors and actresses from the show,
|
||||
including <a href=\"https://www.mobygames.com/person/9411/gillian-anderson/\">Gillian
|
||||
Anderson</a> (Scully) and <a href=\"https://www.mobygames.com/person/9412/david-duchovny/\">David
|
||||
Duchovny</a> (Mulder).</p>\n<p>As the game begins, you are given a briefing
|
||||
of your mission. Gather up all state-of-the-art spy tools (night vision goggles,
|
||||
a digital camera, PDA, lock picks, evidence kit, a standard issue revolver,
|
||||
handcuffs and badge) and then head out to follow their trail. As you explore
|
||||
the various locations, take photographs, pick up pieces of evidence and talk
|
||||
with people. Use your Newton PDA to access the navigational map, to make notes
|
||||
and send/receive e-mail. Trace telephone numbers, run background checks and
|
||||
license plate ids and even post an All Points Bulletin on missing persons
|
||||
using the computer network at your home or office. By using photo viewer software,
|
||||
download field photographs to the computer where they can be enlarged and
|
||||
studied more closely for clues.</p>\n<p>Features include emotion icons for
|
||||
interjecting different tones during conversations (mean, humorous or technical)
|
||||
which effect the answer given. An in-game hint system, Artificial Intelligence,
|
||||
can be set on or off. In addition, there are multiple endings as a direct
|
||||
result of actions you take.</p>","game_id":1,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":2,"genre_name":"Adventure"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Art
|
||||
Style","genre_category_id":13,"genre_id":217,"genre_name":"Live action"},{"genre_category":"Setting","genre_category_id":10,"genre_id":8,"genre_name":"Sci-fi
|
||||
/ futuristic"},{"genre_category":"Narrative Theme/Topic","genre_category_id":8,"genre_id":55,"genre_name":"Detective
|
||||
/ mystery"},{"genre_category":"Other Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":7.1,"moby_url":"https://www.mobygames.com/game/1/the-x-files-game/","num_votes":57,"official_url":"https://www.hyperbole.com/xsite/","platforms":[{"first_release_date":"1998","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1999","platform_id":6,"platform_name":"PlayStation"},{"first_release_date":"1998-06","platform_id":74,"platform_name":"Macintosh"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4062982-the-x-files-game-windows-front-cover.jpg","platforms":["Windows","Macintosh"],"thumbnail_image":"https://cdn.mobygames.com/872aed6c-aba4-11ed-a188-02420a00019a.webp","width":690},"sample_screenshots":[{"caption":"Outside
|
||||
Agent Craig Willmore''s apartment ","height":480,"image":"https://cdn.mobygames.com/screenshots/11077669-the-x-files-game-windows-outside-agent-craig-willmores-apartment.jpg","thumbnail_image":"https://cdn.mobygames.com/93317876-ac15-11ed-b444-02420a000134.webp","width":640},{"caption":"Arriving
|
||||
at Dockside Warehouse","height":480,"image":"https://cdn.mobygames.com/screenshots/11072419-the-x-files-game-windows-arriving-at-dockside-warehouse.jpg","thumbnail_image":"https://cdn.mobygames.com/916394e8-ac15-11ed-803a-02420a000131.webp","width":640},{"caption":"Boat
|
||||
dock","height":480,"image":"https://cdn.mobygames.com/screenshots/11076046-the-x-files-game-windows-boat-dock.jpg","thumbnail_image":"https://cdn.mobygames.com/9a3b5ef2-ac15-11ed-b075-02420a00012f.webp","width":640},{"caption":"Medical
|
||||
examiner and very dead James Wong","height":480,"image":"https://cdn.mobygames.com/screenshots/11073361-the-x-files-game-windows-medical-examiner-and-very-dead-james-wo.jpg","thumbnail_image":"https://cdn.mobygames.com/8f6b8eb6-ac15-11ed-833b-02420a000131.webp","width":640},{"caption":"Main
|
||||
Menu","height":576,"image":"https://cdn.mobygames.com/screenshots/10470736-the-x-files-game-playstation-main-menu.jpg","thumbnail_image":"https://cdn.mobygames.com/6cfd26d2-ac10-11ed-803a-02420a000131.webp","width":720}],"title":"The
|
||||
X-Files Game"},{"alternate_titles":[],"description":"<p>Roger Rabbit has been
|
||||
framed for the murder of Marvin Acme, head of the Acme Corporation. Acme''s
|
||||
will states that upon his death, Toon Town would be left to the Toons, but
|
||||
the will is nowhere to be found. You have to find the will and save your wife,
|
||||
Jessica, from Judge Doom and his weasels.</p>\n<p>The game takes place in
|
||||
Hollywood, 1947, where Toons are alive. There are 4 levels in the game, 2
|
||||
of which are driving levels (levels 1 & 3). Roger and Benny the cab have
|
||||
to beat the weasels to the destination, while dodging cars, trams and Judge
|
||||
Doom''s dip which is scattered on the road. There are pick-ups to help you
|
||||
on your way.</p>\n<p>Level 2 is the Ink & Paint Club. The will is on one
|
||||
of the tables. Roger has to pick up all the pieces of paper the penguin waiters
|
||||
put down, whilst avoiding the alcohol and gorilla bouncer.</p>\n<p>Level 4
|
||||
has you in Judge Doom''s warehouse trying to save your wife, Jessica, from
|
||||
the dip truck. You have to use gags to progress and make the weasels laugh
|
||||
themselves to death, literally!</p>","game_id":2,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":9,"genre_name":"Arcade"},{"genre_category":"Other
|
||||
Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":5.9,"moby_url":"https://www.mobygames.com/game/2/who-framed-roger-rabbit/","num_votes":34,"official_url":null,"platforms":[{"first_release_date":"1988","platform_id":2,"platform_name":"DOS"},{"first_release_date":"1988","platform_id":19,"platform_name":"Amiga"},{"first_release_date":"1988","platform_id":24,"platform_name":"Atari
|
||||
ST"},{"first_release_date":"1988","platform_id":27,"platform_name":"Commodore
|
||||
64"},{"first_release_date":"1988","platform_id":31,"platform_name":"Apple
|
||||
II"}],"sample_cover":{"height":700,"image":"https://cdn.mobygames.com/covers/4068119-who-framed-roger-rabbit-amiga-front-cover.jpg","platforms":["Amiga"],"thumbnail_image":"https://cdn.mobygames.com/b59d6706-aba4-11ed-ba50-02420a000199.webp","width":467},"sample_screenshots":[{"caption":"But
|
||||
don''t let the Waiter catch you. (CGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/7288598-who-framed-roger-rabbit-dos-but-dont-let-the-waiter-catch-you-cg.png","thumbnail_image":"https://cdn.mobygames.com/272f1ce8-abf5-11ed-92cb-02420a000132.webp","width":320},{"caption":"The
|
||||
map of the city and where you need to go. Your remaining lives are represented
|
||||
by empty spaces for dip barrels","height":375,"image":"https://cdn.mobygames.com/screenshots/445776-who-framed-roger-rabbit-amiga-the-map-of-the-city-and-where-you-.png","thumbnail_image":"https://cdn.mobygames.com/2750450e-ab6d-11ed-b165-02420a000198.webp","width":640},{"caption":"Title
|
||||
screen","height":375,"image":"https://cdn.mobygames.com/screenshots/445808-who-framed-roger-rabbit-amiga-title-screen.png","thumbnail_image":"https://cdn.mobygames.com/2762b126-ab6d-11ed-b325-02420a00019e.webp","width":640},{"caption":"Pick
|
||||
up the pieces of paper and dodge the gorilla and alcohol","height":375,"image":"https://cdn.mobygames.com/screenshots/445448-who-framed-roger-rabbit-amiga-pick-up-the-pieces-of-paper-and-do.png","thumbnail_image":"https://cdn.mobygames.com/26a9c35a-ab6d-11ed-ba04-02420a00019f.webp","width":640},{"caption":"Roger
|
||||
must collect all the pieces of paper from the table. Avoid the gorilla bouncer
|
||||
(EGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/442192-who-framed-roger-rabbit-dos-roger-must-collect-all-the-pieces-of.png","thumbnail_image":"https://cdn.mobygames.com/2039dc44-ab6d-11ed-9ab3-02420a0001a0.webp","width":320}],"title":"Who
|
||||
Framed Roger Rabbit"}]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b8ef1eee6369c-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:34:59 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "253"
|
||||
Ratelimit-Reset:
|
||||
- "1501"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=bNtxtmMrn0FmS62RuueXYOcA9z8srPXnBwUSNkIGraJq86Ly7EjaKGeapwllYC9SkAGE59wKSBWTeeqbGIXE%2BVAe%2Bs4951vfHuHTX9QO"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,225 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?format=normal&limit=5
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"games":[{"alternate_titles":[{"description":"Finnish title","title":"Salaiset
|
||||
Kansiot"},{"description":"PSX title","title":"The X Files"},{"description":"Spanish
|
||||
title","title":"The X-Files: Expediente X - El Juego"},{"description":"French
|
||||
title","title":"The X-Files: Le Jeu"}],"description":"<p>As an extension of
|
||||
one of the most long-running television series of all time, <em>The X-Files</em>,
|
||||
play through the eyes of Special Agent Craig Willmore, a new FBI field investigator
|
||||
brought in to locate missing agents Fox Mulder and Dana Scully whose last
|
||||
location was the Everett, Washington, area. In this \"movie quality\" video
|
||||
production, characters are played by the actors and actresses from the show,
|
||||
including <a href=\"https://www.mobygames.com/person/9411/gillian-anderson/\">Gillian
|
||||
Anderson</a> (Scully) and <a href=\"https://www.mobygames.com/person/9412/david-duchovny/\">David
|
||||
Duchovny</a> (Mulder).</p>\n<p>As the game begins, you are given a briefing
|
||||
of your mission. Gather up all state-of-the-art spy tools (night vision goggles,
|
||||
a digital camera, PDA, lock picks, evidence kit, a standard issue revolver,
|
||||
handcuffs and badge) and then head out to follow their trail. As you explore
|
||||
the various locations, take photographs, pick up pieces of evidence and talk
|
||||
with people. Use your Newton PDA to access the navigational map, to make notes
|
||||
and send/receive e-mail. Trace telephone numbers, run background checks and
|
||||
license plate ids and even post an All Points Bulletin on missing persons
|
||||
using the computer network at your home or office. By using photo viewer software,
|
||||
download field photographs to the computer where they can be enlarged and
|
||||
studied more closely for clues.</p>\n<p>Features include emotion icons for
|
||||
interjecting different tones during conversations (mean, humorous or technical)
|
||||
which effect the answer given. An in-game hint system, Artificial Intelligence,
|
||||
can be set on or off. In addition, there are multiple endings as a direct
|
||||
result of actions you take.</p>","game_id":1,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":2,"genre_name":"Adventure"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Art
|
||||
Style","genre_category_id":13,"genre_id":217,"genre_name":"Live action"},{"genre_category":"Setting","genre_category_id":10,"genre_id":8,"genre_name":"Sci-fi
|
||||
/ futuristic"},{"genre_category":"Narrative Theme/Topic","genre_category_id":8,"genre_id":55,"genre_name":"Detective
|
||||
/ mystery"},{"genre_category":"Other Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":7.1,"moby_url":"https://www.mobygames.com/game/1/the-x-files-game/","num_votes":57,"official_url":"https://www.hyperbole.com/xsite/","platforms":[{"first_release_date":"1998","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1999","platform_id":6,"platform_name":"PlayStation"},{"first_release_date":"1998-06","platform_id":74,"platform_name":"Macintosh"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4062982-the-x-files-game-windows-front-cover.jpg","platforms":["Windows","Macintosh"],"thumbnail_image":"https://cdn.mobygames.com/872aed6c-aba4-11ed-a188-02420a00019a.webp","width":690},"sample_screenshots":[{"caption":"Outside
|
||||
Agent Craig Willmore''s apartment ","height":480,"image":"https://cdn.mobygames.com/screenshots/11077669-the-x-files-game-windows-outside-agent-craig-willmores-apartment.jpg","thumbnail_image":"https://cdn.mobygames.com/93317876-ac15-11ed-b444-02420a000134.webp","width":640},{"caption":"Arriving
|
||||
at Dockside Warehouse","height":480,"image":"https://cdn.mobygames.com/screenshots/11072419-the-x-files-game-windows-arriving-at-dockside-warehouse.jpg","thumbnail_image":"https://cdn.mobygames.com/916394e8-ac15-11ed-803a-02420a000131.webp","width":640},{"caption":"Boat
|
||||
dock","height":480,"image":"https://cdn.mobygames.com/screenshots/11076046-the-x-files-game-windows-boat-dock.jpg","thumbnail_image":"https://cdn.mobygames.com/9a3b5ef2-ac15-11ed-b075-02420a00012f.webp","width":640},{"caption":"Medical
|
||||
examiner and very dead James Wong","height":480,"image":"https://cdn.mobygames.com/screenshots/11073361-the-x-files-game-windows-medical-examiner-and-very-dead-james-wo.jpg","thumbnail_image":"https://cdn.mobygames.com/8f6b8eb6-ac15-11ed-833b-02420a000131.webp","width":640},{"caption":"Main
|
||||
Menu","height":576,"image":"https://cdn.mobygames.com/screenshots/10470736-the-x-files-game-playstation-main-menu.jpg","thumbnail_image":"https://cdn.mobygames.com/6cfd26d2-ac10-11ed-803a-02420a000131.webp","width":720}],"title":"The
|
||||
X-Files Game"},{"alternate_titles":[],"description":"<p>Roger Rabbit has been
|
||||
framed for the murder of Marvin Acme, head of the Acme Corporation. Acme''s
|
||||
will states that upon his death, Toon Town would be left to the Toons, but
|
||||
the will is nowhere to be found. You have to find the will and save your wife,
|
||||
Jessica, from Judge Doom and his weasels.</p>\n<p>The game takes place in
|
||||
Hollywood, 1947, where Toons are alive. There are 4 levels in the game, 2
|
||||
of which are driving levels (levels 1 & 3). Roger and Benny the cab have
|
||||
to beat the weasels to the destination, while dodging cars, trams and Judge
|
||||
Doom''s dip which is scattered on the road. There are pick-ups to help you
|
||||
on your way.</p>\n<p>Level 2 is the Ink & Paint Club. The will is on one
|
||||
of the tables. Roger has to pick up all the pieces of paper the penguin waiters
|
||||
put down, whilst avoiding the alcohol and gorilla bouncer.</p>\n<p>Level 4
|
||||
has you in Judge Doom''s warehouse trying to save your wife, Jessica, from
|
||||
the dip truck. You have to use gags to progress and make the weasels laugh
|
||||
themselves to death, literally!</p>","game_id":2,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":9,"genre_name":"Arcade"},{"genre_category":"Other
|
||||
Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":5.9,"moby_url":"https://www.mobygames.com/game/2/who-framed-roger-rabbit/","num_votes":34,"official_url":null,"platforms":[{"first_release_date":"1988","platform_id":2,"platform_name":"DOS"},{"first_release_date":"1988","platform_id":19,"platform_name":"Amiga"},{"first_release_date":"1988","platform_id":24,"platform_name":"Atari
|
||||
ST"},{"first_release_date":"1988","platform_id":27,"platform_name":"Commodore
|
||||
64"},{"first_release_date":"1988","platform_id":31,"platform_name":"Apple
|
||||
II"}],"sample_cover":{"height":700,"image":"https://cdn.mobygames.com/covers/4068119-who-framed-roger-rabbit-amiga-front-cover.jpg","platforms":["Amiga"],"thumbnail_image":"https://cdn.mobygames.com/b59d6706-aba4-11ed-ba50-02420a000199.webp","width":467},"sample_screenshots":[{"caption":"But
|
||||
don''t let the Waiter catch you. (CGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/7288598-who-framed-roger-rabbit-dos-but-dont-let-the-waiter-catch-you-cg.png","thumbnail_image":"https://cdn.mobygames.com/272f1ce8-abf5-11ed-92cb-02420a000132.webp","width":320},{"caption":"The
|
||||
map of the city and where you need to go. Your remaining lives are represented
|
||||
by empty spaces for dip barrels","height":375,"image":"https://cdn.mobygames.com/screenshots/445776-who-framed-roger-rabbit-amiga-the-map-of-the-city-and-where-you-.png","thumbnail_image":"https://cdn.mobygames.com/2750450e-ab6d-11ed-b165-02420a000198.webp","width":640},{"caption":"Title
|
||||
screen","height":375,"image":"https://cdn.mobygames.com/screenshots/445808-who-framed-roger-rabbit-amiga-title-screen.png","thumbnail_image":"https://cdn.mobygames.com/2762b126-ab6d-11ed-b325-02420a00019e.webp","width":640},{"caption":"Pick
|
||||
up the pieces of paper and dodge the gorilla and alcohol","height":375,"image":"https://cdn.mobygames.com/screenshots/445448-who-framed-roger-rabbit-amiga-pick-up-the-pieces-of-paper-and-do.png","thumbnail_image":"https://cdn.mobygames.com/26a9c35a-ab6d-11ed-ba04-02420a00019f.webp","width":640},{"caption":"Roger
|
||||
must collect all the pieces of paper from the table. Avoid the gorilla bouncer
|
||||
(EGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/442192-who-framed-roger-rabbit-dos-roger-must-collect-all-the-pieces-of.png","thumbnail_image":"https://cdn.mobygames.com/2039dc44-ab6d-11ed-9ab3-02420a0001a0.webp","width":320}],"title":"Who
|
||||
Framed Roger Rabbit"},{"alternate_titles":[{"description":"Working title","title":"Squadron"},{"description":"Common
|
||||
abbreviation","title":"WC1"},{"description":"German tag-lined title","title":"Wing
|
||||
Commander: Der 3D-Raumkampf-Simulator"},{"description":"Tag-lined title","title":"Wing
|
||||
Commander: The 3-D Space Combat Simulator"},{"description":"Working title","title":"Wingleader"}],"description":"<p>The
|
||||
Confederation have been at war with the Kilrathi for the past 20 years, and
|
||||
you''re just now joining the Vega campaign. You''re a 2nd Lieutenant just
|
||||
out of the Academy, with some good work under your belt. You''re posted to
|
||||
Tiger''s Claw, the flagship of the Confederation Fleet. Will you help the
|
||||
Confederation to victory, or go down in infamy? </p>\n<p><em>Wing Commander</em>
|
||||
is a space combat simulator interspersed with shipboard dialogs. Onboard the
|
||||
ship, you can save/load game, visit the bar to get the latest gossip, or go
|
||||
on to the next mission briefing, and the 3D space combat part. </p>\n<p>The
|
||||
3D space combat has you sitting in the cockpit, where you control the craft
|
||||
like roll, turn, up/down, afterburner, as well as fire guns and launch missiles.
|
||||
There are four different crafts on the Confed side, each with different flight
|
||||
characteristics and armament. You will have a wingman on each mission, and
|
||||
you should keep the wingman alive as the wingman will help you if you issue
|
||||
the right orders. You can also taunt the enemy. You''ll be fighting several
|
||||
different types of enemy fighters and capital ships, and even combat a few
|
||||
Kilrathi aces. </p>\n<p>When the mission is complete, land back onboard the
|
||||
ship and get ready for the next one. The campaign tree has both winning and
|
||||
losing paths.</p>","game_id":3,"genres":[{"genre_category":"Basic Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":3,"genre_name":"Simulation"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Vehicular
|
||||
Themes","genre_category_id":11,"genre_id":151,"genre_name":"Space flight"},{"genre_category":"Vehicular
|
||||
Themes","genre_category_id":11,"genre_id":159,"genre_name":"Vehicular combat"},{"genre_category":"Setting","genre_category_id":10,"genre_id":8,"genre_name":"Sci-fi
|
||||
/ futuristic"}],"moby_score":7.9,"moby_url":"https://www.mobygames.com/game/3/wing-commander/","num_votes":201,"official_url":null,"platforms":[{"first_release_date":"1990","platform_id":2,"platform_name":"DOS"},{"first_release_date":"1992","platform_id":15,"platform_name":"SNES"},{"first_release_date":"1992","platform_id":19,"platform_name":"Amiga"},{"first_release_date":"1994","platform_id":20,"platform_name":"SEGA
|
||||
CD"},{"first_release_date":"1992-12","platform_id":102,"platform_name":"FM
|
||||
Towns"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/6743023-wing-commander-dos-front-cover.jpg","platforms":["DOS"],"thumbnail_image":"https://cdn.mobygames.com/dad1dfd8-abf0-11ed-8a97-02420a00012d.webp","width":560},"sample_screenshots":[{"caption":"The
|
||||
Officers'' Lounge","height":224,"image":"https://cdn.mobygames.com/screenshots/15762759-wing-commander-snes-the-officers-lounge.png","thumbnail_image":"https://cdn.mobygames.com/ed048970-bede-11ed-9c42-02420a000140.webp","width":256},{"caption":"Encountering
|
||||
a Dralthi","height":400,"image":"https://cdn.mobygames.com/screenshots/350994-wing-commander-amiga-encountering-a-dralthi.png","thumbnail_image":"https://cdn.mobygames.com/696381aa-ab6c-11ed-bd13-02420a00019c.webp","width":640},{"caption":"red
|
||||
alert! (EGA)","height":400,"image":"https://cdn.mobygames.com/screenshots/273924-wing-commander-dos-red-alert-ega.png","thumbnail_image":"https://cdn.mobygames.com/bfbe68d6-ab6b-11ed-89d0-02420a00019d.webp","width":640},{"caption":"Raptor
|
||||
Cockpit","height":200,"image":"https://cdn.mobygames.com/screenshots/6756305-wing-commander-dos-raptor-cockpit.png","thumbnail_image":"https://cdn.mobygames.com/f5d649ea-abf0-11ed-902e-02420a00012d.webp","width":320},{"caption":"Rapier
|
||||
Cockpit","height":200,"image":"https://cdn.mobygames.com/screenshots/6756317-wing-commander-dos-rapier-cockpit.png","thumbnail_image":"https://cdn.mobygames.com/f6a3bd4e-abf0-11ed-902e-02420a00012d.webp","width":320}],"title":"Wing
|
||||
Commander"},{"alternate_titles":[{"description":"Common abbreviation","title":"SMAC"}],"description":"<p>After
|
||||
the 20th century, humankind reaches its hand out across the stars. Seeking
|
||||
to escape the overcrowded chaos of Earth, the United Nations builds a single
|
||||
seedship, the UNS Unity, and sends her on a mission towards the Alpha Centauri
|
||||
star system. After a long journey in cryogenic suspension, the Unity reaches
|
||||
Alpha Centauri where the Captain is killed under mysterious circumstances.
|
||||
Suspecting the motives of one another, the officers and the crew split into
|
||||
7 factions, each lead with a distinct ideology and motives that they seek
|
||||
to build the planet in their image...</p>\n<p><em>Sid Meier''s Alpha Centauri</em>
|
||||
is best compared to <a href=\"https://www.mobygames.com/game/15/sid-meiers-civilization-ii/\">Civilization
|
||||
II</a>, but features many distinct differences in gameplay and thinking. In
|
||||
Civilization, the objective was to evolve a society from primitive tribes,
|
||||
whereas Alpha Centauri starts with the landing of colony pods on a barren
|
||||
planet with society becoming fractured. Each faction (aka nation) receives
|
||||
it''s own share of the Unity''s resources and tech base. For the basics, bases
|
||||
produce nutrients, materials and energy. Nutrients are required to feed to
|
||||
population, Materials are used in production and energy represents the commerce
|
||||
effect which can be traded to players diplomatically or spent on improvements.
|
||||
The 7 factions each have their own agenda, which is determined in large part
|
||||
by the Social Engineering. This enables a faction to customize its values,
|
||||
earning a bonus for what it considers important and a penalty for what it
|
||||
doesn''t. Social Engineering system are discovered through research, the same
|
||||
as other improvements, such as structures and units.</p>\n<p>Research is divided
|
||||
into 4 types of technologies, which form an intertwining tree of dependencies.
|
||||
They are: Conquer (direct military applications), Explore (indirect technologies
|
||||
for units and bases), Build (direct infrastructure application) and Discovery
|
||||
(Science for the sake of science, indirect applications). Because of the separation,
|
||||
factions can focus on what they hope the intended result of their science
|
||||
will be, and can be changed at any time. To explore the planet, units are
|
||||
needed. Any unit can be customized out of known technologies; consisting of
|
||||
a chassis type, reactor, weapon, armor and special abilities. Each of these
|
||||
components has a different expense, with untested technologies having additional
|
||||
overhead (prototype). </p>\n<p>Finally, Alpha Centauri is not a desolate star
|
||||
system. There is life on the planet, in the form of alien fungus that litters
|
||||
the ground and strange creatures such as mindworms. Initially hostile to all
|
||||
factions, this form of life holds its own secrets and effects on the world
|
||||
at large.</p>","game_id":4,"genres":[{"genre_category":"Basic Genres","genre_category_id":1,"genre_id":4,"genre_name":"Strategy
|
||||
/ tactics"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":129,"genre_name":"Diagonal-down"},{"genre_category":"Visual
|
||||
Presentation","genre_category_id":12,"genre_id":134,"genre_name":"Free camera"},{"genre_category":"Visual
|
||||
Presentation","genre_category_id":12,"genre_id":25,"genre_name":"Isometric"},{"genre_category":"Pacing","genre_category_id":9,"genre_id":106,"genre_name":"Turn-based"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":142,"genre_name":"4X"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":40,"genre_name":"Managerial
|
||||
/ business simulation"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":226,"genre_name":"Turn-based
|
||||
strategy (TBS)"},{"genre_category":"Interface/Control","genre_category_id":7,"genre_id":173,"genre_name":"Multiple
|
||||
units/characters control"},{"genre_category":"Interface/Control","genre_category_id":7,"genre_id":167,"genre_name":"Point
|
||||
and select"},{"genre_category":"Setting","genre_category_id":10,"genre_id":8,"genre_name":"Sci-fi
|
||||
/ futuristic"}],"moby_score":8.0,"moby_url":"https://www.mobygames.com/game/4/sid-meiers-alpha-centauri/","num_votes":172,"official_url":"https://web.archive.org/web/20021017023243/http://www.firaxis.com/smac/","platforms":[{"first_release_date":"1999-02","platform_id":3,"platform_name":"Windows"},{"first_release_date":"2000-02","platform_id":74,"platform_name":"Macintosh"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4024341-sid-meiers-alpha-centauri-windows-front-cover.jpg","platforms":["Windows"],"thumbnail_image":"https://cdn.mobygames.com/30989ad6-aba3-11ed-98cd-02420a00019e.webp","width":657},"sample_screenshots":[{"caption":"Governor
|
||||
screen","height":600,"image":"https://cdn.mobygames.com/screenshots/10393646-sid-meiers-alpha-centauri-windows-governor-screen.jpg","thumbnail_image":"https://cdn.mobygames.com/d4034204-ac0f-11ed-aca6-02420a000132.webp","width":800},{"caption":"Moderately
|
||||
developed cities","height":600,"image":"https://cdn.mobygames.com/screenshots/10232216-sid-meiers-alpha-centauri-windows-moderately-developed-cities.jpg","thumbnail_image":"https://cdn.mobygames.com/72c9f722-ac0e-11ed-9941-02420a000133.webp","width":800},{"caption":"Choosing
|
||||
a faction","height":768,"image":"https://cdn.mobygames.com/screenshots/16876257-sid-meiers-alpha-centauri-windows-choosing-a-faction.png","thumbnail_image":"https://cdn.mobygames.com/a79f3e1c-c3fb-11ed-be63-02420a000121.webp","width":1024},{"caption":"Main
|
||||
menu","height":768,"image":"https://cdn.mobygames.com/screenshots/16876134-sid-meiers-alpha-centauri-windows-main-menu.png","thumbnail_image":"https://cdn.mobygames.com/9809267a-c3fb-11ed-be63-02420a000121.webp","width":1024},{"caption":"Design
|
||||
workshop","height":600,"image":"https://cdn.mobygames.com/screenshots/10393989-sid-meiers-alpha-centauri-windows-design-workshop.jpg","thumbnail_image":"https://cdn.mobygames.com/ce21e552-ac0f-11ed-897b-02420a00012d.webp","width":800}],"title":"Sid
|
||||
Meier''s Alpha Centauri"},{"alternate_titles":[{"description":"Informal name","title":"Indy
|
||||
500"}],"description":"<p>The famous Indianapolis 500 Mile race, held annually
|
||||
at the Indianapolis Motor Speedway on the Memorial Day weekend, is one of
|
||||
the most famous automobile racing events in North America. The event is simulated
|
||||
here using 3D polygon graphics to recreate the 33 cars in the race. Players
|
||||
are able to choose their car and customize it with a variety of options. Modifications
|
||||
include wing down-force, tire pressures, wheel stagger (making the right-side
|
||||
wheels larger, to compensate for the banked corners) and turbo output (which
|
||||
provides boost, but stresses the engine and uses more fuel).</p>\n<p>After
|
||||
qualifying (by performing during four laps and taking the average), players
|
||||
can race over 10, 30, 60 or the full 200 laps. Lower modes remove car damage
|
||||
and the ''full-course yellow'' system, the absence of these can make for repeat
|
||||
carnage including traffic collisions and huge pile-ups.</p>","game_id":5,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":6,"genre_name":"Racing / Driving"},{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":3,"genre_name":"Simulation"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Vehicular
|
||||
Themes","genre_category_id":11,"genre_id":188,"genre_name":"Automobile"},{"genre_category":"Vehicular
|
||||
Themes","genre_category_id":11,"genre_id":192,"genre_name":"Track racing"},{"genre_category":"Vehicular
|
||||
Themes","genre_category_id":11,"genre_id":158,"genre_name":"Vehicle simulator"},{"genre_category":"Other
|
||||
Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":7.6,"moby_url":"https://www.mobygames.com/game/5/indianapolis-500-the-simulation/","num_votes":64,"official_url":null,"platforms":[{"first_release_date":"1989-12","platform_id":2,"platform_name":"DOS"},{"first_release_date":"1990","platform_id":19,"platform_name":"Amiga"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/868063-indianapolis-500-the-simulation-dos-front-cover.jpg","platforms":["DOS"],"thumbnail_image":"https://cdn.mobygames.com/87cf8842-ab70-11ed-beab-02420a000199.webp","width":608},"sample_screenshots":[{"caption":"Copy
|
||||
protection (Tandy)","height":200,"image":"https://cdn.mobygames.com/screenshots/9497213-indianapolis-500-the-simulation-dos-copy-protection-tandy.png","thumbnail_image":"https://cdn.mobygames.com/889a5652-ac07-11ed-9f52-02420a000130.webp","width":320},{"caption":"Behind
|
||||
the wheel of Penske Chevrolet (VGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/9496819-indianapolis-500-the-simulation-dos-behind-the-wheel-of-penske-c.png","thumbnail_image":"https://cdn.mobygames.com/87c9e1c0-ac07-11ed-bc39-02420a000131.webp","width":320},{"caption":"Title
|
||||
Screen (VGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/5487218-indianapolis-500-the-simulation-dos-title-screen-vga.png","thumbnail_image":"https://cdn.mobygames.com/d609b908-abe6-11ed-893a-02420a00012f.webp","width":320},{"caption":"Pit
|
||||
stop (CGA w/Composite Monitor)","height":400,"image":"https://cdn.mobygames.com/screenshots/7045342-indianapolis-500-the-simulation-dos-pit-stop-cga-wcomposite-moni.png","thumbnail_image":"https://cdn.mobygames.com/3e2f9866-abf3-11ed-92cb-02420a000132.webp","width":640},{"caption":"We
|
||||
hit the wall","height":200,"image":"https://cdn.mobygames.com/screenshots/15985099-indianapolis-500-the-simulation-amiga-we-hit-the-wall.png","thumbnail_image":"https://cdn.mobygames.com/fc99b64c-bf21-11ed-9c42-02420a000140.webp","width":320}],"title":"Indianapolis
|
||||
500: The Simulation"}]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b898d0909a21a-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:31:18 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "323"
|
||||
Ratelimit-Reset:
|
||||
- "1722"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=d3Y%2Bi5S1ZbnCplET22LftuqQSYgRqyfmNuKuCCwKhtIj5I9Bs0qDwdjS%2BewKBeoJdg%2B1clGMG83kF0Aq8Im8MfLmVaylh2F5xnval6gK"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,141 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?genre=1&format=normal&limit=3
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"games":[{"alternate_titles":[],"description":"<p>Roger Rabbit has
|
||||
been framed for the murder of Marvin Acme, head of the Acme Corporation. Acme''s
|
||||
will states that upon his death, Toon Town would be left to the Toons, but
|
||||
the will is nowhere to be found. You have to find the will and save your wife,
|
||||
Jessica, from Judge Doom and his weasels.</p>\n<p>The game takes place in
|
||||
Hollywood, 1947, where Toons are alive. There are 4 levels in the game, 2
|
||||
of which are driving levels (levels 1 & 3). Roger and Benny the cab have
|
||||
to beat the weasels to the destination, while dodging cars, trams and Judge
|
||||
Doom''s dip which is scattered on the road. There are pick-ups to help you
|
||||
on your way.</p>\n<p>Level 2 is the Ink & Paint Club. The will is on one
|
||||
of the tables. Roger has to pick up all the pieces of paper the penguin waiters
|
||||
put down, whilst avoiding the alcohol and gorilla bouncer.</p>\n<p>Level 4
|
||||
has you in Judge Doom''s warehouse trying to save your wife, Jessica, from
|
||||
the dip truck. You have to use gags to progress and make the weasels laugh
|
||||
themselves to death, literally!</p>","game_id":2,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":9,"genre_name":"Arcade"},{"genre_category":"Other
|
||||
Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":5.9,"moby_url":"https://www.mobygames.com/game/2/who-framed-roger-rabbit/","num_votes":34,"official_url":null,"platforms":[{"first_release_date":"1988","platform_id":2,"platform_name":"DOS"},{"first_release_date":"1988","platform_id":19,"platform_name":"Amiga"},{"first_release_date":"1988","platform_id":24,"platform_name":"Atari
|
||||
ST"},{"first_release_date":"1988","platform_id":27,"platform_name":"Commodore
|
||||
64"},{"first_release_date":"1988","platform_id":31,"platform_name":"Apple
|
||||
II"}],"sample_cover":{"height":700,"image":"https://cdn.mobygames.com/covers/4068119-who-framed-roger-rabbit-amiga-front-cover.jpg","platforms":["Amiga"],"thumbnail_image":"https://cdn.mobygames.com/b59d6706-aba4-11ed-ba50-02420a000199.webp","width":467},"sample_screenshots":[{"caption":"But
|
||||
don''t let the Waiter catch you. (CGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/7288598-who-framed-roger-rabbit-dos-but-dont-let-the-waiter-catch-you-cg.png","thumbnail_image":"https://cdn.mobygames.com/272f1ce8-abf5-11ed-92cb-02420a000132.webp","width":320},{"caption":"The
|
||||
map of the city and where you need to go. Your remaining lives are represented
|
||||
by empty spaces for dip barrels","height":375,"image":"https://cdn.mobygames.com/screenshots/445776-who-framed-roger-rabbit-amiga-the-map-of-the-city-and-where-you-.png","thumbnail_image":"https://cdn.mobygames.com/2750450e-ab6d-11ed-b165-02420a000198.webp","width":640},{"caption":"Title
|
||||
screen","height":375,"image":"https://cdn.mobygames.com/screenshots/445808-who-framed-roger-rabbit-amiga-title-screen.png","thumbnail_image":"https://cdn.mobygames.com/2762b126-ab6d-11ed-b325-02420a00019e.webp","width":640},{"caption":"Pick
|
||||
up the pieces of paper and dodge the gorilla and alcohol","height":375,"image":"https://cdn.mobygames.com/screenshots/445448-who-framed-roger-rabbit-amiga-pick-up-the-pieces-of-paper-and-do.png","thumbnail_image":"https://cdn.mobygames.com/26a9c35a-ab6d-11ed-ba04-02420a00019f.webp","width":640},{"caption":"Roger
|
||||
must collect all the pieces of paper from the table. Avoid the gorilla bouncer
|
||||
(EGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/442192-who-framed-roger-rabbit-dos-roger-must-collect-all-the-pieces-of.png","thumbnail_image":"https://cdn.mobygames.com/2039dc44-ab6d-11ed-9ab3-02420a0001a0.webp","width":320}],"title":"Who
|
||||
Framed Roger Rabbit"},{"alternate_titles":[{"description":"Working title","title":"Squadron"},{"description":"Common
|
||||
abbreviation","title":"WC1"},{"description":"German tag-lined title","title":"Wing
|
||||
Commander: Der 3D-Raumkampf-Simulator"},{"description":"Tag-lined title","title":"Wing
|
||||
Commander: The 3-D Space Combat Simulator"},{"description":"Working title","title":"Wingleader"}],"description":"<p>The
|
||||
Confederation have been at war with the Kilrathi for the past 20 years, and
|
||||
you''re just now joining the Vega campaign. You''re a 2nd Lieutenant just
|
||||
out of the Academy, with some good work under your belt. You''re posted to
|
||||
Tiger''s Claw, the flagship of the Confederation Fleet. Will you help the
|
||||
Confederation to victory, or go down in infamy? </p>\n<p><em>Wing Commander</em>
|
||||
is a space combat simulator interspersed with shipboard dialogs. Onboard the
|
||||
ship, you can save/load game, visit the bar to get the latest gossip, or go
|
||||
on to the next mission briefing, and the 3D space combat part. </p>\n<p>The
|
||||
3D space combat has you sitting in the cockpit, where you control the craft
|
||||
like roll, turn, up/down, afterburner, as well as fire guns and launch missiles.
|
||||
There are four different crafts on the Confed side, each with different flight
|
||||
characteristics and armament. You will have a wingman on each mission, and
|
||||
you should keep the wingman alive as the wingman will help you if you issue
|
||||
the right orders. You can also taunt the enemy. You''ll be fighting several
|
||||
different types of enemy fighters and capital ships, and even combat a few
|
||||
Kilrathi aces. </p>\n<p>When the mission is complete, land back onboard the
|
||||
ship and get ready for the next one. The campaign tree has both winning and
|
||||
losing paths.</p>","game_id":3,"genres":[{"genre_category":"Basic Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":3,"genre_name":"Simulation"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Vehicular
|
||||
Themes","genre_category_id":11,"genre_id":151,"genre_name":"Space flight"},{"genre_category":"Vehicular
|
||||
Themes","genre_category_id":11,"genre_id":159,"genre_name":"Vehicular combat"},{"genre_category":"Setting","genre_category_id":10,"genre_id":8,"genre_name":"Sci-fi
|
||||
/ futuristic"}],"moby_score":7.9,"moby_url":"https://www.mobygames.com/game/3/wing-commander/","num_votes":201,"official_url":null,"platforms":[{"first_release_date":"1990","platform_id":2,"platform_name":"DOS"},{"first_release_date":"1992","platform_id":15,"platform_name":"SNES"},{"first_release_date":"1992","platform_id":19,"platform_name":"Amiga"},{"first_release_date":"1994","platform_id":20,"platform_name":"SEGA
|
||||
CD"},{"first_release_date":"1992-12","platform_id":102,"platform_name":"FM
|
||||
Towns"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/6743023-wing-commander-dos-front-cover.jpg","platforms":["DOS"],"thumbnail_image":"https://cdn.mobygames.com/dad1dfd8-abf0-11ed-8a97-02420a00012d.webp","width":560},"sample_screenshots":[{"caption":"The
|
||||
Officers'' Lounge","height":224,"image":"https://cdn.mobygames.com/screenshots/15762759-wing-commander-snes-the-officers-lounge.png","thumbnail_image":"https://cdn.mobygames.com/ed048970-bede-11ed-9c42-02420a000140.webp","width":256},{"caption":"Encountering
|
||||
a Dralthi","height":400,"image":"https://cdn.mobygames.com/screenshots/350994-wing-commander-amiga-encountering-a-dralthi.png","thumbnail_image":"https://cdn.mobygames.com/696381aa-ab6c-11ed-bd13-02420a00019c.webp","width":640},{"caption":"red
|
||||
alert! (EGA)","height":400,"image":"https://cdn.mobygames.com/screenshots/273924-wing-commander-dos-red-alert-ega.png","thumbnail_image":"https://cdn.mobygames.com/bfbe68d6-ab6b-11ed-89d0-02420a00019d.webp","width":640},{"caption":"Raptor
|
||||
Cockpit","height":200,"image":"https://cdn.mobygames.com/screenshots/6756305-wing-commander-dos-raptor-cockpit.png","thumbnail_image":"https://cdn.mobygames.com/f5d649ea-abf0-11ed-902e-02420a00012d.webp","width":320},{"caption":"Rapier
|
||||
Cockpit","height":200,"image":"https://cdn.mobygames.com/screenshots/6756317-wing-commander-dos-rapier-cockpit.png","thumbnail_image":"https://cdn.mobygames.com/f6a3bd4e-abf0-11ed-902e-02420a00012d.webp","width":320}],"title":"Wing
|
||||
Commander"},{"alternate_titles":[],"description":"<p>Take control of a mechanical
|
||||
digging machine as you tunnel your way through the earth, searching for valuable
|
||||
gems and the even more valuable bags of gold! But watch out for Nobbins and
|
||||
Hobbins, and don''t be careless enough to let the bags of gold crush you!</p>\n<p><em>Digger</em>
|
||||
is an arcade game combining elements of the popular arcade games <a href=\"https://www.mobygames.com/game/139/dig-dug/\">Dig
|
||||
Dug</a> and <a href=\"https://www.mobygames.com/game/9764/mr-do/\">Mr. Do!</a>.
|
||||
Players control the titular ''Digger'' that can tunnel through dirt with ease.
|
||||
The goal of each level is to gather up each of the gems, which allows you
|
||||
to progress to the next stage. However, Nobbins and Hobbins are also lurking
|
||||
within the levels - Nobbins are fairly slow, but transform into Hobbins which
|
||||
are much quicker. The enemies can only chase Digger through the tunnels he
|
||||
creates - they cannot dig through the dirt themselves.</p>\n<p>Digger''s defenses
|
||||
consist of being able to shoot a single, rechargeable shot in the direction
|
||||
he is facing with F1 (which recharges after about thirty seconds), crushing
|
||||
his foes by digging underneath a gold bag and letting it plummet down, crushing
|
||||
anything in its path, or by collecting the bonus cherry that sometimes appears,
|
||||
causing Digger to become temporarily invincible.</p>","game_id":18,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":17,"genre_name":"Side
|
||||
view"},{"genre_category":"Visual Presentation","genre_category_id":12,"genre_id":133,"genre_name":"Fixed
|
||||
/ flip-screen"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":9,"genre_name":"Arcade"}],"moby_score":7.2,"moby_url":"https://www.mobygames.com/game/18/digger/","num_votes":55,"official_url":null,"platforms":[{"first_release_date":"1983","platform_id":4,"platform_name":"PC
|
||||
Booter"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/3944239-digger-pc-booter-front-cover.jpg","platforms":["PC
|
||||
Booter"],"thumbnail_image":"https://cdn.mobygames.com/6bec59b8-aba0-11ed-9e18-02420a00019a.webp","width":511},"sample_screenshots":[{"caption":"Level
|
||||
8 (If you manage to complete this level you will return to level 6)","height":200,"image":"https://cdn.mobygames.com/screenshots/173445-digger-pc-booter-level-8-if-you-manage-to-complete-this-level-yo.png","thumbnail_image":"https://cdn.mobygames.com/f6ca57dc-ab6a-11ed-b042-02420a00019f.webp","width":320},{"caption":"Title","height":200,"image":"https://cdn.mobygames.com/screenshots/1778002-digger-pc-booter-title.png","thumbnail_image":"https://cdn.mobygames.com/27bfa1a0-ab78-11ed-837d-02420a00019b.webp","width":320},{"caption":"Level
|
||||
6","height":200,"image":"https://cdn.mobygames.com/screenshots/1777003-digger-pc-booter-level-6.png","thumbnail_image":"https://cdn.mobygames.com/25bf187c-ab78-11ed-87d1-02420a000197.webp","width":320},{"caption":"Level
|
||||
2","height":200,"image":"https://cdn.mobygames.com/screenshots/1778183-digger-pc-booter-level-2.png","thumbnail_image":"https://cdn.mobygames.com/280f1c76-ab78-11ed-837d-02420a00019b.webp","width":320},{"caption":"Level
|
||||
3","height":200,"image":"https://cdn.mobygames.com/screenshots/1778600-digger-pc-booter-level-3.png","thumbnail_image":"https://cdn.mobygames.com/28ebf54c-ab78-11ed-837d-02420a00019b.webp","width":320}],"title":"Digger"}]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b92ff4e91a234-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:37:45 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "215"
|
||||
Ratelimit-Reset:
|
||||
- "1335"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=BwegzCAuSTI5MVyTG8jRXPkRZIOn8xEpx4aT6R0AEQ4u2nIVDtf96724abi6IJJ9AVUudEArfXGjvcW6EeQbM7ZnHmg09vHHgEjoO7e2"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,118 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?format=normal&limit=2&offset=0
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"games":[{"alternate_titles":[{"description":"Finnish title","title":"Salaiset
|
||||
Kansiot"},{"description":"PSX title","title":"The X Files"},{"description":"Spanish
|
||||
title","title":"The X-Files: Expediente X - El Juego"},{"description":"French
|
||||
title","title":"The X-Files: Le Jeu"}],"description":"<p>As an extension of
|
||||
one of the most long-running television series of all time, <em>The X-Files</em>,
|
||||
play through the eyes of Special Agent Craig Willmore, a new FBI field investigator
|
||||
brought in to locate missing agents Fox Mulder and Dana Scully whose last
|
||||
location was the Everett, Washington, area. In this \"movie quality\" video
|
||||
production, characters are played by the actors and actresses from the show,
|
||||
including <a href=\"https://www.mobygames.com/person/9411/gillian-anderson/\">Gillian
|
||||
Anderson</a> (Scully) and <a href=\"https://www.mobygames.com/person/9412/david-duchovny/\">David
|
||||
Duchovny</a> (Mulder).</p>\n<p>As the game begins, you are given a briefing
|
||||
of your mission. Gather up all state-of-the-art spy tools (night vision goggles,
|
||||
a digital camera, PDA, lock picks, evidence kit, a standard issue revolver,
|
||||
handcuffs and badge) and then head out to follow their trail. As you explore
|
||||
the various locations, take photographs, pick up pieces of evidence and talk
|
||||
with people. Use your Newton PDA to access the navigational map, to make notes
|
||||
and send/receive e-mail. Trace telephone numbers, run background checks and
|
||||
license plate ids and even post an All Points Bulletin on missing persons
|
||||
using the computer network at your home or office. By using photo viewer software,
|
||||
download field photographs to the computer where they can be enlarged and
|
||||
studied more closely for clues.</p>\n<p>Features include emotion icons for
|
||||
interjecting different tones during conversations (mean, humorous or technical)
|
||||
which effect the answer given. An in-game hint system, Artificial Intelligence,
|
||||
can be set on or off. In addition, there are multiple endings as a direct
|
||||
result of actions you take.</p>","game_id":1,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":2,"genre_name":"Adventure"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Art
|
||||
Style","genre_category_id":13,"genre_id":217,"genre_name":"Live action"},{"genre_category":"Setting","genre_category_id":10,"genre_id":8,"genre_name":"Sci-fi
|
||||
/ futuristic"},{"genre_category":"Narrative Theme/Topic","genre_category_id":8,"genre_id":55,"genre_name":"Detective
|
||||
/ mystery"},{"genre_category":"Other Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":7.1,"moby_url":"https://www.mobygames.com/game/1/the-x-files-game/","num_votes":57,"official_url":"https://www.hyperbole.com/xsite/","platforms":[{"first_release_date":"1998","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1999","platform_id":6,"platform_name":"PlayStation"},{"first_release_date":"1998-06","platform_id":74,"platform_name":"Macintosh"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4062982-the-x-files-game-windows-front-cover.jpg","platforms":["Windows","Macintosh"],"thumbnail_image":"https://cdn.mobygames.com/872aed6c-aba4-11ed-a188-02420a00019a.webp","width":690},"sample_screenshots":[{"caption":"Outside
|
||||
Agent Craig Willmore''s apartment ","height":480,"image":"https://cdn.mobygames.com/screenshots/11077669-the-x-files-game-windows-outside-agent-craig-willmores-apartment.jpg","thumbnail_image":"https://cdn.mobygames.com/93317876-ac15-11ed-b444-02420a000134.webp","width":640},{"caption":"Arriving
|
||||
at Dockside Warehouse","height":480,"image":"https://cdn.mobygames.com/screenshots/11072419-the-x-files-game-windows-arriving-at-dockside-warehouse.jpg","thumbnail_image":"https://cdn.mobygames.com/916394e8-ac15-11ed-803a-02420a000131.webp","width":640},{"caption":"Boat
|
||||
dock","height":480,"image":"https://cdn.mobygames.com/screenshots/11076046-the-x-files-game-windows-boat-dock.jpg","thumbnail_image":"https://cdn.mobygames.com/9a3b5ef2-ac15-11ed-b075-02420a00012f.webp","width":640},{"caption":"Medical
|
||||
examiner and very dead James Wong","height":480,"image":"https://cdn.mobygames.com/screenshots/11073361-the-x-files-game-windows-medical-examiner-and-very-dead-james-wo.jpg","thumbnail_image":"https://cdn.mobygames.com/8f6b8eb6-ac15-11ed-833b-02420a000131.webp","width":640},{"caption":"Main
|
||||
Menu","height":576,"image":"https://cdn.mobygames.com/screenshots/10470736-the-x-files-game-playstation-main-menu.jpg","thumbnail_image":"https://cdn.mobygames.com/6cfd26d2-ac10-11ed-803a-02420a000131.webp","width":720}],"title":"The
|
||||
X-Files Game"},{"alternate_titles":[],"description":"<p>Roger Rabbit has been
|
||||
framed for the murder of Marvin Acme, head of the Acme Corporation. Acme''s
|
||||
will states that upon his death, Toon Town would be left to the Toons, but
|
||||
the will is nowhere to be found. You have to find the will and save your wife,
|
||||
Jessica, from Judge Doom and his weasels.</p>\n<p>The game takes place in
|
||||
Hollywood, 1947, where Toons are alive. There are 4 levels in the game, 2
|
||||
of which are driving levels (levels 1 & 3). Roger and Benny the cab have
|
||||
to beat the weasels to the destination, while dodging cars, trams and Judge
|
||||
Doom''s dip which is scattered on the road. There are pick-ups to help you
|
||||
on your way.</p>\n<p>Level 2 is the Ink & Paint Club. The will is on one
|
||||
of the tables. Roger has to pick up all the pieces of paper the penguin waiters
|
||||
put down, whilst avoiding the alcohol and gorilla bouncer.</p>\n<p>Level 4
|
||||
has you in Judge Doom''s warehouse trying to save your wife, Jessica, from
|
||||
the dip truck. You have to use gags to progress and make the weasels laugh
|
||||
themselves to death, literally!</p>","game_id":2,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":9,"genre_name":"Arcade"},{"genre_category":"Other
|
||||
Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":5.9,"moby_url":"https://www.mobygames.com/game/2/who-framed-roger-rabbit/","num_votes":34,"official_url":null,"platforms":[{"first_release_date":"1988","platform_id":2,"platform_name":"DOS"},{"first_release_date":"1988","platform_id":19,"platform_name":"Amiga"},{"first_release_date":"1988","platform_id":24,"platform_name":"Atari
|
||||
ST"},{"first_release_date":"1988","platform_id":27,"platform_name":"Commodore
|
||||
64"},{"first_release_date":"1988","platform_id":31,"platform_name":"Apple
|
||||
II"}],"sample_cover":{"height":700,"image":"https://cdn.mobygames.com/covers/4068119-who-framed-roger-rabbit-amiga-front-cover.jpg","platforms":["Amiga"],"thumbnail_image":"https://cdn.mobygames.com/b59d6706-aba4-11ed-ba50-02420a000199.webp","width":467},"sample_screenshots":[{"caption":"But
|
||||
don''t let the Waiter catch you. (CGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/7288598-who-framed-roger-rabbit-dos-but-dont-let-the-waiter-catch-you-cg.png","thumbnail_image":"https://cdn.mobygames.com/272f1ce8-abf5-11ed-92cb-02420a000132.webp","width":320},{"caption":"The
|
||||
map of the city and where you need to go. Your remaining lives are represented
|
||||
by empty spaces for dip barrels","height":375,"image":"https://cdn.mobygames.com/screenshots/445776-who-framed-roger-rabbit-amiga-the-map-of-the-city-and-where-you-.png","thumbnail_image":"https://cdn.mobygames.com/2750450e-ab6d-11ed-b165-02420a000198.webp","width":640},{"caption":"Title
|
||||
screen","height":375,"image":"https://cdn.mobygames.com/screenshots/445808-who-framed-roger-rabbit-amiga-title-screen.png","thumbnail_image":"https://cdn.mobygames.com/2762b126-ab6d-11ed-b325-02420a00019e.webp","width":640},{"caption":"Pick
|
||||
up the pieces of paper and dodge the gorilla and alcohol","height":375,"image":"https://cdn.mobygames.com/screenshots/445448-who-framed-roger-rabbit-amiga-pick-up-the-pieces-of-paper-and-do.png","thumbnail_image":"https://cdn.mobygames.com/26a9c35a-ab6d-11ed-ba04-02420a00019f.webp","width":640},{"caption":"Roger
|
||||
must collect all the pieces of paper from the table. Avoid the gorilla bouncer
|
||||
(EGA)","height":200,"image":"https://cdn.mobygames.com/screenshots/442192-who-framed-roger-rabbit-dos-roger-must-collect-all-the-pieces-of.png","thumbnail_image":"https://cdn.mobygames.com/2039dc44-ab6d-11ed-9ab3-02420a0001a0.webp","width":320}],"title":"Who
|
||||
Framed Roger Rabbit"}]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b92d7a8ada1e1-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:37:38 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "216"
|
||||
Ratelimit-Reset:
|
||||
- "1342"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=cMdhBH49LSqcqJmaDvUSsqiOz5Hbb%2FavRFtM9lRILRDZj%2F5bvW8KRjLD7KB7wslOEGg0w%2BymmjYkHzgn0hLhWp%2F66peXeX5mNSPjpcrY"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,158 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?platform=1&format=normal&limit=3
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"games":[{"alternate_titles":[{"description":"Japanese title","title":"\u014czora
|
||||
no Kishi"}],"description":"<p><em>Knights of the Sky</em> is a World War I
|
||||
flight combat sim where you pilot one of 20 different aircraft (each handles
|
||||
differently) as you engage in simple missions or join a full campaign where
|
||||
you will progress through World War I, going on a variety of missions, patrols,
|
||||
and even encounter enemy aces. You can engage balloons and blimps, enemy fighters
|
||||
and bombers, even strafe group targets such as supply trains. If you do really
|
||||
well in the game, one of the enemy aces may drop a note to challenge you to
|
||||
an aerial duel. </p>\n<p>A unique feature of the game is the capability of
|
||||
modem link so you can challenge fellow modem owners to dogfights, something
|
||||
that none of its contemporaries had.</p>","game_id":28,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":3,"genre_name":"Simulation"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":126,"genre_name":"3rd-person
|
||||
(Other)"},{"genre_category":"Vehicular Themes","genre_category_id":11,"genre_id":19,"genre_name":"Flight
|
||||
/ aviation"},{"genre_category":"Vehicular Themes","genre_category_id":11,"genre_id":159,"genre_name":"Vehicular
|
||||
combat"},{"genre_category":"Setting","genre_category_id":10,"genre_id":14,"genre_name":"Historical
|
||||
events"},{"genre_category":"Setting","genre_category_id":10,"genre_id":214,"genre_name":"World
|
||||
War I"}],"moby_score":7.3,"moby_url":"https://www.mobygames.com/game/28/knights-of-the-sky/","num_votes":41,"official_url":null,"platforms":[{"first_release_date":"2016-07-21","platform_id":1,"platform_name":"Linux"},{"first_release_date":"1990","platform_id":2,"platform_name":"DOS"},{"first_release_date":"2016-07-21","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1991","platform_id":19,"platform_name":"Amiga"},{"first_release_date":"1991","platform_id":24,"platform_name":"Atari
|
||||
ST"},{"first_release_date":"2016-07-21","platform_id":74,"platform_name":"Macintosh"},{"first_release_date":"1993-06-12","platform_id":95,"platform_name":"PC-98"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/6872241-knights-of-the-sky-dos-front-cover.jpg","platforms":["DOS"],"thumbnail_image":"https://cdn.mobygames.com/e9addfc4-abf1-11ed-b206-02420a000131.webp","width":686},"sample_screenshots":[{"caption":"Home
|
||||
base desk","height":400,"image":"https://cdn.mobygames.com/screenshots/1714001-knights-of-the-sky-pc-98-home-base-desk.png","thumbnail_image":"https://cdn.mobygames.com/94e90a88-ab77-11ed-ac16-02420a000199.webp","width":640},{"caption":"Title
|
||||
Screen","height":200,"image":"https://cdn.mobygames.com/screenshots/1688400-knights-of-the-sky-dos-title-screen.png","thumbnail_image":"https://cdn.mobygames.com/5a38e728-ab77-11ed-bd13-02420a00019c.webp","width":320},{"caption":"Side
|
||||
View","height":200,"image":"https://cdn.mobygames.com/screenshots/1683785-knights-of-the-sky-dos-side-view.png","thumbnail_image":"https://cdn.mobygames.com/4edf93e0-ab77-11ed-81b6-02420a00019d.webp","width":320},{"caption":"He
|
||||
flies straight into my crosshairs","height":200,"image":"https://cdn.mobygames.com/screenshots/15988514-knights-of-the-sky-atari-st-he-flies-straight-into-my-crosshairs.png","thumbnail_image":"https://cdn.mobygames.com/cd160442-bf22-11ed-9c42-02420a000140.webp","width":320},{"caption":"Title
|
||||
screen","height":400,"image":"https://cdn.mobygames.com/screenshots/1760327-knights-of-the-sky-pc-98-title-screen.png","thumbnail_image":"https://cdn.mobygames.com/ff7a1a90-ab77-11ed-934f-02420a0001a0.webp","width":640}],"title":"Knights
|
||||
of the Sky"},{"alternate_titles":[{"description":"International title","title":"Shadow
|
||||
of the Comet"}],"description":"<p>In 1834, in the small New England fishing
|
||||
village of Illsmouth, the distinguished British scientist Lord Boleskine lost
|
||||
his mind. After studying ancient manuscripts of evil repute, he had travelled
|
||||
to this place to observe the passing of Halley''s comet. What he observed
|
||||
that night, however, turned him into a raving lunatic. Now, 76 years later,
|
||||
Halley''s comet is coming back, and young reporter John T. Parker has travelled
|
||||
to Illsmouth to try to uncover the truth in Boleskine''s wild claims, and
|
||||
see the comet\nfor himself.</p>\n<p><em>Shadow of the Comet</em> is a horror
|
||||
adventure game, inspired by the terrifying writings of <a href=\"https://www.mobygames.com/person/633/h-p-lovecraft/\">H.
|
||||
P. Lovecraft</a>. Contrary to many adventure games from the early 90s, the
|
||||
game has a keyboard driven interface with a system of actions activated either
|
||||
by pressing the corresponding key (L for look, G for get, T for talk, U for
|
||||
use) or selecting them from the menu activated by the TAB key. The CD release
|
||||
was enhanced with a mouse-driven interface. Typical for Infogrames titles
|
||||
(e.g. <a href=\"https://www.mobygames.com/game/1368/eternam/\">Eternam</a>),
|
||||
the game contains vector-based cut scenes with enlarged graphics of the faces
|
||||
of the speakers during dialogues.</p>\n<p>The GOG.com release of this game
|
||||
includes both Floppy and CD versions of the game. The main differences are
|
||||
new graphics, mouse-driven interface and full voice-acting whereas the Floppy
|
||||
version doesn''t feature any voice-acting. The CD version''s launcher screen
|
||||
additionally includes a <em>Museum</em> mini-game where character can walk
|
||||
through a museum and look at various mystical objects and paintings inspired
|
||||
by <a href=\"https://www.mobygames.com/person/633/h-p-lovecraft/\">H. P. Lovecraft</a>.</p>","game_id":132,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":2,"genre_name":"Adventure"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":17,"genre_name":"Side
|
||||
view"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":146,"genre_name":"Graphic
|
||||
adventure"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":30,"genre_name":"Puzzle
|
||||
elements"},{"genre_category":"Interface/Control","genre_category_id":7,"genre_id":168,"genre_name":"Direct
|
||||
control"},{"genre_category":"Narrative Theme/Topic","genre_category_id":8,"genre_id":83,"genre_name":"Horror"},{"genre_category":"Other
|
||||
Attributes","genre_category_id":6,"genre_id":82,"genre_name":"Licensed"}],"moby_score":7.7,"moby_url":"https://www.mobygames.com/game/132/call-of-cthulhu-shadow-of-the-comet/","num_votes":49,"official_url":"http://www.infogrames.net/shadow/","platforms":[{"first_release_date":"2015-07-21","platform_id":1,"platform_name":"Linux"},{"first_release_date":"1993","platform_id":2,"platform_name":"DOS"},{"first_release_date":"2015-07-21","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1995-03-03","platform_id":95,"platform_name":"PC-98"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4022611-call-of-cthulhu-shadow-of-the-comet-dos-front-cover.jpg","platforms":["DOS"],"thumbnail_image":"https://cdn.mobygames.com/20f0b6b8-aba3-11ed-a519-02420a00019d.webp","width":642},"sample_screenshots":[{"caption":"Doctor
|
||||
Cobble''s house","height":200,"image":"https://cdn.mobygames.com/screenshots/212030-call-of-cthulhu-shadow-of-the-comet-dos-doctor-cobbles-house.png","thumbnail_image":"https://cdn.mobygames.com/43ce164a-ab6b-11ed-8e21-02420a00019b.webp","width":320},{"caption":"Hey,
|
||||
don''t you think this guy looks like George W. Bush? :)","height":400,"image":"https://cdn.mobygames.com/screenshots/16098317-call-of-cthulhu-shadow-of-the-comet-pc-98-hey-dont-you-think-thi.png","thumbnail_image":"https://cdn.mobygames.com/ab361db8-bf71-11ed-9521-02420a000152.webp","width":640},{"caption":"Title","height":200,"image":"https://cdn.mobygames.com/screenshots/194693-call-of-cthulhu-shadow-of-the-comet-dos-title.png","thumbnail_image":"https://cdn.mobygames.com/211ac36e-ab6b-11ed-a5ec-02420a00019b.webp","width":320},{"caption":"Arriving
|
||||
at the sleepy town","height":400,"image":"https://cdn.mobygames.com/screenshots/16098383-call-of-cthulhu-shadow-of-the-comet-pc-98-arriving-at-the-sleepy.png","thumbnail_image":"https://cdn.mobygames.com/b153b0e8-bf71-11ed-9521-02420a000152.webp","width":640},{"caption":"The
|
||||
interface looks similar to Sierra adventures, but it''s keyboard only","height":400,"image":"https://cdn.mobygames.com/screenshots/15650596-call-of-cthulhu-shadow-of-the-comet-pc-98-the-interface-looks-si.png","thumbnail_image":"https://cdn.mobygames.com/359d61aa-bdf1-11ed-b791-02420a0001bb.webp","width":640}],"title":"Call
|
||||
of Cthulhu: Shadow of the Comet"},{"alternate_titles":[{"description":"Simplified
|
||||
Chinese title","title":"Bantiao Ming"},{"description":"Common abbreviation","title":"HL"},{"description":"Cover
|
||||
/ in-game title","title":"H\u03bblf-Life"},{"description":"Working title","title":"Quiver"}],"description":"<p>The
|
||||
Black Mesa Research Facility is an ultra-secret laboratory under a government
|
||||
contract to conduct top-secret and extremely volatile experiments. The scientist
|
||||
Gordon Freeman is a Black Mesa employee. One morning, as usual, he pits his
|
||||
way to the research facility for a run-of-the-mill experiment. However, Gordon
|
||||
comes to realize that it might not be as ordinary as he thought. Odd things
|
||||
happen as he makes his way to one of the Black Mesa test chambers. Even stranger
|
||||
things happen when he begins to move the test sample towards the anti-mass
|
||||
spectrometer.</p>\n<p>At that moment, everything goes horribly wrong. Aliens
|
||||
from the dimension Xen suddenly invade the facility, injuring or killing many
|
||||
of the employees. Soon afterwards, marines arrive to contain the situation
|
||||
by killing the aliens as well as the surviving human witnesses. Gordon understands
|
||||
what that means: he will have to fight his way through both aliens and marines
|
||||
to get to the top of the Black Mesa complex and to freedom.</p>\n<p>The story
|
||||
of <em>Half-Life</em> is told entirely in-game: everything is seen through
|
||||
the eyes of the protagonist. Most story elements unfold via scripted sequences,
|
||||
triggered by the player reaching a certain area. If other characters have
|
||||
information to reveal, they address Gordon directly. The Black Mesa complex
|
||||
in the game is made up of both distinct levels which progress in a linear
|
||||
fashion as well as hubs where backtracking may be required to unlock further
|
||||
areas.</p>\n<p>The game''s weapon arsenal mostly consists of realistic weapons
|
||||
like pistols, machine guns and explosives, but there are also futuristic energy
|
||||
weapons developed at Black Mesa as well as organic weapons acquired from the
|
||||
invading aliens. Most weapons feature an alternate firing mode.</p>\n<p>Enemies
|
||||
fall into two categories: aliens and human soldiers. While most of the aliens
|
||||
are not very bright, the humans display some relatively advanced artificial
|
||||
intelligence: they seek cover, retreat when hit and try to drive the player
|
||||
from his cover by throwing grenades. Some of the alien enemies cannot be killed
|
||||
by normal means. The environment must be used against them instead, going
|
||||
with a general tendency of the game to alternate the combat with environmental
|
||||
puzzles. </p>\n<p>As of the 25th Anniversary Update from 17 November 2023,
|
||||
the Steam version of Half-Life includes content from <a href=\"https://www.mobygames.com/game/25393/half-life-uplink/\">Half-Life:
|
||||
Uplink</a> as well as sprays and maps from <a href=\"https://www.mobygames.com/game/58630/half-life-further-data-v1/\">Half-Life:
|
||||
Further Data V.1</a>.</p>","game_id":155,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":7,"genre_name":"1st-person"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":22,"genre_name":"Shooter"},{"genre_category":"Interface/Control","genre_category_id":7,"genre_id":168,"genre_name":"Direct
|
||||
control"},{"genre_category":"Setting","genre_category_id":10,"genre_id":207,"genre_name":"North
|
||||
America"},{"genre_category":"Setting","genre_category_id":10,"genre_id":8,"genre_name":"Sci-fi
|
||||
/ futuristic"},{"genre_category":"Other Attributes","genre_category_id":6,"genre_id":223,"genre_name":"Regional
|
||||
differences"}],"moby_score":8.7,"moby_url":"https://www.mobygames.com/game/155/half-life/","num_votes":607,"official_url":null,"platforms":[{"first_release_date":"2013-02-06","platform_id":1,"platform_name":"Linux"},{"first_release_date":"1998-11","platform_id":3,"platform_name":"Windows"},{"first_release_date":"2013-01-25","platform_id":74,"platform_name":"Macintosh"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4597980-half-life-windows-front-cover.jpg","platforms":["Windows"],"thumbnail_image":"https://cdn.mobygames.com/455b871a-abb9-11ed-aecf-02420a000198.webp","width":661},"sample_screenshots":[{"caption":"Beautiful
|
||||
view outside! Doesn''t look very safe, though...","height":768,"image":"https://cdn.mobygames.com/screenshots/10587038-half-life-windows-beautiful-view-outside-doesnt-look-very-safe-t.jpg","thumbnail_image":"https://cdn.mobygames.com/65e552ce-ac11-11ed-962d-02420a000135.webp","width":1024},{"caption":"Marines
|
||||
incoming in an Osprey","height":360,"image":"https://cdn.mobygames.com/screenshots/12487516-half-life-windows-marines-incoming-in-an-osprey.jpg","thumbnail_image":"https://cdn.mobygames.com/7f1f0270-ac21-11ed-b57a-02420a000130.webp","width":480},{"caption":"The
|
||||
title and main menu","height":800,"image":"https://cdn.mobygames.com/screenshots/16475530-half-life-linux-the-title-and-main-menu.png","thumbnail_image":"https://cdn.mobygames.com/982e3a32-c225-11ed-ab6b-02420a000194.webp","width":1000},{"caption":"Some
|
||||
of the environments seem radioactive, but this is purely a stylistic choice","height":768,"image":"https://cdn.mobygames.com/screenshots/3278622-half-life-windows-some-of-the-environments-seem-radioactive-but-.jpg","thumbnail_image":"https://cdn.mobygames.com/4da4b7de-ab8c-11ed-b6ec-02420a00019b.webp","width":1024},{"caption":"The
|
||||
test chamber","height":600,"image":"https://cdn.mobygames.com/screenshots/10105274-half-life-windows-the-test-chamber.jpg","thumbnail_image":"https://cdn.mobygames.com/33ea9530-ac0d-11ed-902e-02420a00012d.webp","width":800}],"title":"Half-Life"}]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b8a1e9f10a22e-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:31:41 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "321"
|
||||
Ratelimit-Reset:
|
||||
- "1699"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=IWsWIvE1hW7HZd%2BZRMeSwQPpiMqRnoTjQg47uzY0Mowq8wqSDs36W91OmXeHSwljKzY5AuNZJ5AaIrB8OicbLvC95l2pNEvreEGh09e8"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,203 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.mobygames.com/v1/games?title=Sonic&format=normal&limit=5
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"games":[{"alternate_titles":[],"description":"<p>The Sonic &
|
||||
Knuckles Collection contains three games ported from the Sega Genesis:</p>\n<ul>\n<li><a
|
||||
href=\"https://www.mobygames.com/game/6612/sonic-the-hedgehog-3/\">Sonic the
|
||||
Hedgehog 3</a></li>\n<li><a href=\"https://www.mobygames.com/game/6614/sonic-knuckles/\">Sonic
|
||||
& Knuckles</a></li>\n<li><em>Sonic 3 & Knuckles</em> (<em>Sonic the
|
||||
Hedgehog 3</em> running with the <em>Sonic & Knuckles</em> modification
|
||||
enhancement)</li>\n</ul>\n<p>In addition it contains <em>Special Stage Mode</em>,
|
||||
a collection of bonus levels previously available in <em>Sonic & Knuckles</em>
|
||||
as \"Blue Sphere\".</p>","game_id":1156,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":76,"genre_name":"Compilation"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":128,"genre_name":"Behind
|
||||
view"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":17,"genre_name":"Side
|
||||
view"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":21,"genre_name":"Platform"}],"moby_score":7.6,"moby_url":"https://www.mobygames.com/game/1156/sonic-knuckles-collection/","num_votes":17,"official_url":null,"platforms":[{"first_release_date":"1996","platform_id":3,"platform_name":"Windows"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/7129144-sonic-knuckles-collection-windows-front-cover.jpg","platforms":["Windows"],"thumbnail_image":"https://cdn.mobygames.com/e39e215a-abf3-11ed-b83f-02420a000135.webp","width":647},"sample_screenshots":[{"caption":"Knuckles
|
||||
glides acroos the landscape","height":222,"image":"https://cdn.mobygames.com/screenshots/1121053-sonic-knuckles-collection-windows-knuckles-glides-acroos-the-lan.jpg","thumbnail_image":"https://cdn.mobygames.com/88e0c3e8-ab72-11ed-96f9-02420a00019a.webp","width":319},{"caption":"Sonic
|
||||
goes for a run","height":224,"image":"https://cdn.mobygames.com/screenshots/1119841-sonic-knuckles-collection-windows-sonic-goes-for-a-run.jpg","thumbnail_image":"https://cdn.mobygames.com/86800456-ab72-11ed-96f9-02420a00019a.webp","width":320},{"caption":"Special
|
||||
bonus stage","height":224,"image":"https://cdn.mobygames.com/screenshots/1119742-sonic-knuckles-collection-windows-special-bonus-stage.jpg","thumbnail_image":"https://cdn.mobygames.com/864afe82-ab72-11ed-96f9-02420a00019a.webp","width":317},{"caption":"Sonic
|
||||
gets ready to party","height":225,"image":"https://cdn.mobygames.com/screenshots/1119619-sonic-knuckles-collection-windows-sonic-gets-ready-to-party.jpg","thumbnail_image":"https://cdn.mobygames.com/86138c36-ab72-11ed-96f9-02420a00019a.webp","width":320}],"title":"Sonic
|
||||
& Knuckles Collection"},{"alternate_titles":[],"description":"<p>While on
|
||||
vacation, Tails and Sonic come across a recruiting advertisement for the World
|
||||
Grand Prix race. Sonic is not interested at first, but once he notices that
|
||||
Dr. Robotnik will participate he decides to enter. Robotnik is only interested
|
||||
in the race because he''s learned the location of the Chaos Emeralds and now
|
||||
he''ll get a chance to beat Sonic while he gets them. Knuckles finds out that
|
||||
Sonic will participate, so he joins him. Amy overhears Robotnik''s plan, so
|
||||
she will also be racing to get the Chaos Emeralds.</p>\n<p>There are many
|
||||
modes of gameplay in Sonic R:</p>\n<p>Grand Prix: You race against the rest
|
||||
of the characters on each of the circuits, and get a chance to race the level''s
|
||||
boss to unlock some of the extra playable characters.</p>\n<p>Time Attack:
|
||||
Run on one of the tracks as fast as you can, get 5 balloons or tag 4 characters.</p>\n<p>Multi
|
||||
Player: 4 players on a split-screen or over a network. You can race against
|
||||
up to three players or challenge them on a balloon finding game. The screen
|
||||
can be divided horizontally or vertically for 2 player games. </p>","game_id":2899,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":6,"genre_name":"Racing / Driving"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":128,"genre_name":"Behind
|
||||
view"},{"genre_category":"Art Style","genre_category_id":13,"genre_id":57,"genre_name":"Anime
|
||||
/ Manga"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":9,"genre_name":"Arcade"}],"moby_score":7.0,"moby_url":"https://www.mobygames.com/game/2899/sonic-r/","num_votes":59,"official_url":null,"platforms":[{"first_release_date":"1997","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1997-10-31","platform_id":23,"platform_name":"SEGA
|
||||
Saturn"}],"sample_cover":{"height":686,"image":"https://cdn.mobygames.com/covers/37033-sonic-r-windows-front-cover.jpg","platforms":["Windows"],"thumbnail_image":"https://cdn.mobygames.com/1b1d2548-ab65-11ed-993c-02420a00017b.webp","width":555},"sample_screenshots":[{"caption":"Title
|
||||
Screen","height":480,"image":"https://cdn.mobygames.com/screenshots/10405548-sonic-r-windows-title-screen.jpg","thumbnail_image":"https://cdn.mobygames.com/ec87b13e-ac0f-11ed-b4f3-02420a000135.webp","width":640},{"caption":"A
|
||||
Waterfall","height":480,"image":"https://cdn.mobygames.com/screenshots/10255167-sonic-r-sega-saturn-a-waterfall.jpg","thumbnail_image":"https://cdn.mobygames.com/a8b9aa8a-ac0e-11ed-8ce3-02420a000131.webp","width":640},{"caption":"Entering
|
||||
a Loop","height":480,"image":"https://cdn.mobygames.com/screenshots/1322722-sonic-r-sega-saturn-entering-a-loop.jpg","thumbnail_image":"https://cdn.mobygames.com/26ae5d0a-ab74-11ed-8d2b-02420a00019e.webp","width":640},{"caption":"Knuckles
|
||||
in a circle of the track, facing the other side, scenery, and a locked (unlockable)
|
||||
door.","height":480,"image":"https://cdn.mobygames.com/screenshots/10406232-sonic-r-windows-knuckles-in-a-circle-of-the-track-facing-the-oth.jpg","thumbnail_image":"https://cdn.mobygames.com/eac27e06-ac0f-11ed-a631-02420a000135.webp","width":640},{"caption":"Main
|
||||
Menu","height":480,"image":"https://cdn.mobygames.com/screenshots/10255137-sonic-r-sega-saturn-main-menu.jpg","thumbnail_image":"https://cdn.mobygames.com/a2eb16e8-ac0e-11ed-8ce3-02420a000131.webp","width":640}],"title":"Sonic
|
||||
R"},{"alternate_titles":[{"description":"Working title","title":"CD Sonic
|
||||
the Hedgehog"},{"description":"In-game title","title":"Sonic the Hedgehog
|
||||
CD"}],"description":"<p>Sonic the Hedgehog and his self-proclaimed girlfriend
|
||||
Amy Rose travel to Never Lake, only to discover the legendary Little Planet
|
||||
there, tied in chains and covered by metal. It appears that Sonic''s archenemy,
|
||||
Dr. Eggman, is using the powers of the planet to manipulate the fabric of
|
||||
time. He created Sonic''s evil counterpart, Metal Sonic, who kidnaps Amy and
|
||||
disappears. Now the brave hedgehog must explore the Little Planet, collect
|
||||
seven jewels capable of altering the passage of time, free Amy, and defeat
|
||||
Metal Sonic along with his master. </p>\n<p><em>Sonic CD</em> is a fast-paced
|
||||
side-scrolling platform action game, similar in gameplay to other installments
|
||||
of the series. Sonic uses his patented spin attacks to destroy the doctor''s
|
||||
minions and collects various items, such as protective rings, shields, and
|
||||
speed shoes. His special attacks include the Spin Dash and the Super Peel
|
||||
Out. A stand-out gameplay feature of this installment is Sonic''s ability
|
||||
to travel to past and future versions of the stages he traverses. Depending
|
||||
on the player''s action in the past version of a level, the future versions
|
||||
(which contain obligatory boss enemies) will change from \"bad\" to \"good\",
|
||||
having more or fewer enemies and obstacles, respectively. </p>\n<p>Time Stones
|
||||
can be collected by completing special stages, in which Sonic has to shoot
|
||||
UFOs within an allotted time limit. The game''s \"good\" ending can be achieved
|
||||
either by collecting all the seven Time Stones or by turning all future level
|
||||
versions into \"good\". The game has features that take advantage of the CD
|
||||
format such as CD audio, video clips, and more levels (over fifty in total).</p>","game_id":3316,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":17,"genre_name":"Side
|
||||
view"},{"genre_category":"Visual Presentation","genre_category_id":12,"genre_id":131,"genre_name":"2D
|
||||
scrolling"},{"genre_category":"Art Style","genre_category_id":13,"genre_id":57,"genre_name":"Anime
|
||||
/ Manga"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":21,"genre_name":"Platform"}],"moby_score":8.0,"moby_url":"https://www.mobygames.com/game/3316/sonic-cd/","num_votes":101,"official_url":null,"platforms":[{"first_release_date":"1995","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1993-09-23","platform_id":20,"platform_name":"SEGA
|
||||
CD"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4219323-sonic-cd-sega-cd-front-cover.jpg","platforms":["SEGA
|
||||
CD"],"thumbnail_image":"https://cdn.mobygames.com/3168cf1a-abaa-11ed-a5ec-02420a00019b.webp","width":473},"sample_screenshots":[{"caption":"Something
|
||||
is wrong with the Little Planet...","height":224,"image":"https://cdn.mobygames.com/screenshots/5361222-sonic-cd-windows-something-is-wrong-with-the-little-planet.png","thumbnail_image":"https://cdn.mobygames.com/43e6cf32-abde-11ed-848a-02420a0001d8.webp","width":320},{"caption":"Good
|
||||
Future","height":240,"image":"https://cdn.mobygames.com/screenshots/482217-sonic-cd-sega-cd-good-future.png","thumbnail_image":"https://cdn.mobygames.com/701f31b4-ab6d-11ed-b721-02420a00019b.webp","width":320},{"caption":"Dr.
|
||||
Robotnik","height":240,"image":"https://cdn.mobygames.com/screenshots/483579-sonic-cd-sega-cd-dr-robotnik.png","thumbnail_image":"https://cdn.mobygames.com/72ef0b3a-ab6d-11ed-be6f-02420a000197.webp","width":320},{"caption":"Title
|
||||
screen","height":224,"image":"https://cdn.mobygames.com/screenshots/5360658-sonic-cd-windows-title-screen.png","thumbnail_image":"https://cdn.mobygames.com/3c6cbeb0-abde-11ed-a48a-02420a0001d0.webp","width":320},{"caption":"The
|
||||
first round of the adventure is called Palmtree Panic.","height":224,"image":"https://cdn.mobygames.com/screenshots/5361426-sonic-cd-windows-the-first-round-of-the-adventure-is-called-palm.png","thumbnail_image":"https://cdn.mobygames.com/46ca1574-abde-11ed-8017-02420a0001d7.webp","width":320}],"title":"Sonic
|
||||
CD"},{"alternate_titles":[{"description":"European title","title":"Sonic 3D:
|
||||
Flickies'' Island"}],"description":"<p>While visiting Flicky Island, Sonic
|
||||
notices that the Flickies, his small, feathered friends that can travel between
|
||||
parallel worlds, were captured and turned into badniks (the robot enemies
|
||||
of Sonic) by Dr. Robotnik in his never-ending quest for the Chaos Emeralds.
|
||||
Seven levels separate Sonic from the final showdown with Robotnik.</p>\n<p><em>Sonic
|
||||
3D</em> was the last Sonic title released for the Mega Drive, the only platforming
|
||||
presence of the blue blur in the Sega Saturn, and one of his rare appearances
|
||||
in Personal Computers. The title, considering the era it was released, is
|
||||
somewhat misleading, as instead of full blown 3D graphics it uses an isometric
|
||||
view where Sonic can move not only forwards and backwards, but also left and
|
||||
right. </p>\n<p>Gameplay is much slower when compared to other games in the
|
||||
series, and the number of badniks is reduced to five in each section. As usual,
|
||||
when Sonic destroys one badnik, an animal leaps free from its insides, but
|
||||
this time Sonic has to pick him up and lead them to an interdimensional ring.
|
||||
While they can be taken one by one, exploring the level with all five allow
|
||||
the player to reach for otherwise inaccessible continue tokens. \nUnlike all
|
||||
other previous games, time isn''t a requirement: if the player completes a
|
||||
level in more than 10 minutes, it would only mean there would be no time bonus
|
||||
at the end. Finally, to collect all seven Chaos Emeralds, Sonic first must
|
||||
find Tails or Knuckles and offer them at least 50 rings.</p>","game_id":3494,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":129,"genre_name":"Diagonal-down"},{"genre_category":"Visual
|
||||
Presentation","genre_category_id":12,"genre_id":25,"genre_name":"Isometric"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":9,"genre_name":"Arcade"}],"moby_score":6.9,"moby_url":"https://www.mobygames.com/game/3494/sonic-3d-blast/","num_votes":100,"official_url":null,"platforms":[{"first_release_date":"2018-05-29","platform_id":1,"platform_name":"Linux"},{"first_release_date":"1997","platform_id":3,"platform_name":"Windows"},{"first_release_date":"1996-11","platform_id":16,"platform_name":"Genesis"},{"first_release_date":"1996-11-20","platform_id":23,"platform_name":"SEGA
|
||||
Saturn"},{"first_release_date":"2018-05-29","platform_id":74,"platform_name":"Macintosh"},{"first_release_date":"2007-11-02","platform_id":82,"platform_name":"Wii"}],"sample_cover":{"height":800,"image":"https://cdn.mobygames.com/covers/4126167-sonic-3d-blast-genesis-front-cover.jpg","platforms":["Genesis"],"thumbnail_image":"https://cdn.mobygames.com/ba95aea6-aba6-11ed-8bb9-02420a000197.webp","width":565},"sample_screenshots":[{"caption":"Tails
|
||||
also unlocks a Special Stage","height":240,"image":"https://cdn.mobygames.com/screenshots/2741317-sonic-3d-blast-windows-tails-also-unlocks-a-special-stage.png","thumbnail_image":"https://cdn.mobygames.com/68b6e744-ab83-11ed-bd13-02420a00019c.webp","width":320},{"caption":"that,
|
||||
in the cannon, is Sonic..","height":224,"image":"https://cdn.mobygames.com/screenshots/362094-sonic-3d-blast-genesis-that-in-the-cannon-is-sonic.png","thumbnail_image":"https://cdn.mobygames.com/7f8e14c2-ab6c-11ed-b042-02420a00019f.webp","width":320},{"caption":"Panic
|
||||
Puppet Zone - ring shower..","height":224,"image":"https://cdn.mobygames.com/screenshots/364951-sonic-3d-blast-genesis-panic-puppet-zone-ring-shower.png","thumbnail_image":"https://cdn.mobygames.com/854b07a8-ab6c-11ed-b042-02420a00019f.webp","width":320},{"caption":"Pause
|
||||
screen, showing off a map and other information","height":240,"image":"https://cdn.mobygames.com/screenshots/3121486-sonic-3d-blast-windows-pause-screen-showing-off-a-map-and-other-.png","thumbnail_image":"https://cdn.mobygames.com/8fc35d12-ab89-11ed-8b6f-02420a00019c.webp","width":320},{"caption":"Second
|
||||
boss","height":240,"image":"https://cdn.mobygames.com/screenshots/2750281-sonic-3d-blast-windows-second-boss.png","thumbnail_image":"https://cdn.mobygames.com/8d210966-ab83-11ed-b042-02420a00019f.webp","width":320}],"title":"Sonic
|
||||
3D Blast"},{"alternate_titles":[{"description":"Updated Edition title","title":"Sonic
|
||||
Adventure International"}],"description":"<p>Yet again Sonic and friends find
|
||||
themselves wrapped up in the schemes of Dr. Robotnik. This time he plans to
|
||||
exploit the power of Chaos, a malevolent being that feeds upon the power of
|
||||
the Chaos Emeralds. In their own way, five heroes will do what they can to
|
||||
save Station Square and, perhaps, the entire world.</p>\n<p>The player can
|
||||
select from which character to play after meeting them with Sonic, and each
|
||||
has its own unique version of each level. Sonic''s levels involve racing through
|
||||
a level as quickly as possible, destroying any robots along the way. Tails
|
||||
can hover over distances, and his levels are similar to Sonic''s except that
|
||||
he must beat an opponent to the finish line. Amy can''t charge up ramps or
|
||||
run as quickly as Sonic and Tails, but she does have a giant hammer she can
|
||||
break obstacles with. The goal of her levels is to avoid being captured by
|
||||
a robot sent after her by Dr. Robotnik. E102 Gamma is a slow, difficult to
|
||||
maneuver robot, but it has guns and missiles that can lock on to several targets
|
||||
at once, chaining together attacks and earning a higher score and more time
|
||||
on the clock. The goal of its levels is to reach a boss enemy and destroy
|
||||
it before time runs out. The final character is Big the Cat, a fisherman searching
|
||||
for his lost frog. Big''s levels are almost entirely devoid of action or fighting;
|
||||
he just fishes. Each character can find items that increase their abilities
|
||||
as the story progresses, such as a ring for Sonic that lets him home along
|
||||
lines of rings, or improved lures for Big. A final chapter wrapping up the
|
||||
story is unlocked after all other characters have been completed.</p>\n<p>Additionally,
|
||||
each of the characters can visit the Chao garden where they can raise baby
|
||||
Chaos. By petting, feeding, and showing animals found in levels to the Chaos
|
||||
they can grow stronger, and optionally be put into races against other Chaos.
|
||||
As well each level in the game has additional objectives that can be completed
|
||||
to acquire extra emblems, although the emblems don''t do anything in this
|
||||
version of the game. Finally, at one time it was possible to go online and
|
||||
download additional levels and bonuses in the game, such as a Christmas theme
|
||||
for Station Square and a level sponsored by AT&T, but since SegaNet shut
|
||||
down this is no longer possible.</p>","game_id":3530,"genres":[{"genre_category":"Basic
|
||||
Genres","genre_category_id":1,"genre_id":1,"genre_name":"Action"},{"genre_category":"Perspective","genre_category_id":2,"genre_id":128,"genre_name":"Behind
|
||||
view"},{"genre_category":"Art Style","genre_category_id":13,"genre_id":57,"genre_name":"Anime
|
||||
/ Manga"},{"genre_category":"Gameplay","genre_category_id":4,"genre_id":24,"genre_name":"Metroidvania"}],"moby_score":8.1,"moby_url":"https://www.mobygames.com/game/3530/sonic-adventure/","num_votes":127,"official_url":"http://web.archive.org/web/20000830142357/http://www.sega.co.jp/sonicadv/","platforms":[{"first_release_date":"1998-12-23","platform_id":8,"platform_name":"Dreamcast"},{"first_release_date":"2010-09-15","platform_id":69,"platform_name":"Xbox
|
||||
360"},{"first_release_date":"2010-09-21","platform_id":81,"platform_name":"PlayStation
|
||||
3"}],"sample_cover":{"height":715,"image":"https://cdn.mobygames.com/covers/36438-sonic-adventure-dreamcast-front-cover.jpg","platforms":["Dreamcast"],"thumbnail_image":"https://cdn.mobygames.com/19f9abdc-ab65-11ed-993c-02420a00017b.webp","width":715},"sample_screenshots":[{"caption":"Steep
|
||||
ramp","height":480,"image":"https://cdn.mobygames.com/screenshots/10406629-sonic-adventure-dreamcast-steep-ramp.jpg","thumbnail_image":"https://cdn.mobygames.com/e82e710e-ac0f-11ed-b7c8-02420a000136.webp","width":768},{"caption":"Sonic
|
||||
vs Chaos 0","height":675,"image":"https://cdn.mobygames.com/screenshots/9648175-sonic-adventure-xbox-360-sonic-vs-chaos-0.png","thumbnail_image":"https://cdn.mobygames.com/cf16b9d0-ac08-11ed-a631-02420a000135.webp","width":1200},{"caption":"Character","height":675,"image":"https://cdn.mobygames.com/screenshots/9643778-sonic-adventure-xbox-360-character.png","thumbnail_image":"https://cdn.mobygames.com/cc574494-ac08-11ed-9eb0-02420a000136.webp","width":1200},{"caption":"Aw,
|
||||
Y E A H! This is happening...","height":675,"image":"https://cdn.mobygames.com/screenshots/9648160-sonic-adventure-xbox-360-aw-y-e-a-h-this-is-happening.png","thumbnail_image":"https://cdn.mobygames.com/c95f7eb4-ac08-11ed-a631-02420a000135.webp","width":1200},{"caption":"Title
|
||||
Screen","height":480,"image":"https://cdn.mobygames.com/screenshots/10405243-sonic-adventure-dreamcast-title-screen.jpg","thumbnail_image":"https://cdn.mobygames.com/ed6efa4e-ac0f-11ed-887e-02420a00012e.webp","width":768}],"title":"Sonic
|
||||
Adventure"}]}
|
||||
|
||||
'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963b8a678d8fea84-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'none';
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:31:53 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Ratelimit-Limit:
|
||||
- "720"
|
||||
Ratelimit-Policy:
|
||||
- 1;w=1, 720;w=3600
|
||||
Ratelimit-Remaining:
|
||||
- "319"
|
||||
Ratelimit-Reset:
|
||||
- "1687"
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=gewoHtfpka1EZGloviSxOZNICxd%2B%2BuVobvZ47ImpJ3RGDq0Eupe3k1t2poUK7hCZpD8MnL6vL9UllErtF%2FUZ1HnWHAWZvTqeHkmK7dSl"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- session-www=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxx.xxxxxxxxxxxxxxxxxxxxxx;
|
||||
HttpOnly; SameSite=Lax; Path=/; Domain=mobygames.com
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Cookie
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -93,7 +93,7 @@ interactions:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://retroachievements.org/API/API_GetUserCompletionProgress.php?u=arcanecraeda&c=5
|
||||
uri: https://retroachievements.org/API/API_GetUserCompletionProgress.php?u=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&c=5
|
||||
response:
|
||||
body:
|
||||
string: '{"Count":0,"Total":0,"Results":[]}'
|
||||
@@ -135,4 +135,86 @@ interactions:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://retroachievements.org/API/API_GetUserCompletionProgress.php?u=arcanecraeda&c=5
|
||||
response:
|
||||
body:
|
||||
string: '{"message":"Unauthenticated.","errors":[{"status":419,"code":"unauthorized","title":"Unauthenticated."}]}'
|
||||
headers:
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
CF-RAY:
|
||||
- 963bc2b9193cf4cc-YYZ
|
||||
Cache-Control:
|
||||
- no-cache, private
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:10:19 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=v42vzRzUlv38Qu6nfqABTwRXH9xci4Rtq02vHnmu4ziBD2DKqb1nS4qM4Y97lE3auWg7LUMH%2FpqvxzixlKcsRulm%2BSNrz6oAxnJFhIDHGxrGzQ%3D%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Robots-Tag:
|
||||
- all
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://retroachievements.org/API/API_GetUserCompletionProgress.php?u=arcanecraeda&c=5
|
||||
response:
|
||||
body:
|
||||
string: '{"Count":0,"Total":0,"Results":[]}'
|
||||
headers:
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
CF-RAY:
|
||||
- 963bc80ffef5544f-YYZ
|
||||
Cache-Control:
|
||||
- no-cache, private
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:13:58 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=V7uJ1rkaLPkSaebnfL%2FVchvTX6z%2BXIngogJGE654HkCwAGU%2FRlxFf4h301nG9sFohNl8EuyHTsfXsAjqDvi7cstOK2KUu%2FaJRm6xJGyz9HtrUQ%3D%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
X-Cache:
|
||||
- MISS
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-Robots-Tag:
|
||||
- all
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.screenscraper.fr/api2/jeuInfos.php?gameid=999999
|
||||
response:
|
||||
body:
|
||||
string: "Erreur : Jeu non trouv\xE9e ! "
|
||||
headers:
|
||||
Access-Control-Allow-Headers:
|
||||
- X-Requested-With
|
||||
Access-Control-Allow-Methods:
|
||||
- GET
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-cache, must-revalidate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 03:47:09 GMT
|
||||
Expires:
|
||||
- "0"
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- nginx/1.14.2
|
||||
Set-Cookie:
|
||||
- SESSID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
status:
|
||||
code: 404
|
||||
message: Not Found
|
||||
version: 1
|
||||
@@ -0,0 +1,38 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.screenscraper.fr/api2/jeuInfos.php?crc=abc123&systemeid=1
|
||||
response:
|
||||
body:
|
||||
string: "Champ crc, md5 ou sha1 erron\xE9 "
|
||||
headers:
|
||||
Access-Control-Allow-Headers:
|
||||
- X-Requested-With
|
||||
Access-Control-Allow-Methods:
|
||||
- GET
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-cache, must-revalidate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 03:46:42 GMT
|
||||
Expires:
|
||||
- "0"
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- nginx/1.14.2
|
||||
Set-Cookie:
|
||||
- SESSID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
status:
|
||||
code: 400
|
||||
message: Bad Request
|
||||
version: 1
|
||||
@@ -0,0 +1,775 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.screenscraper.fr/api2/jeuInfos.php?gameid=1
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
"{\n\t\"header\" : {\n\t\t\"APIversion\" : \"2.0\",\n\t\t\"dateTime\"\
|
||||
\ : \"2025-07-19\",\n\t\t\"commandRequested\" : \"https://neoclone.screenscraper.fr/api2/jeuInfos.php?gameid=1&devid=zurdi15&devpassword=xTJwoOFjOQG&output=json&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\
|
||||
,\n\t\t\"success\": \"true\",\n\t\t\"error\": \"\"\n\t},\n\t\"response\" :\
|
||||
\ {\n\t\t\"serveurs\" : {\n\t\t\t\"cpu1\": \"0\",\n\t\t\t\"cpu2\": \"0\",\n\
|
||||
\t\t\t\"cpu3\": \"0\",\n\t\t\t\"cpu4\": \"0\",\n\t\t\t\"threadsmin\": \"3484\"\
|
||||
,\n\t\t\t\"nbscrapeurs\": \"535\",\n\t\t\t\"apiacces\": \"7207526\",\n\t\t\
|
||||
\t\"closefornomember\": \"0\",\n\t\t\t\"closeforleecher\": \"0\",\n\t\t\t\"\
|
||||
maxthreadfornonmember\": \"256\",\n\t\t\t\"threadfornonmember\": \"31\",\n\
|
||||
\t\t\t\"maxthreadformember\": \"4096\",\n\t\t\t\"threadformember\": \"114\"\
|
||||
\n\t\t},\n\t\t\"ssuser\" : {\n\t\t\t\"id\": \"arcaneasada\",\n\t\t\t\"numid\"\
|
||||
: \"27224648\",\n\t\t\t\"niveau\": \"1\",\n\t\t\t\"contribution\": \"0\",\n\
|
||||
\t\t\t\"uploadsysteme\": \"0\",\n\t\t\t\"uploadinfos\": \"0\",\n\t\t\t\"romasso\"\
|
||||
: \"0\",\n\t\t\t\"uploadmedia\": \"0\",\n\t\t\t\"propositionok\": \"0\",\n\
|
||||
\t\t\t\"propositionko\": \"0\",\n\t\t\t\"quotarefu\": \"0\",\n\t\t\t\"maxthreads\"\
|
||||
: \"1\",\n\t\t\t\"maxdownloadspeed\": \"128\",\n\t\t\t\"requeststoday\": \"\
|
||||
0\",\n\t\t\t\"requestskotoday\": \"0\",\n\t\t\t\"maxrequestspermin\": \"3072\"\
|
||||
,\n\t\t\t\"maxrequestsperday\": \"20000\",\n\t\t\t\"maxrequestskoperday\"\
|
||||
: \"2000\",\n\t\t\t\"visites\": \"19\",\n\t\t\t\"datedernierevisite\": \"\
|
||||
2025-07-22 04:21:46\",\n\t\t\t\"favregion\": \"\"\n\t\t\t},\n\t\t\"jeu\" :\
|
||||
\ {\n\t\t\t\"id\": \"1\",\n\t\t\t\"romid\": \"\",\n\t\t\t\"notgame\": \"false\"\
|
||||
,\n\t\t\t\"noms\": [\n\t\t\t\t{\"region\": \"ss\", \"text\": \"Battletoads\"\
|
||||
},\n\t\t\t\t{\"region\": \"wor\", \"text\": \"Battletoads\"}\n\t\t\t],\n\t\
|
||||
\t\t\"cloneof\": \"0\",\n\t\t\t\"systeme\": {\"id\": \"1\", \"text\": \"Megadrive\"\
|
||||
},\n\t\t\t\"editeur\": {\"id\": \"1\", \"text\": \"Tradewest\"},\n\t\t\t\"\
|
||||
developpeur\": {\"id\": \"2\", \"text\": \"Rareware\"},\n\t\t\t\"joueurs\"\
|
||||
: {\"text\": \"1-2\"},\n\t\t\t\"note\": {\"text\": \"14\"},\n\t\t\t\"topstaff\"\
|
||||
: \"0\",\n\t\t\t\"rotation\": \"0\",\n\t\t\t\"synopsis\": [\n\t\t\t\t{ \"\
|
||||
langue\": \"en\", \"text\": \"The Dark Queen has attacked and kidnapped (toadnapped?)\
|
||||
\ your best buddy.. To make matters worse, she nabbed the best looking girl\
|
||||
\ around! So, what will you do? Cry? Whimper? Hide? Call the Starcops? NO!\
|
||||
\ Cause you're a Battletoad! Battletoads don't cry, hide or call for help!\
|
||||
\ And they certainly don't whimper! They get MAD then they get EVEN! So grab\
|
||||
\ your blaster, hop on the Toadster and go get 'em!\\n\\nYou will control\
|
||||
\ a battletoad through various levels. Some are side-scrolling beat-em-ups,\
|
||||
\ other are vertical drops down a shaft, fighting as you go, others have you\
|
||||
\ riding a rocket or surfboard, shooting or avoiding enemies. At the end of\
|
||||
\ each area, there is a boss. In some areas, about half-way through, you may\
|
||||
\ have a mini-boss.\"},\n\t\t\t\t{ \"langue\": \"fr\", \"text\": \"Battletoads\
|
||||
\ sur Megadrive vous met dans la peau de Zitz, crapaud aux pouvoirs qui sortent\
|
||||
\ de l'ordinaire. Votre but : sauver vos deux fr\\u00e8res enlev\\u00e9s par\
|
||||
\ la Reine Noire. D\\u00e9barrassez-vous de vos adversaires \\u00e0 travers\
|
||||
\ diverses phases de jeu passant par la conduite de v\\u00e9hicules, le simple\
|
||||
\ combat rapproch\\u00e9 ou le jeu de plates-formes.\"},\n\t\t\t\t{ \"langue\"\
|
||||
: \"de\", \"text\": \"Die Dunkle K\\u00f6nigin hat deine besten Freunde entf\\\
|
||||
u00fchrt. Auf deiner Reise durch die gef\\u00e4hrliche Umgebung erwarten dich\
|
||||
\ hinterlistige Fallen und gef\\u00e4hrliche Gegner, w\\u00e4hrend du dich\
|
||||
\ durch unterirdische H\\u00f6hlen und gef\\u00e4hrliche Steppen bis hin zum\
|
||||
\ Schattenturm durchk\\u00e4mpfst, in dem dich die Dunkle K\\u00f6nigin pers\\\
|
||||
u00f6nlich erwartet.\"},\n\t\t\t\t{ \"langue\": \"es\", \"text\": \"Cuando\
|
||||
\ la malvada Reina de la Oscuridad secuestra a su mejor amigo y a la Princesa\
|
||||
\ Ang\\u00e9lica, es el momento de volverse loco para despu\\u00e9s desquitarse,\
|
||||
\ as\\u00ed que prepara las armas nucleares porque las vas a necesitar para\
|
||||
\ rescatar a tus amigos y salir con la piel verde intacta.\"},\n\t\t\t\t{\
|
||||
\ \"langue\": \"it\", \"text\": \"La cattiva Regina delle Tenebre rapisce\
|
||||
\ il vostro migliore amico e la bella Principessa Angelica! C'\\u00e8 veramente\
|
||||
\ da IMPAZZIRE e VENDICARSI! Preparate i vostri pugni nucleari perch\\u00e9\
|
||||
\ ne avrete bisogno per salvare i vostri amici e venirne fuori sani e salvi!\"\
|
||||
},\n\t\t\t\t{ \"langue\": \"pt\", \"text\": \"Sapo (\\u00e9 voc\\u00ea mesmo),\
|
||||
\ a Rainha das Trevas acaba de raptar a Princesa Ang\\u00e9lica e Pimple,\
|
||||
\ seu melhor companheiro. A paz acabou e suas encrencas est\\u00e3o apenas\
|
||||
\ come\\u00e7ando.\\nEsperando por voc\\u00ea estar\\u00e3o 12 fases de desfiladeiros\
|
||||
\ intermin\\u00e1veis, t\\u00faneis aterrorizantes, infernos ardentes e muito\
|
||||
\ mais (Sem mencionar em todos os inimigos que habitam estes lugares!). Os\
|
||||
\ combates n\\u00e3o ser\\u00e3o somente em terra firme, voc\\u00ea ter\\\
|
||||
u00e1 a oportunidade de experimentar uma Bicicleta de Corrida, um Jato Turbo\
|
||||
\ e uma Prancha Espacial!!\\nE se preferir uma companhia, chame um outro sapo\
|
||||
\ e fa\\u00e7a uma amea\\u00e7a dupla para a Rainha das Trevas. A situa\\\
|
||||
u00e7\\u00e3o \\u00e9 esta, e apesar de preta voc\\u00ea ter\\u00e1 que fazer\
|
||||
\ alguma coisa a respeito. Agora!!!\"}\n\t\t\t],\n\t\t\t\"classifications\"\
|
||||
: [\n\t\t\t\t{\"type\": \"VRC\", \"text\": \"GA\"}\n\t\t\t],\n\t\t\t\"dates\"\
|
||||
: [\n\t\t\t\t{ \"region\": \"fr\", \"text\": \"1993-03-23\"},\n\t\t\t\t{ \"\
|
||||
region\": \"us\", \"text\": \"1991-07-13\"},\n\t\t\t\t{ \"region\": \"jp\"\
|
||||
, \"text\": \"1993-03-23\"},\n\t\t\t\t{ \"region\": \"eu\", \"text\": \"1993-03-26\"\
|
||||
}\n\t\t\t],\n\t\t\t\"genres\": [\n\t\t\t\t{ \"id\": \"1\",\n\t\t\t\t\"nomcourt\"\
|
||||
: \"0107\",\n\t\t\t\t\"principale\": \"1\",\n\t\t\t\t\"parentid\": \"0\",\n\
|
||||
\t\t\t\t\"noms\": [\n\t\t\t\t\t{ \"langue\": \"en\", \"text\": \"Beat'em Up\"\
|
||||
},\n\t\t\t\t\t{ \"langue\": \"fr\", \"text\": \"Beat'em All\"},\n\t\t\t\t\t\
|
||||
{ \"langue\": \"de\", \"text\": \"Beat'em Up\"},\n\t\t\t\t\t{ \"langue\":\
|
||||
\ \"es\", \"text\": \"Beat'em Up\"},\n\t\t\t\t\t{ \"langue\": \"it\", \"text\"\
|
||||
: \"Beat'em All\"},\n\t\t\t\t\t{ \"langue\": \"pt\", \"text\": \"Briga de\
|
||||
\ rua\"}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"modes\": [\n\t\t\t\t{\
|
||||
\ \"id\": \"4\",\n\t\t\t\t\"nomcourt\": \"\",\n\t\t\t\t\"principale\": \"\
|
||||
1\",\n\t\t\t\t\"parentid\": \"0\",\n\t\t\t\t\"noms\": [\n\t\t\t\t\t{ \"langue\"\
|
||||
: \"en\", \"text\": \"Single-player\"},\n\t\t\t\t\t{ \"langue\": \"fr\", \"\
|
||||
text\": \"Jouable en solo\"},\n\t\t\t\t\t{ \"langue\": \"de\", \"text\": \"\
|
||||
Einzelspieler\"},\n\t\t\t\t\t{ \"langue\": \"es\", \"text\": \"Jugador individual\"\
|
||||
},\n\t\t\t\t\t{ \"langue\": \"it\", \"text\": \"Giocatore singolo\"},\n\t\t\
|
||||
\t\t\t{ \"langue\": \"pt\", \"text\": \"Jogador individual\"}\n\t\t\t\t\t\
|
||||
]\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"familles\": [\n\t\t\t\t{ \"id\": \"20297\"\
|
||||
,\n\t\t\t\t\"nomcourt\": \"\",\n\t\t\t\t\"principale\": \"0\",\n\t\t\t\t\"\
|
||||
parentid\": \"0\",\n\t\t\t\t\"noms\": [\n\t\t\t\t\t{ \"langue\": \"fr\", \"\
|
||||
text\": \"Battletoads\"}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"themes\"\
|
||||
: [\n\t\t\t\t{ \"id\": \"3\",\n\t\t\t\t\"nomcourt\": \"\",\n\t\t\t\t\"principale\"\
|
||||
: \"1\",\n\t\t\t\t\"parentid\": \"0\",\n\t\t\t\t\"noms\": [\n\t\t\t\t\t{ \"\
|
||||
langue\": \"fr\", \"text\": \"Animaux\"},\n\t\t\t\t\t{ \"langue\": \"de\"\
|
||||
, \"text\": \"Haustiere\"},\n\t\t\t\t\t{ \"langue\": \"es\", \"text\": \"\
|
||||
Animales\"}\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"medias\": [\n\t\t\t\
|
||||
\t{\"type\": \"sstitle\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\":\
|
||||
\ \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=sstitle(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"ddf76beb\",\n\t\t\t\t\"\
|
||||
md5\": \"90a26094162b11dc8f914725b5b6e67d\",\n\t\t\t\t\"sha1\": \"c38dc8d5f3af5d72c25c3704a1e02ec23cfede5f\"\
|
||||
,\n\t\t\t\t\"size\": \"5617\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"ss\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=ss(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"7c994e8a\",\n\t\t\t\t\"\
|
||||
md5\": \"dbe6f3584a023a8988e6fa9ad21908d8\",\n\t\t\t\t\"sha1\": \"bb7198fcce3c7ccf77e1a83577e586ba721bddef\"\
|
||||
,\n\t\t\t\t\"size\": \"16857\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"fanart\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=fanart\"\
|
||||
,\n\t\t\t\t\"crc\": \"e2c71d0f\",\n\t\t\t\t\"md5\": \"631016b964745e7f9d390470e3d10ab8\"\
|
||||
,\n\t\t\t\t\"sha1\": \"fa95c01f527de41e7971883f4ca94913763359f1\",\n\t\t\t\
|
||||
\t\"size\": \"973047\",\n\t\t\t\t\"format\": \"jpg\"},\n\t\t\t\t{\"type\"\
|
||||
: \"video\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaVideoJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=video\"\
|
||||
,\n\t\t\t\t\"crc\": \"40387683\",\n\t\t\t\t\"md5\": \"1551b6607157aa781e45c5d2394019eb\"\
|
||||
,\n\t\t\t\t\"sha1\": \"a007fbb781840d6cbc16e7cf93ce8b1eddb8e687\",\n\t\t\t\
|
||||
\t\"size\": \"6655535\",\n\t\t\t\t\"format\": \"mp4\"},\n\t\t\t\t{\"type\"\
|
||||
: \"video-normalized\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaVideoJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=video-normalized\"\
|
||||
,\n\t\t\t\t\"crc\": \"b6dd91f7\",\n\t\t\t\t\"md5\": \"68983d07332efb381c691326a556bf1d\"\
|
||||
,\n\t\t\t\t\"sha1\": \"7c4de9deda10dbceec3749d4248dd00bc90eaf4e\",\n\t\t\t\
|
||||
\t\"size\": \"1878898\",\n\t\t\t\t\"format\": \"mp4\"},\n\t\t\t\t{\"type\"\
|
||||
: \"screenmarquee\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=screenmarquee(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"a757a9ee\",\n\t\t\t\t\"\
|
||||
md5\": \"5d7245561ecf710f8cc979cf0700f5fc\",\n\t\t\t\t\"sha1\": \"745e7e3772879bb251319d8706fda2e62e0a29ca\"\
|
||||
,\n\t\t\t\t\"size\": \"432162\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"screenmarqueesmall\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\
|
||||
\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=screenmarqueesmall(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"7f85fc93\",\n\t\t\t\t\"\
|
||||
md5\": \"ce81c0c3dc46a9e8f028f5a76cfcafac\",\n\t\t\t\t\"sha1\": \"4f4f0b8961670ddb264d35a5841e1fa9178b2914\"\
|
||||
,\n\t\t\t\t\"size\": \"89719\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"manuel\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaManuelJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=manuel(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"45f85918\",\n\t\t\t\t\"\
|
||||
md5\": \"8266356bc75fbf9de80160dfaf344637\",\n\t\t\t\t\"sha1\": \"68cff0e5da79fea0ceed988b3ddb2280d504dd0a\"\
|
||||
,\n\t\t\t\t\"size\": \"3005546\",\n\t\t\t\t\"format\": \"pdf\"},\n\t\t\t\t\
|
||||
{\"type\": \"manuel\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaManuelJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=manuel(fr)\"\
|
||||
,\n\t\t\t\t\"region\": \"fr\",\n\t\t\t\t\"crc\": \"5eaa6741\",\n\t\t\t\t\"\
|
||||
md5\": \"2820e451731c06d70178e5378fbc5348\",\n\t\t\t\t\"sha1\": \"4d90b2cd02ee2a4b293cdbbc64bf86f621877b87\"\
|
||||
,\n\t\t\t\t\"size\": \"2796200\",\n\t\t\t\t\"format\": \"pdf\"},\n\t\t\t\t\
|
||||
{\"type\": \"manuel\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaManuelJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=manuel(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"08a3c847\",\n\t\t\t\t\"\
|
||||
md5\": \"45606798e8941bccc2f3d908adf28c3e\",\n\t\t\t\t\"sha1\": \"d2eb6608fd725c4f043849f453458a8dcd21f120\"\
|
||||
,\n\t\t\t\t\"size\": \"5133703\",\n\t\t\t\t\"format\": \"pdf\"},\n\t\t\t\t\
|
||||
{\"type\": \"manuel\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaManuelJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=manuel(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"6139c927\",\n\t\t\t\t\"\
|
||||
md5\": \"1a9776fce40d8d5e3afb4f3cc173129f\",\n\t\t\t\t\"sha1\": \"639e1c10db086d7e0be36c3cecb457bcb2e2f3f5\"\
|
||||
,\n\t\t\t\t\"size\": \"2483191\",\n\t\t\t\t\"format\": \"pdf\"},\n\t\t\t\t\
|
||||
{\"type\": \"steamgrid\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\":\
|
||||
\ \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=steamgrid\"\
|
||||
,\n\t\t\t\t\"crc\": \"091457a6\",\n\t\t\t\t\"md5\": \"4f6edca1f6cb9bac62d8fadd128dc817\"\
|
||||
,\n\t\t\t\t\"sha1\": \"f41419ab4e54e1cff9c26d5d63cb828ee375cc47\",\n\t\t\t\
|
||||
\t\"size\": \"32938\",\n\t\t\t\t\"format\": \"jpg\"},\n\t\t\t\t{\"type\":\
|
||||
\ \"maps\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=maps(Plans(In-GameMap))\"\
|
||||
,\n\t\t\t\t\"version\": \"In-GameMap\",\n\t\t\t\t\"crc\": \"f002242c\",\n\t\
|
||||
\t\t\t\"md5\": \"418120091fa2fd5e01fa127e2be2747b\",\n\t\t\t\t\"sha1\": \"\
|
||||
0e41b9bce09f88399c24987a9cb7bf2c74ec7ecf\",\n\t\t\t\t\"size\": \"118486\"\
|
||||
,\n\t\t\t\t\"format\": \"jpg\"},\n\t\t\t\t{\"type\": \"wheel\",\n\t\t\t\t\"\
|
||||
parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=wheel(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"1d7d33ff\",\n\t\t\t\t\"\
|
||||
md5\": \"a74e7b3bf2bc3b74a97a0a96bfcd52f2\",\n\t\t\t\t\"sha1\": \"fa205b072aa0dea362d26716fdafc54ce84ff702\"\
|
||||
,\n\t\t\t\t\"size\": \"69689\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"wheel\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=wheel(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"e970d5af\",\n\t\t\t\t\"\
|
||||
md5\": \"58bff299678b2e857b8c700f2fc5d9eb\",\n\t\t\t\t\"sha1\": \"a79283e96d16eea9a12e62255ffa9d29ea1a3ebb\"\
|
||||
,\n\t\t\t\t\"size\": \"265602\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"wheel-carbon\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=wheel-carbon(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"8caab1a9\",\n\t\t\t\t\"\
|
||||
md5\": \"e037dff95ea877800892015825ea25d0\",\n\t\t\t\t\"sha1\": \"e190db495840236ae02fe2cb250c6c190468d1dc\"\
|
||||
,\n\t\t\t\t\"size\": \"198853\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"wheel-carbon\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=wheel-carbon(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"cbea2dcf\",\n\t\t\t\t\"\
|
||||
md5\": \"834c39a33123e428ca7f34d15dfbfe13\",\n\t\t\t\t\"sha1\": \"59a7c800f75ee1a9de9f1ce79bf65bbee737e460\"\
|
||||
,\n\t\t\t\t\"size\": \"219591\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"wheel-steel\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=wheel-steel(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"42e226af\",\n\t\t\t\t\"\
|
||||
md5\": \"707cb35881420b0294859c8440333c94\",\n\t\t\t\t\"sha1\": \"5a643a147dd5d9d9cd4df35255c1d5f80aa4c22d\"\
|
||||
,\n\t\t\t\t\"size\": \"194132\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"wheel-steel\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=wheel-steel(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"0d27311d\",\n\t\t\t\t\"\
|
||||
md5\": \"b2b2443758967416ab74c84e99f14fab\",\n\t\t\t\t\"sha1\": \"2a95ee0f05ea13450e74649ae239d6a9da1fb174\"\
|
||||
,\n\t\t\t\t\"size\": \"213643\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"89a6a850\",\n\t\t\t\t\"\
|
||||
md5\": \"f14783603804a0c9cf07705b18b8d966\",\n\t\t\t\t\"sha1\": \"bab2bc4d4ea9dfd08b871560f0e8eed2b8bfa6f4\"\
|
||||
,\n\t\t\t\t\"size\": \"643493\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"158da78d\",\n\t\t\t\t\"\
|
||||
md5\": \"d931115cba2c348a712b9bb7e46c56b6\",\n\t\t\t\t\"sha1\": \"aa84b394eda103fcfd1a94163283a96b7f757059\"\
|
||||
,\n\t\t\t\t\"size\": \"418696\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"0d4110f9\",\n\t\t\t\t\"\
|
||||
md5\": \"62754f8a982d598d4abdad30077950c8\",\n\t\t\t\t\"sha1\": \"a111bbcbad535fef762ad37417ec99a550404a1e\"\
|
||||
,\n\t\t\t\t\"size\": \"449095\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D(ss)\"\
|
||||
,\n\t\t\t\t\"region\": \"ss\",\n\t\t\t\t\"crc\": \"b69bc1c7\",\n\t\t\t\t\"\
|
||||
md5\": \"5bbd87d55da17687d6930e36460734a1\",\n\t\t\t\t\"sha1\": \"8dbc8a8f1cf28b2db8868b1357937b592961e173\"\
|
||||
,\n\t\t\t\t\"size\": \"331213\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"2cc57d37\",\n\t\t\t\t\"\
|
||||
md5\": \"b892a42f67cfa56381e8cfc15470a7b0\",\n\t\t\t\t\"sha1\": \"d9a668452f3c6a1935de47602f27c731f8fd315f\"\
|
||||
,\n\t\t\t\t\"size\": \"700746\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D-side\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-side(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"4b64f14e\",\n\t\t\t\t\"\
|
||||
md5\": \"db67f58bb07fe6ebe28f14ca070a4c5c\",\n\t\t\t\t\"sha1\": \"f42af90b90f4e419c20868fd76715ef026a04fab\"\
|
||||
,\n\t\t\t\t\"size\": \"106734\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D-side\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-side(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"d7bfcdc6\",\n\t\t\t\t\"\
|
||||
md5\": \"71a52b35dd7f2981531893fb6431c754\",\n\t\t\t\t\"sha1\": \"f57c1d55ee569cf6e6ff1a4f2e1eb460a1c7e6ad\"\
|
||||
,\n\t\t\t\t\"size\": \"59777\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"box-2D-side\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-side(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"43f0d646\",\n\t\t\t\t\"\
|
||||
md5\": \"5559883c733232a9fa7d8ca7dc9a4c11\",\n\t\t\t\t\"sha1\": \"1cdc8ebf2b467281021e0c821fc0703a64965ef5\"\
|
||||
,\n\t\t\t\t\"size\": \"74199\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"box-2D-side\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-side(ss)\"\
|
||||
,\n\t\t\t\t\"region\": \"ss\",\n\t\t\t\t\"crc\": \"705df4bf\",\n\t\t\t\t\"\
|
||||
md5\": \"d184f24dd7ab94655daa100fa98e9009\",\n\t\t\t\t\"sha1\": \"84a4d630b3c17bee49522e5094b1d66128c92902\"\
|
||||
,\n\t\t\t\t\"size\": \"48507\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"box-2D-side\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-side(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"d1add4d1\",\n\t\t\t\t\"\
|
||||
md5\": \"d2dd81cc702ee80b4f8cd53f525757db\",\n\t\t\t\t\"sha1\": \"a17f6feb12a43021c3ed003793f4ba7d6504d28d\"\
|
||||
,\n\t\t\t\t\"size\": \"68485\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"\
|
||||
type\": \"box-2D-back\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-back(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"140640e2\",\n\t\t\t\t\"\
|
||||
md5\": \"8ddb244d4d89bac1262c42ee6fe0960c\",\n\t\t\t\t\"sha1\": \"59747f9e1b1ab407fc30a080fa83d6da83f99ebd\"\
|
||||
,\n\t\t\t\t\"size\": \"629992\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D-back\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-back(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"f0357f29\",\n\t\t\t\t\"\
|
||||
md5\": \"4802432668e00760ff160d1c46a82b60\",\n\t\t\t\t\"sha1\": \"fb1ae4a69a82731500b35b3a958b593eb91eb93f\"\
|
||||
,\n\t\t\t\t\"size\": \"420565\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D-back\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-back(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"4e921238\",\n\t\t\t\t\"\
|
||||
md5\": \"91ab99cfbb4fb05fe78df9c5d73b87b7\",\n\t\t\t\t\"sha1\": \"8f4883baf133f00d20d8202a920a4eaa8e51fd7e\"\
|
||||
,\n\t\t\t\t\"size\": \"374091\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D-back\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-back(ss)\"\
|
||||
,\n\t\t\t\t\"region\": \"ss\",\n\t\t\t\t\"crc\": \"64feef27\",\n\t\t\t\t\"\
|
||||
md5\": \"9dd426a788f74754fd4c2fd387d9a6f9\",\n\t\t\t\t\"sha1\": \"7a5d36a0da252acbf94b0400485d4d5e30b2b988\"\
|
||||
,\n\t\t\t\t\"size\": \"245238\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-2D-back\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-2D-back(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"eb8ed430\",\n\t\t\t\t\"\
|
||||
md5\": \"2076d4b97e31a9db52abed469be3c56a\",\n\t\t\t\t\"sha1\": \"a80f8b7ff5e7993cb07c2987de49bb2f43cbaa75\"\
|
||||
,\n\t\t\t\t\"size\": \"492930\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-texture(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"9e9a3fbb\",\n\t\t\t\t\"\
|
||||
md5\": \"7c5b2a9a2904d68a3bd58351e31ceefa\",\n\t\t\t\t\"sha1\": \"32c2181b3f0e3cb02bf0f1f55547fa9f867f2e26\"\
|
||||
,\n\t\t\t\t\"size\": \"1421194\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-texture(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"ee52c689\",\n\t\t\t\t\"\
|
||||
md5\": \"0bc905681226cc33e2c0d89b6a022268\",\n\t\t\t\t\"sha1\": \"65318ead086188388f4735729ed82c9ecac89f73\"\
|
||||
,\n\t\t\t\t\"size\": \"941994\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-texture(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"852340d8\",\n\t\t\t\t\"\
|
||||
md5\": \"de667b0a13211a2640663239807384ee\",\n\t\t\t\t\"sha1\": \"b014eb5deb752f3332d3926f3f6efc27b41455ee\"\
|
||||
,\n\t\t\t\t\"size\": \"955924\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-texture(ss)\"\
|
||||
,\n\t\t\t\t\"region\": \"ss\",\n\t\t\t\t\"crc\": \"93e8091e\",\n\t\t\t\t\"\
|
||||
md5\": \"ecb19b04096b70b12ff2212c4eb9fb70\",\n\t\t\t\t\"sha1\": \"9aa91cd5e8aaa8d1ed56e03a0bf91c938de41b22\"\
|
||||
,\n\t\t\t\t\"size\": \"636144\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-texture(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"34ede8d1\",\n\t\t\t\t\"\
|
||||
md5\": \"eccf0e0c6eaad09caf48accba4a49640\",\n\t\t\t\t\"sha1\": \"287b08f503e56f7cf943c0cd250eb7d50857289b\"\
|
||||
,\n\t\t\t\t\"size\": \"1306276\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-3D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-3D(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"bbeaf7f5\",\n\t\t\t\t\"\
|
||||
md5\": \"116d15ba879f4e8525cb48976a4db36c\",\n\t\t\t\t\"sha1\": \"b3b3c808cf90b317160e2e92b94b3af0073e6360\"\
|
||||
,\n\t\t\t\t\"size\": \"316050\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-3D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-3D(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"85fc0f3b\",\n\t\t\t\t\"\
|
||||
md5\": \"49ccd261568517e8edff7c8709ea6113\",\n\t\t\t\t\"sha1\": \"ffabb3332ada283e38b80e37d49e813b3da75fb5\"\
|
||||
,\n\t\t\t\t\"size\": \"294215\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-3D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-3D(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"83f18955\",\n\t\t\t\t\"\
|
||||
md5\": \"800ed63a7da2cbdea144ce96cfb678a0\",\n\t\t\t\t\"sha1\": \"e8c0e92060f77d985e980fe379c4a93d0b7cf301\"\
|
||||
,\n\t\t\t\t\"size\": \"302227\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-3D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-3D(ss)\"\
|
||||
,\n\t\t\t\t\"region\": \"ss\",\n\t\t\t\t\"crc\": \"63776bb6\",\n\t\t\t\t\"\
|
||||
md5\": \"8fa15f192355f39c31c01d574339fbaf\",\n\t\t\t\t\"sha1\": \"22aeaaff03fc6b44829125e3ded4ad3ef04a047d\"\
|
||||
,\n\t\t\t\t\"size\": \"277513\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"box-3D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=box-3D(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"ba99757a\",\n\t\t\t\t\"\
|
||||
md5\": \"5e12aaf66a7250544611d556d0daced9\",\n\t\t\t\t\"sha1\": \"2dd36014a54e31da2c586318d481f3cfd9406f84\"\
|
||||
,\n\t\t\t\t\"size\": \"334608\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"support-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"\
|
||||
url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=support-texture(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"6d8c4829\",\n\t\t\t\t\"\
|
||||
md5\": \"4b7e94dc7f00c65717744122b47f5661\",\n\t\t\t\t\"sha1\": \"ae358928ab5e99c31475ff72323ea69875fa7749\"\
|
||||
,\n\t\t\t\t\"size\": \"490753\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"support-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"\
|
||||
url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=support-texture(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"9c743227\",\n\t\t\t\t\"\
|
||||
md5\": \"31ac29bf015704cf19e77ce1426f795e\",\n\t\t\t\t\"sha1\": \"f8cb1b37b1e468c2c1ed761d8764d28b31ddf20f\"\
|
||||
,\n\t\t\t\t\"size\": \"159135\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"support-texture\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"\
|
||||
url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=support-texture(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"1d6627c3\",\n\t\t\t\t\"\
|
||||
md5\": \"825284e75a611da4d2e40bc89619ef75\",\n\t\t\t\t\"sha1\": \"fbfbce3c03a3ffcc6951bde45ac25ee6c3d02ecf\"\
|
||||
,\n\t\t\t\t\"size\": \"146646\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"support-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=support-2D(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"dc0d420e\",\n\t\t\t\t\"\
|
||||
md5\": \"adac27d77f965390d57632c7fa1bbb64\",\n\t\t\t\t\"sha1\": \"6dbe02886a60576984eef2b4f4f1bd0a22c4b057\"\
|
||||
,\n\t\t\t\t\"size\": \"395673\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"support-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=support-2D(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"fd71b060\",\n\t\t\t\t\"\
|
||||
md5\": \"4e5e6ff89c165acee93f6b75666b4a8c\",\n\t\t\t\t\"sha1\": \"ff72ce676aa4b676422c0c1fd1e272f2cbb1265f\"\
|
||||
,\n\t\t\t\t\"size\": \"347279\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"support-2D\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=support-2D(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"a26a1d84\",\n\t\t\t\t\"\
|
||||
md5\": \"62c9dffb72ec017fcd4e776d13c26acd\",\n\t\t\t\t\"sha1\": \"a5429bb3850d1276b6b264e60716d8b47f371157\"\
|
||||
,\n\t\t\t\t\"size\": \"290295\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"bezel-16-9\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"posx\"\
|
||||
: \"246\",\n\t\t\t\t\"posy\": \"2\",\n\t\t\t\t\"posw\": \"1431\",\n\t\t\t\t\
|
||||
\"posh\": \"1075\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=bezel-16-9(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"d6baf7e9\",\n\t\t\t\t\"\
|
||||
md5\": \"c769736a347a58e57d8903faf35185ea\",\n\t\t\t\t\"sha1\": \"20c8f3d56601d85c2d1d6b46bde677642d81c691\"\
|
||||
,\n\t\t\t\t\"size\": \"190243\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv1\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv1(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"a91f5fa2\",\n\t\t\t\t\"\
|
||||
md5\": \"290cba7a3290bd9265b3e40a40891640\",\n\t\t\t\t\"sha1\": \"aaedeff48edb003e35a5d5febe2fe957e4d12e0f\"\
|
||||
,\n\t\t\t\t\"size\": \"712267\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv1\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv1(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"0c4d8b11\",\n\t\t\t\t\"\
|
||||
md5\": \"df1c6a15c4372f5c7c0d9a0707ae4603\",\n\t\t\t\t\"sha1\": \"603891d8789d59376f21f4aef8be5ba1cabd5410\"\
|
||||
,\n\t\t\t\t\"size\": \"706827\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv1\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv1(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"834b485c\",\n\t\t\t\t\"\
|
||||
md5\": \"53b34100f671ea3af207fd604fb1eaf8\",\n\t\t\t\t\"sha1\": \"1fed4664da917bea20ce60b130d114523a4d1d67\"\
|
||||
,\n\t\t\t\t\"size\": \"654423\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv1\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv1(ss)\"\
|
||||
,\n\t\t\t\t\"region\": \"ss\",\n\t\t\t\t\"crc\": \"d855f49d\",\n\t\t\t\t\"\
|
||||
md5\": \"cd2507a75ddc9cbd9a0d42d479c6e798\",\n\t\t\t\t\"sha1\": \"f91704f1e12b8b7e3c6f6a68277c0a88dc60d757\"\
|
||||
,\n\t\t\t\t\"size\": \"705104\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv1\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv1(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"2bac7aa1\",\n\t\t\t\t\"\
|
||||
md5\": \"a4df1777843dcfeb63ff69d58e269a0d\",\n\t\t\t\t\"sha1\": \"aaa0540f01e87aa2f2e8487fc2624a85b5fc1266\"\
|
||||
,\n\t\t\t\t\"size\": \"726053\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv1\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv1(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"0c4d8b11\",\n\t\t\t\t\"\
|
||||
md5\": \"df1c6a15c4372f5c7c0d9a0707ae4603\",\n\t\t\t\t\"sha1\": \"603891d8789d59376f21f4aef8be5ba1cabd5410\"\
|
||||
,\n\t\t\t\t\"size\": \"706827\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv2\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv2(br)\"\
|
||||
,\n\t\t\t\t\"region\": \"br\",\n\t\t\t\t\"crc\": \"d4dd1722\",\n\t\t\t\t\"\
|
||||
md5\": \"bad2224fda71942eb6c12a9c7c54e9d7\",\n\t\t\t\t\"sha1\": \"eda8a19573c66b550fa02b8bdc81d51c6d196163\"\
|
||||
,\n\t\t\t\t\"size\": \"950327\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv2\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv2(eu)\"\
|
||||
,\n\t\t\t\t\"region\": \"eu\",\n\t\t\t\t\"crc\": \"96bf48ce\",\n\t\t\t\t\"\
|
||||
md5\": \"dd4f0b44587ace5799f05dfcc22ea496\",\n\t\t\t\t\"sha1\": \"20577ede69a3e6207afcdfc07af92e9f05b731d7\"\
|
||||
,\n\t\t\t\t\"size\": \"945860\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv2\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv2(jp)\"\
|
||||
,\n\t\t\t\t\"region\": \"jp\",\n\t\t\t\t\"crc\": \"bfe8fe40\",\n\t\t\t\t\"\
|
||||
md5\": \"70a1627918029c1dd600876f0cbea898\",\n\t\t\t\t\"sha1\": \"11305b8394292a9cfbb170ef83c3937282282f8f\"\
|
||||
,\n\t\t\t\t\"size\": \"903353\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv2\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv2(ss)\"\
|
||||
,\n\t\t\t\t\"region\": \"ss\",\n\t\t\t\t\"crc\": \"7698c4e4\",\n\t\t\t\t\"\
|
||||
md5\": \"140f1674da33eab21fdf958c36b3d889\",\n\t\t\t\t\"sha1\": \"73c7e69afb0abf49ee4c4257b33ac0a82625a7ff\"\
|
||||
,\n\t\t\t\t\"size\": \"944619\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv2\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv2(us)\"\
|
||||
,\n\t\t\t\t\"region\": \"us\",\n\t\t\t\t\"crc\": \"b85437a2\",\n\t\t\t\t\"\
|
||||
md5\": \"50044e63bf44c36665bd409420d2920c\",\n\t\t\t\t\"sha1\": \"a2b1a5c2c2f73faf89e511e80b37718198263da6\"\
|
||||
,\n\t\t\t\t\"size\": \"950374\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"mixrbv2\",\n\t\t\t\t\"parent\": \"jeu\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaJeu.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&systemeid=1&jeuid=1&media=mixrbv2(wor)\"\
|
||||
,\n\t\t\t\t\"region\": \"wor\",\n\t\t\t\t\"crc\": \"96bf48ce\",\n\t\t\t\t\"\
|
||||
md5\": \"dd4f0b44587ace5799f05dfcc22ea496\",\n\t\t\t\t\"sha1\": \"20577ede69a3e6207afcdfc07af92e9f05b731d7\"\
|
||||
,\n\t\t\t\t\"size\": \"945860\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t\
|
||||
{\"type\": \"pictoliste\",\n\t\t\t\t\"parent\": \"region\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaGroup.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=57&media=pictoliste\"\
|
||||
,\n\t\t\t\t\"crc\": \"3e16ef2f\",\n\t\t\t\t\"md5\": \"477d7229d6acc6f9fe10410d8523f91a\"\
|
||||
,\n\t\t\t\t\"sha1\": \"99cb5eab43e90a3dd73b1034b140a8ead05c224f\",\n\t\t\t\
|
||||
\t\"format\": \"png\"},\n\t\t\t\t{\"type\": \"pictomonochrome\",\n\t\t\t\t\
|
||||
\"parent\": \"editeur\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaCompagnie.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=1&media=logo-monochrome\"\
|
||||
,\n\t\t\t\t\"crc\": \"57fa0967\",\n\t\t\t\t\"md5\": \"b9431112b9c59d7f5231a25cba667d7f\"\
|
||||
,\n\t\t\t\t\"sha1\": \"34a44f3308ce5e55254bd3ae695736ddefd1afd9\",\n\t\t\t\
|
||||
\t\"size\": \"29109\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"type\":\
|
||||
\ \"pictocouleur\",\n\t\t\t\t\"parent\": \"editeur\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaCompagnie.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=1&media=wheel\"\
|
||||
,\n\t\t\t\t\"crc\": \"113ab6b7\",\n\t\t\t\t\"md5\": \"ba1042f2bcde5fd495d0f9b2616a7907\"\
|
||||
,\n\t\t\t\t\"sha1\": \"84e503c6943178ed1b9134e4e42d3de0d62cb202\",\n\t\t\t\
|
||||
\t\"size\": \"11946\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"type\":\
|
||||
\ \"pictomonochrome\",\n\t\t\t\t\"parent\": \"developpeur\",\n\t\t\t\t\"url\"\
|
||||
: \"https://neoclone.screenscraper.fr/api2/mediaCompagnie.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=2&media=logo-monochrome\"\
|
||||
,\n\t\t\t\t\"crc\": \"420daf73\",\n\t\t\t\t\"md5\": \"70dfa243c209ca136ebb063593c2998c\"\
|
||||
,\n\t\t\t\t\"sha1\": \"e32ad05386671f826f1ed828ac9bfccec2cfe9c7\",\n\t\t\t\
|
||||
\t\"size\": \"9943\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"type\": \"\
|
||||
pictocouleur\",\n\t\t\t\t\"parent\": \"developpeur\",\n\t\t\t\t\"url\": \"\
|
||||
https://neoclone.screenscraper.fr/api2/mediaCompagnie.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=2&media=wheel\"\
|
||||
,\n\t\t\t\t\"crc\": \"85c43b5b\",\n\t\t\t\t\"md5\": \"4530c00e3338dd6120ad835ee53e5427\"\
|
||||
,\n\t\t\t\t\"sha1\": \"f66e49db1229db033275a0b15f0f8980982a7789\",\n\t\t\t\
|
||||
\t\"size\": \"35241\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"type\":\
|
||||
\ \"pictoliste\",\n\t\t\t\t\"parent\": \"joueurs\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaGroup.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=3304&media=pictoliste\"\
|
||||
,\n\t\t\t\t\"crc\": \"0abdc9bf\",\n\t\t\t\t\"md5\": \"05057bd8fcfe368b23628ba314e94df0\"\
|
||||
,\n\t\t\t\t\"sha1\": \"6d9b48053c720498ece35d0029621b7d25010d3b\",\n\t\t\t\
|
||||
\t\"size\": \"449\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"type\": \"\
|
||||
pictoliste\",\n\t\t\t\t\"parent\": \"note\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaGroup.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=3553&media=pictoliste\"\
|
||||
,\n\t\t\t\t\"crc\": \"612df705\",\n\t\t\t\t\"md5\": \"6ee8a25c785597ef3035b7139189482a\"\
|
||||
,\n\t\t\t\t\"sha1\": \"570ad57ff7c5643595c1a2148bacc373f326cffe\",\n\t\t\t\
|
||||
\t\"size\": \"840\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"type\": \"\
|
||||
pictoliste\",\n\t\t\t\t\"parent\": \"classification\",\n\t\t\t\t\"subparent\"\
|
||||
: \"VRC\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaGroup.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=3733&media=pictoliste\"\
|
||||
,\n\t\t\t\t\"crc\": \"e34ba136\",\n\t\t\t\t\"md5\": \"b481137eba34134a8178d68c6414cb32\"\
|
||||
,\n\t\t\t\t\"sha1\": \"fa812a3c467414111be23bcd692aa8548bf5b461\",\n\t\t\t\
|
||||
\t\"size\": \"762\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"id\": \"1\"\
|
||||
,\n\t\t\t\t\"parent\": \"genre\",\n\t\t\t\t\"subparent\": \"Beat'em All\"\
|
||||
,\n\t\t\t\t\"type\": \"pictoliste\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaGroup.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=1&media=pictoliste\"\
|
||||
,\n\t\t\t\t\"crc\": \"151e4161\",\n\t\t\t\t\"md5\": \"00cb0cb4842e9aba77214ca54b713d0b\"\
|
||||
,\n\t\t\t\t\"sha1\": \"95b8ef111b7e5be0c1750123da620e76523dec62\",\n\t\t\t\
|
||||
\t\"size\": \"1126\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"id\": \"\
|
||||
1\",\n\t\t\t\t\"parent\": \"genre\",\n\t\t\t\t\"subparent\": \"Beat'em All\"\
|
||||
,\n\t\t\t\t\"type\": \"pictomonochrome\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaGroup.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=1&media=logo-monochrome\"\
|
||||
,\n\t\t\t\t\"crc\": \"3974c2c4\",\n\t\t\t\t\"md5\": \"19fa1571415cc4d60d321d1c42dd45b5\"\
|
||||
,\n\t\t\t\t\"sha1\": \"27a372f24bbf11f0a7050bfea0bbcab612935244\",\n\t\t\t\
|
||||
\t\"size\": \"9003\",\n\t\t\t\t\"format\": \"png\"},\n\t\t\t\t{\"id\": \"\
|
||||
1\",\n\t\t\t\t\"parent\": \"genre\",\n\t\t\t\t\"subparent\": \"Beat'em All\"\
|
||||
,\n\t\t\t\t\"type\": \"pictomonochrome\",\n\t\t\t\t\"url\": \"https://neoclone.screenscraper.fr/api2/mediaGroup.php?devid=zurdi15&devpassword=xTJwoOFjOQG&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&groupid=1&media=logo-monochrome-svg\"\
|
||||
,\n\t\t\t\t\"crc\": \"ccb72604\",\n\t\t\t\t\"md5\": \"a3cce15fcad1ab4329a718a6b0d27d7e\"\
|
||||
,\n\t\t\t\t\"sha1\": \"1de1e8426d307e4cc56b909ffe353813822278e3\",\n\t\t\t\
|
||||
\t\"size\": \"2918\",\n\t\t\t\t\"format\": \"svg\"}\n\t\t\t],\n\t\t\t\"roms\"\
|
||||
: [\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"73\",\n\t\t\t\t\t\"romsize\": \"359440\"\
|
||||
,\n\t\t\t\t\t\"romfilename\": \"Battletoads (World).zip\",\n\t\t\t\t\t\"romnumsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\t\t\t\"romcloneof\"\
|
||||
: \"0\",\n\t\t\t\t\t\"romcrc\": \"BE55BF2A\",\n\t\t\t\t\t\"rommd5\": \"CA32E3BFB4507F9713F37B5D345BAAB1\"\
|
||||
,\n\t\t\t\t\t\"romsha1\": \"9D3CDCCBFE2659BFB609987707B2CC2CA4317BEB\",\n\t\
|
||||
\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\"\
|
||||
: \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\
|
||||
\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\
|
||||
\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\"\
|
||||
: [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"\
|
||||
regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\
|
||||
\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"\
|
||||
],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\
|
||||
\t\t{\n\t\t\t\t\t\"id\": \"85954\",\n\t\t\t\t\t\"romsize\": \"329618\",\n\t\
|
||||
\t\t\t\t\"romfilename\": \"Battletoads (World).7z\",\n\t\t\t\t\t\"romnumsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\t\t\t\"romcloneof\"\
|
||||
: \"0\",\n\t\t\t\t\t\"romcrc\": \"74002736\",\n\t\t\t\t\t\"rommd5\": \"CBEFC8F7C7CA94942926CA75B7FFCDB5\"\
|
||||
,\n\t\t\t\t\t\"romsha1\": \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"\
|
||||
demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\
|
||||
\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"\
|
||||
0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\
|
||||
\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\"\
|
||||
: [\"wor\"],\n\t\t\t\t\t\t\"regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\"\
|
||||
: [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\"\
|
||||
: [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\
|
||||
\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"118779\",\n\t\t\t\t\t\"romsize\": \"\
|
||||
359418\",\n\t\t\t\t\t\"romfilename\": \"Battletoads (World).zip\",\n\t\t\t\
|
||||
\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\
|
||||
\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"749B17FC\",\n\t\t\t\t\
|
||||
\t\"rommd5\": \"177F750741F8B44F13A2A7435F0DB6EE\",\n\t\t\t\t\t\"romsha1\"\
|
||||
: \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\
|
||||
\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\
|
||||
\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\":\
|
||||
\ \"0\",\n\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\
|
||||
\t\t\"regions_id\": [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"wor\"\
|
||||
],\n\t\t\t\t\t\t\"regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\":\
|
||||
\ [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\"\
|
||||
: [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\
|
||||
\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"133799\",\n\t\t\t\t\t\"romsize\": \"\
|
||||
524288\",\n\t\t\t\t\t\"romfilename\": \"Battletoads (World).md\",\n\t\t\t\t\
|
||||
\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\t\
|
||||
\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"D10E103A\",\n\t\t\t\t\t\
|
||||
\"rommd5\": \"F1E299D6EB40E3ECEC6460D96E1E4DC9\",\n\t\t\t\t\t\"romsha1\":\
|
||||
\ \"5EF3C29B6BDD04D24552AB200D0530F647AFDB08\",\n\t\t\t\t\t\"beta\": \"0\"\
|
||||
,\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\t\"trad\"\
|
||||
: \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\
|
||||
\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\": \"0\"\
|
||||
,\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\t\t\t\
|
||||
\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\": [\"World\"\
|
||||
],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\":\
|
||||
\ [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"203570\"\
|
||||
,\n\t\t\t\t\t\"romsize\": \"358534\",\n\t\t\t\t\t\"romfilename\": \"Battletoads\
|
||||
\ (W) [!].gen\",\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"83C7944F\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"D7AFC4D4D8D9B90F6F268239C742924C\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\"\
|
||||
,\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\"\
|
||||
: \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\
|
||||
\"best\": \"1\",\n\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\
|
||||
\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\":\
|
||||
\ [\"wor\"],\n\t\t\t\t\t\t\"regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\"\
|
||||
: [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\"\
|
||||
: [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\
|
||||
\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"203571\",\n\t\t\t\t\t\"romsize\": \"\
|
||||
358549\",\n\t\t\t\t\t\"romfilename\": \"battletoads (w) [b1].zip\",\n\t\t\t\
|
||||
\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\
|
||||
\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"CEB6B839\",\n\t\t\t\t\
|
||||
\t\"rommd5\": \"D709384172D28F8563E79278BF2B7613\",\n\t\t\t\t\t\"romsha1\"\
|
||||
: \"\",\n\t\t\t\t\t\"beta\": \"1\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\
|
||||
\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\
|
||||
\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\":\
|
||||
\ \"0\",\n\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\
|
||||
\t\t\"regions_id\": [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"wor\"\
|
||||
],\n\t\t\t\t\t\t\"regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\":\
|
||||
\ [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\"\
|
||||
: [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\
|
||||
\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"203572\",\n\t\t\t\t\t\"romsize\": \"\
|
||||
358539\",\n\t\t\t\t\t\"romfilename\": \"Battletoads (W) [h1C].gen\",\n\t\t\
|
||||
\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\
|
||||
\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"C58567C4\",\n\t\t\t\
|
||||
\t\t\"rommd5\": \"AE72B7B66A65F04312FA03D2111BB27F\",\n\t\t\t\t\t\"romsha1\"\
|
||||
: \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\
|
||||
\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"1\",\n\
|
||||
\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\":\
|
||||
\ \"0\",\n\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\
|
||||
\t\t\"regions_id\": [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"wor\"\
|
||||
],\n\t\t\t\t\t\t\"regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\":\
|
||||
\ [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\"\
|
||||
: [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\
|
||||
\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"203573\",\n\t\t\t\t\t\"romsize\": \"\
|
||||
358552\",\n\t\t\t\t\t\"romfilename\": \"Battletoads (W) [p1].zip\",\n\t\t\t\
|
||||
\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\
|
||||
\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"E817C9A8\",\n\t\t\t\t\
|
||||
\t\"rommd5\": \"5F303CA1FA3D7E10E35E6E9730B4E6BB\",\n\t\t\t\t\t\"romsha1\"\
|
||||
: \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\
|
||||
\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\
|
||||
\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\":\
|
||||
\ \"0\",\n\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\
|
||||
\t\t\"regions_id\": [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"wor\"\
|
||||
],\n\t\t\t\t\t\t\"regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\":\
|
||||
\ [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\"\
|
||||
: [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\
|
||||
\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"203574\",\n\t\t\t\t\t\"romsize\": \"\
|
||||
358662\",\n\t\t\t\t\t\"romfilename\": \"Battletoads (W) [T Rus_NewGame].bin\"\
|
||||
,\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"\
|
||||
1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"40E74041\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"A0F847B3507CB8919A58095EC3B16841\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\"\
|
||||
,\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"1\",\n\t\t\t\t\t\"hack\"\
|
||||
: \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\
|
||||
\"best\": \"0\",\n\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\
|
||||
\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\":\
|
||||
\ [\"wor\"],\n\t\t\t\t\t\t\"regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\"\
|
||||
: [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\"\
|
||||
: [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\
|
||||
\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"203589\",\n\t\t\t\t\t\"romsize\": \"\
|
||||
9215010\",\n\t\t\t\t\t\"romfilename\": \"Battletoads RAMM (Hack).bin\",\n\t\
|
||||
\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\
|
||||
\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"A81D68E4\",\n\t\t\
|
||||
\t\t\t\"rommd5\": \"EE990D0B09E6AEB71CB8547EC81BFAE5\",\n\t\t\t\t\t\"romsha1\"\
|
||||
: \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\
|
||||
\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"1\",\n\
|
||||
\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\":\
|
||||
\ \"0\",\n\t\t\t\t\t\"netplay\": \"0\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"\
|
||||
id\": \"203590\",\n\t\t\t\t\t\"romsize\": \"358616\",\n\t\t\t\t\t\"romfilename\"\
|
||||
: \"Battletoads Remastered (Hack).bin\",\n\t\t\t\t\t\"romnumsupport\": \"\
|
||||
1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\t\t\t\"romcloneof\": \"\
|
||||
0\",\n\t\t\t\t\t\"romcrc\": \"6AC6502D\",\n\t\t\t\t\t\"rommd5\": \"2727700BA908BAE3B66A16F1BD2CDBF8\"\
|
||||
,\n\t\t\t\t\t\"romsha1\": \"\",\n\t\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"\
|
||||
demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\
|
||||
\t\t\t\t\"hack\": \"1\",\n\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"\
|
||||
0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\": \"0\"\n\t\t\t\t},\n\
|
||||
\t\t\t\t{\n\t\t\t\t\t\"id\": \"543806\",\n\t\t\t\t\t\"romsize\": \"373731\"\
|
||||
,\n\t\t\t\t\t\"romfilename\": \"Battletoads (World).zip\",\n\t\t\t\t\t\"romnumsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\t\t\t\"romcloneof\"\
|
||||
: \"0\",\n\t\t\t\t\t\"romcrc\": \"7409C241\",\n\t\t\t\t\t\"rommd5\": \"D71124F17920403DB40D99B6A04DDAB4\"\
|
||||
,\n\t\t\t\t\t\"romsha1\": \"3D58BA8A6C1662F735B6A1FA36B022D31D9A2FEE\",\n\t\
|
||||
\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\"\
|
||||
: \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\
|
||||
\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\
|
||||
\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\"\
|
||||
: [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"\
|
||||
regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\
|
||||
\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"\
|
||||
],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\
|
||||
\t\t{\n\t\t\t\t\t\"id\": \"546554\",\n\t\t\t\t\t\"romsize\": \"359873\",\n\
|
||||
\t\t\t\t\t\"romfilename\": \"Battletoads (JUE) [!].zip\",\n\t\t\t\t\t\"romnumsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\t\t\t\"romcloneof\"\
|
||||
: \"0\",\n\t\t\t\t\t\"romcrc\": \"7A2EA990\",\n\t\t\t\t\t\"rommd5\": \"55B012D3ED8335A3466F144EBE1935E4\"\
|
||||
,\n\t\t\t\t\t\"romsha1\": \"8046E44E1C486B6BD470194726D71A07184E6B7F\",\n\t\
|
||||
\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\"\
|
||||
: \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\
|
||||
\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"1\",\n\
|
||||
\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\"\
|
||||
: [\"46\",\"48\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"us\",\"eu\"],\n\t\
|
||||
\t\t\t\t\t\"regions_en\": [\"USA\",\"Europe\"],\n\t\t\t\t\t\t\"regions_fr\"\
|
||||
: [\"USA\",\"Europe\"],\n\t\t\t\t\t\t\"regions_de\": [\"USA\",\"Europa\"],\n\
|
||||
\t\t\t\t\t\t\"regions_es\": [\"USA\",\"Europa\"],\n\t\t\t\t\t\t\"regions_it\"\
|
||||
: [\"USA\"],\n\t\t\t\t\t\t\"regions_pt\": [\"EUA\",\"Europa\"]\n\t\t\t\t\t\
|
||||
}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"587987\",\n\t\t\t\t\t\"romsize\"\
|
||||
: \"358664\",\n\t\t\t\t\t\"romfilename\": \"Battletoads (World).zip\",\n\t\
|
||||
\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\
|
||||
\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"4B87BB34\",\n\t\t\
|
||||
\t\t\t\"rommd5\": \"629DAF16944422D58C34EB27EDE88D4F\",\n\t\t\t\t\t\"romsha1\"\
|
||||
: \"BF02FAE9E5AE8CEC13EBACACFCD44BE99B88C49A\",\n\t\t\t\t\t\"beta\": \"0\"\
|
||||
,\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\t\"trad\"\
|
||||
: \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\
|
||||
\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\": \"0\"\
|
||||
,\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\t\t\t\
|
||||
\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\": [\"World\"\
|
||||
],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\":\
|
||||
\ [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"613629\"\
|
||||
,\n\t\t\t\t\t\"romsize\": \"358532\",\n\t\t\t\t\t\"romfilename\": \"Battletoads\
|
||||
\ (World).zip\",\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"3DBC3FFF\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"A81218CDBDB3B168CB88FC4FF593FB32\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"B998131C1896904A4A4B16870112D4B60413197A\",\n\t\t\t\t\t\"beta\"\
|
||||
: \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\
|
||||
\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\
|
||||
\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\"\
|
||||
: \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\
|
||||
\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"613772\"\
|
||||
,\n\t\t\t\t\t\"romsize\": \"359882\",\n\t\t\t\t\t\"romfilename\": \"Battletoads.zip\"\
|
||||
,\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"\
|
||||
1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"C5E95063\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"D33949CC4D01F181D498E602BD01B196\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"882520B3B51D799FADE6C777C714205B8FA11871\",\n\t\t\t\t\t\"beta\"\
|
||||
: \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\
|
||||
\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\
|
||||
\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\"\
|
||||
: \"0\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"616262\",\n\t\t\t\t\t\"\
|
||||
romsize\": \"524288\",\n\t\t\t\t\t\"romfilename\": \"Battletoads [T Rus_NewGame].bin\"\
|
||||
,\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"\
|
||||
1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"D2859FFF\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"204014B305BB5A0EA93EFB0FCA36FEA7\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"EBB9B88F1E50FE99D85B1DB5044349599BA127C0\",\n\t\t\t\t\t\"beta\"\
|
||||
: \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\
|
||||
\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\
|
||||
\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\"\
|
||||
: \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\
|
||||
\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"616263\"\
|
||||
,\n\t\t\t\t\t\"romsize\": \"524288\",\n\t\t\t\t\t\"romfilename\": \"Battletoads\
|
||||
\ (W) [T Rus Pirate][b1].gen\",\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\
|
||||
\t\t\t\"romtotalsupport\": \"1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\
|
||||
\t\t\"romcrc\": \"D67397A8\",\n\t\t\t\t\t\"rommd5\": \"3A670957B3C409761EF042078F118D88\"\
|
||||
,\n\t\t\t\t\t\"romsha1\": \"1DDD7C7F5B170E0ABAED1F27B32DBCC099BDC726\",\n\t\
|
||||
\t\t\t\t\"beta\": \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\"\
|
||||
: \"0\",\n\t\t\t\t\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\
|
||||
\t\"unl\": \"0\",\n\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\
|
||||
\t\t\t\t\t\"netplay\": \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\"\
|
||||
: [\"57\"],\n\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"\
|
||||
regions_en\": [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\
|
||||
\t\t\t\t\"regions_de\": [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"\
|
||||
],\n\t\t\t\t\t\t\"regions_pt\": [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\
|
||||
\t\t{\n\t\t\t\t\t\"id\": \"616264\",\n\t\t\t\t\t\"romsize\": \"524288\",\n\
|
||||
\t\t\t\t\t\"romfilename\": \"Battletoads (W) [T Rus Pirate].zip\",\n\t\t\t\
|
||||
\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"1\",\n\t\t\
|
||||
\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"2DA7DBCD\",\n\t\t\t\t\
|
||||
\t\"rommd5\": \"86945FE2547CA90D0F226BEA981BAACE\",\n\t\t\t\t\t\"romsha1\"\
|
||||
: \"1A351E9CD7312B3B13D7DF82F2AFCFC9AEF33CD8\",\n\t\t\t\t\t\"beta\": \"0\"\
|
||||
,\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\t\"trad\"\
|
||||
: \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\t\t\t\t\t\
|
||||
\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\": \"0\"\
|
||||
,\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\t\t\t\
|
||||
\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\": [\"World\"\
|
||||
],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\":\
|
||||
\ [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"617681\"\
|
||||
,\n\t\t\t\t\t\"romsize\": \"358538\",\n\t\t\t\t\t\"romfilename\": \"Battletoads\
|
||||
\ (World).zip\",\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"3033A130\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"3AEB63777E8953E963A1AD58AC367CA5\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"C498315A6B303A0D9001FF3AE19068EBC2B62AAD\",\n\t\t\t\t\t\"beta\"\
|
||||
: \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\
|
||||
\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\
|
||||
\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\"\
|
||||
: \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\
|
||||
\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"619657\"\
|
||||
,\n\t\t\t\t\t\"romsize\": \"524288\",\n\t\t\t\t\t\"romfilename\": \"battle_toads.bin\"\
|
||||
,\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\": \"\
|
||||
1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"6CDBFFCD\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"31E7DF020CCBC090404800FF44410FEE\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"66C4B98103EA006957481892F22B4336A240D8B6\",\n\t\t\t\t\t\"beta\"\
|
||||
: \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\
|
||||
\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"1\",\n\t\t\t\t\t\"unl\": \"0\",\n\
|
||||
\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\"\
|
||||
: \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\
|
||||
\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"id\": \"630883\"\
|
||||
,\n\t\t\t\t\t\"romsize\": \"524288\",\n\t\t\t\t\t\"romfilename\": \"Battletoads\
|
||||
\ (W) [b1].gen\",\n\t\t\t\t\t\"romnumsupport\": \"1\",\n\t\t\t\t\t\"romtotalsupport\"\
|
||||
: \"1\",\n\t\t\t\t\t\"romcloneof\": \"0\",\n\t\t\t\t\t\"romcrc\": \"7B6199EB\"\
|
||||
,\n\t\t\t\t\t\"rommd5\": \"2E603829DE7B38475F32370A72207FCA\",\n\t\t\t\t\t\
|
||||
\"romsha1\": \"053AEC63CC7AE74130404CE2557C7EFB45150FE4\",\n\t\t\t\t\t\"beta\"\
|
||||
: \"0\",\n\t\t\t\t\t\"demo\": \"0\",\n\t\t\t\t\t\"proto\": \"0\",\n\t\t\t\t\
|
||||
\t\"trad\": \"0\",\n\t\t\t\t\t\"hack\": \"0\",\n\t\t\t\t\t\"unl\": \"0\",\n\
|
||||
\t\t\t\t\t\"alt\": \"0\",\n\t\t\t\t\t\"best\": \"0\",\n\t\t\t\t\t\"netplay\"\
|
||||
: \"0\",\n\t\t\t\t\t\"regions\": {\n\t\t\t\t\t\t\"regions_id\": [\"57\"],\n\
|
||||
\t\t\t\t\t\t\"regions_shortname\": [\"wor\"],\n\t\t\t\t\t\t\"regions_en\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_fr\": [\"Monde\"],\n\t\t\t\t\t\t\"regions_de\"\
|
||||
: [\"World\"],\n\t\t\t\t\t\t\"regions_es\": [\"Mundo\"],\n\t\t\t\t\t\t\"regions_pt\"\
|
||||
: [\"Mundo\"]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t}\n}\n "
|
||||
headers:
|
||||
Access-Control-Allow-Headers:
|
||||
- X-Requested-With
|
||||
Access-Control-Allow-Methods:
|
||||
- GET
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-cache, must-revalidate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 03:46:44 GMT
|
||||
Expires:
|
||||
- "0"
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- nginx/1.14.2
|
||||
Set-Cookie:
|
||||
- SESSID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,38 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.screenscraper.fr/api2/jeuInfos.php?crc=FFFFFFFFFFFFFFFF&systemeid=999999
|
||||
response:
|
||||
body:
|
||||
string: "Champ crc, md5 ou sha1 erron\xE9 "
|
||||
headers:
|
||||
Access-Control-Allow-Headers:
|
||||
- X-Requested-With
|
||||
Access-Control-Allow-Methods:
|
||||
- GET
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-cache, must-revalidate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 03:47:10 GMT
|
||||
Expires:
|
||||
- "0"
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- nginx/1.14.2
|
||||
Set-Cookie:
|
||||
- SESSID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
status:
|
||||
code: 400
|
||||
message: Bad Request
|
||||
version: 1
|
||||
@@ -0,0 +1,62 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers: {}
|
||||
method: GET
|
||||
uri: https://api.screenscraper.fr/api2/jeuRecherche.php?recherche=ZZZNonexistentGameZZZ
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
"{\n\t\"header\" : {\n\t\t\"APIversion\" : \"2.0\",\n\t\t\"dateTime\"\
|
||||
\ : \"2025-07-23 05:47:11\",\n\t\t\"commandRequested\" : \"https://neoclone.screenscraper.fr/api2/jeuRecherche.php?recherche=ZZZNonexistentGameZZZ&devid=zurdi15&devpassword=xTJwoOFjOQG&output=json&softname=romm&ssid=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&sspassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\
|
||||
,\n\t\t\"success\": \"true\",\n\t\t\"error\": \"\"\n\t},\n\t\"response\" :\
|
||||
\ {\n\t\t\"serveurs\" : {\n\t\t\t\"cpu1\": \"0\",\n\t\t\t\"cpu2\": \"0\",\n\
|
||||
\t\t\t\"cpu3\": \"0\",\n\t\t\t\"cpu4\": \"0\",\n\t\t\t\"threadsmin\": \"3580\"\
|
||||
,\n\t\t\t\"nbscrapeurs\": \"547\",\n\t\t\t\"apiacces\": \"7207526\",\n\t\t\
|
||||
\t\"closefornomember\": \"0\",\n\t\t\t\"closeforleecher\": \"0\",\n\t\t\t\"\
|
||||
maxthreadfornonmember\": \"256\",\n\t\t\t\"threadfornonmember\": \"30\",\n\
|
||||
\t\t\t\"maxthreadformember\": \"4096\",\n\t\t\t\"threadformember\": \"107\"\
|
||||
\n\t\t},\n\t\t\"ssuser\" : {\n\t\t\t\"id\": \"arcaneasada\",\n\t\t\t\"numid\"\
|
||||
: \"27224648\",\n\t\t\t\"niveau\": \"1\",\n\t\t\t\"contribution\": \"0\",\n\
|
||||
\t\t\t\"uploadsysteme\": \"0\",\n\t\t\t\"uploadinfos\": \"0\",\n\t\t\t\"romasso\"\
|
||||
: \"0\",\n\t\t\t\"uploadmedia\": \"0\",\n\t\t\t\"propositionok\": \"0\",\n\
|
||||
\t\t\t\"propositionko\": \"0\",\n\t\t\t\"quotarefu\": \"0\",\n\t\t\t\"maxthreads\"\
|
||||
: \"1\",\n\t\t\t\"maxdownloadspeed\": \"128\",\n\t\t\t\"requeststoday\": \"\
|
||||
0\",\n\t\t\t\"requestskotoday\": \"0\",\n\t\t\t\"maxrequestspermin\": \"3072\"\
|
||||
,\n\t\t\t\"maxrequestsperday\": \"20000\",\n\t\t\t\"maxrequestskoperday\"\
|
||||
: \"2000\",\n\t\t\t\"visites\": \"19\",\n\t\t\t\"datedernierevisite\": \"\
|
||||
2025-07-22 04:21:46\",\n\t\t\t\"favregion\": \"\"\n\t\t\t},\n\t\t\"jeux\"\
|
||||
\ : [{\n\t\t\t}]\n\t\t}\n\t}\n "
|
||||
headers:
|
||||
Access-Control-Allow-Headers:
|
||||
- X-Requested-With
|
||||
Access-Control-Allow-Methods:
|
||||
- GET
|
||||
Access-Control-Allow-Origin:
|
||||
- "*"
|
||||
Cache-Control:
|
||||
- no-cache, must-revalidate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 03:47:24 GMT
|
||||
Expires:
|
||||
- "0"
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- nginx/1.14.2
|
||||
Set-Cookie:
|
||||
- SESSID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,390 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963ba52e3dbe36ad-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:50:09 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=GnRdROZEhzHeGB80TI3Xm2ysNJiSE0CRzhJq%2FOksqtw22DF8hDUZE7P3kP48HP0E1bxvLCnJmcbvckA%2FCzQpWGdkzHuyvrbOZpxltDE%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"errors":["Invalid key format"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963ba52f3bb036a7-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- "49"
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:50:09 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=jriR4bDFn7rpLE4vJt%2Bh293Q8PH3vtjukPKl45PnOlrPChdPs5BcPg0M0ktbIDLZ3jHNdNDPNGonX8TgNozrLWhwpAU0tUl9gG%2BQxSdU4%2B83"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=f04g5p1qlseta58ucl8gm4m1jv; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 13:50:09 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
WWW-Authenticate:
|
||||
- Bearer
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963ba53b7dc5a21d-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:50:11 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=p6%2FEWt5LymBxMj9hVwjqB7sD8pMIhRmq8TA1AnXvzVgyMRuZE%2BlpCDtRjvPoYOYlTolBvkZ19%2F2cPEzGaoGXIwq1L5wx1sc0ol3FxPQ%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"errors":["Invalid key format"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963ba53c4fd436b4-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- "49"
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:50:11 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=cCfX8aarQozXEGuHHnA9d10OfJWIekhcA3N2xsjh2s4HhGt%2Fcw42nTjgWWWLhU6CQUTwuojMjOBSN9Ry2oVmOle3VY6lqXEbOM63%2BDKxub%2Fb"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=l7n9uu15r2eolpb61s05ritb6n; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 13:50:11 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
WWW-Authenticate:
|
||||
- Bearer
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb26c8dc536b5-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:59:12 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=kdY8QNZlrUZJ%2BMX3%2BAFwasHcuIesOnPKfDe0nVGcuZ9FEkPZBpXvCH7TYotPu4tfNfFg7UosTXZwmnwAYTvjlc577YqgEU54whed9no%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"errors":["Invalid key format"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb26d888c36a4-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- "49"
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:59:12 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=MRTBMv7D6B2fAnWUlyr8K11xyKQraub%2FGty8%2Bh7suSM8CkRUfRb6O%2FDRhKX4nxANUb%2BxUPc2c2O85vD1LnQCtVt%2FSPiMNVrRw6tCVDmM%2BNPH"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=7faess7v3i10vagume9rsn9j5v; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 13:59:12 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
WWW-Authenticate:
|
||||
- Bearer
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb4d98839a211-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:00:51 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=nfhxNJ%2FzrBrl7cItciNeWn0bangAEGR1pqqCvQ2q3V4Fu4rh6XAJcbMs454T04PriTWzyWNppJTmhAGGWKHwVoGqwGfbNu1i4AJHQKc%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer invalid_key
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"errors":["Invalid key format"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb4da5923a211-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- "49"
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:00:51 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=j1vlbJENjs6rCUnIgwT1NyYkOlLq1htaxqRHxnVPCqQE3ROoF4F4Wk0m1JHDzj16vVGjpNKUaKzTAgfsdX11NcOFpteQGDzlJIhF3%2BbLGHrV"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=p4p1c1npililcqpr261po7n04a; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 14:00:51 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
WWW-Authenticate:
|
||||
- Bearer
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
version: 1
|
||||
@@ -0,0 +1,487 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb3694d3753ef-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:59:52 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ch1dBE3F3onEyQsvqw7uMSOLJPAJUqZQuLoXkvlAAnV97ujBgHHY9aeY1WVBZ1zFpKxvWPeY0WkwRMvUL9OdRm0KKeSWLBLBR%2BBgIfM%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"errors":["Invalid key format"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb36a1cbea1e0-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- "49"
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 13:59:52 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=okSHvs6tHx%2FRv2pMcOJbM19zLD%2BpTZ2IkZFNgkezH6FxtxFRjJ9vzkVLUIV0PVeIcgk4%2Bm9ULssW3m27ClxhKQ8DhH4stySQwcPyGSsZWPVh"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=i4eosftvg3bhn5j1fus4ajomdd; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 13:59:52 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
WWW-Authenticate:
|
||||
- Bearer
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb4038de1a235-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:00:17 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=eu0Z14X7u66upRbLT1quhB9e9riOTQ7dXdshXeGWep4Bu6MFzdREIjBS8yyOPGZdLnGp2K53Dns0SniDis4%2FSYhRKFGlQ0pa%2FS9wlAs%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"status":404,"errors":["Game not found"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb4046a53ab3d-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:00:17 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=Vh3fPYAnnmgibA6aDJthL7R0mdV6qi%2F7AD7LtZ%2BWL7doKjnNkdeI%2FaEMxIG581WS7CrRnIgLJimyMs47Usr3WQS65kAt8ksFXcocO22gyu1d"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=tkpigi0q5pm95srra031mm08of; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 14:00:17 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 404
|
||||
message: Not Found
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb92b8947a217-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:03:48 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=8mxiNNz85Kr44gfZgKbwlWYuDE2IvDz4m36bMoAz4ObA%2BDmmAxF8kXFsVkW1djUFbKupjS6NorJHN%2BxVUg46Pl4IoNO3OIHpBvMswBs%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"errors":["Invalid key format"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb92c5cd636b1-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- "49"
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:03:48 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ZeTqedMusPeyeJlzc1AsaWcnT2bohu%2FaHoTGXirK7w9%2Fgq3DZlCFeb5%2F4pYklrIkYe7LpIzUdNqD73G6YrP4DIAq4LuK0%2FBIsptN1rIRPqyG"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=l6600cbte3ak61eshc0gf1mj1i; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 14:03:48 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
WWW-Authenticate:
|
||||
- Bearer
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 401
|
||||
message: Unauthorized
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb9770d0fab3b-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:04:00 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=uvvKly%2FYGFL6vChpDFX3uMcTAsiOeGWEAql%2FZZbZe87Brq3J0klL77pgazlvmgLHfI9tJWE4bcAVF3qE5gwJMV6mdEkM6FZqL3jWpJk%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"status":404,"errors":["Game not found"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bb97848ef53fb-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:04:00 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=etAsRH%2FDT3yIDOQxfvpkX0at5cWVwfR1RtiAt25n44lPM9cBKxCykb9FTBUvCoLKKwTcffxA7iTCFe%2FQfsA9ORd2t8suKL53jhvxUgEKc3xu"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=r9ojdsaqa56ekpe294s5pioa7v; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 14:04:00 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 404
|
||||
message: Not Found
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bbb53bbeda1d8-YYZ
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:05:16 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=KQhQcauvvT5L6prthgAbtFSgLD2GYR018YXcmmDHoBU9UkmQKX9VOKMq9RlfJ1I2Rzy9AI1nRex2QTUTEoSz7dpZOWrJ2o0Rk0%2BDQ28%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/999999
|
||||
response:
|
||||
body:
|
||||
string: '{"success":false,"status":404,"errors":["Game not found"]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963bbb548a625467-YYZ
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 14:05:16 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=Ls7pB5VkDglRzmJm39FkeS9jFm8DleyV1fhgmtKi8VmvlUbQA6pDXIT7uDwImRttih7sW7Rp8PNHfjpFyOrGMe4Ami5KiTdPgfVlxOQVZKI2"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=8isnvjts9e1sn7a3d562d5lgd2; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 14:05:16 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 404
|
||||
message: Not Found
|
||||
version: 1
|
||||
@@ -0,0 +1,101 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/1?limit=5
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865f18b3ca2af-YUL
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:42 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/1?limit=5
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=myDfJHMyciw8u%2Bm4SW%2FgXNRZaNUQaPt%2Bj9JxWRpgr2kRjCy1UAGJF0S3EkpmnzdOXcJ4DVDJ%2FyHqsLVjFT1MHq2Q0yQFVTlOWopYXCU%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/1?limit=5
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"success":true,"page":0,"total":14,"limit":5,"data":[{"id":103281,"score":0,"style":"alternate","width":600,"height":900,"nsfw":false,"humor":false,"notes":null,"mime":"image\/png","language":"en","url":"https:\/\/cdn2.steamgriddb.com\/grid\/bafd5785e9335ccde7bcde6051b5dcf6.png","thumb":"https:\/\/cdn2.steamgriddb.com\/thumb\/bafd5785e9335ccde7bcde6051b5dcf6.png","lock":false,"epilepsy":false,"upvotes":0,"downvotes":0,"author":{"name":"duckdicks","steam64":"76561198265411778","avatar":"https:\/\/avatars.steamstatic.com\/834b570adf40c70d7d2379cf0f9332e2534dde13_medium.jpg"}},{"id":318178,"score":0,"style":"alternate","width":600,"height":900,"nsfw":false,"humor":false,"notes":null,"mime":"image\/jpeg","language":"en","url":"https:\/\/cdn2.steamgriddb.com\/grid\/fe80a3e914e35e5ba0157e1a85a3d14f.jpg","thumb":"https:\/\/cdn2.steamgriddb.com\/thumb\/fe80a3e914e35e5ba0157e1a85a3d14f.jpg","lock":false,"epilepsy":false,"upvotes":0,"downvotes":0,"author":{"name":"DebonairTBS","steam64":"76561198008715015","avatar":"https:\/\/avatars.steamstatic.com\/560614290f1bce0865ecfca921bd90ceab6d2507_medium.jpg"}},{"id":92728,"score":0,"style":"alternate","width":920,"height":430,"nsfw":false,"humor":false,"notes":null,"mime":"image\/png","language":"en","url":"https:\/\/cdn2.steamgriddb.com\/grid\/da56e25a64be3b08d6027242e1d85606.png","thumb":"https:\/\/cdn2.steamgriddb.com\/thumb\/da56e25a64be3b08d6027242e1d85606.jpg","lock":false,"epilepsy":false,"upvotes":0,"downvotes":0,"author":{"name":"ZombiJambi","steam64":"76561197965184469","avatar":"https:\/\/avatars.steamstatic.com\/7e56965baf567e68693b40eb170cb4ce75ad68ff_medium.jpg"}},{"id":25987,"score":0,"style":"alternate","width":460,"height":215,"nsfw":false,"humor":false,"notes":null,"mime":"image\/png","language":"en","url":"https:\/\/cdn2.steamgriddb.com\/grid\/cffb7924cc48c212e70437f8b32c5831.png","thumb":"https:\/\/cdn2.steamgriddb.com\/thumb\/cffb7924cc48c212e70437f8b32c5831.jpg","lock":false,"epilepsy":false,"upvotes":0,"downvotes":0,"author":{"name":"TheBoss86","steam64":"76561197988452487","avatar":"https:\/\/avatars.steamstatic.com\/e641aef239f588fc89270581be496fe91afa8c54_medium.jpg"}},{"id":410400,"score":0,"style":"alternate","width":600,"height":900,"nsfw":false,"humor":false,"notes":"Reuploaded-Template
|
||||
and Art By Castcoder","mime":"image\/jpeg","language":"en","url":"https:\/\/cdn2.steamgriddb.com\/grid\/31087231e09099106762217619ccdb6e.jpg","thumb":"https:\/\/cdn2.steamgriddb.com\/thumb\/31087231e09099106762217619ccdb6e.jpg","lock":false,"epilepsy":false,"upvotes":0,"downvotes":0,"author":{"name":"FattestWrestlingFan","steam64":"76561199418084858","avatar":"https:\/\/avatars.steamstatic.com\/bd7fdd4ab203fcd4f0e93a3d2dd43ff4744b97df_medium.jpg"}}]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865f26dcaa297-YUL
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:42 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=IIdVcbFwljMp8P%2FsUYL7SdhgNRM%2FICuC2Pa2G8ohVX8vm8TUZ8zLCVLSbf0qiNjfAlZ0XeiPQ66HDd2M5ABlOSSnA8Enc514ClGRi6c6sAKF"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=dtofqheldo0dcr1a7tlt0tbie3; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 04:22:42 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,99 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/grids/game/1?styles=material&limit=3
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865f38bbaa31d-YUL
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:42 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/grids/game/1?styles=material&limit=3
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=NySxv89agQjTn2An8NrPLzvimuTV5bVWBm8sv2zuZCfPiBUAUCxxLNyA%2F%2F5cW5BGcE3fvYWn4OX4o3qW56rh%2B6LUAhKjRCI6lxHVvlc%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/grids/game/1?styles=material&limit=3
|
||||
response:
|
||||
body:
|
||||
string: '{"success":true,"page":0,"total":0,"limit":3,"data":[]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865f48c75a2ac-YUL
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:42 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=b1XJuD5Wtrw4OiT3%2BiKAWnDAkHWFMF5icQ8g5VEdm%2Fo6CCoQ0TWTP1oN1E4c2o5yJ13cmAWpic26LKkHM8x8zjt1x8ra607%2B%2BQYc%2BgJnV9DL"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=fdd0a1h1ebo3fvha468voq0e7f; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 04:22:42 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,99 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/search/autocomplete/ZZZNonexistentGameZZZ
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865f77a04a2d0-YUL
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:43 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/search/autocomplete/ZZZNonexistentGameZZZ
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=pYNrN%2BWi60AMMpjkoyabcuZnbHYE6nca2vcqJYqViGPwoYZ8ylsPoO%2FHG4VrXnnaBRB4FmAGoPFm1muWqdNBcxXDR%2FDhiKp%2Batb0%2B6Q%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/search/autocomplete/ZZZNonexistentGameZZZ
|
||||
response:
|
||||
body:
|
||||
string: '{"success":true,"data":[]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865f84a46a2f4-YUL
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:43 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=4SLMo5kYuAK7cLrIu9U%2FQqwicvxv2EGqx5gjhBiZTUPL7jL5ra91UGH%2FsBVZqtJVlxbPLAdyHF6CWED16rcsi38tlst6KYxtH7soAZU%2Fjm2y"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=86a94v08tvbn9r0vd4m79th9b0; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 04:22:43 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,109 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://steamgriddb.com/api/v2/search/autocomplete/Mario
|
||||
response:
|
||||
body: {}
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865ef9e73a2c3-YUL
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- text/html
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:41 GMT
|
||||
Location:
|
||||
- https://www.steamgriddb.com/api/v2/search/autocomplete/Mario
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=zkUdYCn%2Bh5KJ4%2BwxN6Ha7ns0cwyNkr5rS2BPcPlG0m5E%2FM3n6pR%2BHqSZRkui%2BytQUu0SecyyoOxMQm7SGBww3ZMpW7Tk4ygjoeb0nHA%3D"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 301
|
||||
message: Moved Permanently
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- "*/*"
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Authorization:
|
||||
- Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
Host:
|
||||
- www.steamgriddb.com
|
||||
User-Agent:
|
||||
- Python/3.13 aiohttp/3.12.14
|
||||
method: GET
|
||||
uri: https://www.steamgriddb.com/api/v2/search/autocomplete/Mario
|
||||
response:
|
||||
body:
|
||||
string:
|
||||
'{"success":true,"data":[{"id":10,"name":"Mario Party 5","verified":true,"types":[],"release_date":1068508800},{"id":51,"name":"Mario''s
|
||||
Picross","verified":true,"types":[],"release_date":795139200},{"id":1266,"name":"Mario
|
||||
Bros.","verified":true,"types":["eshop"],"release_date":425001600},{"id":1681,"name":"Mario
|
||||
Golf: Advance Tour","verified":true,"types":["eshop"],"release_date":1082592000},{"id":12862,"name":"Mario
|
||||
Pinball Land","verified":true,"types":["eshop"],"release_date":1093478400},{"id":21164,"name":"Mario
|
||||
Kart 64","verified":true,"types":["eshop"],"release_date":850521600},{"id":25624,"name":"Mario
|
||||
Kart DS","verified":true,"types":["eshop"],"release_date":1131926400},{"id":25642,"name":"Mario
|
||||
Tennis","verified":true,"types":["eshop"],"release_date":964137600},{"id":29847,"name":"Mario''s
|
||||
Early Years: Fun with Numbers","verified":true,"types":[],"release_date":778377600},{"id":33767,"name":"Mario
|
||||
Party 9","verified":true,"types":[],"release_date":1330646400}]}'
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 963865f08837a2ac-YUL
|
||||
Cache-Control:
|
||||
- no-store, no-cache, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- DYNAMIC
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- br
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 23 Jul 2025 04:22:42 GMT
|
||||
Expires:
|
||||
- Thu, 19 Nov 1981 08:52:00 GMT
|
||||
Nel:
|
||||
- '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}'
|
||||
Pragma:
|
||||
- no-cache
|
||||
Report-To:
|
||||
- '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=uyfaS2i0FtUqdstHe5UxIduv0T9ls74TxoL0LMh5GtHVFY78hE0CbdWngsV%2Fd3XUON1sEzJB%2FAzepsGEMh3YrB8iA%2BKxWVR6lclrfNyOJQir"}]}'
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- PHPSESSID=2dlv64pgf4nk9n94g0fggujltj; Path=/; Max-Age=604800; Expires=Wed,
|
||||
30 Jul 2025 04:22:42 GMT
|
||||
- PHPSESSID=deleted; Path=/; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:01 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,18 @@
|
||||
from contextvars import ContextVar
|
||||
|
||||
import aiohttp
|
||||
import pytest_asyncio
|
||||
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def mock_ctx_aiohttp_session():
|
||||
"""Create a real aiohttp session for integration tests."""
|
||||
session = aiohttp.ClientSession()
|
||||
ctx_aiohttp_session: ContextVar[aiohttp.ClientSession] = ContextVar(
|
||||
"aiohttp_session"
|
||||
)
|
||||
ctx_aiohttp_session.set(session)
|
||||
try:
|
||||
yield ctx_aiohttp_session
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
735
backend/adapters/services/tests/test_mobygames.py
Normal file
735
backend/adapters/services/tests/test_mobygames.py
Normal file
@@ -0,0 +1,735 @@
|
||||
import asyncio
|
||||
import http
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import aiohttp
|
||||
import pytest
|
||||
import yarl
|
||||
from adapters.services.mobygames import (
|
||||
MobyGamesService,
|
||||
auth_middleware,
|
||||
)
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
INVALID_GAME_ID = 999999
|
||||
|
||||
MockResponse = dict[str, list[dict[str, int]]]
|
||||
|
||||
|
||||
class TestAuthMiddleware:
|
||||
@patch("adapters.services.mobygames.MOBYGAMES_API_KEY", "test_api_key")
|
||||
@pytest.mark.asyncio
|
||||
async def test_auth_middleware_adds_api_key(self):
|
||||
"""Test that auth middleware adds API key to request URL."""
|
||||
# Create a real request-like object
|
||||
mock_request = MagicMock()
|
||||
mock_request.url = yarl.URL("https://api.mobygames.com/v1/games")
|
||||
|
||||
mock_handler = AsyncMock()
|
||||
mock_response = MagicMock()
|
||||
mock_handler.return_value = mock_response
|
||||
|
||||
result = await auth_middleware(mock_request, mock_handler)
|
||||
|
||||
# Check that the URL now contains the API key
|
||||
expected_url = yarl.URL("https://api.mobygames.com/v1/games").with_query(
|
||||
api_key="test_api_key"
|
||||
)
|
||||
assert mock_request.url == expected_url
|
||||
mock_handler.assert_called_once_with(mock_request)
|
||||
assert result == mock_response
|
||||
|
||||
@patch("adapters.services.mobygames.MOBYGAMES_API_KEY", "")
|
||||
@pytest.mark.asyncio
|
||||
async def test_auth_middleware_with_empty_api_key(self):
|
||||
"""Test that auth middleware adds empty API key when none configured."""
|
||||
mock_request = MagicMock()
|
||||
mock_request.url = yarl.URL("https://api.mobygames.com/v1/games")
|
||||
|
||||
mock_handler = AsyncMock()
|
||||
mock_response = MagicMock()
|
||||
mock_handler.return_value = mock_response
|
||||
|
||||
result = await auth_middleware(mock_request, mock_handler)
|
||||
|
||||
expected_url = yarl.URL("https://api.mobygames.com/v1/games").with_query(
|
||||
api_key=""
|
||||
)
|
||||
assert mock_request.url == expected_url
|
||||
assert result == mock_response
|
||||
|
||||
|
||||
class TestMobyGamesServiceUnit:
|
||||
"""Unit tests with mocked dependencies."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a MobyGamesService instance for testing."""
|
||||
return MobyGamesService()
|
||||
|
||||
@pytest.fixture
|
||||
def service_custom_url(self):
|
||||
"""Create a MobyGamesService instance with custom URL."""
|
||||
return MobyGamesService("https://custom.api.com")
|
||||
|
||||
def test_init_default_url(self, service):
|
||||
"""Test service initialization with default URL."""
|
||||
assert str(service.url) == "https://api.mobygames.com/v1"
|
||||
|
||||
def test_init_custom_url(self, service_custom_url):
|
||||
"""Test service initialization with custom URL."""
|
||||
assert str(service_custom_url.url) == "https://custom.api.com"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_success(self, service):
|
||||
"""Test successful API request."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {
|
||||
"games": [{"game_id": 1, "title": "Test Game"}]
|
||||
}
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request("https://api.mobygames.com/v1/games")
|
||||
|
||||
assert result == {"games": [{"game_id": 1, "title": "Test Game"}]}
|
||||
mock_session.get.assert_called_once()
|
||||
mock_response.raise_for_status.assert_called_once()
|
||||
mock_response.json.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_connection_error(self, service):
|
||||
"""Test request with connection error."""
|
||||
mock_session = AsyncMock()
|
||||
mock_session.get.side_effect = aiohttp.ClientConnectionError(
|
||||
"Connection failed"
|
||||
)
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service._request("https://api.mobygames.com/v1/games")
|
||||
|
||||
assert exc_info.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
|
||||
assert "Can't connect to MobyGames" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_timeout_with_retry(self, service):
|
||||
"""Test request timeout with successful retry."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"games": []}
|
||||
mock_response.raise_for_status.return_value = None
|
||||
|
||||
# First call times out, second succeeds
|
||||
mock_session.get.side_effect = [
|
||||
aiohttp.ServerTimeoutError("Timeout"),
|
||||
mock_response,
|
||||
]
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request("https://api.mobygames.com/v1/games")
|
||||
|
||||
assert result == {"games": []}
|
||||
assert mock_session.get.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_unauthorized_returns_empty_dict(self, service):
|
||||
"""Test that 401 Unauthorized returns empty dict."""
|
||||
mock_session = AsyncMock()
|
||||
unauthorized_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.UNAUTHORIZED,
|
||||
)
|
||||
mock_session.get.side_effect = unauthorized_error
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request("https://api.mobygames.com/v1/games")
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_rate_limit_with_retry(self, service):
|
||||
"""Test rate limit handling with retry."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"games": []}
|
||||
mock_response.raise_for_status.return_value = None
|
||||
|
||||
# First call hits rate limit, second succeeds
|
||||
rate_limit_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.TOO_MANY_REQUESTS,
|
||||
)
|
||||
mock_session.get.side_effect = [rate_limit_error, mock_response]
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
with patch("asyncio.sleep") as mock_sleep:
|
||||
result = await service._request("https://api.mobygames.com/v1/games")
|
||||
|
||||
assert result == {
|
||||
"games": []
|
||||
} # First call returns empty dict, retry happens on second call
|
||||
mock_sleep.assert_called_once_with(2)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_json_decode_error(self, service):
|
||||
"""Test handling of JSON decode error."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.side_effect = json.JSONDecodeError("Expecting value", "", 0)
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request("https://api.mobygames.com/v1/games")
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_other_client_error(self, service):
|
||||
"""Test handling of other client errors."""
|
||||
mock_session = AsyncMock()
|
||||
client_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
mock_session.get.side_effect = client_error
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request("https://api.mobygames.com/v1/games")
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_default_parameters(self, service):
|
||||
"""Test list_games with default parameters."""
|
||||
mock_response = {"games": [{"game_id": 1, "title": "Test Game"}]}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games()
|
||||
|
||||
assert result == [{"game_id": 1, "title": "Test Game"}]
|
||||
mock_request.assert_called_once()
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "https://api.mobygames.com/v1/games" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_game_id(self, service):
|
||||
"""Test list_games with specific game ID."""
|
||||
mock_response = {"games": [{"game_id": 123, "title": "Specific Game"}]}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(game_id=123)
|
||||
|
||||
assert result == [{"game_id": 123, "title": "Specific Game"}]
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "id=123" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_platform_ids(self, service):
|
||||
"""Test list_games with platform IDs."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(platform_ids=[1, 2, 3])
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "platform=1" in call_args
|
||||
assert "platform=2" in call_args
|
||||
assert "platform=3" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_genre_ids(self, service):
|
||||
"""Test list_games with genre IDs."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(genre_ids=[5, 10])
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "genre=5" in call_args
|
||||
assert "genre=10" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_group_ids(self, service):
|
||||
"""Test list_games with group IDs."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(group_ids=[100, 200])
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "group=100" in call_args
|
||||
assert "group=200" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_title(self, service):
|
||||
"""Test list_games with title search."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(title="Sonic")
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "title=Sonic" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_output_format_id(self, service):
|
||||
"""Test list_games with ID output format."""
|
||||
mock_response = {"games": [1, 2, 3]}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(output_format="id")
|
||||
|
||||
assert result == [1, 2, 3]
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "format=id" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_output_format_brief(self, service):
|
||||
"""Test list_games with brief output format."""
|
||||
mock_response = {"games": [{"game_id": 1, "title": "Test", "moby_url": "url"}]}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(output_format="brief")
|
||||
|
||||
assert result == [{"game_id": 1, "title": "Test", "moby_url": "url"}]
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "format=brief" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_pagination(self, service):
|
||||
"""Test list_games with limit and offset."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(limit=10, offset=20)
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "limit=10" in call_args
|
||||
assert "offset=20" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_all_parameters(self, service):
|
||||
"""Test list_games with all parameters."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(
|
||||
game_id=1,
|
||||
platform_ids=[2, 3],
|
||||
genre_ids=[4, 5],
|
||||
group_ids=[6, 7],
|
||||
title="Test Game",
|
||||
output_format="normal",
|
||||
limit=5,
|
||||
offset=10,
|
||||
)
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "id=1" in call_args
|
||||
assert "platform=2" in call_args
|
||||
assert "platform=3" in call_args
|
||||
assert "genre=4" in call_args
|
||||
assert "genre=5" in call_args
|
||||
assert "group=6" in call_args
|
||||
assert "group=7" in call_args
|
||||
assert "title=Test+Game" in call_args or "title=Test%20Game" in call_args
|
||||
assert "format=normal" in call_args
|
||||
assert "limit=5" in call_args
|
||||
assert "offset=10" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_empty_response(self, service):
|
||||
"""Test list_games with empty response."""
|
||||
mock_response: dict[str, int] = {}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.list_games()
|
||||
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_missing_games_key(self, service):
|
||||
"""Test list_games with missing games key in response."""
|
||||
mock_response = {"total": 0}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.list_games()
|
||||
|
||||
assert result == []
|
||||
|
||||
|
||||
class TestMobyGamesServiceIntegration:
|
||||
"""Integration tests with real API calls using VCR cassettes."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a MobyGamesService instance for integration testing."""
|
||||
return MobyGamesService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_real_api(self, service, mock_ctx_aiohttp_session):
|
||||
"""Test list_games with real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(limit=5)
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
if result: # If there are games
|
||||
game = result[0]
|
||||
assert "game_id" in game
|
||||
assert "title" in game
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_with_platform_filter_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test list_games with platform filter using real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(platform_ids=[1], limit=3) # PC platform
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
assert len(result) <= 3 # Should respect limit
|
||||
if result:
|
||||
game = result[0]
|
||||
assert "game_id" in game
|
||||
assert "title" in game
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_with_title_search_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test list_games with title search using real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(title="Sonic", limit=5)
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
if result:
|
||||
game = result[0]
|
||||
assert "game_id" in game
|
||||
assert "title" in game
|
||||
# Title should contain "Sonic" (case insensitive)
|
||||
assert "sonic" in game["title"].lower()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_brief_format_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test list_games with brief output format using real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(output_format="brief", limit=3)
|
||||
|
||||
# Verify response structure for brief format
|
||||
assert isinstance(result, list)
|
||||
if result:
|
||||
game = result[0]
|
||||
assert "game_id" in game
|
||||
assert "title" in game
|
||||
assert "moby_url" in game
|
||||
# Brief format should not have detailed fields
|
||||
assert "description" not in game
|
||||
assert "genres" not in game
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_id_format_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test list_games with ID output format using real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(output_format="id", limit=5)
|
||||
|
||||
# Verify response structure for ID format
|
||||
assert isinstance(result, list)
|
||||
if result:
|
||||
# Should return list of integers (game IDs)
|
||||
assert all(isinstance(game_id, int) for game_id in result)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_normal_format_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test list_games with normal output format using real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(output_format="normal", limit=2)
|
||||
|
||||
# Verify response structure for normal format
|
||||
assert isinstance(result, list)
|
||||
if result:
|
||||
game = result[0]
|
||||
assert "game_id" in game
|
||||
assert "title" in game
|
||||
# Normal format should have detailed fields
|
||||
expected_fields = [
|
||||
"description",
|
||||
"genres",
|
||||
"platforms",
|
||||
"moby_url",
|
||||
"alternate_titles",
|
||||
]
|
||||
# Check that at least some detailed fields are present
|
||||
assert any(field in game for field in expected_fields)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_error_handling_real_api(self, service, mock_ctx_aiohttp_session):
|
||||
"""Test error handling with real API calls."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
with patch("adapters.services.mobygames.MOBYGAMES_API_KEY", "invalid_key"):
|
||||
# This should handle the error gracefully
|
||||
result = await service.list_games(game_id=INVALID_GAME_ID)
|
||||
assert isinstance(result, list)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_with_pagination_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test list_games with pagination using real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(limit=2, offset=0)
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
assert len(result) <= 2 # Should respect limit
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_list_games_with_genre_filter_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test list_games with genre filter using real API call."""
|
||||
with patch(
|
||||
"adapters.services.mobygames.ctx_aiohttp_session", mock_ctx_aiohttp_session
|
||||
):
|
||||
result = await service.list_games(genre_ids=[1], limit=3) # Action genre
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
if result:
|
||||
game = result[0]
|
||||
assert "game_id" in game
|
||||
assert "title" in game
|
||||
|
||||
|
||||
# Performance tests
|
||||
class TestMobyGamesServicePerformance:
|
||||
"""Performance tests for MobyGames service."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a MobyGamesService instance for performance testing."""
|
||||
return MobyGamesService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_concurrent_requests(self, service):
|
||||
"""Test multiple concurrent API requests."""
|
||||
mock_response = {"games": [{"game_id": 1, "title": "Test Game"}]}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
# Run 5 concurrent requests
|
||||
tasks = [service.list_games(limit=1) for _ in range(5)]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
# All should succeed
|
||||
assert all(len(result) == 1 for result in results)
|
||||
assert len(results) == 5
|
||||
assert mock_request.call_count == 5
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_timeout_handling(self, service):
|
||||
"""Test handling of request timeouts."""
|
||||
mock_session = AsyncMock()
|
||||
|
||||
# Simulate timeout on first call, success on retry
|
||||
timeout_error = aiohttp.ServerTimeoutError("Request timeout")
|
||||
success_response = AsyncMock()
|
||||
success_response.json.return_value = {"games": []}
|
||||
success_response.raise_for_status.return_value = None
|
||||
|
||||
mock_session.get.side_effect = [timeout_error, success_response]
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.mobygames.com/v1/games", request_timeout=1
|
||||
)
|
||||
|
||||
assert result == {"games": []}
|
||||
assert mock_session.get.call_count == 2
|
||||
|
||||
|
||||
# Edge case tests
|
||||
class TestMobyGamesServiceEdgeCases:
|
||||
"""Edge case tests for MobyGames service."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a MobyGamesService instance for edge case testing."""
|
||||
return MobyGamesService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_empty_collections(self, service):
|
||||
"""Test list_games with empty collections."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(
|
||||
platform_ids=[],
|
||||
genre_ids=[],
|
||||
group_ids=[],
|
||||
)
|
||||
|
||||
assert result == []
|
||||
# Empty collections should not add parameters to URL
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "platform=" not in call_args
|
||||
assert "genre=" not in call_args
|
||||
assert "group=" not in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_zero_limit(self, service):
|
||||
"""Test list_games with zero limit."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(limit=0)
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "limit=0" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_zero_offset(self, service):
|
||||
"""Test list_games with zero offset."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(offset=0)
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "offset=0" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_games_with_special_characters_in_title(self, service):
|
||||
"""Test list_games with special characters in title."""
|
||||
mock_response: MockResponse = {"games": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.list_games(title="Pac-Man & Ms. Pac-Man")
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
# URL should be properly encoded
|
||||
assert "title=" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_with_custom_timeout(self, service):
|
||||
"""Test request with custom timeout."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"games": []}
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.mobygames.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.mobygames.com/v1/games", request_timeout=30
|
||||
)
|
||||
|
||||
assert result == {"games": []}
|
||||
# Verify timeout was passed correctly
|
||||
call_kwargs = mock_session.get.call_args[1]
|
||||
assert call_kwargs["timeout"].total == 30
|
||||
@@ -1,9 +1,7 @@
|
||||
from contextvars import ContextVar
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import aiohttp
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
import yarl
|
||||
from adapters.services.retroachievements import (
|
||||
RetroAchievementsService,
|
||||
@@ -85,19 +83,6 @@ class TestRetroAchievementsServiceIntegration:
|
||||
"""Create a RetroAchievementsService instance for integration testing."""
|
||||
return RetroAchievementsService()
|
||||
|
||||
@pytest_asyncio.fixture
|
||||
async def mock_ctx_aiohttp_session(self):
|
||||
"""Create a real aiohttp session for integration tests."""
|
||||
session = aiohttp.ClientSession()
|
||||
ctx_aiohttp_session: ContextVar[aiohttp.ClientSession] = ContextVar(
|
||||
"aiohttp_session"
|
||||
)
|
||||
ctx_aiohttp_session.set(session)
|
||||
try:
|
||||
yield ctx_aiohttp_session
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_get_game_extended_details_real_api(
|
||||
@@ -167,11 +152,11 @@ class TestRetroAchievementsServiceIntegration:
|
||||
):
|
||||
result = await service.get_user_completion_progress("arcanecraeda", limit=5)
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, dict)
|
||||
assert "Total" in result
|
||||
assert "Results" in result
|
||||
assert isinstance(result["Results"], list)
|
||||
if result: # Non-empty response
|
||||
assert "Total" in result
|
||||
assert "Results" in result
|
||||
assert isinstance(result["Results"], list)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
|
||||
874
backend/adapters/services/tests/test_screenscraper.py
Normal file
874
backend/adapters/services/tests/test_screenscraper.py
Normal file
@@ -0,0 +1,874 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import http
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import aiohttp
|
||||
import pytest
|
||||
import yarl
|
||||
from adapters.services.screenscraper import (
|
||||
LOGIN_ERROR_CHECK,
|
||||
SS_DEV_ID,
|
||||
SS_DEV_PASSWORD,
|
||||
ScreenScraperService,
|
||||
auth_middleware,
|
||||
)
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
INVALID_GAME_ID = 999999
|
||||
INVALID_SYSTEM_ID = 999999
|
||||
|
||||
|
||||
class TestScreenScraperConstants:
|
||||
"""Test ScreenScraper constants and configuration."""
|
||||
|
||||
def test_ss_dev_id_decoded(self):
|
||||
"""Test that SS_DEV_ID is properly decoded."""
|
||||
expected = base64.b64decode("enVyZGkxNQ==").decode()
|
||||
assert SS_DEV_ID == expected
|
||||
|
||||
def test_ss_dev_password_decoded(self):
|
||||
"""Test that SS_DEV_PASSWORD is properly decoded."""
|
||||
expected = base64.b64decode("eFRKd29PRmpPUUc=").decode()
|
||||
assert SS_DEV_PASSWORD == expected
|
||||
|
||||
def test_login_error_check_constant(self):
|
||||
"""Test that LOGIN_ERROR_CHECK constant is defined."""
|
||||
assert LOGIN_ERROR_CHECK == "Erreur de login"
|
||||
|
||||
|
||||
class TestAuthMiddleware:
|
||||
@patch("adapters.services.screenscraper.SCREENSCRAPER_USER", "test_user")
|
||||
@patch("adapters.services.screenscraper.SCREENSCRAPER_PASSWORD", "test_pass")
|
||||
@pytest.mark.asyncio
|
||||
async def test_auth_middleware_adds_auth_params(self):
|
||||
"""Test that auth middleware adds all required authentication parameters."""
|
||||
# Create a real request-like object
|
||||
mock_request = MagicMock()
|
||||
mock_request.url = yarl.URL("https://api.screenscraper.fr/api2/jeuInfos.php")
|
||||
|
||||
mock_handler = AsyncMock()
|
||||
mock_response = MagicMock()
|
||||
mock_handler.return_value = mock_response
|
||||
|
||||
result = await auth_middleware(mock_request, mock_handler)
|
||||
|
||||
# Check that the URL now contains all auth parameters
|
||||
expected_params = {
|
||||
"devid": SS_DEV_ID,
|
||||
"devpassword": SS_DEV_PASSWORD,
|
||||
"output": "json",
|
||||
"softname": "romm",
|
||||
"ssid": "test_user",
|
||||
"sspassword": "test_pass",
|
||||
}
|
||||
expected_url = yarl.URL(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
).with_query(**expected_params)
|
||||
assert mock_request.url == expected_url
|
||||
mock_handler.assert_called_once_with(mock_request)
|
||||
assert result == mock_response
|
||||
|
||||
@patch("adapters.services.screenscraper.SCREENSCRAPER_USER", "")
|
||||
@patch("adapters.services.screenscraper.SCREENSCRAPER_PASSWORD", "")
|
||||
@pytest.mark.asyncio
|
||||
async def test_auth_middleware_with_empty_credentials(self):
|
||||
"""Test that auth middleware adds empty credentials when none configured."""
|
||||
mock_request = MagicMock()
|
||||
mock_request.url = yarl.URL("https://api.screenscraper.fr/api2/jeuInfos.php")
|
||||
|
||||
mock_handler = AsyncMock()
|
||||
mock_response = MagicMock()
|
||||
mock_handler.return_value = mock_response
|
||||
|
||||
result = await auth_middleware(mock_request, mock_handler)
|
||||
|
||||
expected_params = {
|
||||
"devid": SS_DEV_ID,
|
||||
"devpassword": SS_DEV_PASSWORD,
|
||||
"output": "json",
|
||||
"softname": "romm",
|
||||
"ssid": "",
|
||||
"sspassword": "",
|
||||
}
|
||||
expected_url = yarl.URL(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
).with_query(**expected_params)
|
||||
assert mock_request.url == expected_url
|
||||
assert result == mock_response
|
||||
|
||||
|
||||
class TestScreenScraperServiceUnit:
|
||||
"""Unit tests with mocked dependencies."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a ScreenScraperService instance for testing."""
|
||||
return ScreenScraperService()
|
||||
|
||||
@pytest.fixture
|
||||
def service_custom_url(self):
|
||||
"""Create a ScreenScraperService instance with custom URL."""
|
||||
return ScreenScraperService("https://custom.api.com")
|
||||
|
||||
def test_init_default_url(self, service):
|
||||
"""Test service initialization with default URL."""
|
||||
assert str(service.url) == "https://api.screenscraper.fr/api2"
|
||||
|
||||
def test_init_custom_url(self, service_custom_url):
|
||||
"""Test service initialization with custom URL."""
|
||||
assert str(service_custom_url.url) == "https://custom.api.com"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_success(self, service):
|
||||
"""Test successful API request."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"response": {"jeu": {"id": "1", "noms": []}}}
|
||||
mock_response.text.return_value = '{"response": {"jeu": {"id": "1"}}}'
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
)
|
||||
|
||||
assert result == {"response": {"jeu": {"id": "1", "noms": []}}}
|
||||
mock_session.get.assert_called_once()
|
||||
mock_response.raise_for_status.assert_called_once()
|
||||
mock_response.json.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_login_error(self, service):
|
||||
"""Test request with login error in response text."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.text.return_value = "Erreur de login: invalid credentials"
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service._request("https://api.screenscraper.fr/api2/jeuInfos.php")
|
||||
|
||||
assert exc_info.value.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
assert "Invalid ScreenScraper credentials" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_connection_error(self, service):
|
||||
"""Test request with connection error."""
|
||||
mock_session = AsyncMock()
|
||||
mock_session.get.side_effect = aiohttp.ClientConnectionError(
|
||||
"Connection failed"
|
||||
)
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service._request("https://api.screenscraper.fr/api2/jeuInfos.php")
|
||||
|
||||
assert exc_info.value.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
|
||||
assert "Can't connect to ScreenScraper" in exc_info.value.detail
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_timeout_with_retry(self, service):
|
||||
"""Test request timeout with successful retry."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"response": {"jeu": {}}}
|
||||
mock_response.text.return_value = '{"response": {"jeu": {}}}'
|
||||
mock_response.raise_for_status.return_value = None
|
||||
|
||||
# First call times out, second succeeds
|
||||
mock_session.get.side_effect = [
|
||||
aiohttp.ServerTimeoutError("Timeout"),
|
||||
mock_response,
|
||||
]
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
)
|
||||
|
||||
assert result == {"response": {"jeu": {}}}
|
||||
assert mock_session.get.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_rate_limit_with_retry(self, service):
|
||||
"""Test rate limit handling with retry."""
|
||||
mock_session = AsyncMock()
|
||||
rate_limit_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.TOO_MANY_REQUESTS,
|
||||
)
|
||||
mock_session.get.side_effect = rate_limit_error
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
with patch("asyncio.sleep") as mock_sleep:
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
)
|
||||
|
||||
assert result == {}
|
||||
mock_sleep.assert_called_once_with(2)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_unauthorized_returns_empty_dict(self, service):
|
||||
"""Test that unauthorized error in retry returns empty dict."""
|
||||
mock_session = AsyncMock()
|
||||
|
||||
# First call timeout, second call unauthorized
|
||||
timeout_error = aiohttp.ServerTimeoutError("Timeout")
|
||||
unauthorized_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.UNAUTHORIZED,
|
||||
)
|
||||
mock_session.get.side_effect = [timeout_error, unauthorized_error]
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
)
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_json_decode_error(self, service):
|
||||
"""Test handling of JSON decode error."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.text.return_value = "Valid response text"
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.side_effect = json.JSONDecodeError("Expecting value", "", 0)
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
)
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_other_client_error(self, service):
|
||||
"""Test handling of other client errors."""
|
||||
mock_session = AsyncMock()
|
||||
client_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
mock_session.get.side_effect = client_error
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php"
|
||||
)
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_crc(self, service):
|
||||
"""Test get_game_info with CRC parameter."""
|
||||
mock_response = {
|
||||
"response": {
|
||||
"jeu": {
|
||||
"id": "1",
|
||||
"noms": [{"region": "wor", "text": "Test Game"}],
|
||||
"systeme": {"id": "1", "text": "NES"},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(crc="ABC123")
|
||||
|
||||
assert result is not None
|
||||
assert result["id"] == "1"
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "crc=ABC123" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_md5(self, service):
|
||||
"""Test get_game_info with MD5 parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(md5="abc123def456")
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "md5=abc123def456" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_sha1(self, service):
|
||||
"""Test get_game_info with SHA1 parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(sha1="abc123def456789")
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "sha1=abc123def456789" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_system_id(self, service):
|
||||
"""Test get_game_info with system ID parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(system_id=1)
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "systemeid=1" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_rom_type(self, service):
|
||||
"""Test get_game_info with ROM type parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(rom_type="rom")
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "romtype=rom" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_rom_name(self, service):
|
||||
"""Test get_game_info with ROM name parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(rom_name="Test Game.nes")
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert (
|
||||
"romnom=Test+Game.nes" in call_args or "romnom=Test%20Game.nes" in call_args
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_rom_size(self, service):
|
||||
"""Test get_game_info with ROM size parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(rom_size_bytes=32768)
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "romtaille=32768" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_serial_number(self, service):
|
||||
"""Test get_game_info with serial number parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(serial_number="NES-ABC-USA")
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "serialnum=NES-ABC-USA" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_game_id(self, service):
|
||||
"""Test get_game_info with game ID parameter."""
|
||||
mock_response = {"response": {"jeu": {"id": "123"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(game_id=123)
|
||||
|
||||
assert result is not None
|
||||
assert result["id"] == "123"
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "gameid=123" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_all_parameters(self, service):
|
||||
"""Test get_game_info with all parameters."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(
|
||||
crc="ABC123",
|
||||
md5="md5hash",
|
||||
sha1="sha1hash",
|
||||
system_id=1,
|
||||
rom_type="rom",
|
||||
rom_name="Test Game",
|
||||
rom_size_bytes=32768,
|
||||
serial_number="NES-ABC-USA",
|
||||
game_id=123,
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "crc=ABC123" in call_args
|
||||
assert "md5=md5hash" in call_args
|
||||
assert "sha1=sha1hash" in call_args
|
||||
assert "systemeid=1" in call_args
|
||||
assert "romtype=rom" in call_args
|
||||
assert "romtaille=32768" in call_args
|
||||
assert "serialnum=NES-ABC-USA" in call_args
|
||||
assert "gameid=123" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_no_game_found(self, service):
|
||||
"""Test get_game_info when no game is found."""
|
||||
mock_response: dict[str, dict] = {"response": {}}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.get_game_info(crc="NOTFOUND")
|
||||
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_empty_jeu_data(self, service):
|
||||
"""Test get_game_info when jeu data is empty."""
|
||||
mock_response: dict[str, dict] = {"response": {"jeu": {}}}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.get_game_info(crc="EMPTY")
|
||||
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_basic(self, service):
|
||||
"""Test search_games with basic term."""
|
||||
mock_response = {
|
||||
"response": {
|
||||
"jeux": [
|
||||
{"id": "1", "noms": [{"region": "wor", "text": "Sonic"}]},
|
||||
{"id": "2", "noms": [{"region": "wor", "text": "Sonic 2"}]},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.search_games(term="Sonic")
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0]["id"] == "1"
|
||||
assert result[1]["id"] == "2"
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "recherche=Sonic" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_with_system_id(self, service):
|
||||
"""Test search_games with system ID filter."""
|
||||
mock_response = {"response": {"jeux": [{"id": "1"}]}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.search_games(term="Mario", system_id=7)
|
||||
|
||||
assert len(result) == 1
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "recherche=Mario" in call_args
|
||||
assert "systemeid=7" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_no_results(self, service):
|
||||
"""Test search_games when no games are found."""
|
||||
mock_response: dict[str, dict] = {"response": {"jeux": [{}]}}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.search_games(term="NonexistentGame")
|
||||
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_empty_response(self, service):
|
||||
"""Test search_games with empty response."""
|
||||
mock_response: dict[str, dict] = {"response": {}}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.search_games(term="Test")
|
||||
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_special_characters(self, service):
|
||||
"""Test search_games with special characters in term."""
|
||||
mock_response: dict[str, dict] = {"response": {"jeux": []}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.search_games(term="Pac-Man & Ms. Pac-Man")
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "recherche=" in call_args
|
||||
|
||||
|
||||
class TestScreenScraperServiceIntegration:
|
||||
"""Integration tests with real API calls using VCR cassettes."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a ScreenScraperService instance for integration testing."""
|
||||
return ScreenScraperService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_get_game_info_by_crc_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test get_game_info with CRC using real API call."""
|
||||
with patch(
|
||||
"adapters.services.screenscraper.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.get_game_info(crc="abc123", system_id=1)
|
||||
|
||||
# Verify response structure (might be None if game not found)
|
||||
if result is not None:
|
||||
assert "id" in result
|
||||
assert "noms" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_get_game_info_by_game_id_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test get_game_info with game ID using real API call."""
|
||||
with patch(
|
||||
"adapters.services.screenscraper.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.get_game_info(game_id=1)
|
||||
|
||||
# Verify response structure
|
||||
if result is not None:
|
||||
assert "id" in result
|
||||
assert "noms" in result
|
||||
assert "systeme" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_search_games_real_api(self, service, mock_ctx_aiohttp_session):
|
||||
"""Test search_games with real API call."""
|
||||
with patch(
|
||||
"adapters.services.screenscraper.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.search_games(term="Mario")
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
if result: # If there are games
|
||||
game = result[0]
|
||||
assert "id" in game
|
||||
assert "noms" in game
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_search_games_with_system_filter_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test search_games with system filter using real API call."""
|
||||
with patch(
|
||||
"adapters.services.screenscraper.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.search_games(term="Sonic", system_id=1)
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
if result:
|
||||
game = result[0]
|
||||
assert "id" in game
|
||||
assert "noms" in game
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_error_handling_real_api(self, service, mock_ctx_aiohttp_session):
|
||||
"""Test error handling with real API calls."""
|
||||
with patch(
|
||||
"adapters.services.screenscraper.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
with patch(
|
||||
"adapters.services.screenscraper.SCREENSCRAPER_USER", "invalid_user"
|
||||
):
|
||||
with patch(
|
||||
"adapters.services.screenscraper.SCREENSCRAPER_PASSWORD",
|
||||
"invalid_pass",
|
||||
):
|
||||
# This should handle the error gracefully
|
||||
try:
|
||||
result = await service.get_game_info(game_id=INVALID_GAME_ID)
|
||||
# Should either return None or handle auth error
|
||||
assert result is None or isinstance(result, dict)
|
||||
except HTTPException as e:
|
||||
# Should be authentication error
|
||||
assert e.status_code in [401, 503]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_get_game_info_not_found_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test get_game_info with non-existent game using real API call."""
|
||||
with patch(
|
||||
"adapters.services.screenscraper.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.get_game_info(
|
||||
crc="FFFFFFFFFFFFFFFF", system_id=INVALID_SYSTEM_ID
|
||||
)
|
||||
|
||||
# Should return None for non-existent game
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_search_games_no_results_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test search_games with term that returns no results using real API call."""
|
||||
with patch(
|
||||
"adapters.services.screenscraper.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.search_games(term="ZZZNonexistentGameZZZ")
|
||||
|
||||
# Should return empty list for no results
|
||||
assert result == []
|
||||
|
||||
|
||||
# Performance tests
|
||||
class TestScreenScraperServicePerformance:
|
||||
"""Performance tests for ScreenScraper service."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a ScreenScraperService instance for performance testing."""
|
||||
return ScreenScraperService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_concurrent_requests(self, service):
|
||||
"""Test multiple concurrent API requests."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
# Run 5 concurrent requests
|
||||
tasks = [service.get_game_info(game_id=i) for i in range(1, 6)]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
# All should succeed
|
||||
assert all(result is not None for result in results)
|
||||
assert len(results) == 5
|
||||
assert mock_request.call_count == 5
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_timeout_handling(self, service):
|
||||
"""Test handling of request timeouts."""
|
||||
mock_session = AsyncMock()
|
||||
|
||||
# Simulate timeout on first call, success on retry
|
||||
timeout_error = aiohttp.ServerTimeoutError("Request timeout")
|
||||
success_response = AsyncMock()
|
||||
success_response.json.return_value = {"response": {"jeu": {}}}
|
||||
success_response.text.return_value = '{"response": {"jeu": {}}}'
|
||||
success_response.raise_for_status.return_value = None
|
||||
|
||||
mock_session.get.side_effect = [timeout_error, success_response]
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php", request_timeout=1
|
||||
)
|
||||
|
||||
assert result == {"response": {"jeu": {}}}
|
||||
assert mock_session.get.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_concurrent_search_requests(self, service):
|
||||
"""Test multiple concurrent search requests."""
|
||||
mock_response = {"response": {"jeux": [{"id": "1"}]}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
# Run 3 concurrent search requests
|
||||
tasks = [
|
||||
service.search_games(term="Mario"),
|
||||
service.search_games(term="Sonic"),
|
||||
service.search_games(term="Zelda"),
|
||||
]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
# All should succeed
|
||||
assert all(len(result) == 1 for result in results)
|
||||
assert len(results) == 3
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
|
||||
# Edge case tests
|
||||
class TestScreenScraperServiceEdgeCases:
|
||||
"""Edge case tests for ScreenScraper service."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a ScreenScraperService instance for edge case testing."""
|
||||
return ScreenScraperService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_zero_values(self, service):
|
||||
"""Test get_game_info with zero values."""
|
||||
mock_response = {"response": {"jeu": {"id": "0"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(
|
||||
system_id=0,
|
||||
rom_size_bytes=0,
|
||||
game_id=0,
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "systemeid=0" in call_args
|
||||
assert "romtaille=0" in call_args
|
||||
assert "gameid=0" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_empty_term(self, service):
|
||||
"""Test search_games with empty term."""
|
||||
mock_response: dict[str, dict] = {"response": {"jeux": []}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.search_games(term="")
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "recherche=" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_info_with_special_characters(self, service):
|
||||
"""Test get_game_info with special characters in parameters."""
|
||||
mock_response = {"response": {"jeu": {"id": "1"}}}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_game_info(
|
||||
rom_name="Test & Game (USA).nes",
|
||||
serial_number="NES-T&G-USA",
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
call_args = mock_request.call_args[0][0]
|
||||
# URL should be properly encoded
|
||||
assert "romnom=" in call_args
|
||||
assert "serialnum=" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_with_custom_timeout(self, service):
|
||||
"""Test request with custom timeout."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"response": {}}
|
||||
mock_response.text.return_value = '{"response": {}}'
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://api.screenscraper.fr/api2/jeuInfos.php", request_timeout=30
|
||||
)
|
||||
|
||||
assert result == {"response": {}}
|
||||
# Verify timeout was passed correctly
|
||||
call_kwargs = mock_session.get.call_args[1]
|
||||
assert call_kwargs["timeout"].total == 30
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_login_error_in_retry_attempt(self, service):
|
||||
"""Test login error detection in retry attempt."""
|
||||
mock_session = AsyncMock()
|
||||
|
||||
# First call times out, second call has login error
|
||||
timeout_error = aiohttp.ServerTimeoutError("Timeout")
|
||||
login_error_response = AsyncMock()
|
||||
login_error_response.text.return_value = "Erreur de login detected"
|
||||
login_error_response.raise_for_status.return_value = None
|
||||
|
||||
mock_session.get.side_effect = [timeout_error, login_error_response]
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.screenscraper.ctx_aiohttp_session", mock_context):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service._request("https://api.screenscraper.fr/api2/jeuInfos.php")
|
||||
|
||||
assert exc_info.value.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
assert "Invalid ScreenScraper credentials" in exc_info.value.detail
|
||||
assert mock_session.get.call_count == 2
|
||||
862
backend/adapters/services/tests/test_steamgriddb.py
Normal file
862
backend/adapters/services/tests/test_steamgriddb.py
Normal file
@@ -0,0 +1,862 @@
|
||||
import asyncio
|
||||
import http
|
||||
import json
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import aiohttp
|
||||
import pytest
|
||||
from adapters.services.steamgriddb import (
|
||||
SteamGridDBService,
|
||||
auth_middleware,
|
||||
)
|
||||
from adapters.services.steamgriddb_types import (
|
||||
SGDBDimension,
|
||||
SGDBMime,
|
||||
SGDBStyle,
|
||||
SGDBTag,
|
||||
SGDBType,
|
||||
)
|
||||
from fastapi import HTTPException
|
||||
|
||||
INVALID_GAME_ID = 999999
|
||||
|
||||
|
||||
class TestAuthMiddleware:
|
||||
@patch("adapters.services.steamgriddb.STEAMGRIDDB_API_KEY", "test_api_key")
|
||||
@pytest.mark.asyncio
|
||||
async def test_auth_middleware_adds_bearer_token(self):
|
||||
"""Test that auth middleware adds Bearer token to request headers."""
|
||||
# Create a real request-like object
|
||||
mock_request = MagicMock()
|
||||
mock_request.headers = {}
|
||||
|
||||
mock_handler = AsyncMock()
|
||||
mock_response = MagicMock()
|
||||
mock_handler.return_value = mock_response
|
||||
|
||||
result = await auth_middleware(mock_request, mock_handler)
|
||||
|
||||
# Check that the Authorization header was added
|
||||
assert mock_request.headers["Authorization"] == "Bearer test_api_key"
|
||||
mock_handler.assert_called_once_with(mock_request)
|
||||
assert result == mock_response
|
||||
|
||||
@patch("adapters.services.steamgriddb.STEAMGRIDDB_API_KEY", "")
|
||||
@pytest.mark.asyncio
|
||||
async def test_auth_middleware_with_empty_api_key(self):
|
||||
"""Test that auth middleware adds empty Bearer token when none configured."""
|
||||
mock_request = MagicMock()
|
||||
mock_request.headers = {}
|
||||
|
||||
mock_handler = AsyncMock()
|
||||
mock_response = MagicMock()
|
||||
mock_handler.return_value = mock_response
|
||||
|
||||
result = await auth_middleware(mock_request, mock_handler)
|
||||
|
||||
assert mock_request.headers["Authorization"] == "Bearer "
|
||||
assert result == mock_response
|
||||
|
||||
|
||||
class TestSteamGridDBServiceUnit:
|
||||
"""Unit tests with mocked dependencies."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a SteamGridDBService instance for testing."""
|
||||
return SteamGridDBService()
|
||||
|
||||
@pytest.fixture
|
||||
def service_custom_url(self):
|
||||
"""Create a SteamGridDBService instance with custom URL."""
|
||||
return SteamGridDBService("https://custom.api.com")
|
||||
|
||||
def test_init_default_url(self, service):
|
||||
"""Test service initialization with default URL."""
|
||||
assert str(service.url) == "https://steamgriddb.com/api/v2"
|
||||
|
||||
def test_init_custom_url(self, service_custom_url):
|
||||
"""Test service initialization with custom URL."""
|
||||
assert str(service_custom_url.url) == "https://custom.api.com"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_success(self, service):
|
||||
"""Test successful API request."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"data": [{"id": 1, "name": "Test Game"}]}
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.steamgriddb.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://steamgriddb.com/api/v2/search/test"
|
||||
)
|
||||
|
||||
assert result == {"data": [{"id": 1, "name": "Test Game"}]}
|
||||
mock_session.get.assert_called_once()
|
||||
mock_response.raise_for_status.assert_called_once()
|
||||
mock_response.json.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_unauthorized_raises_exception(self, service):
|
||||
"""Test that 401 Unauthorized raises SGDBInvalidAPIKeyException."""
|
||||
mock_session = AsyncMock()
|
||||
unauthorized_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.UNAUTHORIZED,
|
||||
)
|
||||
mock_session.get.side_effect = unauthorized_error
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.steamgriddb.ctx_aiohttp_session", mock_context):
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service._request("https://steamgriddb.com/api/v2/search/test")
|
||||
assert exc_info.value.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_other_client_error(self, service):
|
||||
"""Test handling of other client errors."""
|
||||
mock_session = AsyncMock()
|
||||
client_error = aiohttp.ClientResponseError(
|
||||
request_info=MagicMock(),
|
||||
history=(),
|
||||
status=http.HTTPStatus.BAD_REQUEST,
|
||||
)
|
||||
mock_session.get.side_effect = client_error
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.steamgriddb.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://steamgriddb.com/api/v2/search/test"
|
||||
)
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_json_decode_error(self, service):
|
||||
"""Test handling of JSON decode error."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_response.json.side_effect = json.JSONDecodeError("Expecting value", "", 0)
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.steamgriddb.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://steamgriddb.com/api/v2/search/test"
|
||||
)
|
||||
|
||||
assert result == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_basic(self, service):
|
||||
"""Test get_grids_for_game with basic game ID."""
|
||||
mock_response = {
|
||||
"page": 0,
|
||||
"total": 2,
|
||||
"limit": 50,
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"score": 100,
|
||||
"style": "material",
|
||||
"url": "https://example.com/grid1.png",
|
||||
"thumb": "https://example.com/thumb1.png",
|
||||
"tags": [],
|
||||
"author": {"name": "TestUser", "steam64": "123", "avatar": ""},
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"score": 90,
|
||||
"style": "alternate",
|
||||
"url": "https://example.com/grid2.png",
|
||||
"thumb": "https://example.com/thumb2.png",
|
||||
"tags": ["humor"],
|
||||
"author": {"name": "TestUser2", "steam64": "456", "avatar": ""},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(123)
|
||||
|
||||
assert result["page"] == 0
|
||||
assert result["total"] == 2
|
||||
assert len(result["data"]) == 2
|
||||
assert result["data"][0]["id"] == 1
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "grids/game/123" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_styles(self, service):
|
||||
"""Test get_grids_for_game with style filters."""
|
||||
mock_response = {
|
||||
"page": 0,
|
||||
"total": 1,
|
||||
"limit": 50,
|
||||
"data": [{"id": 1, "style": "material"}],
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123, styles=[SGDBStyle.MATERIAL, SGDBStyle.ALTERNATE]
|
||||
)
|
||||
|
||||
assert len(result["data"]) == 1
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert (
|
||||
"styles=material%2Calternate" in call_args
|
||||
or "styles=material,alternate" in call_args
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_dimensions(self, service):
|
||||
"""Test get_grids_for_game with dimension filters."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123,
|
||||
dimensions=[
|
||||
SGDBDimension.STEAM_HORIZONTAL,
|
||||
SGDBDimension.STEAM_VERTICAL,
|
||||
],
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "dimensions=" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_mimes(self, service):
|
||||
"""Test get_grids_for_game with MIME type filters."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123, mimes=[SGDBMime.PNG, SGDBMime.WEBP]
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "mimes=" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_types(self, service):
|
||||
"""Test get_grids_for_game with type filters."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123, types=[SGDBType.STATIC, SGDBType.ANIMATED]
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "types=" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_tags(self, service):
|
||||
"""Test get_grids_for_game with tag filters."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123, any_of_tags=[SGDBTag.HUMOR, SGDBTag.NSFW]
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "oneoftag=" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_boolean_filters(self, service):
|
||||
"""Test get_grids_for_game with boolean filters."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123, is_nsfw=True, is_humor=False, is_epilepsy="any"
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "nsfw=true" in call_args
|
||||
assert "humor=false" in call_args
|
||||
assert "epilepsy=any" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_pagination(self, service):
|
||||
"""Test get_grids_for_game with pagination parameters."""
|
||||
mock_response = {"page": 1, "total": 100, "limit": 10, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(123, limit=10, page_number=1)
|
||||
|
||||
assert result["page"] == 1
|
||||
assert result["limit"] == 10
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "limit=10" in call_args
|
||||
assert "page=1" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_all_parameters(self, service):
|
||||
"""Test get_grids_for_game with all parameters."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 5, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123,
|
||||
styles=[SGDBStyle.MATERIAL],
|
||||
dimensions=[SGDBDimension.STEAM_HORIZONTAL],
|
||||
mimes=[SGDBMime.PNG],
|
||||
types=[SGDBType.STATIC],
|
||||
any_of_tags=[SGDBTag.HUMOR],
|
||||
is_nsfw=False,
|
||||
is_humor=True,
|
||||
is_epilepsy="any",
|
||||
limit=5,
|
||||
page_number=0,
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "styles=material" in call_args
|
||||
assert "dimensions=460x215" in call_args
|
||||
assert "mimes=image/png" in call_args
|
||||
assert "types=static" in call_args
|
||||
assert "oneoftag=humor" in call_args
|
||||
assert "nsfw=false" in call_args
|
||||
assert "humor=true" in call_args
|
||||
assert "epilepsy=any" in call_args
|
||||
assert "limit=5" in call_args
|
||||
assert "page=0" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_empty_response(self, service):
|
||||
"""Test get_grids_for_game with empty response."""
|
||||
mock_response: dict = {}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.get_grids_for_game(123)
|
||||
|
||||
assert result["page"] == 0
|
||||
assert result["total"] == 0
|
||||
assert result["limit"] == 50
|
||||
assert result["data"] == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_grids_for_game_single_page(self, service):
|
||||
"""Test iter_grids_for_game with single page of results."""
|
||||
mock_grid_1 = {"id": 1, "score": 100}
|
||||
mock_grid_2 = {"id": 2, "score": 90}
|
||||
mock_response = {
|
||||
"page": 0,
|
||||
"total": 2,
|
||||
"limit": 50,
|
||||
"data": [mock_grid_1, mock_grid_2],
|
||||
}
|
||||
|
||||
with patch.object(service, "get_grids_for_game", return_value=mock_response):
|
||||
results = []
|
||||
async for grid in service.iter_grids_for_game(123):
|
||||
results.append(grid)
|
||||
|
||||
assert len(results) == 2
|
||||
assert results[0] == mock_grid_1
|
||||
assert results[1] == mock_grid_2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_grids_for_game_multiple_pages(self, service):
|
||||
"""Test iter_grids_for_game with multiple pages of results."""
|
||||
# First page
|
||||
mock_response_1 = {
|
||||
"page": 0,
|
||||
"total": 75,
|
||||
"limit": 50,
|
||||
"data": [{"id": i} for i in range(1, 51)], # 50 items
|
||||
}
|
||||
# Second page
|
||||
mock_response_2 = {
|
||||
"page": 1,
|
||||
"total": 75,
|
||||
"limit": 50,
|
||||
"data": [{"id": i} for i in range(51, 76)], # 25 items
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
service,
|
||||
"get_grids_for_game",
|
||||
side_effect=[mock_response_1, mock_response_2],
|
||||
) as mock_get:
|
||||
results = []
|
||||
async for grid in service.iter_grids_for_game(123):
|
||||
results.append(grid)
|
||||
|
||||
assert len(results) == 75
|
||||
assert results[0]["id"] == 1
|
||||
assert results[49]["id"] == 50
|
||||
assert results[50]["id"] == 51
|
||||
assert results[74]["id"] == 75
|
||||
assert mock_get.call_count == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_grids_for_game_empty_results(self, service):
|
||||
"""Test iter_grids_for_game with no results."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(service, "get_grids_for_game", return_value=mock_response):
|
||||
results = []
|
||||
async for grid in service.iter_grids_for_game(123):
|
||||
results.append(grid)
|
||||
|
||||
assert len(results) == 0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_grids_for_game_with_filters(self, service):
|
||||
"""Test iter_grids_for_game with filters passed through."""
|
||||
mock_response = {"page": 0, "total": 1, "limit": 50, "data": [{"id": 1}]}
|
||||
|
||||
with patch.object(
|
||||
service, "get_grids_for_game", return_value=mock_response
|
||||
) as mock_get:
|
||||
results = []
|
||||
async for grid in service.iter_grids_for_game(
|
||||
123,
|
||||
styles=[SGDBStyle.MATERIAL],
|
||||
is_nsfw=False,
|
||||
):
|
||||
results.append(grid)
|
||||
|
||||
assert len(results) == 1
|
||||
# Verify filters were passed through
|
||||
mock_get.assert_called_with(
|
||||
123,
|
||||
styles=[SGDBStyle.MATERIAL],
|
||||
dimensions=None,
|
||||
mimes=None,
|
||||
types=None,
|
||||
any_of_tags=None,
|
||||
is_nsfw=False,
|
||||
is_humor=None,
|
||||
is_epilepsy=None,
|
||||
limit=50,
|
||||
page_number=0,
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_basic(self, service):
|
||||
"""Test search_games with basic term."""
|
||||
mock_response = {
|
||||
"data": [
|
||||
{"id": 1, "name": "Test Game 1", "types": ["game"], "verified": True},
|
||||
{"id": 2, "name": "Test Game 2", "types": ["game"], "verified": False},
|
||||
]
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.search_games("test")
|
||||
|
||||
assert len(result) == 2
|
||||
assert result[0]["id"] == 1
|
||||
assert result[0]["name"] == "Test Game 1"
|
||||
assert result[1]["id"] == 2
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "search/autocomplete/test" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_no_results(self, service):
|
||||
"""Test search_games with no results."""
|
||||
mock_response: dict = {"data": []}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.search_games("nonexistent")
|
||||
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_empty_response(self, service):
|
||||
"""Test search_games with empty response."""
|
||||
mock_response: dict = {}
|
||||
|
||||
with patch.object(service, "_request", return_value=mock_response):
|
||||
result = await service.search_games("test")
|
||||
|
||||
assert result == []
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_special_characters(self, service):
|
||||
"""Test search_games with special characters in term."""
|
||||
mock_response: dict = {"data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.search_games("Pac-Man & Ms. Pac-Man")
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "search/autocomplete/" in call_args
|
||||
|
||||
|
||||
class TestSteamGridDBServiceIntegration:
|
||||
"""Integration tests with real API calls using VCR cassettes."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a SteamGridDBService instance for integration testing."""
|
||||
return SteamGridDBService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_search_games_real_api(self, service, mock_ctx_aiohttp_session):
|
||||
"""Test search_games with real API call."""
|
||||
with patch(
|
||||
"adapters.services.steamgriddb.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.search_games("Mario")
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, list)
|
||||
if result: # If there are games
|
||||
game = result[0]
|
||||
assert "id" in game
|
||||
assert "name" in game
|
||||
assert "types" in game
|
||||
assert "verified" in game
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_get_grids_for_game_real_api(self, service, mock_ctx_aiohttp_session):
|
||||
"""Test get_grids_for_game with real API call."""
|
||||
with patch(
|
||||
"adapters.services.steamgriddb.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.get_grids_for_game(1, limit=5)
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, dict)
|
||||
assert "page" in result
|
||||
assert "total" in result
|
||||
assert "limit" in result
|
||||
assert "data" in result
|
||||
assert isinstance(result["data"], list)
|
||||
if result["data"]: # If there are grids
|
||||
grid = result["data"][0]
|
||||
assert "id" in grid
|
||||
assert "score" in grid
|
||||
assert "style" in grid
|
||||
assert "url" in grid
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_get_grids_for_game_with_filters_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test get_grids_for_game with filters using real API call."""
|
||||
with patch(
|
||||
"adapters.services.steamgriddb.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.get_grids_for_game(
|
||||
1, styles=[SGDBStyle.MATERIAL], limit=3
|
||||
)
|
||||
|
||||
# Verify response structure
|
||||
assert isinstance(result, dict)
|
||||
assert len(result["data"]) <= 3 # Should respect limit
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_iter_grids_for_game_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test iter_grids_for_game with real API call."""
|
||||
with patch(
|
||||
"adapters.services.steamgriddb.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
results = []
|
||||
count = 0
|
||||
async for grid in service.iter_grids_for_game(1):
|
||||
results.append(grid)
|
||||
count += 1
|
||||
if count >= 5: # Limit iterations for testing
|
||||
break
|
||||
|
||||
# Verify we got results
|
||||
if results:
|
||||
grid = results[0]
|
||||
assert isinstance(grid, dict)
|
||||
assert "id" in grid
|
||||
assert "score" in grid
|
||||
assert "style" in grid
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_error_handling_real_api(self, service, mock_ctx_aiohttp_session):
|
||||
"""Test error handling with real API calls."""
|
||||
with patch(
|
||||
"adapters.services.steamgriddb.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
with patch(
|
||||
"adapters.services.steamgriddb.STEAMGRIDDB_API_KEY", "invalid_key"
|
||||
):
|
||||
# This should handle the error gracefully
|
||||
try:
|
||||
result = await service.get_grids_for_game(INVALID_GAME_ID)
|
||||
# Should either return empty result or handle auth error
|
||||
assert isinstance(result, dict)
|
||||
except HTTPException as exc:
|
||||
# Should be authentication error with 401 status
|
||||
assert exc.status_code == 401
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.vcr
|
||||
async def test_search_games_no_results_real_api(
|
||||
self, service, mock_ctx_aiohttp_session
|
||||
):
|
||||
"""Test search_games with term that returns no results using real API call."""
|
||||
with patch(
|
||||
"adapters.services.steamgriddb.ctx_aiohttp_session",
|
||||
mock_ctx_aiohttp_session,
|
||||
):
|
||||
result = await service.search_games("ZZZNonexistentGameZZZ")
|
||||
|
||||
# Should return empty list for no results
|
||||
assert result == []
|
||||
|
||||
|
||||
# Performance tests
|
||||
class TestSteamGridDBServicePerformance:
|
||||
"""Performance tests for SteamGridDB service."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a SteamGridDBService instance for performance testing."""
|
||||
return SteamGridDBService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_concurrent_requests(self, service):
|
||||
"""Test multiple concurrent API requests."""
|
||||
mock_response = {"data": [{"id": 1, "name": "Test Game"}]}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
# Run 5 concurrent requests
|
||||
tasks = [service.search_games("test") for _ in range(5)]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
# All should succeed
|
||||
assert all(len(result) == 1 for result in results)
|
||||
assert len(results) == 5
|
||||
assert mock_request.call_count == 5
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_concurrent_grid_requests(self, service):
|
||||
"""Test multiple concurrent grid requests."""
|
||||
mock_response = {"page": 0, "total": 1, "limit": 50, "data": [{"id": 1}]}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
# Run 3 concurrent grid requests
|
||||
tasks = [service.get_grids_for_game(i) for i in range(1, 4)]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
# All should succeed
|
||||
assert all(len(result["data"]) == 1 for result in results)
|
||||
assert len(results) == 3
|
||||
assert mock_request.call_count == 3
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_grids_pagination_performance(self, service):
|
||||
"""Test performance of pagination in iter_grids_for_game."""
|
||||
# Mock multiple pages
|
||||
mock_responses = [
|
||||
{
|
||||
"page": 0,
|
||||
"total": 100,
|
||||
"limit": 50,
|
||||
"data": [{"id": i} for i in range(1, 51)],
|
||||
},
|
||||
{
|
||||
"page": 1,
|
||||
"total": 100,
|
||||
"limit": 50,
|
||||
"data": [{"id": i} for i in range(51, 101)],
|
||||
},
|
||||
]
|
||||
|
||||
with patch.object(
|
||||
service, "get_grids_for_game", side_effect=mock_responses
|
||||
) as mock_get:
|
||||
results = []
|
||||
async for grid in service.iter_grids_for_game(123):
|
||||
results.append(grid)
|
||||
|
||||
assert len(results) == 100
|
||||
assert mock_get.call_count == 2
|
||||
|
||||
|
||||
# Edge case tests
|
||||
class TestSteamGridDBServiceEdgeCases:
|
||||
"""Edge case tests for SteamGridDB service."""
|
||||
|
||||
@pytest.fixture
|
||||
def service(self):
|
||||
"""Create a SteamGridDBService instance for edge case testing."""
|
||||
return SteamGridDBService()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_empty_collections(self, service):
|
||||
"""Test get_grids_for_game with empty collections."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123,
|
||||
styles=[],
|
||||
dimensions=[],
|
||||
mimes=[],
|
||||
types=[],
|
||||
any_of_tags=[],
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
# Empty collections should not add parameters to URL
|
||||
assert "styles=" not in call_args
|
||||
assert "dimensions=" not in call_args
|
||||
assert "mimes=" not in call_args
|
||||
assert "types=" not in call_args
|
||||
assert "oneoftag=" not in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_for_game_with_zero_values(self, service):
|
||||
"""Test get_grids_for_game with zero values."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 0, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(123, limit=0, page_number=0)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "limit=0" in call_args
|
||||
assert "page=0" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_games_empty_term(self, service):
|
||||
"""Test search_games with empty term."""
|
||||
mock_response: dict = {"data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.search_games("")
|
||||
|
||||
assert result == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
assert "search/autocomplete/" in call_args
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_with_custom_timeout(self, service):
|
||||
"""Test request with custom timeout."""
|
||||
mock_session = AsyncMock()
|
||||
mock_response = AsyncMock()
|
||||
mock_response.json.return_value = {"data": []}
|
||||
mock_response.raise_for_status.return_value = None
|
||||
mock_session.get.return_value = mock_response
|
||||
|
||||
mock_context = MagicMock()
|
||||
mock_context.get.return_value = mock_session
|
||||
|
||||
with patch("adapters.services.steamgriddb.ctx_aiohttp_session", mock_context):
|
||||
result = await service._request(
|
||||
"https://steamgriddb.com/api/v2/search/test", request_timeout=30
|
||||
)
|
||||
|
||||
assert result == {"data": []}
|
||||
# Verify timeout was passed correctly
|
||||
call_kwargs = mock_session.get.call_args[1]
|
||||
assert call_kwargs["timeout"].total == 30
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_grids_stops_at_exact_total(self, service):
|
||||
"""Test that iter_grids_for_game stops when reaching exact total."""
|
||||
# Mock response where we reach exact total
|
||||
mock_response = {
|
||||
"page": 0,
|
||||
"total": 50,
|
||||
"limit": 50,
|
||||
"data": [{"id": i} for i in range(1, 51)], # Exactly 50 items
|
||||
}
|
||||
|
||||
with patch.object(
|
||||
service, "get_grids_for_game", return_value=mock_response
|
||||
) as mock_get:
|
||||
results = []
|
||||
async for grid in service.iter_grids_for_game(123):
|
||||
results.append(grid)
|
||||
|
||||
assert len(results) == 50
|
||||
assert mock_get.call_count == 1 # Should only call once
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_grids_boolean_filter_none_values(self, service):
|
||||
"""Test get_grids_for_game with None values for boolean filters."""
|
||||
mock_response = {"page": 0, "total": 0, "limit": 50, "data": []}
|
||||
|
||||
with patch.object(
|
||||
service, "_request", return_value=mock_response
|
||||
) as mock_request:
|
||||
result = await service.get_grids_for_game(
|
||||
123, is_nsfw=None, is_humor=None, is_epilepsy=None
|
||||
)
|
||||
|
||||
assert result["data"] == []
|
||||
call_args = mock_request.call_args[0][0]
|
||||
# None values should not add parameters to URL
|
||||
assert "nsfw=" not in call_args
|
||||
assert "humor=" not in call_args
|
||||
assert "epilepsy=" not in call_args
|
||||
@@ -10,6 +10,10 @@ env =
|
||||
IGDB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
RETROACHIEVEMENTS_USERNAME=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
RETROACHIEVEMENTS_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
MOBYGAMES_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
SCREENSCRAPER_USER=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
SCREENSCRAPER_PASSWORD=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
STEAMGRIDDB_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
LAUNCHBOX_API_ENABLED=true
|
||||
ROMM_AUTH_SECRET_KEY=843f6cefc5ba1430d54061301c2893be00c2aef11dae39ffec13a2af1a86e867
|
||||
ENABLE_RESCAN_ON_FILESYSTEM_CHANGE=true
|
||||
|
||||
Reference in New Issue
Block a user