mirror of
https://github.com/rommapp/romm.git
synced 2026-02-18 00:27:41 +01:00
Direct download for rom files
This commit is contained in:
@@ -17,7 +17,7 @@ from fastapi import Request
|
||||
from handler.auth.constants import Scope
|
||||
from handler.database import db_platform_handler, db_rom_handler
|
||||
from handler.metadata import meta_igdb_handler
|
||||
from handler.metadata.base_hander import SWITCH_TITLEDB_REGEX
|
||||
from handler.metadata.base_hander import SWITCH_PRODUCT_ID_REGEX, SWITCH_TITLEDB_REGEX
|
||||
from models.rom import Rom
|
||||
from starlette.datastructures import URLPath
|
||||
from utils.router import APIRouter
|
||||
@@ -141,10 +141,32 @@ async def tinfoil_index_feed(
|
||||
) -> dict[str, TinfoilFeedTitleDBSchema]:
|
||||
titledb = {}
|
||||
for rom in roms:
|
||||
match = SWITCH_TITLEDB_REGEX.search(rom.fs_name)
|
||||
if match:
|
||||
tdb_match = SWITCH_TITLEDB_REGEX.search(rom.fs_name)
|
||||
pid_match = SWITCH_PRODUCT_ID_REGEX.search(rom.fs_name)
|
||||
if tdb_match:
|
||||
_search_term, index_entry = (
|
||||
await meta_igdb_handler._switch_titledb_format(match, rom.fs_name)
|
||||
await meta_igdb_handler._switch_titledb_format(
|
||||
tdb_match, rom.fs_name
|
||||
)
|
||||
)
|
||||
if index_entry:
|
||||
titledb[str(index_entry["nsuId"])] = TinfoilFeedTitleDBSchema(
|
||||
id=str(index_entry["nsuId"]),
|
||||
name=index_entry["name"],
|
||||
description=index_entry["description"],
|
||||
size=index_entry["size"],
|
||||
version=index_entry["version"] or 0,
|
||||
region=index_entry["region"] or "US",
|
||||
releaseDate=index_entry["releaseDate"] or 19700101,
|
||||
rating=index_entry["rating"] or 0,
|
||||
publisher=index_entry["publisher"] or "",
|
||||
rank=0,
|
||||
)
|
||||
elif pid_match:
|
||||
_search_term, index_entry = (
|
||||
await meta_igdb_handler._switch_productid_format(
|
||||
pid_match, rom.fs_name
|
||||
)
|
||||
)
|
||||
if index_entry:
|
||||
titledb[str(index_entry["nsuId"])] = TinfoilFeedTitleDBSchema(
|
||||
@@ -166,11 +188,17 @@ async def tinfoil_index_feed(
|
||||
files=[
|
||||
TinfoilFeedFileSchema(
|
||||
url=str(
|
||||
request.url_for("get_rom_content", id=rom.id, file_name=rom.fs_name)
|
||||
request.url_for(
|
||||
"get_romfile_content",
|
||||
id=rom_file.id,
|
||||
file_name=rom_file.file_name,
|
||||
)
|
||||
),
|
||||
size=rom.fs_size_bytes,
|
||||
size=rom_file.file_size_bytes,
|
||||
)
|
||||
for rom in roms
|
||||
for rom_file in rom.files
|
||||
if rom_file.file_extension in ["xci", "nsp", "nsz", "xcz", "nro"]
|
||||
],
|
||||
directories=[],
|
||||
success="RomM Switch Library",
|
||||
|
||||
@@ -21,6 +21,7 @@ from decorators.auth import protected_route
|
||||
from endpoints.responses import MessageResponse
|
||||
from endpoints.responses.rom import (
|
||||
DetailedRomSchema,
|
||||
RomFileSchema,
|
||||
RomSchema,
|
||||
RomUserSchema,
|
||||
SimpleRomSchema,
|
||||
@@ -288,9 +289,11 @@ async def get_rom_content(
|
||||
file_name: Zip file output name
|
||||
|
||||
Returns:
|
||||
FileResponse: Returns one file for single file roms
|
||||
Response: Returns a response with headers
|
||||
|
||||
Yields:
|
||||
FileResponse: Returns one file for single file roms
|
||||
FileRedirectResponse: Redirects to the file download path
|
||||
ZipResponse: Returns a response for nginx to serve a Zip file for multi-part roms
|
||||
"""
|
||||
|
||||
@@ -785,3 +788,75 @@ async def update_rom_user(request: Request, id: int) -> RomUserSchema:
|
||||
rom_user = db_rom_handler.update_rom_user(db_rom_user.id, cleaned_data)
|
||||
|
||||
return RomUserSchema.model_validate(rom_user)
|
||||
|
||||
|
||||
@protected_route(
|
||||
router.get,
|
||||
"files/{id}",
|
||||
[] if DISABLE_DOWNLOAD_ENDPOINT_AUTH else [Scope.ROMS_READ],
|
||||
)
|
||||
async def get_romfile(
|
||||
request: Request,
|
||||
id: int,
|
||||
) -> RomFileSchema:
|
||||
file = db_rom_handler.get_rom_file_by_id(id)
|
||||
if not file:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="File not found",
|
||||
)
|
||||
|
||||
return RomFileSchema.model_validate(file)
|
||||
|
||||
|
||||
@protected_route(
|
||||
router.get,
|
||||
"files/{id}/content/{file_name}",
|
||||
[] if DISABLE_DOWNLOAD_ENDPOINT_AUTH else [Scope.ROMS_READ],
|
||||
)
|
||||
async def get_romfile_content(
|
||||
request: Request,
|
||||
id: int,
|
||||
file_name: str,
|
||||
):
|
||||
"""Download rom file endpoint
|
||||
|
||||
Args:
|
||||
request (Request): Fastapi Request object
|
||||
id (int): Rom internal id
|
||||
file_id (int): File internal id
|
||||
|
||||
Returns:
|
||||
FileResponse: Returns the response with headers
|
||||
"""
|
||||
|
||||
current_username = (
|
||||
request.user.username if request.user.is_authenticated else "unknown"
|
||||
)
|
||||
|
||||
file = db_rom_handler.get_rom_file_by_id(id)
|
||||
if not file:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="File not found",
|
||||
)
|
||||
|
||||
log.info(f"User {current_username} is downloading {file_name}")
|
||||
|
||||
# Serve the file directly in development mode for emulatorjs
|
||||
if DEV_MODE:
|
||||
rom_path = f"{LIBRARY_BASE_PATH}/{file.full_path}"
|
||||
return FileResponse(
|
||||
path=rom_path,
|
||||
filename=file_name,
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{quote(file_name)}"',
|
||||
"Content-Type": "application/octet-stream",
|
||||
"Content-Length": str(file.file_size_bytes),
|
||||
},
|
||||
)
|
||||
|
||||
# Otherwise proxy through nginx
|
||||
return FileRedirectResponse(
|
||||
download_path=Path(f"/library/{file.full_path}"),
|
||||
)
|
||||
|
||||
@@ -62,6 +62,24 @@ class RomFile(BaseModel):
|
||||
def full_path(self) -> str:
|
||||
return f"{self.file_path}/{self.file_name}"
|
||||
|
||||
@cached_property
|
||||
def file_name_no_tags(self) -> str:
|
||||
from handler.filesystem import fs_rom_handler
|
||||
|
||||
return fs_rom_handler.get_file_name_with_no_tags(self.file_name)
|
||||
|
||||
@cached_property
|
||||
def file_name_no_ext(self) -> str:
|
||||
from handler.filesystem import fs_rom_handler
|
||||
|
||||
return fs_rom_handler.get_file_name_with_no_extension(self.file_name)
|
||||
|
||||
@cached_property
|
||||
def file_extension(self) -> str:
|
||||
from handler.filesystem import fs_rom_handler
|
||||
|
||||
return fs_rom_handler.parse_file_extension(self.file_name)
|
||||
|
||||
def file_name_for_download(self, rom: Rom, hidden_folder: bool = False) -> str:
|
||||
# This needs a trailing slash in the path to work!
|
||||
return self.full_path.replace(
|
||||
|
||||
Reference in New Issue
Block a user