Risolve timeout creazione sessione YouTube lato app.
La setup broadcast YouTube (~23s) passa in background job; l'API risponde subito. Aumentato anche il timeout Dio mobile per createSession YouTube. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
12
backend/app/jobs/youtube_broadcast_setup_job.rb
Normal file
12
backend/app/jobs/youtube_broadcast_setup_job.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class YoutubeBroadcastSetupJob < ApplicationJob
|
||||||
|
queue_as :default
|
||||||
|
|
||||||
|
retry_on Youtube::SetupBroadcast::Error, wait: 10.seconds, attempts: 3
|
||||||
|
|
||||||
|
def perform(session_id, youtube_channel = nil)
|
||||||
|
session = StreamSession.find_by(id: session_id)
|
||||||
|
return unless session
|
||||||
|
|
||||||
|
Youtube::SetupBroadcast.call(session, youtube_channel: youtube_channel.presence)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -27,10 +27,10 @@ module Sessions
|
|||||||
status: "idle"
|
status: "idle"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
youtube_channel = nil
|
||||||
if session.platform == "youtube"
|
if session.platform == "youtube"
|
||||||
youtube_channel = @params[:youtube_channel].presence
|
youtube_channel = @params[:youtube_channel].presence
|
||||||
assert_youtube_channel!(youtube_channel)
|
assert_youtube_channel!(youtube_channel)
|
||||||
attach_youtube_broadcast!(session, youtube_channel: youtube_channel)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
StreamSession.transaction do
|
StreamSession.transaction do
|
||||||
@@ -40,10 +40,13 @@ module Sessions
|
|||||||
mtx.create_path(session)
|
mtx.create_path(session)
|
||||||
mtx.create_overlay_path(session)
|
mtx.create_overlay_path(session)
|
||||||
start_overlay_relay(session)
|
start_overlay_relay(session)
|
||||||
start_youtube_relay(session)
|
|
||||||
log_event(session, "pairing", { created: true, platform: session.platform })
|
log_event(session, "pairing", { created: true, platform: session.platform })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if session.platform == "youtube"
|
||||||
|
YoutubeBroadcastSetupJob.perform_later(session.id, youtube_channel)
|
||||||
|
end
|
||||||
|
|
||||||
session
|
session
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -60,18 +63,7 @@ module Sessions
|
|||||||
end
|
end
|
||||||
|
|
||||||
def attach_youtube_broadcast!(session, youtube_channel: nil)
|
def attach_youtube_broadcast!(session, youtube_channel: nil)
|
||||||
yt = Youtube::BroadcastService.new(session.match.team, youtube_channel: youtube_channel)
|
Youtube::SetupBroadcast.call(session, youtube_channel: youtube_channel)
|
||||||
title = "#{session.match.team.name} vs #{session.match.opponent_name}"
|
|
||||||
broadcast = yt.create_broadcast!(
|
|
||||||
title: title,
|
|
||||||
privacy_status: session.privacy_status,
|
|
||||||
scheduled_at: session.match.scheduled_at
|
|
||||||
)
|
|
||||||
|
|
||||||
session.youtube_broadcast_id = broadcast[:broadcast_id]
|
|
||||||
session.youtube_stream_id = broadcast[:stream_id]
|
|
||||||
session.stream_key = broadcast[:stream_key]
|
|
||||||
session.rtmp_url = broadcast[:rtmp_url]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_overlay_relay(session)
|
def start_overlay_relay(session)
|
||||||
@@ -80,14 +72,6 @@ module Sessions
|
|||||||
Rails.logger.warn("[Sessions::Create] Overlay relay: #{e.message}")
|
Rails.logger.warn("[Sessions::Create] Overlay relay: #{e.message}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_youtube_relay(session)
|
|
||||||
return unless session.platform == "youtube"
|
|
||||||
|
|
||||||
Streams::YoutubeRelay.start(session)
|
|
||||||
rescue Streams::YoutubeRelay::Error => e
|
|
||||||
Rails.logger.warn("[Sessions::Create] YouTube relay: #{e.message}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_event(session, type, metadata)
|
def log_event(session, type, metadata)
|
||||||
session.stream_events.create!(event_type: type, metadata: metadata, occurred_at: Time.current)
|
session.stream_events.create!(event_type: type, metadata: metadata, occurred_at: Time.current)
|
||||||
end
|
end
|
||||||
|
|||||||
52
backend/app/services/youtube/setup_broadcast.rb
Normal file
52
backend/app/services/youtube/setup_broadcast.rb
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
module Youtube
|
||||||
|
# Crea broadcast/stream YouTube e avvia relay (operazione lenta, ~20s).
|
||||||
|
class SetupBroadcast
|
||||||
|
class Error < StandardError; end
|
||||||
|
|
||||||
|
def self.call(session, youtube_channel: nil)
|
||||||
|
new(session, youtube_channel: youtube_channel).call
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(session, youtube_channel: nil)
|
||||||
|
@session = session
|
||||||
|
@youtube_channel = youtube_channel
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
@session.reload
|
||||||
|
return @session if @session.terminal?
|
||||||
|
return @session if @session.platform != "youtube"
|
||||||
|
return @session if @session.youtube_broadcast_id.present?
|
||||||
|
|
||||||
|
yt = BroadcastService.new(@session.match.team, channel: @youtube_channel)
|
||||||
|
title = "#{@session.match.team.name} vs #{@session.match.opponent_name}"
|
||||||
|
broadcast = yt.create_broadcast!(
|
||||||
|
title: title,
|
||||||
|
privacy_status: @session.privacy_status,
|
||||||
|
scheduled_at: @session.match.scheduled_at
|
||||||
|
)
|
||||||
|
|
||||||
|
@session.update!(
|
||||||
|
youtube_broadcast_id: broadcast[:broadcast_id],
|
||||||
|
youtube_stream_id: broadcast[:stream_id],
|
||||||
|
stream_key: broadcast[:stream_key],
|
||||||
|
rtmp_url: broadcast[:rtmp_url]
|
||||||
|
)
|
||||||
|
|
||||||
|
Streams::YoutubeRelay.start(@session)
|
||||||
|
@session.stream_events.create!(
|
||||||
|
event_type: "youtube_ready",
|
||||||
|
metadata: { broadcast_id: broadcast[:broadcast_id] },
|
||||||
|
occurred_at: Time.current
|
||||||
|
)
|
||||||
|
@session
|
||||||
|
rescue BroadcastService::Error => e
|
||||||
|
@session.stream_events.create!(
|
||||||
|
event_type: "error",
|
||||||
|
metadata: { source: "youtube_setup", message: e.message },
|
||||||
|
occurred_at: Time.current
|
||||||
|
)
|
||||||
|
raise Error, e.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -76,6 +76,17 @@ class _StepTransmissionState extends ConsumerState<StepTransmission> {
|
|||||||
message: apiErr!.message,
|
message: apiErr!.message,
|
||||||
billingUrl: apiErr.billingUrl,
|
billingUrl: apiErr.billingUrl,
|
||||||
);
|
);
|
||||||
|
} else if (e.type == DioExceptionType.receiveTimeout ||
|
||||||
|
e.type == DioExceptionType.sendTimeout ||
|
||||||
|
e.type == DioExceptionType.connectionTimeout) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'Il server impiega più del previsto (YouTube). Attendi e non ritentare subito: '
|
||||||
|
'controlla se la sessione è già stata creata.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(apiErr?.message ?? 'Errore nella creazione sessione')),
|
SnackBar(content: Text(apiErr?.message ?? 'Errore nella creazione sessione')),
|
||||||
|
|||||||
@@ -167,6 +167,10 @@ class ApiClient {
|
|||||||
int? targetBitrate,
|
int? targetBitrate,
|
||||||
int? targetFps,
|
int? targetFps,
|
||||||
}) async {
|
}) async {
|
||||||
|
// YouTube: creazione broadcast + path MediaMTX può richiedere 20–30s lato server.
|
||||||
|
final sessionTimeout = platform == 'youtube'
|
||||||
|
? const Duration(seconds: 60)
|
||||||
|
: const Duration(seconds: 30);
|
||||||
final response = await _dio.post<Map<String, dynamic>>(
|
final response = await _dio.post<Map<String, dynamic>>(
|
||||||
'/matches/$matchId/sessions',
|
'/matches/$matchId/sessions',
|
||||||
data: {
|
data: {
|
||||||
@@ -177,6 +181,10 @@ class ApiClient {
|
|||||||
if (targetBitrate != null) 'target_bitrate': targetBitrate,
|
if (targetBitrate != null) 'target_bitrate': targetBitrate,
|
||||||
if (targetFps != null) 'target_fps': targetFps,
|
if (targetFps != null) 'target_fps': targetFps,
|
||||||
},
|
},
|
||||||
|
options: Options(
|
||||||
|
sendTimeout: sessionTimeout,
|
||||||
|
receiveTimeout: sessionTimeout,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return StreamSession.fromJson(response.data!);
|
return StreamSession.fromJson(response.data!);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user