add shared watch state, continue watching and new main menu

- watch_state table: resume point, watched, play count, last played per file,
  shared by all clients (Kodi plugin, Android TV app)
- POST/GET /api/state/:fileId for players' progress reports (watched at 90% or
  on end; late resends carry `at` and lose to newer state)
- listings carry watch state (files: playcount, resume, lastplayed; shows and
  seasons: watched/total episodes), episodes sorted by season/episode
- all/continue: unfinished files and next episodes, latest activity first
- context menus: mark (un)watched for a file, season or show; hide from
  continue watching
- main menu: Continue watching, Movies, Shows, scans (Torr/LRT unlisted)
- DATA_ROOT env overrides /srv/data for local runs outside docker

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jurgis SakalauskasandClaude Opus 5.5 committed 2026-10-12 10:01:39 +03:00
1 parent 02522bbd04
commit be7d0bc535
16 files changed
+819 -35

No files matched your search

@@ -0,0 +1,29 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateWatchStateTable1791786266498 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`create table watch_state
(
file_id INTEGER primary key,
position REAL default 0 not null,
duration REAL,
watched TINYINT default 0 not null,
play_count INTEGER default 0 not null,
last_played INTEGER,
updated_at INTEGER not null,
client VARCHAR(32),
hidden TINYINT default 0 not null
);`,
);
await queryRunner.query(
`create index watch_state_last_played
on watch_state (last_played);`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE watch_state`);
}
}