add ota update channels

This commit is contained in:
2025-09-27 16:17:44 -04:00
parent 10d38e14ae
commit 2d84865155
5 changed files with 97 additions and 25 deletions

View File

@ -435,7 +435,7 @@ class AllnetServlet:
else: else:
machine = await self.data.arcade.get_machine(req.serial) machine = await self.data.arcade.get_machine(req.serial)
if not machine or not machine['ota_enable'] or not machine['is_cab']: if not machine or not machine['ota_channel'] or not machine['is_cab']:
resp = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n" resp = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n"
if is_dfi: if is_dfi:
return PlainTextResponse( return PlainTextResponse(
@ -445,16 +445,14 @@ class AllnetServlet:
iv = bytes([random.randint(2, 255) for _ in range(16)]) iv = bytes([random.randint(2, 255) for _ in range(16)])
return PlainTextResponse(content=self.enc_lite(litekey, iv, resp)) return PlainTextResponse(content=self.enc_lite(litekey, iv, resp))
return PlainTextResponse(resp) return PlainTextResponse(resp)
update = await self.data.arcade.get_ota_update(req.game_id, req.ver, machine['ota_channel'])
if update:
if update['app_ini'] and path.exists(f"{self.config.allnet.update_cfg_folder}/{update['app_ini']}"):
resp.uri = f"http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{update['app_ini']}"
if path.exists( if update['opt_ini'] and path.exists(f"{self.config.allnet.update_cfg_folder}/{update['opt_ini']}"):
f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-app.ini" resp.uri += f"|http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{update['opt_ini']}"
):
resp.uri = f"http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-app.ini"
if path.exists(
f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-opt.ini"
):
resp.uri += f"|http://{self.config.server.hostname}:{self.config.server.port}/dl/ini/{req.game_id}-{req.ver.replace('.', '')}-opt.ini"
if resp.uri: if resp.uri:
self.logger.info(f"Sending download uri {resp.uri}") self.logger.info(f"Sending download uri {resp.uri}")
@ -496,7 +494,7 @@ class AllnetServlet:
f"{self.config.allnet.update_cfg_folder}/{req_file}", "r", encoding="utf-8" f"{self.config.allnet.update_cfg_folder}/{req_file}", "r", encoding="utf-8"
).read()) ).read())
self.logger.info(f"DL INI File {req_file} not found") self.logger.warning(f"DL INI File {req_file} not found")
return PlainTextResponse() return PlainTextResponse()
async def handle_dlorder_report(self, request: Request) -> bytes: async def handle_dlorder_report(self, request: Request) -> bytes:

View File

@ -0,0 +1,42 @@
"""update_channels
Revision ID: 7070a6fa8cdc
Revises: f6007bbf057d
Create Date: 2025-09-27 16:09:55.853051
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '7070a6fa8cdc'
down_revision = 'f6007bbf057d'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('machine_update',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('game', sa.CHAR(length=4), nullable=False),
sa.Column('version', sa.VARCHAR(length=15), nullable=False),
sa.Column('channel', sa.VARCHAR(length=260), nullable=False),
sa.Column('app_ini', sa.VARCHAR(length=260), nullable=True),
sa.Column('opt_ini', sa.VARCHAR(length=260), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('game', 'version', 'channel', name='machine_update_uk'),
mysql_charset='utf8mb4'
)
op.add_column('machine', sa.Column('ota_channel', sa.VARCHAR(length=260), nullable=True))
op.drop_column('machine', 'ota_enable')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('machine', sa.Column('ota_enable', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True))
op.drop_column('machine', 'ota_channel')
op.drop_table('machine_update')
# ### end Alembic commands ###

View File

@ -7,7 +7,7 @@ from sqlalchemy.dialects.mysql import insert
from sqlalchemy.engine import Row from sqlalchemy.engine import Row
from sqlalchemy.sql import func, select from sqlalchemy.sql import func, select
from sqlalchemy.sql.schema import ForeignKey, PrimaryKeyConstraint from sqlalchemy.sql.schema import ForeignKey, PrimaryKeyConstraint
from sqlalchemy.types import JSON, Boolean, Integer, String, BIGINT, INTEGER, CHAR, FLOAT from sqlalchemy.types import JSON, Boolean, Integer, String, BIGINT, INTEGER, CHAR, FLOAT, VARCHAR
from core.data.schema.base import BaseData, metadata from core.data.schema.base import BaseData, metadata
@ -41,13 +41,26 @@ machine: Table = Table(
Column("game", String(4)), Column("game", String(4)),
Column("country", String(3)), # overwrites if not null Column("country", String(3)), # overwrites if not null
Column("timezone", String(255)), Column("timezone", String(255)),
Column("ota_enable", Boolean),
Column("memo", String(255)), Column("memo", String(255)),
Column("is_cab", Boolean), Column("is_cab", Boolean),
Column("ota_channel", VARCHAR(260)),
Column("data", JSON), Column("data", JSON),
mysql_charset="utf8mb4", mysql_charset="utf8mb4",
) )
update: Table = Table(
"machine_update",
metadata,
Column("id", Integer, primary_key=True, nullable=False),
Column("game", CHAR(4), nullable=False),
Column("version", VARCHAR(15), nullable=False),
Column("channel", VARCHAR(260), nullable=False),
Column("app_ini", VARCHAR(260)),
Column("opt_ini", VARCHAR(260)),
UniqueConstraint("game", "version", "channel", name="machine_update_uk"),
mysql_charset="utf8mb4",
)
arcade_owner: Table = Table( arcade_owner: Table = Table(
"arcade_owner", "arcade_owner",
metadata, metadata,
@ -250,12 +263,12 @@ class ArcadeData(BaseData):
return False return False
return True return True
async def set_machine_can_ota(self, machine_id: int, can_ota: bool = False) -> bool: async def set_machine_ota_channel(self, machine_id: int, channel_name: Optional[str] = None) -> bool:
sql = machine.update(machine.c.id == machine_id).values(ota_enable = can_ota) sql = machine.update(machine.c.id == machine_id).values(ota_channel = channel_name)
result = await self.execute(sql) result = await self.execute(sql)
if result is None: if result is None:
self.logger.error(f"Failed to update machine {machine_id} ota_enable to {can_ota}") self.logger.error(f"Failed to update machine {machine_id} ota channel to {channel_name}")
return False return False
return True return True
@ -530,6 +543,29 @@ class ArcadeData(BaseData):
if result is not None: if result is not None:
return result.fetchone() return result.fetchone()
async def create_ota_update(self, game_id: str, ver: str, channel: str, app: Optional[str], opt: Optional[str] = None) -> Optional[int]:
result = await self.execute(insert(update).values(
game = game_id,
version = ver,
channel = channel,
app_ini = app,
opt_ini = opt
))
if result is None:
self.logger.error(f"Failed to create {game_id} v{ver} update on channel {channel}")
return result.lastrowid
async def get_ota_update(self, game_id: str, ver: str, channel: str) -> Optional[Row]:
result = await self.execute(update.select(and_(
and_(update.c.game == game_id, update.c.version == ver),
update.c.channel == channel
)))
if result is None:
return None
return result.fetchone()
def format_serial( def format_serial(
self, platform_code: str, platform_rev: int, serial_letter: str, serial_num: int, append: int, dash: bool = False self, platform_code: str, platform_rev: int, serial_letter: str, serial_num: int, append: int, dash: bool = False
) -> str: ) -> str:

View File

@ -1146,7 +1146,7 @@ class FE_Machine(FE_Base):
new_country = frm.get('country', None) new_country = frm.get('country', None)
new_tz = frm.get('tz', None) new_tz = frm.get('tz', None)
new_is_cab = frm.get('is_cab', False) == 'on' new_is_cab = frm.get('is_cab', False) == 'on'
new_is_ota = frm.get('is_ota', False) == 'on' new_ota_channel = frm.get('ota_channel', None)
new_memo = frm.get('memo', None) new_memo = frm.get('memo', None)
try: try:
@ -1158,7 +1158,7 @@ class FE_Machine(FE_Base):
did_country = await self.data.arcade.set_machine_country(cab['id'], new_country if new_country else None) did_country = await self.data.arcade.set_machine_country(cab['id'], new_country if new_country else None)
did_timezone = await self.data.arcade.set_machine_timezone(cab['id'], new_tz if new_tz else None) did_timezone = await self.data.arcade.set_machine_timezone(cab['id'], new_tz if new_tz else None)
did_real_cab = await self.data.arcade.set_machine_real_cabinet(cab['id'], new_is_cab) did_real_cab = await self.data.arcade.set_machine_real_cabinet(cab['id'], new_is_cab)
did_ota = await self.data.arcade.set_machine_can_ota(cab['id'], new_is_ota) did_ota = await self.data.arcade.set_machine_ota_channel(cab['id'], new_ota_channel if new_is_cab else None)
did_memo = await self.data.arcade.set_machine_memo(cab['id'], new_memo if new_memo else None) did_memo = await self.data.arcade.set_machine_memo(cab['id'], new_memo if new_memo else None)
if not did_game or not did_country or not did_timezone or not did_real_cab or not did_ota or not did_memo: if not did_game or not did_country or not did_timezone or not did_real_cab or not did_ota or not did_memo:

View File

@ -3,13 +3,9 @@
<script type="text/javascript"> <script type="text/javascript">
function swap_ota() { function swap_ota() {
let is_cab = document.getElementById("is_cab").checked; let is_cab = document.getElementById("is_cab").checked;
let cbx_ota = document.getElementById("is_ota"); let txt_ota = document.getElementById("ota_channel");
cbx_ota.disabled = !is_cab; cbx_ota.disabled = !is_cab;
if (cbx_ota.disabled) {
cbx_ota.checked = false;
}
} }
</script> </script>
<h1>Machine: {{machine.serial}}</h1> <h1>Machine: {{machine.serial}}</h1>
@ -64,8 +60,8 @@ Info
<label for="is_cab" class="form-label">Real Cabinet</label> <label for="is_cab" class="form-label">Real Cabinet</label>
</div> </div>
<div class="col mb-3"> <div class="col mb-3">
<input type="checkbox" class="form-control-check" id="is_ota" name="is_ota" {{ 'checked' if machine.ota_enable else ''}}> <input type="text" class="form-control-check" id="ota_channel" name="ota_channel" value={{ machine.ota_channel }} {{ 'disabled' if not machine.is_cab else '' }}>
<label for="is_ota" class="form-label">Allow OTA updates</label> <label for="ota_channel" class="form-label">OTA Update Channel</label>
</div> </div>
<div class="col mb-3"> <div class="col mb-3">
</div> </div>