diff --git a/extensions/saves.py b/extensions/saves.py index 54983b8..76d1ba4 100644 --- a/extensions/saves.py +++ b/extensions/saves.py @@ -1,64 +1,17 @@ from datetime import datetime -from io import BytesIO from urllib.parse import quote_plus -from os import makedirs, path, remove -from typing import Dict, List, Tuple, Union -from uuid import uuid4 -from zipfile import ZipFile, ZIP_DEFLATED +from os import remove +from typing import Dict, List, Union from xmltodict import parse from models.saves import StardewSave from modules.app import app, get_api_key, user_by_key -from modules.utils import configGet, saveFile from modules.database import col_devices, col_saves from fastapi import HTTPException, Depends, UploadFile from fastapi.responses import UJSONResponse, FileResponse, Response from fastapi.openapi.models import APIKey from starlette.status import HTTP_204_NO_CONTENT, HTTP_404_NOT_FOUND, HTTP_406_NOT_ACCEPTABLE -def zip_saves(save_filename: str, save_bytes: bytes, saveinfo_bytes: bytes) -> Tuple[str, str]: - - save_uuid = str(uuid4()) - - makedirs(path.join(configGet("data", "locations"), "files", save_uuid)) - - #saveFile(save_bytes, filename=save_filename, dirname=zipname) - #saveFile(saveinfo_bytes, filename="SaveGameInfo", dirname=zipname) - - with ZipFile(path.join(configGet("data", "locations"), "files", save_uuid, save_filename+".svsave"), 'w', ZIP_DEFLATED, compresslevel=configGet("compression")) as ziph: - ziph.writestr("SaveGameInfo", saveinfo_bytes) - ziph.writestr(save_filename, save_bytes) - - return save_uuid, path.join(configGet("data", "locations"), "files", save_uuid, save_filename+".svsave") - - -def zipfiles(filenames, save_name: str) -> Response: - - zip_filename = save_name+".svsave" - - s = BytesIO() - zf = ZipFile(s, "w") - - for fpath in filenames: - - # Calculate path for file in zip - fdir, fname = path.split(fpath) - - # Add file, at correct path - for entry in (list(col_saves.find({"files.save.uuid": fname})) + list(col_saves.find({"files.saveinfo.uuid": fname}))): - filename = entry["file"]["save"]["name"] if (entry["file"]["save"]["uuid"] == fname) else entry["file"]["saveinfo"]["name"] - zf.write(fpath, filename) - - # Must close zip for all contents to be written - zf.close() - - # Grab ZIP file from in-memory, make response with correct MIME-type - return Response( - s.getvalue(), - media_type="application/x-zip-compressed", - headers={ - 'Content-Disposition': f'attachment;filename={quote_plus(zip_filename)}' - } - ) +from modules.utils import zip_saves @app.get("/saves", response_class=UJSONResponse, response_model=Dict[str, StardewSave], description="Get all available game saves") @@ -149,7 +102,6 @@ async def saves_download(id: int, save_date: int, device: Union[str, None] = Non } ) return response - # return zipfiles([saves_entry["file"]["save"]["path"], saves_entry["file"]["saveinfo"]["path"]], save_name=f'{saves_entry["data"]["farmer"]}_{saves_entry["id"]}') else: raise HTTPException(HTTP_404_NOT_FOUND, detail="Could not find save with such id.") @@ -204,7 +156,8 @@ async def saves_post(device: str, files: List[UploadFile], apikey: APIKey = Depe "save_time": int(save_info["Farmer"]["saveTime"]), "year": int(save_info["Farmer"]["yearForSaveGame"]), "season": int(save_info["Farmer"]["seasonForSaveGame"]), - "day": int(save_info["Farmer"]["dayOfMonthForSaveGame"]) + "day": int(save_info["Farmer"]["dayOfMonthForSaveGame"]), + "game_version": save_info["Farmer"]["gameVersion"] }, "file": { "name": save_data_filename, diff --git a/models/saves.py b/models/saves.py index 5a96e50..82b5bdc 100644 --- a/models/saves.py +++ b/models/saves.py @@ -8,6 +8,7 @@ class StardewSaveData(BaseModel): year: int season: int day: int + game_version: str class StardewSave(BaseModel): id: int diff --git a/modules/utils.py b/modules/utils.py index d0bb836..7db5c44 100644 --- a/modules/utils.py +++ b/modules/utils.py @@ -1,6 +1,7 @@ from os import makedirs, path from typing import Any, Tuple, Union from uuid import uuid4 +from zipfile import ZIP_DEFLATED, ZipFile from ujson import loads, dumps, JSONDecodeError from traceback import print_exc @@ -62,20 +63,24 @@ def configGet(key: str, *args: str) -> Any: this_key = this_key[dict_key] return this_key[key] -def saveFile(filebytes: bytes, filename: Union[str, None] = None, dirname: Union[str, None] = None) -> Tuple[str, str]: - """Save some bytedata into random file and return its ID +def zip_saves(save_filename: str, save_bytes: bytes, saveinfo_bytes: bytes) -> Tuple[str, str]: + """Save files of the SV save into archive and return uuid and path ### Args: - * filebytes (`bytes`): Bytes to write into file + * save_filename (`str`): Filename of the save file + vsave_bytes (`bytes`): Bytes of the save file + * saveinfo_bytes (`bytes`): Bytes of the save info file ### Returns: - * `Tuple[str, str]`: Tuple where first item is an ID and the second is an absolute path to file + * `Tuple[str, str]`: First element is an UUID and the second is a filepath """ - pathlist = [configGet("data", "locations"), "files"] - if dirname is not None: - pathlist.append(dirname) - makedirs(path.join(pathlist), exist_ok=True) - filename = str(uuid4()) if filename is None else filename - with open(path.join(pathlist+[filename], "wb")) as file: - file.write(filebytes) - return filename, path.join(pathlist+[filename]) \ No newline at end of file + + save_uuid = str(uuid4()) + + makedirs(path.join(configGet("data", "locations"), "files", save_uuid)) + + with ZipFile(path.join(configGet("data", "locations"), "files", save_uuid, save_filename+".svsave"), 'w', ZIP_DEFLATED, compresslevel=configGet("compression")) as ziph: + ziph.writestr("SaveGameInfo", saveinfo_bytes) + ziph.writestr(save_filename, save_bytes) + + return save_uuid, path.join(configGet("data", "locations"), "files", save_uuid, save_filename+".svsave") \ No newline at end of file