Small fixes from a review of the archive, catch-up and VOD paths; each turns
a possible warning or TypeError into a clean step.
- archive: a segment pruned between the existence check and fopen() ended
the archive with feof(false); it is skipped. The wait for the next segment
polls every 50 ms, not 10 (about 200 stat calls a second per channel).
- timeshift (admin): a file removed since it was listed stops the answer
instead of looping on feof(false).
- A stream's first source is read through StreamUtils::firstSource(): the
TMDb refresh and a series' playlist read `json_decode(...)[0]` unguarded,
and an empty source led to a TMDb search for an empty title.
- cron:servers reads a server's connection counts with a default.
Closes#353
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AneTzAHPPkYWPQjwyZ2td9