mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 06:46:00 +00:00
feat(saves): add slot-based save sync with content hash deduplication
- Add device registration and save synchronization - Implement slot-based save organization with datetime tagging - Add conflict detection for multi-device sync scenarios - Add content hash computation for save deduplication - Support ZIP inner-file hashing for consistent deduplication - Add confirm_download endpoint for sync state management - Add overwrite parameter to bypass conflict checks
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from collections.abc import Sequence
|
||||
|
||||
from sqlalchemy import and_, delete, select, update
|
||||
from sqlalchemy import and_, delete, desc, select, update
|
||||
from sqlalchemy.orm import QueryableAttribute, Session, load_only
|
||||
|
||||
from decorators.database import begin_session
|
||||
@@ -42,12 +42,28 @@ class DBSavesHandler(DBBaseHandler):
|
||||
.limit(1)
|
||||
).first()
|
||||
|
||||
@begin_session
|
||||
def get_save_by_content_hash(
|
||||
self,
|
||||
user_id: int,
|
||||
rom_id: int,
|
||||
content_hash: str,
|
||||
session: Session = None, # type: ignore
|
||||
) -> Save | None:
|
||||
return session.scalar(
|
||||
select(Save)
|
||||
.filter_by(rom_id=rom_id, user_id=user_id, content_hash=content_hash)
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
@begin_session
|
||||
def get_saves(
|
||||
self,
|
||||
user_id: int,
|
||||
rom_id: int | None = None,
|
||||
platform_id: int | None = None,
|
||||
slot: str | None = None,
|
||||
order_by_updated_at_desc: bool = False,
|
||||
only_fields: Sequence[QueryableAttribute] | None = None,
|
||||
session: Session = None, # type: ignore
|
||||
) -> Sequence[Save]:
|
||||
@@ -61,6 +77,12 @@ class DBSavesHandler(DBBaseHandler):
|
||||
Rom.platform_id == platform_id
|
||||
)
|
||||
|
||||
if slot is not None:
|
||||
query = query.filter(Save.slot == slot)
|
||||
|
||||
if order_by_updated_at_desc:
|
||||
query = query.order_by(desc(Save.updated_at))
|
||||
|
||||
if only_fields:
|
||||
query = query.options(load_only(*only_fields))
|
||||
|
||||
@@ -125,3 +147,28 @@ class DBSavesHandler(DBBaseHandler):
|
||||
)
|
||||
|
||||
return missing_saves
|
||||
|
||||
@begin_session
|
||||
def get_saves_summary(
|
||||
self,
|
||||
user_id: int,
|
||||
rom_id: int,
|
||||
session: Session = None, # type: ignore
|
||||
) -> dict:
|
||||
saves = session.scalars(
|
||||
select(Save)
|
||||
.filter_by(user_id=user_id, rom_id=rom_id)
|
||||
.order_by(desc(Save.updated_at))
|
||||
).all()
|
||||
|
||||
slots_data: dict[str | None, dict] = {}
|
||||
for save in saves:
|
||||
slot_key = save.slot
|
||||
if slot_key not in slots_data:
|
||||
slots_data[slot_key] = {"slot": slot_key, "count": 0, "latest": save}
|
||||
slots_data[slot_key]["count"] += 1
|
||||
|
||||
return {
|
||||
"total_count": len(saves),
|
||||
"slots": list(slots_data.values()),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user