fix(playback): canonicalize base URL scheme case
FFmpeg's protocol lookup is case-sensitive, so a stored "Https://" base URL reaches mpv verbatim through the direct-play string concat and fails with "Protocol not found" (API calls survive because Dart's Uri lowercases the scheme). Canonicalize at Jellyfin URL intake and in the connection constructor so persisted configs self-heal on load, and register mpv-escaped (https\://) redaction variants so option-value logs stop leaking the server host. close #1465
This commit is contained in:
@@ -3,7 +3,9 @@ import 'url_utils.dart';
|
||||
class LogRedactionManager {
|
||||
// Size limits for bounded sets (FIFO eviction when exceeded)
|
||||
static const int _maxTokens = 50;
|
||||
static const int _maxUrls = 20;
|
||||
// Each server registers up to 8 literals (slash/origin/mpv-escaped
|
||||
// variants), so keep headroom for several servers before FIFO eviction.
|
||||
static const int _maxUrls = 40;
|
||||
static const int _maxCustomValues = 50;
|
||||
|
||||
// Use LinkedHashSet for FIFO ordering
|
||||
@@ -74,22 +76,33 @@ class LogRedactionManager {
|
||||
final strippedSlash = stripTrailingSlash(normalized);
|
||||
|
||||
if (strippedSlash.isNotEmpty) {
|
||||
_addWithLimit(_urls, strippedSlash, _maxUrls);
|
||||
_addWithLimit(_urls, '$strippedSlash/', _maxUrls);
|
||||
_addUrl(strippedSlash);
|
||||
_addUrl('$strippedSlash/');
|
||||
}
|
||||
|
||||
// Capture origin and host-level strings as well to cover most cases.
|
||||
if (uri != null && uri.host.isNotEmpty) {
|
||||
final origin = '${uri.scheme.isEmpty ? 'https' : uri.scheme}://${uri.host}${uri.hasPort ? ':${uri.port}' : ''}';
|
||||
_addWithLimit(_urls, origin, _maxUrls);
|
||||
_addUrl(origin);
|
||||
if (origin.endsWith('/')) {
|
||||
_addWithLimit(_urls, origin.substring(0, origin.length - 1), _maxUrls);
|
||||
_addUrl(origin.substring(0, origin.length - 1));
|
||||
}
|
||||
}
|
||||
|
||||
_rebuildCombinedPattern();
|
||||
}
|
||||
|
||||
/// Registers a URL literal plus the form mpv uses when echoing list-option
|
||||
/// values (`:` escaped as `\:`, e.g. `sub-files=https\://host/...`), which
|
||||
/// would otherwise slip past the literal match and leak the host.
|
||||
static void _addUrl(String url) {
|
||||
_addWithLimit(_urls, url, _maxUrls);
|
||||
final escaped = url.replaceAll(':', r'\:');
|
||||
if (escaped != url) {
|
||||
_addWithLimit(_urls, escaped, _maxUrls);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience: register a server's URL and access token together.
|
||||
/// Call this before any HTTP traffic so the very first probe URL doesn't
|
||||
/// leak credentials verbatim.
|
||||
|
||||
@@ -13,3 +13,18 @@ String stripTrailingSlash(String input) {
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
final RegExp _schemePattern = RegExp(r'^[A-Za-z][A-Za-z\d+.-]*://');
|
||||
|
||||
/// Canonicalizes a server base URL: trims, strips one trailing `/`, and
|
||||
/// lowercases the scheme (`Https://host` → `https://host`). Dart's `Uri`
|
||||
/// normalizes scheme case for API requests, but URLs handed to the player as
|
||||
/// raw strings don't get that treatment, and FFmpeg's protocol lookup is
|
||||
/// case-sensitive — a mixed-case scheme fails with "Protocol not found".
|
||||
/// Everything after `://` is left untouched.
|
||||
String canonicalizeBaseUrl(String input) {
|
||||
final stripped = stripTrailingSlash(input);
|
||||
final match = _schemePattern.firstMatch(stripped);
|
||||
if (match == null) return stripped;
|
||||
return stripped.replaceRange(0, match.end, match.group(0)!.toLowerCase());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user