Bug Fix: Version update notification persisting after upgrading to the notified version (e.g. "v0.20.2 available" shown while already running v0.20.2). Root cause: check_for_version_update.delay() was called from AppConfig.ready(), which fires inside Celery prefork pool subprocesses before the broker connection is established, causing the dispatch to fail silently with no log output. Fixed by moving the startup dispatch to the worker_ready signal in celery.py (consistent with the existing recover_recordings_on_startup pattern), and deleting the stale version-{current_version} notification at the top of the production check path so it is cleared even when GitHub is unreachable. A WebSocket update is sent immediately on deletion so the frontend badge clears without waiting for the API response.

This commit is contained in:
SergeantPanda 2026-03-08 13:38:18 -05:00
parent 0a658007ac
commit 76271fc9fd
4 changed files with 22 additions and 15 deletions

View file

@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Version update notification persisting after upgrading to the notified version (e.g. "v0.20.2 available" shown while already running v0.20.2). Root cause: `check_for_version_update.delay()` was called from `AppConfig.ready()`, which fires inside Celery prefork pool subprocesses before the broker connection is established, causing the dispatch to fail silently with no log output. Fixed by moving the startup dispatch to the `worker_ready` signal in `celery.py` (consistent with the existing `recover_recordings_on_startup` pattern), and deleting the stale `version-{current_version}` notification at the top of the production check path so it is cleared even when GitHub is unreachable. A WebSocket update is sent immediately on deletion so the frontend badge clears without waiting for the API response.
- VOD orphan cleanup crashing with a `ForeignKeyViolation` (`IntegrityError`) when a concurrent refresh task created a new `M3UMovieRelation` or `M3USeriesRelation` for a movie/series between the orphan-detection query and the `DELETE` SQL. Both `orphaned_movies.delete()` and `orphaned_series.delete()` are now wrapped in `try/except IntegrityError`; affected records are skipped with a warning and will be cleaned up on the next scheduled run.
- XC stream refresh crashing with a `null value in column "name"` database error when a provider returns streams with a null or empty name. Affected streams are now assigned a generated fallback name in the format `<account name> - <stream_id>` so the refresh completes successfully and the stream remains accessible. A warning is logged for each affected stream.
- 504 Gateway Timeout when saving M3U group settings on slower hardware (e.g. Synology NAS). Replaced per-row `update_or_create()` loops with `bulk_create(update_conflicts=True)` wrapped in `transaction.atomic()` for both `ChannelGroupM3UAccount` and `M3UVODCategoryRelation`, reducing hundreds of individual DB round-trips to a single query per model. (Fixes #745) — Thanks [@nickgerrer](https://github.com/nickgerrer)

View file

@ -31,7 +31,6 @@ class CoreConfig(AppConfig):
return
self._sync_developer_notifications()
self._check_version_update()
def _sync_developer_notifications(self):
"""Sync developer notifications from JSON file to database."""
@ -47,14 +46,3 @@ class CoreConfig(AppConfig):
except Exception as e:
logger.warning(f"Failed to sync developer notifications on startup: {e}")
def _check_version_update(self):
"""Check for version updates on startup."""
import logging
logger = logging.getLogger(__name__)
try:
from core.tasks import check_for_version_update
check_for_version_update.delay()
except Exception as e:
logger.warning(f"Failed to check for version updates on startup: {e}")

View file

@ -789,6 +789,7 @@ def check_for_version_update():
try:
is_dev_build = __timestamp__ is not None
DISPATCHARR_HEADERS = {'User-Agent': f'Dispatcharr/{__version__}'}
if is_dev_build:
# Check Docker Hub for newer dev builds
docker_hub_url = "https://hub.docker.com/v2/repositories/dispatcharr/dispatcharr/tags/dev"
@ -878,7 +879,21 @@ def check_for_version_update():
}
)
else:
# Production build - check GitHub for stable releases
# Production build - check GitHub for stable releases.
# Delete any stale notification for the currently running version upfront;
# a "vX is available" notification is meaningless once the user is already on vX.
# Notify the frontend immediately so the badge clears without waiting for the API call.
deleted_count = SystemNotification.objects.filter(
notification_key=f"version-{__version__}",
notification_type='version_update',
).delete()[0]
if deleted_count > 0:
send_websocket_update(
'updates',
'update',
{'success': True, 'type': 'notifications_cleared', 'count': deleted_count}
)
github_api_url = "https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest"
headers = {"Accept": "application/vnd.github.v3+json", **DISPATCHARR_HEADERS}
response = requests.get(

View file

@ -54,7 +54,7 @@ app.conf.update(
def cleanup_task_memory(**kwargs):
"""Clean up memory and database connections after each task completes"""
from django.db import connection
# Get task name from kwargs
task_name = kwargs.get('task').name if kwargs.get('task') else ''
@ -153,6 +153,9 @@ def setup_celery_logging(**kwargs):
@worker_ready.connect
def on_worker_ready(**kwargs):
"""Resume or finalize interrupted DVR recordings after a worker restart."""
"""Tasks to run once the worker is fully connected and ready."""
from apps.channels.tasks import recover_recordings_on_startup
recover_recordings_on_startup.delay()
from core.tasks import check_for_version_update
check_for_version_update.delay()