Skip to content

[BUG] 500/405 error when adding Season to tracker: get_tv() bucket mismatch triggers duplicate key IntegrityError on app_tv_unique_item_user #997

Description

@Aresitoo

Bug Description

When attempting to add a season to the tracker (e.g. via the Season detail page / track modal), POST /media_save crashes with HTTP 500:

django.db.utils.IntegrityError: duplicate key value violates unique constraint "app_tv_unique_item_user"
DETAIL: Key (user_id, item_id)=(1, 12457) already exists.

Because the form submission fails with a 500 error, browser navigation falls back to GET /media_save, displaying a blank page with HTTP ERROR 405 (Method Not Allowed).

Root Cause

  1. In app/models/tv.py -> Season.get_tv():
    is_anime_bucket = self.item.library_media_type == MediaTypes.ANIME.value
    
    def _bucket_scoped(queryset):
        if is_anime_bucket:
            return queryset.filter(item__library_media_type=MediaTypes.ANIME.value)
        return queryset.exclude(item__library_media_type=MediaTypes.ANIME.value)
  2. If a show was originally created in the library under library_media_type="anime", but a new season is accessed/created via a route/metadata where library_media_type="tv" (or vice-versa), _bucket_scoped() raises TV.DoesNotExist because the existing TV row has the other bucket type.
  3. In except TV.DoesNotExist:, get_tv() assumes no TV record exists for the user and tries to create a new TV(item=item, user=self.user).
  4. However, Item.objects.get_or_create(...) finds the existing parent show Item, so saving the new TV instance attempts to insert a duplicate (user_id, item_id) pair, violating the UniqueConstraint on TV.

Additional Bug in Item.fetch_releases()

Item.fetch_releases() in app/models/item.py calls events.tasks.reload_calendar.apply_async(...), but events/__init__.py is empty and app/models/item.py only does import events, causing:
AttributeError: module 'events' has no attribute 'tasks'

Proposed Fix

  1. In app/models/tv.py -> Season.get_tv():
    Add a fallback check before creating a new TV instance:
    except TV.DoesNotExist:
        existing_tv = (
            TV.objects.filter(
                item__media_id=self.item.media_id,
                item__media_type=MediaTypes.TV.value,
                item__season_number=None,
                item__source=self.item.source,
                user=self.user,
            )
            .order_by("id")
            .first()
        )
        if existing_tv is not None:
            return existing_tv
  2. In events/__init__.py:
    Add from . import tasks so events.tasks is always available when importing events.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions