mai2: fix ghost saving, add memorial photo upload
This commit is contained in:
parent
c6e7100f51
commit
d0e43140ba
@ -1,6 +1,8 @@
|
|||||||
from datetime import datetime, date, timedelta
|
from datetime import datetime
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
import logging
|
import logging
|
||||||
|
from base64 import b64decode
|
||||||
|
from os import path, stat
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
from titles.mai2.const import Mai2Constants
|
from titles.mai2.const import Mai2Constants
|
||||||
@ -773,4 +775,64 @@ class Mai2Base:
|
|||||||
self.logger.debug(data)
|
self.logger.debug(data)
|
||||||
|
|
||||||
def handle_upload_user_photo_api_request(self, data: Dict) -> Dict:
|
def handle_upload_user_photo_api_request(self, data: Dict) -> Dict:
|
||||||
self.logger.debug(data)
|
if not self.game_config.uploads.photos or not self.game_config.uploads.photos_dir:
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
photo = data.get("userPhoto", {})
|
||||||
|
|
||||||
|
if photo is None or not photo:
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
order_id = int(photo.get("orderId", -1))
|
||||||
|
user_id = int(photo.get("userId", -1))
|
||||||
|
div_num = int(photo.get("divNumber", -1))
|
||||||
|
div_len = int(photo.get("divLength", -1))
|
||||||
|
div_data = photo.get("divData", "")
|
||||||
|
playlog_id = int(photo.get("playlogId", -1))
|
||||||
|
track_num = int(photo.get("trackNo", -1))
|
||||||
|
upload_date = photo.get("uploadDate", "")
|
||||||
|
|
||||||
|
if order_id < 0 or user_id <= 0 or div_num < 0 or div_len <= 0 or not div_data or playlog_id < 0 or track_num <= 0 or not upload_date:
|
||||||
|
self.logger.warn(f"Malformed photo upload request")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
if order_id == 0 and div_num > 0:
|
||||||
|
self.logger.warn(f"Failed to set orderId properly (still 0 after first chunk)")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
if div_num == 0 and order_id > 0:
|
||||||
|
self.logger.warn(f"First chuck re-send, Ignore")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
if div_num >= div_len:
|
||||||
|
self.logger.warn(f"Sent extra chunks ({div_num} >= {div_len})")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
if div_len >= 100:
|
||||||
|
self.logger.warn(f"Photo too large ({div_len} * 10240 = {div_len * 10240} bytes)")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
photo_chunk = b64decode(div_data)
|
||||||
|
|
||||||
|
if len(photo_chunk) > 10240 or (len(photo_chunk) < 10240 and div_num + 1 != div_len):
|
||||||
|
self.logger.warn(f"Incorrect data size after decoding (Expected 10240, got {len(photo_chunk)})")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
if not path.exists(f"{self.game_config.uploads.photos_dir}/{user_id}_{playlog_id}_{track_num}.jpeg") and div_num != 0:
|
||||||
|
self.logger.warn(f"Out of order photo upload (div_num {div_num})")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
if path.exists(f"{self.game_config.uploads.photos_dir}/{user_id}_{playlog_id}_{track_num}.jpeg") and div_num == 0:
|
||||||
|
self.logger.warn(f"Duplicate file upload")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
elif path.exists(f"{self.game_config.uploads.photos_dir}/{user_id}_{playlog_id}_{track_num}.jpeg"):
|
||||||
|
fstats = stat(f"{self.game_config.uploads.photos_dir}/{user_id}_{playlog_id}_{track_num}.jpeg")
|
||||||
|
if fstats.st_size != 10240 * div_num:
|
||||||
|
self.logger.warn(f"Out of order photo upload (trying to upload div {div_num}, expected div {fstats.st_size / 10240} for file sized {fstats.st_size} bytes)")
|
||||||
|
return {'returnCode': 0, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
|
||||||
|
with open(f"{self.game_config.uploads.photos_dir}/{user_id}_{playlog_id}_{track_num}.jpeg", "ab") as f:
|
||||||
|
f.write(photo_chunk)
|
||||||
|
|
||||||
|
return {'returnCode': order_id + 1, 'apiName': 'UploadUserPhotoApi'}
|
||||||
|
@ -117,7 +117,7 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
if "userGhost" in upsert:
|
if "userGhost" in upsert:
|
||||||
for ghost in upsert["userGhost"]:
|
for ghost in upsert["userGhost"]:
|
||||||
self.data.profile.put_profile_extend(user_id, self.version, ghost)
|
self.data.profile.put_profile_ghost(user_id, self.version, ghost)
|
||||||
|
|
||||||
if "userOption" in upsert and len(upsert["userOption"]) > 0:
|
if "userOption" in upsert and len(upsert["userOption"]) > 0:
|
||||||
self.data.profile.put_profile_option(
|
self.data.profile.put_profile_option(
|
||||||
@ -217,9 +217,6 @@ class Mai2DX(Mai2Base):
|
|||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
||||||
|
|
||||||
def handle_user_logout_api_request(self, data: Dict) -> Dict:
|
|
||||||
return {"returnCode": 1}
|
|
||||||
|
|
||||||
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
def handle_get_user_data_api_request(self, data: Dict) -> Dict:
|
||||||
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
profile = self.data.profile.get_profile_detail(data["userId"], self.version)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
@ -568,3 +565,10 @@ class Mai2DX(Mai2Base):
|
|||||||
"nextIndex": next_index,
|
"nextIndex": next_index,
|
||||||
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
"userMusicList": [{"userMusicDetailList": music_detail_list}],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def handle_user_login_api_request(self, data: Dict) -> Dict:
|
||||||
|
ret = super().handle_user_login_api_request(data)
|
||||||
|
if ret is None or not ret:
|
||||||
|
return ret
|
||||||
|
ret['loginId'] = ret.get('loginCount', 0)
|
||||||
|
return ret
|
||||||
|
@ -7,7 +7,7 @@ import string
|
|||||||
import logging, coloredlogs
|
import logging, coloredlogs
|
||||||
import zlib
|
import zlib
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
from os import path
|
from os import path, mkdir
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
from core.config import CoreConfig
|
from core.config import CoreConfig
|
||||||
@ -109,6 +109,19 @@ class Mai2Servlet:
|
|||||||
f"{core_cfg.title.hostname}",
|
f"{core_cfg.title.hostname}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
if self.game_cfg.uploads.photos and self.game_cfg.uploads.photos_dir and not path.exists(self.game_cfg.uploads.photos_dir):
|
||||||
|
try:
|
||||||
|
mkdir(self.game_cfg.uploads.photos_dir)
|
||||||
|
except:
|
||||||
|
self.logger.error(f"Failed to make photo upload directory at {self.game_cfg.uploads.photos_dir}")
|
||||||
|
|
||||||
|
if self.game_cfg.uploads.movies and self.game_cfg.uploads.movies_dir and not path.exists(self.game_cfg.uploads.movies_dir):
|
||||||
|
try:
|
||||||
|
mkdir(self.game_cfg.uploads.movies_dir)
|
||||||
|
except:
|
||||||
|
self.logger.error(f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}")
|
||||||
|
|
||||||
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
def render_POST(self, request: Request, version: int, url_path: str) -> bytes:
|
||||||
if url_path.lower() == "ping":
|
if url_path.lower() == "ping":
|
||||||
return zlib.compress(b'{"returnCode": "1"}')
|
return zlib.compress(b'{"returnCode": "1"}')
|
||||||
|
Loading…
Reference in New Issue
Block a user