mirror of
https://github.com/rommapp/romm.git
synced 2026-06-27 22:35:57 +00:00
Merge pull request #3602 from rommapp/fix/webp-convert-on-startup
fix(webp): backfill cover conversion on startup when enabled
This commit is contained in:
@@ -49,6 +49,7 @@ from utils.context import initialize_context
|
||||
tracer = trace.get_tracer(__name__)
|
||||
|
||||
RECOMPUTE_SAVE_HASHES_JOB_ID = "recompute_save_content_hashes_bootstrap"
|
||||
CONVERT_IMAGES_TO_WEBP_JOB_ID = "convert_images_to_webp_bootstrap"
|
||||
|
||||
|
||||
def _enqueue_recompute_save_hashes_if_needed() -> None:
|
||||
@@ -98,6 +99,38 @@ def _enqueue_recompute_save_hashes_if_needed() -> None:
|
||||
)
|
||||
|
||||
|
||||
def _enqueue_convert_images_to_webp() -> None:
|
||||
"""Backfill .webp covers when WebP conversion is enabled.
|
||||
|
||||
The frontend rewrites cover URLs to .webp as soon as the feature flag is
|
||||
on, but the scheduled task only runs at its next cron time and the inline
|
||||
conversion in the resources handler only fires for covers fetched after
|
||||
enabling. Without a backfill, existing covers have no .webp sibling and
|
||||
every request 404s until the cron eventually runs."""
|
||||
try:
|
||||
if Job.exists(CONVERT_IMAGES_TO_WEBP_JOB_ID, low_prio_queue.connection):
|
||||
log.info(
|
||||
"convert_images_to_webp already queued or running from a "
|
||||
"previous restart; skipping enqueue"
|
||||
)
|
||||
return
|
||||
|
||||
low_prio_queue.enqueue(
|
||||
convert_images_to_webp_task.run,
|
||||
job_id=CONVERT_IMAGES_TO_WEBP_JOB_ID,
|
||||
job_timeout=TASK_TIMEOUT,
|
||||
meta={
|
||||
"task_name": convert_images_to_webp_task.title,
|
||||
"task_type": convert_images_to_webp_task.task_type.value,
|
||||
},
|
||||
)
|
||||
log.info("Enqueued convert_images_to_webp backfill on low-priority worker")
|
||||
except Exception:
|
||||
log.exception(
|
||||
"Failed to enqueue convert_images_to_webp; admins can run it manually"
|
||||
)
|
||||
|
||||
|
||||
@tracer.start_as_current_span("main")
|
||||
async def main() -> None:
|
||||
"""Run startup tasks."""
|
||||
@@ -121,6 +154,7 @@ async def main() -> None:
|
||||
if ENABLE_SCHEDULED_CONVERT_IMAGES_TO_WEBP:
|
||||
log.info("Starting scheduled convert images to webp")
|
||||
convert_images_to_webp_task.init()
|
||||
_enqueue_convert_images_to_webp()
|
||||
if ENABLE_SCHEDULED_RETROACHIEVEMENTS_PROGRESS_SYNC:
|
||||
log.info("Starting scheduled RetroAchievements progress sync")
|
||||
sync_retroachievements_progress_task.init()
|
||||
|
||||
@@ -88,3 +88,47 @@ def test_enqueue_recompute_swallows_enqueue_error(mocker):
|
||||
)
|
||||
|
||||
startup._enqueue_recompute_save_hashes_if_needed()
|
||||
|
||||
|
||||
def test_enqueue_convert_webp_fires_when_not_queued(mocker):
|
||||
"""No in-flight bootstrap job -> enqueue the backfill exactly once."""
|
||||
mocker.patch.object(startup.Job, "exists", return_value=False)
|
||||
enqueue = mocker.patch.object(startup.low_prio_queue, "enqueue")
|
||||
|
||||
startup._enqueue_convert_images_to_webp()
|
||||
|
||||
enqueue.assert_called_once()
|
||||
args, kwargs = enqueue.call_args
|
||||
assert args[0].__self__ is startup.convert_images_to_webp_task
|
||||
assert kwargs["meta"]["task_name"] == startup.convert_images_to_webp_task.title
|
||||
assert kwargs["meta"]["task_type"] == (
|
||||
startup.convert_images_to_webp_task.task_type.value
|
||||
)
|
||||
assert kwargs["job_timeout"] == startup.TASK_TIMEOUT
|
||||
assert kwargs["job_id"] == startup.CONVERT_IMAGES_TO_WEBP_JOB_ID
|
||||
|
||||
|
||||
def test_convert_webp_job_id_is_valid_rq_id():
|
||||
"""An invalid job_id raises in set_id, which the broad except swallows ->
|
||||
backfill silently never enqueues. Assert the id matches RQ's contract."""
|
||||
assert JOB_ID_PATTERN.fullmatch(startup.CONVERT_IMAGES_TO_WEBP_JOB_ID)
|
||||
|
||||
|
||||
def test_enqueue_convert_webp_skips_when_already_queued(mocker):
|
||||
"""An in-flight job from a previous restart -> skip enqueue, don't double up."""
|
||||
mocker.patch.object(startup.Job, "exists", return_value=True)
|
||||
enqueue = mocker.patch.object(startup.low_prio_queue, "enqueue")
|
||||
|
||||
startup._enqueue_convert_images_to_webp()
|
||||
|
||||
enqueue.assert_not_called()
|
||||
|
||||
|
||||
def test_enqueue_convert_webp_swallows_enqueue_error(mocker):
|
||||
"""A failed enqueue must not crash startup."""
|
||||
mocker.patch.object(startup.Job, "exists", return_value=False)
|
||||
mocker.patch.object(
|
||||
startup.low_prio_queue, "enqueue", side_effect=RuntimeError("redis gone")
|
||||
)
|
||||
|
||||
startup._enqueue_convert_images_to_webp()
|
||||
|
||||
Reference in New Issue
Block a user