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,7 @@
class ApplicationJob < ActiveJob::Base
# Automatically retry jobs that encountered a deadlock
# retry_on ActiveRecord::Deadlocked
# Most jobs are safe to ignore if the underlying records are no longer available
# discard_on ActiveJob::DeserializationError
end

View File

@@ -0,0 +1,12 @@
class CleanupExpiredSessionsJob
include Sidekiq::Job
def perform
StreamSession.where(status: %w[connecting reconnecting])
.where("updated_at < ?", 6.hours.ago)
.find_each do |session|
session.fail! if session.may_fail?
Mediamtx::Client.new.delete_path(session)
end
end
end

View File

@@ -0,0 +1,22 @@
class DisconnectionTimeoutJob
include Sidekiq::Job
def perform(session_id)
session = StreamSession.find_by(id: session_id)
return unless session&.reconnecting?
session.finish! if session.may_finish?
session.update!(timeout_job_id: nil)
session.stream_events.create!(
event_type: "error",
metadata: { reason: "reconnect_timeout" },
occurred_at: Time.current
)
SessionChannel.broadcast_message(session, {
type: "stream_event",
event: "timeout_ended",
message: "Session ended after reconnect timeout"
})
Sessions::Stop.new(session).call
end
end

View File

@@ -0,0 +1,17 @@
class NotifyStreamHealthJob
include Sidekiq::Job
def perform(session_id)
session = StreamSession.find_by(id: session_id)
return unless session&.live?
camera = session.device_states.find_by(device_role: "camera")
return unless camera&.current_bitrate && camera.current_bitrate < session.target_bitrate * 0.5
SessionChannel.broadcast_message(session, {
type: "stream_event",
event: "quality_degraded",
bitrate: camera.current_bitrate
})
end
end

View File

@@ -0,0 +1,16 @@
class StreamAnalyticsJob
include Sidekiq::Job
def perform(session_id)
session = StreamSession.find_by(id: session_id)
return unless session&.ended?
duration = session.total_duration_secs
disconnects = session.disconnection_count
session.stream_events.create!(
event_type: "quality_changed",
metadata: { analytics: true, duration: duration, disconnects: disconnects },
occurred_at: Time.current
)
end
end