domain: player/reporting ticks -> milliseconds (phase 4c)

Playback position now crosses the IPC boundary in milliseconds. Ticks
survive only inside Rust (DB storage, Jellyfin API) and at the genuine
remote-session boundary (session seek / transfer / RemoteControls).

Rust command signatures (ms in, converted to ticks internally):
- storage_update_playback_progress / _context: position_ms
- repository_report_playback_start / _progress / _stopped: position_ms
- PlaybackProgress.position_ticks -> position_ms (converted in the query)

Frontend:
- playbackReporting, playerEvents, VideoPlayer, Queue, player/[id] resume:
  seconds*1000 / durationMs/1000 instead of tick math.
- repository-client + syncService param names -> positionMs.
- Tests updated to ms fixtures/assertions.

Out of scope (legitimately ticks): NowPlayingItem, PlayState.positionTicks,
sessionSeek, playbackModeTransferToLocal, RemoteControls, SessionCard — the
remote Jellyfin session API.

Rust 456, frontend 644, check clean.
This commit is contained in:
2026-07-23 22:02:29 +02:00
parent 93d198ce21
commit ec8a7610f5
14 changed files with 88 additions and 71 deletions
+9 -9
View File
@@ -26,7 +26,7 @@ export async function reportPlaybackStart(
contextType: "container" | "single" = "single",
contextId: string | null = null
): Promise<void> {
const positionTicks = Math.floor(positionSeconds * 10000000);
const positionMs = Math.floor(positionSeconds * 1000);
const userId = auth.getUserId();
console.log(
@@ -42,7 +42,7 @@ export async function reportPlaybackStart(
// Update local DB with context (always works, even offline)
if (userId) {
try {
await commands.storageUpdatePlaybackContext(userId, itemId, positionTicks, contextType, contextId);
await commands.storageUpdatePlaybackContext(userId, itemId, positionMs, contextType, contextId);
} catch (e) {
console.error("[PlaybackReporting] Failed to update playback context:", e);
}
@@ -62,7 +62,7 @@ export async function reportPlaybackProgress(
positionSeconds: number,
_isPaused = false
): Promise<void> {
const positionTicks = Math.floor(positionSeconds * 10000000);
const positionMs = Math.floor(positionSeconds * 1000);
const userId = auth.getUserId();
// Reduce logging for frequent progress updates
@@ -73,7 +73,7 @@ export async function reportPlaybackProgress(
// Update local DB only (progress updates are frequent, don't report to server)
if (userId) {
try {
await commands.storageUpdatePlaybackProgress(userId, itemId, positionTicks);
await commands.storageUpdatePlaybackProgress(userId, itemId, positionMs);
} catch (e) {
console.error("[PlaybackReporting] Failed to update local progress:", e);
}
@@ -89,7 +89,7 @@ export async function reportPlaybackProgress(
* TRACES: UR-005, UR-025 | DR-028
*/
export async function reportPlaybackStopped(itemId: string, positionSeconds: number): Promise<void> {
const positionTicks = Math.floor(positionSeconds * 10000000);
const positionMs = Math.floor(positionSeconds * 1000);
const userId = auth.getUserId();
console.log("[PlaybackReporting] reportPlaybackStopped - itemId:", itemId, "positionSeconds:", positionSeconds);
@@ -97,7 +97,7 @@ export async function reportPlaybackStopped(itemId: string, positionSeconds: num
// Update local DB first (always works, even offline)
if (userId) {
try {
await commands.storageUpdatePlaybackProgress(userId, itemId, positionTicks);
await commands.storageUpdatePlaybackProgress(userId, itemId, positionMs);
} catch (e) {
console.error("[PlaybackReporting] Failed to update local progress:", e);
}
@@ -108,7 +108,7 @@ export async function reportPlaybackStopped(itemId: string, positionSeconds: num
try {
// Get the repository to check if we should queue
const repo = auth.getRepository();
await repo.reportPlaybackStopped(itemId, positionTicks);
await repo.reportPlaybackStopped(itemId, positionMs);
} catch (e) {
console.error("[PlaybackReporting] Failed to report to server:", e);
// Server error - could queue, but for now just log
@@ -140,8 +140,8 @@ export async function markAsPlayed(itemId: string): Promise<void> {
const repo = auth.getRepository();
const item = await repo.getItem(itemId);
if (item.runTimeTicks) {
await repo.reportPlaybackStopped(itemId, item.runTimeTicks);
if (item.durationMs) {
await repo.reportPlaybackStopped(itemId, item.durationMs);
}
} catch (e) {
console.error("[PlaybackReporting] Failed to report as played:", e);