goon/alembic/versions/20260528_0018_movie_play_progress.py
jtrzupek 6eb7cdd320 feat(movies): watched/continue-watching tracking end-to-end
Bug-report b207ff17 2026-05-26 ("przydaloby sie oznaczenie filmow juz
obejrzanych" - sceny mialy watched badge + dim, filmom brakowalo).

Backend:
- alembic 0018_movie_play_progress: nowa tabela (mirror scene_play_progress)
- MoviePlayProgress SQLAlchemy model
- MovieOut schema dolane finished/position_sec/last_played_at
- POST+DELETE /movies/{id}/progress endpointy (upsert via pg ON CONFLICT)
- _movie_to_out wstrzykuje progress z DB

Mobile:
- RouteParams.entityKind: 'scene'|'movie' (default scene dla back-compat)
- PlayerScreen NativeVideoPlayer + EmbedWebViewPlayer dispatchuja
  upsertProgress vs upsertMovieProgress po entityKind
- MovieDetailScreen przekazuje entityKind='movie' do nav
- MoviePosterCard renderuje dim + check badge + progress bar
  (parity ze ScenesScreen pattern)

Wczesniej MovieDetail przekazywal movieId jako sceneId -> backend
/scenes/<movieId>/progress zwracal 404 (silently caught). Po dodaniu
dedykowanego movie endpoint proper routing dziala.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 23:24:06 +02:00

47 lines
1.6 KiB
Python

"""movie_play_progress — pozycja odtwarzania per film (continue watching).
Revision ID: 0018_movie_play_progress
Revises: 0017_drop_realdebrid_cache
Create Date: 2026-05-28
Mirror `scene_play_progress`: pojedyncza tabela, PK=movie_id (single-user app).
position_sec + finished + last_played_at. duration_sec mirror z filmu (movies.duration_sec
może być None gdy connector go nie wyciągnął — pozwala na progress_pct mimo to).
User-report 2026-05-26 (b207ff17): "Tutaj też przydałoby się oznaczenie filmów już
obejrzanych" — sceny mają watched badge + dim, filmów brakowało.
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
revision: str = "0018_movie_play_progress"
down_revision: str | None = "0017_drop_realdebrid_cache"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.create_table(
"movie_play_progress",
sa.Column(
"movie_id",
sa.dialects.postgresql.UUID(as_uuid=True),
sa.ForeignKey("movies.id", ondelete="CASCADE"),
primary_key=True,
),
sa.Column("position_sec", sa.Integer(), nullable=False, server_default="0"),
sa.Column("duration_sec", sa.Integer(), nullable=True),
sa.Column("finished", sa.Boolean(), nullable=False, server_default="false"),
sa.Column(
"last_played_at",
sa.DateTime(timezone=True),
server_default=sa.func.now(),
nullable=False,
),
)
def downgrade() -> None:
op.drop_table("movie_play_progress")