forked from Foxyz/sync_replay_project
Sessions leave redacted logs; viewers wait for a member relay's source before opening it
Logs: one redaction pass (query strings to names, member tokens,
passwords, cookies, auth headers) for every line; daily files with size
and age caps in the app (hosted server included) and the dedicated
server (--log-dir); swaps, relay channels, per-member /r /m /j outcomes,
server fetches, mpv load errors, yt-dlp runs. Settings > About: Copy
diagnostics, Open logs folder.
Bilibili through someone else: the viewer opened /r/ before the source
dialled in; the server holds that request and mpv gives up after 5 s
("could not be opened"). Viewers now open a member relay once it is
live, the putter starts its source before its own player, and track
sizes are read in parallel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
572104f49e
commit
4ab37c55cf
25 files changed
+1124
-108
No files matched your search
@@ -8,7 +8,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
import 'package:media_kit_video/media_kit_video.dart';
|
||||
import 'package:signals/signals_flutter.dart';
|
||||
import 'package:sync_relay_core/core.dart' show JellyfinStreamType;
|
||||
import 'package:sync_relay_core/core.dart' show JellyfinStreamType, LogThrottle, createLogger;
|
||||
|
||||
import '../jellyfin/track_prefs.dart' show MpvTrack;
|
||||
import '../shaders/shader_assets.dart';
|
||||
@@ -361,6 +361,7 @@ class PlaybackController {
|
||||
// an error there while shaders are on is a shader that failed to load
|
||||
// or compile (mpv then renders without it).
|
||||
void _onLog(PlayerLog log) {
|
||||
_toAppLog(log);
|
||||
if (log.prefix == 'ytdl_hook' && (log.level == 'error' || log.level == 'fatal')) {
|
||||
final text = log.text.trim().replaceFirst(RegExp(r'^ERROR:\s*'), '');
|
||||
if (text.isNotEmpty) {
|
||||
@@ -380,6 +381,8 @@ class PlaybackController {
|
||||
if (!_loadErrorSent && _loadFailed(log, duration.peek())) {
|
||||
_loadErrorSent = true;
|
||||
final http = _httpError;
|
||||
_mpvLog.warning('cannot open the media: [${log.prefix}] ${log.text.trim()}'
|
||||
'${http == null ? ' (no HTTP error logged; a request that timed out after 5 s looks like this)' : ' ($http)'}');
|
||||
onLoadError?.call(http == null ? 'the video stream could not be opened' : 'the video server answered $http');
|
||||
return;
|
||||
}
|
||||
@@ -402,6 +405,23 @@ class PlaybackController {
|
||||
}
|
||||
}
|
||||
|
||||
// mpv's warnings and errors (the session player's; ffmpeg's through
|
||||
// whichever player logs them) go to the app log, a few per kind at a
|
||||
// time: a refused request makes ffmpeg retry and warn on every try.
|
||||
static final _mpvLog = createLogger('mpv');
|
||||
static final _mpvThrottle = LogThrottle(burst: 5);
|
||||
|
||||
static void _toAppLog(PlayerLog log, {String player = ''}) {
|
||||
final level = log.level;
|
||||
if (level != 'warn' && level != 'error' && level != 'fatal') return;
|
||||
final text = log.text.trim();
|
||||
if (text.isEmpty) return;
|
||||
final pass = _mpvThrottle.allow('$player${log.prefix} $level ${text.length > 40 ? text.substring(0, 40) : text}');
|
||||
if (pass == null) return;
|
||||
final line = '$pass$player[${log.prefix}] $text';
|
||||
level == 'warn' ? _mpvLog.info(line) : _mpvLog.warning(line);
|
||||
}
|
||||
|
||||
/// [log] says the media could not be opened: before a [duration] is
|
||||
/// known, mpv's fatal "nothing to play" or a demuxer error, or a yt-dlp
|
||||
/// hook error.
|
||||
@@ -438,6 +458,19 @@ class PlaybackController {
|
||||
}
|
||||
}
|
||||
|
||||
/// `mpv-version` and `ffmpeg-version` of the session player, for
|
||||
/// diagnostics; null when there is no player.
|
||||
Future<String?> mpvVersion() async {
|
||||
final p = _player;
|
||||
if (p == null) return null;
|
||||
try {
|
||||
final dynamic platform = p.platform;
|
||||
return '${await platform?.getProperty('mpv-version')}, ffmpeg ${await platform?.getProperty('ffmpeg-version')}';
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens [url] paused. [headers] go with every request for it (mpv
|
||||
/// `http-header-fields`): a Jellyfin token travels there, not in the URL.
|
||||
/// Each media gets its own player: when [preload] holds the same [url]
|
||||
@@ -708,6 +741,7 @@ class PlaybackController {
|
||||
// that is gone (a promotion disposes it) the next one created, which
|
||||
// can be this preload. Its HTTP errors still name the session's.
|
||||
if (log.prefix == 'ffmpeg') return _onLog(log);
|
||||
_toAppLog(log, player: 'preload ');
|
||||
if (_loadFailed(log, p.state.duration)) pre.fail();
|
||||
});
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user