YouTube come destinazione pubblica, non Match Live TV.
Non propone più il link MLTV per sessioni YouTube; la pagina /live reindirizza a YouTube; il relay verso YouTube si riavvia al connect. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -45,6 +45,7 @@ class _StepNetworkTestState extends ConsumerState<StepNetworkTest> {
|
||||
int _latencyMs = 0;
|
||||
String _networkType = '—';
|
||||
bool _starting = false;
|
||||
Timer? _youtubePoll;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -54,6 +55,45 @@ class _StepNetworkTestState extends ConsumerState<StepNetworkTest> {
|
||||
if (session?.score != null) {
|
||||
ref.read(scoreProvider.notifier).reset(session!.score!);
|
||||
}
|
||||
_startYoutubeLinkPoll();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_youtubePoll?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _startYoutubeLinkPoll() {
|
||||
final session = ref.read(sessionProvider).session;
|
||||
if (session == null || session.platform != 'youtube') return;
|
||||
if (session.youtubeReady || (session.youtubeWatchUrl?.isNotEmpty ?? false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_youtubePoll?.cancel();
|
||||
var attempts = 0;
|
||||
_youtubePoll = Timer.periodic(const Duration(seconds: 2), (_) async {
|
||||
attempts++;
|
||||
if (!mounted || attempts > 30) {
|
||||
_youtubePoll?.cancel();
|
||||
return;
|
||||
}
|
||||
final current = ref.read(sessionProvider).session;
|
||||
if (current == null) return;
|
||||
try {
|
||||
final updated =
|
||||
await ref.read(apiClientProvider).fetchSession(current.id);
|
||||
ref
|
||||
.read(sessionProvider.notifier)
|
||||
.setSession(updated, match: ref.read(sessionProvider).match);
|
||||
if (updated.youtubeReady ||
|
||||
(updated.youtubeWatchUrl?.isNotEmpty ?? false)) {
|
||||
_youtubePoll?.cancel();
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -240,6 +280,24 @@ class _StepNetworkTestState extends ConsumerState<StepNetworkTest> {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
if (_testCompleted &&
|
||||
session != null &&
|
||||
session.platform == 'youtube' &&
|
||||
shareUrl == null) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.surfaceElevated,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'Preparazione diretta sul canale YouTube Match Live TV… '
|
||||
'Il link apparirà qui tra pochi secondi.',
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
if (_testCompleted && session != null && shareUrl != null) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
@@ -252,7 +310,7 @@ class _StepNetworkTestState extends ConsumerState<StepNetworkTest> {
|
||||
children: [
|
||||
Text(
|
||||
session.platform == 'youtube'
|
||||
? 'Link video YouTube'
|
||||
? 'Link diretta YouTube (canale Match Live TV)'
|
||||
: 'Link diretta Match Live TV',
|
||||
style: theme.textTheme.titleSmall,
|
||||
),
|
||||
@@ -261,6 +319,13 @@ class _StepNetworkTestState extends ConsumerState<StepNetworkTest> {
|
||||
shareUrl,
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
if (session.platform == 'youtube') ...[
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'Condividi solo questo link: gli spettatori guardano su YouTube, non sul sito.',
|
||||
style: theme.textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
|
||||
@@ -47,6 +47,38 @@ class SessionNotifier extends StateNotifier<SessionState> {
|
||||
state = state.copyWith(session: session, match: match);
|
||||
}
|
||||
|
||||
void updateYoutubeLinks({
|
||||
required String? youtubeWatchUrl,
|
||||
required String? shareUrl,
|
||||
String? youtubeBroadcastId,
|
||||
bool youtubeReady = true,
|
||||
}) {
|
||||
final s = state.session;
|
||||
if (s == null || s.platform != 'youtube') return;
|
||||
state = state.copyWith(
|
||||
session: StreamSession(
|
||||
id: s.id,
|
||||
matchId: s.matchId,
|
||||
status: s.status,
|
||||
platform: s.platform,
|
||||
rtmpIngestUrl: s.rtmpIngestUrl,
|
||||
hlsPlaybackUrl: s.hlsPlaybackUrl,
|
||||
watchPageUrl: s.watchPageUrl,
|
||||
shareUrl: shareUrl,
|
||||
youtubeWatchUrl: youtubeWatchUrl,
|
||||
youtubeBroadcastId: youtubeBroadcastId ?? s.youtubeBroadcastId,
|
||||
youtubeReady: youtubeReady,
|
||||
privacyStatus: s.privacyStatus,
|
||||
qualityPreset: s.qualityPreset,
|
||||
targetBitrate: s.targetBitrate,
|
||||
startedAt: s.startedAt,
|
||||
disconnectionCount: s.disconnectionCount,
|
||||
score: s.score,
|
||||
devices: s.devices,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void setMatch(MatchModel match) {
|
||||
state = state.copyWith(match: match);
|
||||
}
|
||||
@@ -91,6 +123,7 @@ class SessionNotifier extends StateNotifier<SessionState> {
|
||||
shareUrl: s.shareUrl,
|
||||
youtubeWatchUrl: s.youtubeWatchUrl,
|
||||
youtubeBroadcastId: s.youtubeBroadcastId,
|
||||
youtubeReady: s.youtubeReady,
|
||||
privacyStatus: s.privacyStatus,
|
||||
qualityPreset: s.qualityPreset,
|
||||
targetBitrate: s.targetBitrate,
|
||||
@@ -133,7 +166,10 @@ class SessionNotifier extends StateNotifier<SessionState> {
|
||||
rtmpIngestUrl: session.rtmpIngestUrl,
|
||||
hlsPlaybackUrl: session.hlsPlaybackUrl,
|
||||
watchPageUrl: session.watchPageUrl,
|
||||
shareUrl: session.shareUrl,
|
||||
youtubeWatchUrl: session.youtubeWatchUrl,
|
||||
youtubeBroadcastId: session.youtubeBroadcastId,
|
||||
youtubeReady: session.youtubeReady,
|
||||
privacyStatus: session.privacyStatus,
|
||||
qualityPreset: session.qualityPreset,
|
||||
targetBitrate: session.targetBitrate,
|
||||
|
||||
@@ -13,6 +13,7 @@ class StreamSession {
|
||||
this.shareUrl,
|
||||
this.youtubeWatchUrl,
|
||||
this.youtubeBroadcastId,
|
||||
this.youtubeReady = false,
|
||||
this.privacyStatus = 'public',
|
||||
this.qualityPreset = '720p_30_2.5mbps',
|
||||
this.targetBitrate = 2500000,
|
||||
@@ -32,6 +33,7 @@ class StreamSession {
|
||||
final String? shareUrl;
|
||||
final String? youtubeWatchUrl;
|
||||
final String? youtubeBroadcastId;
|
||||
final bool youtubeReady;
|
||||
final String privacyStatus;
|
||||
final String qualityPreset;
|
||||
final int targetBitrate;
|
||||
@@ -62,6 +64,7 @@ class StreamSession {
|
||||
shareUrl: json['share_url'] as String?,
|
||||
youtubeWatchUrl: json['youtube_watch_url'] as String?,
|
||||
youtubeBroadcastId: json['youtube_broadcast_id'] as String?,
|
||||
youtubeReady: json['youtube_ready'] as bool? ?? false,
|
||||
privacyStatus: json['privacy_status'] as String? ?? 'public',
|
||||
qualityPreset: json['quality_preset'] as String? ?? '720p_30_2.5mbps',
|
||||
targetBitrate: json['target_bitrate'] as int? ?? 2500000,
|
||||
|
||||
@@ -5,7 +5,10 @@ import 'package:share_plus/share_plus.dart';
|
||||
import 'models/stream_session.dart';
|
||||
|
||||
String? watchShareUrl(StreamSession session) {
|
||||
return session.shareUrl ?? session.youtubeWatchUrl ?? session.watchPageUrl;
|
||||
if (session.platform == 'youtube') {
|
||||
return session.youtubeWatchUrl ?? session.shareUrl;
|
||||
}
|
||||
return session.shareUrl ?? session.watchPageUrl;
|
||||
}
|
||||
|
||||
String watchShareLabel(StreamSession session) {
|
||||
|
||||
@@ -121,6 +121,13 @@ class WebsocketService {
|
||||
_ref.read(sessionProvider.notifier).applyCommand('resume_stream');
|
||||
}
|
||||
break;
|
||||
case 'youtube_ready':
|
||||
_ref.read(sessionProvider.notifier).updateYoutubeLinks(
|
||||
youtubeWatchUrl: data['youtube_watch_url'] as String?,
|
||||
shareUrl: data['share_url'] as String?,
|
||||
youtubeBroadcastId: data['youtube_broadcast_id'] as String?,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user