Allinea iOS a i18n Android 2.0.5 e aggiunge monitoraggio termico nativo.

Completa L10n su login/hub/wizard/broadcast e introduce ThermalStateManager su iOS/Android con degradazione qualità e indicatore in overlay.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Emiliano Frascaro
2026-07-24 11:19:37 +02:00
co-authored by Cursor
parent ff4de33cc5
commit ea5da1eb86
46 changed files with 2675 additions and 1241 deletions
@@ -179,10 +179,22 @@ final class LiveBroadcastEngine: ObservableObject {
await stopBroadcast()
}
/// Adattamento termico: aggiorna profilo video senza interrompere la sessione RTMP attiva.
func applyThermalProfile(videoBitrate: Int, fps: Int) async {
guard var current = config else { return }
guard current.videoBitrate != videoBitrate || current.fps != fps else { return }
current.videoBitrate = videoBitrate
current.fps = fps
config = current
if pipelineConfigured, let stream = await rtmpSession?.stream {
try? await applyCodecSettings(to: stream, config: current)
}
}
private func configurePipeline(_ config: BroadcastConfig) async throws {
guard AVCaptureDevice.authorizationStatus(for: .video) == .authorized,
AVCaptureDevice.authorizationStatus(for: .audio) == .authorized else {
throw APIError.http(403, "Permessi camera e microfono richiesti")
throw APIError.http(403, L10n.t("broadcast.error.permissions.camera.mic"))
}
if pipelineConfigured {
await attachPreviewIfNeeded()
@@ -252,7 +264,7 @@ final class LiveBroadcastEngine: ObservableObject {
guard previewAttached, generation == broadcastGeneration else {
if generation == broadcastGeneration {
publishPending = false
setPhase(.error, error: "Anteprima camera non pronta")
setPhase(.error, error: L10n.t("broadcast.error.camera.preview.not.ready"))
}
return
}
@@ -274,14 +286,14 @@ final class LiveBroadcastEngine: ObservableObject {
scheduleReconnect()
return
}
let message = UserFacingError.message(for: error) ?? "Connessione RTMP fallita"
let message = UserFacingError.message(for: error) ?? L10n.t("broadcast.error.rtmp.connect.failed")
setPhase(.error, error: message)
}
}
private func publish(config: BroadcastConfig, generation: Int) async throws {
guard let url = URL(string: config.rtmpUrl) else {
throw APIError.http(400, "URL RTMP non valido")
throw APIError.http(400, L10n.t("broadcast.error.rtmp.url.invalid"))
}
await Self.ensureRTMPFactoryRegistered()
await applyVideoOrientationIfNeeded(force: true)
@@ -291,7 +303,7 @@ final class LiveBroadcastEngine: ObservableObject {
.setMode(.publish)
.build()
guard let session else {
throw APIError.http(500, "Impossibile creare sessione RTMP")
throw APIError.http(500, L10n.t("broadcast.error.rtmp.session.create"))
}
let stream = await session.stream
@@ -395,7 +407,7 @@ final class LiveBroadcastEngine: ObservableObject {
reconnectAttempts += 1
if reconnectAttempts > config.maxReconnectAttempts {
reconnectTask = nil
setPhase(.error, error: "Connessione RTMP interrotta")
setPhase(.error, error: L10n.t("error.rtmp.interrupted"))
return
}