Così overflow Hetzner e VOD YouTube restano in archivio dopo lo spegnimento del nodo, e la colonna ingest non si svuota. Co-authored-by: Cursor <cursoragent@cursor.com>
119 lines
3.7 KiB
Ruby
119 lines
3.7 KiB
Ruby
module Sessions
|
|
class Create
|
|
def self.normalize_privacy(value)
|
|
value.to_s == "public" ? "public" : "unlisted"
|
|
end
|
|
|
|
def initialize(user:, match:, params:)
|
|
@user = user
|
|
@match = match
|
|
@params = params
|
|
end
|
|
|
|
def call
|
|
platform = @params[:platform].presence || "matchlivetv"
|
|
ent = @match.team.entitlements
|
|
ent.assert_can_stream_on!(platform)
|
|
ent.assert_concurrent_stream!
|
|
|
|
session = StreamSession.new(
|
|
match: @match,
|
|
user: @user,
|
|
platform: platform,
|
|
privacy_status: Sessions::Create.normalize_privacy(@params[:privacy_status]),
|
|
quality_preset: @params[:quality_preset] || "720p_30_2.5mbps",
|
|
target_bitrate: @params[:target_bitrate] || 2_500_000,
|
|
target_fps: @params[:target_fps] || 30,
|
|
status: "idle"
|
|
)
|
|
Sessions::ApplyClientInfo.call(session, @params[:client])
|
|
|
|
youtube_channel = nil
|
|
if session.platform == "youtube"
|
|
youtube_channel = @params[:youtube_channel].presence
|
|
assert_youtube_channel!(youtube_channel)
|
|
end
|
|
|
|
StreamSession.transaction do
|
|
session.stream_node = Streams::NodeRegistry.allocate!
|
|
session.save!
|
|
Scoring::Engine.ensure_score_for(session)
|
|
Streams::CoverSlateEnsurer.ensure_for!(@match)
|
|
Streams::SlateDistributor.ensure_for!(session)
|
|
Mediamtx::Client.for_session(session).create_path(session)
|
|
log_event(
|
|
session,
|
|
"pairing",
|
|
{
|
|
created: true,
|
|
platform: session.platform,
|
|
stream_node: session.stream_node&.slug,
|
|
client_os: session.client_os,
|
|
app_version: session.app_version,
|
|
device_model: session.device_model
|
|
}.compact
|
|
)
|
|
end
|
|
|
|
kick_autoscaler_if_soft_limit!
|
|
|
|
if session.platform == "youtube"
|
|
YoutubeBroadcastSetupJob.perform_later(session.id, youtube_channel)
|
|
end
|
|
|
|
session
|
|
rescue Streams::NodeRegistry::NoCapacityError => e
|
|
raise_no_capacity!(e)
|
|
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
|
|
raise Streams::IngestUnavailableError, "Ingest temporaneamente non disponibile (#{e.class})"
|
|
rescue Mediamtx::Client::Error => e
|
|
if e.message.to_s.match?(/failed to open|Connection refused|Timeout|timed out/i)
|
|
raise Streams::IngestUnavailableError, e.message
|
|
end
|
|
raise
|
|
end
|
|
|
|
private
|
|
|
|
def raise_no_capacity!(error)
|
|
if Streams::Autoscaler.enabled?
|
|
Streams::AutoscalerJob.kick!
|
|
raise Teams::EntitlementError.new(
|
|
"Capacità streaming in espansione. Riprova tra poco.",
|
|
code: "stream_capacity_scaling"
|
|
)
|
|
end
|
|
|
|
raise Teams::EntitlementError.new(error.message, code: "stream_capacity_exhausted")
|
|
end
|
|
|
|
def kick_autoscaler_if_soft_limit!
|
|
return unless Streams::Autoscaler.enabled?
|
|
|
|
free = Streams::Autoscaler.metrics[:free_slots].to_i
|
|
return if free > Streams::Autoscaler.soft_free_slots
|
|
|
|
Streams::AutoscalerJob.kick!
|
|
end
|
|
|
|
|
|
def assert_youtube_channel!(youtube_channel)
|
|
resolver = Youtube::CredentialResolver.new(@match.team, channel: youtube_channel)
|
|
if resolver.resolve.blank?
|
|
raise Teams::EntitlementError.new(
|
|
"Canale YouTube selezionato non disponibile",
|
|
code: "youtube_channel_unavailable"
|
|
)
|
|
end
|
|
end
|
|
|
|
def attach_youtube_broadcast!(session, youtube_channel: nil)
|
|
Youtube::SetupBroadcast.call(session, youtube_channel: youtube_channel)
|
|
end
|
|
|
|
def log_event(session, type, metadata)
|
|
session.stream_events.create!(event_type: type, metadata: metadata, occurred_at: Time.current)
|
|
end
|
|
end
|
|
end
|