Bug Fix: 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.

This commit is contained in:
SergeantPanda 2026-03-08 11:56:52 -05:00
parent 753bc1a893
commit dfb91db987
2 changed files with 19 additions and 2 deletions

View file

@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- 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)
- Improved frontend table stability during M3U imports: Fixed incorrect default `state` initialization (`[]``{}`) in `CustomTable` to match TanStack Table v8's expected state object shape. Added `autoResetPageIndex: false` and `autoResetExpanded: false` to prevent TanStack Table from issuing internal state resets on data updates. Memoized `processedData` in `M3UsTable` to avoid redundant sort/filter recomputation on re-renders. - Thanks [@marcinolek](https://github.com/marcinolek)

View file

@ -1645,14 +1645,30 @@ def cleanup_orphaned_vod_content(stale_days=0, scan_start_time=None, account_id=
orphaned_movie_count = orphaned_movies.count()
if orphaned_movie_count > 0:
logger.info(f"Deleting {orphaned_movie_count} orphaned movies with no M3U relations")
orphaned_movies.delete()
try:
orphaned_movies.delete()
except IntegrityError:
# A concurrent refresh task created a new relation for one of these movies
# between our query and the DELETE. Skip and let the next cleanup run handle it.
logger.warning(
"Skipped some orphaned movie deletions due to concurrent modifications; "
"they will be retried on the next cleanup run."
)
orphaned_movie_count = 0
# Clean up series with no relations (orphaned)
orphaned_series = Series.objects.filter(m3u_relations__isnull=True)
orphaned_series_count = orphaned_series.count()
if orphaned_series_count > 0:
logger.info(f"Deleting {orphaned_series_count} orphaned series with no M3U relations")
orphaned_series.delete()
try:
orphaned_series.delete()
except IntegrityError:
logger.warning(
"Skipped some orphaned series deletions due to concurrent modifications; "
"they will be retried on the next cleanup run."
)
orphaned_series_count = 0
result = (f"Cleaned up {stale_movie_count} stale movie relations, "
f"{stale_series_count} stale series relations, "