mirror of
https://github.com/sakaljurgis/kodi-api-server.git
synced 2026-11-06 18:58:01 +00:00
- 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>
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
import { BadRequestException } from '@nestjs/common';
|
|
|
|
export type PlaybackEvent = 'start' | 'tick' | 'pause' | 'stop' | 'end';
|
|
|
|
const EVENTS: PlaybackEvent[] = ['start', 'tick', 'pause', 'stop', 'end'];
|
|
|
|
/** a player's progress report, positions in seconds */
|
|
export class PlaybackReport {
|
|
constructor(
|
|
readonly position: number,
|
|
readonly duration: number | null,
|
|
readonly event: PlaybackEvent,
|
|
readonly client: string | null,
|
|
/**
|
|
* when it happened (ms since epoch), only for reports sent late (offline,
|
|
* imported history); live reports leave it out and get the server's time
|
|
*/
|
|
readonly at: number | null,
|
|
) {}
|
|
|
|
static fromBody(body: Record<string, unknown>): PlaybackReport {
|
|
const position = Number(body?.position);
|
|
if (!Number.isFinite(position) || position < 0) {
|
|
throw new BadRequestException('position (seconds) is required');
|
|
}
|
|
const duration = Number(body.duration);
|
|
const at = Number(body.at);
|
|
|
|
return new PlaybackReport(
|
|
position,
|
|
Number.isFinite(duration) && duration > 0 ? duration : null,
|
|
EVENTS.find((event) => event === body.event) ?? 'tick',
|
|
typeof body.client === 'string' ? body.client.slice(0, 32) : null,
|
|
Number.isFinite(at) && at > 0 ? at : null,
|
|
);
|
|
}
|
|
}
|