Commit Graph

4 Commits

Author SHA1 Message Date
Arkadiusz Fal
53fda7574c Fix a crash when syncing watch entries whose video ID exceeds the CloudKit record name limit
Extracted media-source video IDs are full file paths, so closing a
long-named WebDAV video built a watch record name longer than 255
characters and CKRecord.ID threw a fatal CKException.

Scoped record names now fall back to a SHA-256 digest of the ID when
the verbatim form would exceed the limit. The sync engine's local
lookups for materializing pending saves and applying remote deletions
recognise the digest form and match it against all entities of that
type instead of the indexed ID lookup. Short IDs keep their existing
names, so records already in iCloud are unaffected.
2026-09-20 23:59:05 +02:00
Arkadiusz Fal
bb6e98f9d2 Say what a timestamp tie actually does to the toggle
The tie test claimed the local record only repeated the server value, but
it fixtures opposing toggles stamped at the same instant, so name it for
the rule it proves. Note beside the strategy why that tie needs no
deterministic tiebreaker: no writer stamps these settings with a fixed
epoch date, unlike accounts and data contexts.
2026-09-08 23:49:43 +02:00
Arkadiusz Fal
e0f6b5e6d4 Let the newer channel notification toggle win its conflict 2026-09-08 23:27:06 +02:00
Arkadiusz Fal
aa6e7fbce9 Stop tracking resume progress for live streams
With "Partially Watched Videos" set to Ask, opening a live stream showed
the resume sheet with a playback timestamp. A live position is relative
to the live edge, not a fixed timeline, so it is meaningless as a
playhead.

Live views are still recorded in history, but carry no resume position:

- WatchEntry gains a persisted isLive flag plus recordLiveWatch(), which
  stores no position and clears state a drifting HLS duration may have
  left behind. progress is always 0 and toVideo() propagates the flag so
  history rows render the LIVE badge.
- from(video:) clamps duration, keeping Piped's -1 live sentinel out of
  storage.
- DataManager routes live saves through recordLiveWatch() and clears the
  flag on the non-live path (before updateProgress, so the auto-finish
  check is not suppressed by the hard-0 live progress), letting an entry
  heal once the stream becomes a VOD.
- PlayerService skips completion saves, always starts at the live edge,
  and drops any startTime passed for a live video.
- The resume prompt, the "Continue at" button label, thumbnail progress
  bars, "X remaining", Continue Watching and tvOS Top Shelf all exclude
  live entries.
- CloudKit syncs isLive with the rest of the watch state: the conflict
  resolver carries it with watchedSeconds/isFinished and the sync engine
  applies it when merging into an existing entry. It is read leniently,
  so the schema version stays at 2 and older clients keep parsing these
  records.
2026-08-03 23:34:06 +02:00