feat: configurable watch together relay server

This commit is contained in:
edde746
2026-03-28 14:44:30 +01:00
parent 53d50dbc3b
commit 059509354d
36 changed files with 319 additions and 38 deletions
@@ -4,6 +4,7 @@ import 'dart:math';
import 'package:flutter/foundation.dart';
import '../../mpv/mpv.dart';
import '../../services/settings_service.dart';
import '../../utils/app_logger.dart';
import '../models/sync_message.dart';
import '../models/watch_session.dart';
@@ -189,7 +190,8 @@ class WatchTogetherProvider with ChangeNotifier {
appLogger.d('WatchTogether: Creating session with control mode: $controlMode');
_peerService = WatchTogetherPeerService();
final customRelayUrl = SettingsService.instanceOrNull?.getCustomRelayUrl();
_peerService = WatchTogetherPeerService(customBaseUrl: customRelayUrl);
_setupPeerServiceListeners();
try {
@@ -236,7 +238,8 @@ class WatchTogetherProvider with ChangeNotifier {
appLogger.d('WatchTogether: Joining session: $sessionId');
_peerService = WatchTogetherPeerService();
final customRelayUrl = SettingsService.instanceOrNull?.getCustomRelayUrl();
_peerService = WatchTogetherPeerService(customBaseUrl: customRelayUrl);
_setupPeerServiceListeners();
_session = WatchSession.joinAsGuest(sessionId: sessionId);
@@ -9,6 +9,7 @@ import 'package:provider/provider.dart';
import '../../i18n/strings.g.dart';
import '../../focus/focusable_button.dart';
import '../../focus/focusable_wrapper.dart';
import '../../services/settings_service.dart';
import '../../utils/app_logger.dart';
import '../../utils/dialogs.dart';
import '../../utils/snackbar_helper.dart';
@@ -80,10 +81,12 @@ class _NotInSessionViewState extends State<_NotInSessionView> {
bool _isCreating = false;
bool _isJoining = false;
bool? _healthOk;
String? _customRelayUrl;
@override
void initState() {
super.initState();
_customRelayUrl = SettingsService.instanceOrNull?.getCustomRelayUrl();
_checkHealth();
}
@@ -91,7 +94,7 @@ class _NotInSessionViewState extends State<_NotInSessionView> {
try {
final client = HttpClient();
client.connectionTimeout = const Duration(seconds: 5);
final request = await client.getUrl(Uri.parse(WatchTogetherPeerService.healthUrl));
final request = await client.getUrl(Uri.parse(WatchTogetherPeerService.healthUrlFor(_customRelayUrl)));
final response = await request.close().namedTimeout(const Duration(seconds: 5), operation: 'WatchTogether health check');
final body = await response.transform(const SystemEncoding().decoder).join();
client.close();
@@ -20,9 +20,24 @@ export '../../services/base_peer_service.dart' show PeerError, PeerErrorType;
/// - Sending/receiving sync messages through the relay server
/// - Reconnection on WebSocket drops
class WatchTogetherPeerService with KeepaliveMixin {
static const String _baseUrl = 'https://ice.plezy.app';
static String get healthUrl => '$_baseUrl/health';
static const String _relayUrl = 'wss://ice.plezy.app/relay';
static const String defaultBaseUrl = 'https://ice.plezy.app';
final String _baseUrl;
static String get healthUrl => '$defaultBaseUrl/health';
static String healthUrlFor(String? customBaseUrl) {
final base = (customBaseUrl != null && customBaseUrl.trim().isNotEmpty) ? customBaseUrl.trim() : defaultBaseUrl;
return '$base/health';
}
String get _relayUrl {
final wsBase = _baseUrl.replaceFirst(RegExp(r'^https://'), 'wss://').replaceFirst(RegExp(r'^http://'), 'ws://');
return '$wsBase/relay';
}
WatchTogetherPeerService({String? customBaseUrl})
: _baseUrl = (customBaseUrl != null && customBaseUrl.trim().isNotEmpty) ? customBaseUrl.trim() : defaultBaseUrl;
WebSocketChannel? _channel;
StreamSubscription? _channelSubscription;