Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Screenscraper integration #1416

Draft
wants to merge 63 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
63fac78
screenscraper API test
zurdi15 Oct 25, 2024
95f0643
merge 'master' into feature/screenscraper-integration
zurdi15 Oct 25, 2024
b3a7ce3
Merge branch 'master' into feature/screenscraper-integration
zurdi15 Oct 25, 2024
3a5fd7d
Merge branch 'master' into feature/screenscraper-integration
zurdi15 Oct 25, 2024
7dfee48
added ss dev token
zurdi15 Oct 25, 2024
443def9
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Dec 10, 2024
374d11b
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Dec 26, 2024
1083dc6
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Dec 27, 2024
0d1b932
Merge branch 'master' into feature/screenscraper-integration
zurdi15 Jan 1, 2025
db4001d
feat: base structure for ss support
zurdi15 Jan 2, 2025
dca80cd
Merge branch 'master' into feature/screenscraper-integration
zurdi15 Jan 2, 2025
4aa2d49
feat: fully integrated screenscrapper
zurdi15 Jan 3, 2025
8ad86f2
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Jan 3, 2025
08de3b3
fix: typecheck
zurdi15 Jan 3, 2025
2c6a165
misc: improved manual match speed
zurdi15 Jan 3, 2025
a9f189b
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Jan 5, 2025
e7342ab
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Jan 6, 2025
d224729
fix: merge conflicts
zurdi15 Jan 6, 2025
23102b8
fix: trunk check
zurdi15 Jan 6, 2025
977d552
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Jan 8, 2025
a7a2e3b
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Jan 9, 2025
9d96501
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Jan 31, 2025
3a4c06f
fix: rebase migrations
zurdi15 Jan 31, 2025
a00eb1f
feat: add ScreenScrapper metadata to setup validation and improve sca…
zurdi15 Jan 31, 2025
cce279a
Merge branch 'master' into feature/screenscraper-integration
zurdi15 Feb 4, 2025
cb54ee3
fix: fixes from trunk
zurdi15 Feb 4, 2025
1089217
fix: adjust layout for CollectionInfoDrawer and improve Gallery view …
zurdi15 Feb 4, 2025
e4b24cb
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Feb 4, 2025
5f048a7
fix: center background image in Auth layout
zurdi15 Feb 4, 2025
402c19d
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Feb 4, 2025
78eb3f9
fix: adapt to new fs_name rom property
zurdi15 Feb 4, 2025
6f90916
fix: simplify scan stats display logic in Scan.vue
zurdi15 Feb 4, 2025
b550588
added png version of auth background
zurdi15 Feb 4, 2025
61dde02
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Feb 5, 2025
2914462
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Feb 5, 2025
2ee6026
Remove SCREENSCRAPER_API_KEY from configuration and handler, replace …
zurdi15 Feb 5, 2025
fc63e68
added grayscale version isotipos
zurdi15 Feb 5, 2025
8a27d62
Updated IDs for screenscraper
danblu3 Feb 5, 2025
ad179a9
Fix SLUG_TO_SS_ID declaration and update avatar style in Scan.vue
zurdi15 Feb 6, 2025
5951aec
Update platforms table with ss_id from SLUG_TO_SS_ID mapping
zurdi15 Feb 6, 2025
5caf3dd
reordered migrations to fit master branch
zurdi15 Feb 6, 2025
2f8beb6
Refactor metadata source handling to use enum for better type safety
zurdi15 Feb 6, 2025
2393e1e
Rename migration files and update platforms table with ss_id from SLU…
zurdi15 Feb 6, 2025
d96239c
Refactor scan functions to use MetadataSource enum for metadata sources
zurdi15 Feb 6, 2025
bd06865
Add support for Screenscraper ID in game details view
zurdi15 Feb 6, 2025
90435e2
Enhance game details view with improved chip layout and icons for IGD…
zurdi15 Feb 6, 2025
380a407
Refactor game details view to simplify ID display and enhance rating …
zurdi15 Feb 6, 2025
9e213bc
Refactor metadata source handling to use string types and update hear…
zurdi15 Feb 6, 2025
5b9a845
Merge branch 'master' into feature/screenscraper-integration
zurdi15 Feb 6, 2025
268bb82
Add Screenscraper ID support to PlatformSchema and update PlatformInf…
zurdi15 Feb 6, 2025
9ae3b7d
feat: added manuals to ss migration
zurdi15 Feb 6, 2025
0660502
feat: screenscraper manual support
zurdi15 Feb 6, 2025
b440846
chore: add TODO comment to extract PDF viewer to a separate component…
zurdi15 Feb 6, 2025
e08a42d
feat: refactor PDF viewer toolbar and update ID configuration
zurdi15 Feb 6, 2025
dca9c1b
feat: extracter pdf viewer to component
zurdi15 Feb 6, 2025
dcc6813
fix: DetailedRom type in PDFViewer component
zurdi15 Feb 6, 2025
22fa1f2
feat: enhance PDFViewer component with responsive toolbar adjustments
zurdi15 Feb 7, 2025
0a41a2f
fix: remove unused title-on-hover attribute in CollectionInfoDrawer c…
zurdi15 Feb 7, 2025
0cbebd6
fix: update translations for 'manual' to 'user manual' in multiple lo…
zurdi15 Feb 7, 2025
42ae385
feat: add manual upload feature in EditRom component
zurdi15 Feb 7, 2025
46678b1
feat: implement manual upload functionality for ROMs
zurdi15 Feb 7, 2025
d2c2fe6
Merge remote-tracking branch 'origin/master' into feature/screenscrap…
zurdi15 Feb 8, 2025
2876492
Add upload functionality and improve upload progress UI
zurdi15 Feb 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
{
"label": "Setup testing environment",
"type": "shell",
"command": "export $(cat .env | grep DB_ROOT_PASSWD | xargs) && docker exec -i mariadb mariadb -u root -p$DB_ROOT_PASSWD < backend/romm_test/setup.sql",
"command": "export $(cat .env | grep DB_ROOT_PASSWD | xargs) && docker exec -i romm-db-dev mariadb -u root -p$DB_ROOT_PASSWD < backend/romm_test/setup.sql",
"problemMatcher": []
},
{
Expand Down
2 changes: 1 addition & 1 deletion backend/alembic/versions/0030_user_email_null.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Change empty string in users.email to NULL.

Revision ID: 951473b0c581
Revision ID: 0030_user_email_null
Revises: 0029_platforms_custom_name
Create Date: 2025-01-14 01:30:39.696257

Expand Down
43 changes: 43 additions & 0 deletions backend/alembic/versions/0034_screenscraper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""empty message

Revision ID: 0034_screenscraper
Revises: 0033_rom_file_and_hashes
Create Date: 2025-01-02 18:58:55.557123

"""

import sqlalchemy as sa
from alembic import op
from utils.database import CustomJSON

# revision identifiers, used by Alembic.
revision = "0034_screenscraper"
down_revision = "0033_rom_file_and_hashes"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("platforms", schema=None) as batch_op:
batch_op.add_column(sa.Column("ss_id", sa.Integer(), nullable=True))

with op.batch_alter_table("roms", schema=None) as batch_op:
batch_op.add_column(sa.Column("ss_id", sa.Integer(), nullable=True))
batch_op.add_column(sa.Column("ss_metadata", CustomJSON(), nullable=True))
batch_op.add_column(sa.Column("url_manual", sa.Text(), nullable=True)),
batch_op.add_column(sa.Column("path_manual", sa.Text(), nullable=True)),
batch_op.create_index("idx_roms_ss_id", ["ss_id"], unique=False)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("roms", schema=None) as batch_op:
batch_op.drop_index("idx_roms_ss_id")
batch_op.drop_column("ss_metadata")
batch_op.drop_column("ss_id")

with op.batch_alter_table("platforms", schema=None) as batch_op:
batch_op.drop_column("ss_id")
# ### end Alembic commands ###
34 changes: 34 additions & 0 deletions backend/alembic/versions/0035_screenscraper_platforms_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""empty message

Revision ID: 0035_screenscraper_platforms_id
Revises: 0034_screenscraper
Create Date: 2025-01-02 18:58:55.557123

"""

import sqlalchemy as sa
from alembic import op
from handler.metadata.ss_handler import SLUG_TO_SS_ID

# revision identifiers, used by Alembic.
revision = "0035_screenscraper_platforms_id"
down_revision = "0034_screenscraper"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
connection = op.get_bind()
for slug, ss_id in SLUG_TO_SS_ID.items():
connection.execute(
sa.text("UPDATE platforms SET ss_id = :ss_id WHERE slug = :slug"),
{"ss_id": ss_id["id"], "slug": slug},
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
4 changes: 4 additions & 0 deletions backend/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ def str_to_bool(value: str) -> bool:
"IGDB_CLIENT_SECRET", os.environ.get("CLIENT_SECRET", "")
)

# SCREENSCRAPER
SCREENSCRAPER_USER: Final = os.environ.get("SCREENSCRAPER_USER", "")
SCREENSCRAPER_PASSWORD: Final = os.environ.get("SCREENSCRAPER_PASSWORD", "")

# STEAMGRIDDB
STEAMGRIDDB_API_KEY: Final = os.environ.get("STEAMGRIDDB_API_KEY", "")

Expand Down
4 changes: 2 additions & 2 deletions backend/endpoints/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ async def add_collection(
)
else:
path_cover_s, path_cover_l = await fs_resource_handler.get_cover(
overwrite=True,
entity=_added_collection,
overwrite=True,
url_cover=_added_collection.url_cover,
)

Expand Down Expand Up @@ -208,8 +208,8 @@ async def update_collection(
{"url_cover": data.get("url_cover", collection.url_cover)}
)
path_cover_s, path_cover_l = await fs_resource_handler.get_cover(
overwrite=True,
entity=collection,
overwrite=True,
url_cover=data.get("url_cover", ""), # type: ignore
)
cleaned_data.update(
Expand Down
6 changes: 5 additions & 1 deletion backend/endpoints/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from handler.metadata.igdb_handler import IGDB_API_ENABLED
from handler.metadata.moby_handler import MOBY_API_ENABLED
from handler.metadata.sgdb_handler import STEAMGRIDDB_API_ENABLED
from handler.metadata.ss_handler import SS_API_ENABLED
from utils import get_version
from utils.router import APIRouter

Expand All @@ -38,9 +39,12 @@ def heartbeat() -> HeartbeatResponse:
"SHOW_SETUP_WIZARD": len(db_user_handler.get_admin_users()) == 0,
},
"METADATA_SOURCES": {
"ANY_SOURCE_ENABLED": IGDB_API_ENABLED or MOBY_API_ENABLED,
"ANY_SOURCE_ENABLED": IGDB_API_ENABLED
or MOBY_API_ENABLED
or SS_API_ENABLED,
"IGDB_API_ENABLED": IGDB_API_ENABLED,
"MOBY_API_ENABLED": MOBY_API_ENABLED,
"SS_API_ENABLED": SS_API_ENABLED,
"STEAMGRIDDB_ENABLED": STEAMGRIDDB_API_ENABLED,
},
"FILESYSTEM": {
Expand Down
1 change: 1 addition & 0 deletions backend/endpoints/responses/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class MetadataSourcesDict(TypedDict):
ANY_SOURCE_ENABLED: bool
IGDB_API_ENABLED: bool
MOBY_API_ENABLED: bool
SS_API_ENABLED: bool
STEAMGRIDDB_ENABLED: bool


Expand Down
1 change: 1 addition & 0 deletions backend/endpoints/responses/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class PlatformSchema(BaseModel):
igdb_id: int | None = None
sgdb_id: int | None = None
moby_id: int | None = None
ss_id: int | None = None
category: str | None = None
generation: int | None = None
family_name: str | None = None
Expand Down
5 changes: 5 additions & 0 deletions backend/endpoints/responses/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class RomSchema(BaseModel):
igdb_id: int | None
sgdb_id: int | None
moby_id: int | None
ss_id: int | None

platform_id: int
platform_slug: str
Expand Down Expand Up @@ -152,11 +153,15 @@ class RomSchema(BaseModel):
age_ratings: list[str]
igdb_metadata: RomIGDBMetadata | None
moby_metadata: RomMobyMetadata | None
ss_metadata: RomMobyMetadata | None

path_cover_s: str | None
path_cover_l: str | None
has_cover: bool
url_cover: str | None
has_manual: bool
path_manual: str | None
url_manual: str | None

revision: str | None
regions: list[str]
Expand Down
2 changes: 2 additions & 0 deletions backend/endpoints/responses/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
class SearchRomSchema(BaseModel):
igdb_id: int | None = None
moby_id: int | None = None
ss_id: int | None = None
slug: str
name: str
summary: str
igdb_url_cover: str = ""
moby_url_cover: str = ""
ss_url_cover: str = ""
platform_id: int


Expand Down
96 changes: 91 additions & 5 deletions backend/endpoints/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
from handler.database import db_collection_handler, db_platform_handler, db_rom_handler
from handler.filesystem import fs_resource_handler, fs_rom_handler
from handler.filesystem.base_handler import CoverSize
from handler.metadata import meta_igdb_handler, meta_moby_handler
from handler.metadata import meta_igdb_handler, meta_moby_handler, meta_ss_handler
from logger.formatter import highlight as hl
from logger.logger import log
from models.rom import Rom, RomUser
from PIL import Image
Expand Down Expand Up @@ -435,16 +436,19 @@ async def update_rom(
"igdb_id": None,
"sgdb_id": None,
"moby_id": None,
"ss_id": None,
"name": rom.fs_name,
"summary": "",
"url_screenshots": [],
"path_screenshots": [],
"path_cover_s": "",
"path_cover_l": "",
"url_cover": "",
"url_manual": "",
"slug": "",
"igdb_metadata": {},
"moby_metadata": {},
"ss_metadata": {},
"revision": "",
},
)
Expand All @@ -458,6 +462,7 @@ async def update_rom(
cleaned_data: dict[str, Any] = {
"igdb_id": data.get("igdb_id", rom.igdb_id),
"moby_id": data.get("moby_id", rom.moby_id),
"ss_id": data.get("ss_id", rom.ss_id),
}

moby_id = cleaned_data["moby_id"]
Expand All @@ -470,9 +475,23 @@ async def update_rom(
)
cleaned_data.update({"path_screenshots": path_screenshots})

igdb_id = cleaned_data["igdb_id"]
if igdb_id and int(igdb_id) != rom.igdb_id:
igdb_rom = await meta_igdb_handler.get_rom_by_id(int(igdb_id))
if (
cleaned_data.get("ss_id", "")
and int(cleaned_data.get("ss_id", "")) != rom.ss_id
):
ss_rom = await meta_ss_handler.get_rom_by_id(cleaned_data["ss_id"])
cleaned_data.update(ss_rom)
path_screenshots = await fs_resource_handler.get_rom_screenshots(
rom=rom,
url_screenshots=cleaned_data.get("url_screenshots", []),
)
cleaned_data.update({"path_screenshots": path_screenshots})

if (
cleaned_data.get("igdb_id", "")
and int(cleaned_data.get("igdb_id", "")) != rom.igdb_id
):
igdb_rom = await meta_igdb_handler.get_rom_by_id(cleaned_data["igdb_id"])
cleaned_data.update(igdb_rom)
path_screenshots = await fs_resource_handler.get_rom_screenshots(
rom=rom,
Expand Down Expand Up @@ -556,14 +575,29 @@ async def update_rom(
):
cleaned_data.update({"url_cover": data.get("url_cover", rom.url_cover)})
path_cover_s, path_cover_l = await fs_resource_handler.get_cover(
overwrite=True,
entity=rom,
overwrite=True,
url_cover=str(data.get("url_cover") or ""),
)
cleaned_data.update(
{"path_cover_s": path_cover_s, "path_cover_l": path_cover_l}
)

if data.get("url_manual", "") != rom.url_manual or not (
await fs_resource_handler.manual_exists(rom)
):
cleaned_data.update({"url_manual": data.get("url_manual", rom.url_manual)})
url_manual = await fs_resource_handler.get_manual(
rom=rom,
overwrite=True,
url_manual=str(data.get("url_manual") or ""),
)
cleaned_data.update({"url_manual": url_manual})

log.debug(
f"Updating {hl(cleaned_data.get('name', ''))} [{id}] with data {cleaned_data}"
)

db_rom_handler.update_rom(id, cleaned_data)
rom = db_rom_handler.get_rom(id)
if not rom:
Expand All @@ -572,6 +606,58 @@ async def update_rom(
return DetailedRomSchema.from_orm_with_request(rom, request)


@protected_route(router.post, "/roms/{id}/manuals", [Scope.ROMS_WRITE])
async def add_rom_manuals(request: Request, id: int):
"""Upload manuals for a rom

Args:
request (Request): Fastapi Request object

Raises:
HTTPException: No files were uploaded
"""
rom = db_rom_handler.get_rom(id)
if not rom:
raise RomNotFoundInDatabaseException(id)

filename = request.headers.get("x-upload-filename")

manuals_path = f"{RESOURCES_BASE_PATH}/{rom.fs_resources_path}/manuals"
log.info(f"Uploading {filename} manuals to {manuals_path}")

# file_location = Path(f"{roms_path}/{filename}")
# parser = StreamingFormDataParser(headers=request.headers)
# parser.register("x-upload-platform", NullTarget())
# parser.register(filename, FileTarget(str(file_location)))

# if await file_location.exists():
# log.warning(f" - Skipping {filename} since the file already exists")
# raise HTTPException(
# status_code=status.HTTP_400_BAD_REQUEST,
# detail=f"File {filename} already exists",
# ) from None

# async def cleanup_partial_file():
# if await file_location.exists():
# await file_location.unlink()

# try:
# async for chunk in request.stream():
# parser.data_received(chunk)
# except ClientDisconnect:
# log.error("Client disconnected during upload")
# await cleanup_partial_file()
# except Exception as exc:
# log.error("Error uploading files", exc_info=exc)
# await cleanup_partial_file()
# raise HTTPException(
# status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
# detail="There was an error uploading the file(s)",
# ) from exc

return Response(status_code=status.HTTP_201_CREATED)


@protected_route(router.post, "/roms/delete", [Scope.ROMS_WRITE])
async def delete_roms(
request: Request,
Expand Down
Loading
Loading