Initial commit: monorepo Match Live TV.

Rails API, app Flutter, infrastruttura Docker/MediaMTX, sito marketing e documentazione di deploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-26 17:45:37 +02:00
commit bba6df52c0
381 changed files with 20599 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
import 'device_state.dart';
import 'score_state.dart';
class StreamSession {
const StreamSession({
required this.id,
required this.matchId,
required this.status,
this.platform = 'matchlivetv',
this.rtmpIngestUrl,
this.hlsPlaybackUrl,
this.watchPageUrl,
this.youtubeBroadcastId,
this.privacyStatus = 'unlisted',
this.qualityPreset = '720p_30_2.5mbps',
this.targetBitrate = 2500000,
this.startedAt,
this.disconnectionCount = 0,
this.score,
this.devices = const [],
});
final String id;
final String matchId;
final String status;
final String platform;
final String? rtmpIngestUrl;
final String? hlsPlaybackUrl;
final String? watchPageUrl;
final String? youtubeBroadcastId;
final String privacyStatus;
final String qualityPreset;
final int targetBitrate;
final DateTime? startedAt;
final int disconnectionCount;
final ScoreState? score;
final List<DeviceStateModel> devices;
bool get isLive => status == 'live' || status == 'reconnecting';
bool get isTerminal => status == 'ended' || status == 'error';
bool get canResumeBroadcast =>
status == 'connecting' ||
status == 'live' ||
status == 'reconnecting' ||
status == 'paused';
factory StreamSession.fromJson(Map<String, dynamic> json) {
return StreamSession(
id: json['id'] as String,
matchId: json['match_id'] as String,
status: json['status'] as String? ?? 'idle',
platform: json['platform'] as String? ?? 'matchlivetv',
rtmpIngestUrl: json['rtmp_ingest_url'] as String?,
hlsPlaybackUrl: json['hls_playback_url'] as String?,
watchPageUrl: json['watch_page_url'] as String?,
youtubeBroadcastId: json['youtube_broadcast_id'] as String?,
privacyStatus: json['privacy_status'] as String? ?? 'unlisted',
qualityPreset: json['quality_preset'] as String? ?? '720p_30_2.5mbps',
targetBitrate: json['target_bitrate'] as int? ?? 2500000,
startedAt: json['started_at'] != null
? DateTime.tryParse(json['started_at'] as String)
: null,
disconnectionCount: json['disconnection_count'] as int? ?? 0,
score: json['score'] != null
? ScoreState.fromJson(json['score'] as Map<String, dynamic>)
: null,
devices: (json['devices'] as List<dynamic>?)
?.map((e) => DeviceStateModel.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
}
}
class PairingTokenResponse {
const PairingTokenResponse({
required this.pairingToken,
required this.expiresAt,
required this.qrPayload,
});
final String pairingToken;
final DateTime expiresAt;
final Map<String, dynamic> qrPayload;
factory PairingTokenResponse.fromJson(Map<String, dynamic> json) {
return PairingTokenResponse(
pairingToken: json['pairing_token'] as String,
expiresAt: DateTime.parse(json['expires_at'] as String),
qrPayload: json['qr_payload'] as Map<String, dynamic>,
);
}
}
class NetworkTestResult {
const NetworkTestResult({
required this.ready,
required this.targetUploadMbps,
});
final bool ready;
final double targetUploadMbps;
factory NetworkTestResult.fromJson(Map<String, dynamic> json) {
return NetworkTestResult(
ready: json['ready'] as bool? ?? false,
targetUploadMbps: (json['target_upload_mbps'] as num?)?.toDouble() ?? 2.5,
);
}
}
class YoutubeStats {
const YoutubeStats({
required this.concurrentViewers,
required this.live,
});
final int concurrentViewers;
final bool live;
factory YoutubeStats.fromJson(Map<String, dynamic> json) {
return YoutubeStats(
concurrentViewers: json['concurrent_viewers'] as int? ?? 0,
live: json['live'] as bool? ?? false,
);
}
}