Aggiunge il mute del microfono in diretta per evitare claim YouTube sulla musica in palestra.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-12 22:38:05 +02:00
co-authored by Cursor
parent bc2257efd2
commit 1fecccaafd
53 changed files with 827 additions and 25 deletions
+4
View File
@@ -55,6 +55,10 @@ class SessionChannel < ApplicationCable::Channel
Sessions::Pause.new(session).call
when "resume_stream"
Sessions::Resume.new(session).call
when "mute_audio"
Sessions::SetAudioMute.new(session, muted: true).call
when "unmute_audio"
Sessions::SetAudioMute.new(session, muted: false).call
end
end
@@ -34,6 +34,12 @@ module Api
render json: session_json(@session)
end
def audio_mute
muted = params.key?(:muted) ? params[:muted] : true
Sessions::SetAudioMute.new(@session, muted: muted).call
render json: session_json(@session)
end
def score
Scoring::SyncState.new(session: @session, payload: score_sync_params).call
render json: session_json(@session, detail: true)
@@ -189,7 +195,8 @@ module Api
quality_preset: session.quality_preset,
target_bitrate: session.target_bitrate,
started_at: session.started_at,
disconnection_count: session.disconnection_count
disconnection_count: session.disconnection_count,
audio_muted: session.audio_muted
}
if detail
data[:score] = session.score_state&.as_cable_payload
@@ -7,7 +7,7 @@ module Public
layout "regia"
protect_from_forgery with: :null_session
skip_before_action :verify_authenticity_token, only: %i[score pause resume stop]
skip_before_action :verify_authenticity_token, only: %i[score pause resume stop audio_mute]
before_action :set_session_from_token
@@ -61,6 +61,12 @@ module Public
render json: status_payload
end
def audio_mute
muted = params.key?(:muted) ? params[:muted] : !@session.audio_muted
Sessions::SetAudioMute.new(@session, muted: muted).call
render json: status_payload
end
private
def set_session_from_token
@@ -78,6 +84,7 @@ module Public
{
status: @session.status,
paused: @session.paused?,
audio_muted: @session.audio_muted,
stream_closed: closed,
live: !closed && (@session.live? || @session.paused? || publisher_online),
on_air: playable || (!closed && @session.status.in?(%w[connecting live reconnecting paused])),
@@ -44,9 +44,12 @@ module Public
partialsPrefix: t("regia.board.partials_prefix"),
resumed: t("regia.js.resumed"),
pausedCover: t("regia.js.paused_cover"),
muted: t("regia.js.muted"),
unmuted: t("regia.js.unmuted"),
closed: t("regia.js.closed"),
resumeError: t("regia.js.resume_error"),
pauseError: t("regia.js.pause_error"),
muteError: t("regia.js.mute_error"),
closeError: t("regia.js.close_error"),
closeConfirm: t("regia.js.close_confirm"),
linkUnavailable: t("regia.js.link_unavailable"),
@@ -58,6 +61,8 @@ module Public
streamEnded: t("regia.preview_ended"),
resumeLabel: t("regia.resume"),
pauseLabel: t("regia.pause"),
muteLabel: t("regia.mute"),
unmuteLabel: t("regia.unmute"),
endedBadge: t("regia.status.ended"),
pausedBadge: t("regia.status.paused"),
liveBadge: t("regia.status.live"),
+1 -1
View File
@@ -1,7 +1,7 @@
class StreamEvent < ApplicationRecord
EVENT_TYPES = %w[
connected disconnected reconnected quality_changed error paused resumed
started ended network_test pairing youtube_ready
started ended network_test pairing youtube_ready audio_muted audio_unmuted
].freeze
belongs_to :stream_session
@@ -0,0 +1,28 @@
module Sessions
# Silenzia o riattiva il microfono della camera in onda.
# Lo stream continua a inviare AAC silenzioso (YouTube/MediaMTX richiedono la traccia audio).
class SetAudioMute
def initialize(session, muted:)
@session = session
@muted = ActiveModel::Type::Boolean.new.cast(muted)
end
def call
return @session if @session.audio_muted == @muted
@session.update!(audio_muted: @muted)
action = @muted ? "mute_audio" : "unmute_audio"
event_type = @muted ? "audio_muted" : "audio_unmuted"
log_event(event_type)
SessionChannel.broadcast_message(@session, { type: "command", action: action, muted: @muted })
SessionChannel.broadcast_message(@session, { type: "stream_event", event: "audio_muted", muted: @muted })
@session
end
private
def log_event(type)
@session.stream_events.create!(event_type: type, occurred_at: Time.current, metadata: { muted: @muted })
end
end
end
+5 -2
View File
@@ -15,6 +15,7 @@
data-score-url="<%= j public_regia_score_path(params[:token]) %>"
data-pause-url="<%= j public_regia_pause_path(params[:token]) %>"
data-resume-url="<%= j public_regia_resume_path(params[:token]) %>"
data-audio-mute-url="<%= j public_regia_audio_mute_path(params[:token]) %>"
data-stop-url="<%= j public_regia_stop_path(params[:token]) %>"
data-cable-url="<%= j "/cable?regia_token=#{params[:token]}" %>"
data-hls-url="<%= j @session.hls_playback_url %>"
@@ -26,7 +27,8 @@
data-live-share-title="<%= j @live_share_title %>"
data-points-target="<%= @match.effective_board_type.in?(%w[volley racket]) ? Scoring::Rules.from_match(@match).points_target(@score.current_set) : 0 %>"
data-stream-closed="<%= @stream_closed %>"
data-initial-paused="<%= @session.paused? %>">
data-initial-paused="<%= @session.paused? %>"
data-initial-audio-muted="<%= @session.audio_muted %>">
<header class="regia-header">
<h1><%= @team.name %> vs <%= @match.opponent_name %></h1>
@@ -77,6 +79,7 @@
<% unless @stream_closed %>
<button type="button" class="regia-btn regia-btn--outline" id="btn-pause"><%= @session.paused? ? t("regia.resume") : t("regia.pause") %></button>
<button type="button" class="regia-btn regia-btn--outline<%= @session.audio_muted ? ' is-muted' : '' %>" id="btn-audio-mute"><%= @session.audio_muted ? t("regia.unmute") : t("regia.mute") %></button>
<button type="button" class="regia-btn regia-btn--danger" id="btn-stop"><%= t("regia.stop") %></button>
<% end %>
</div>
@@ -94,4 +97,4 @@
</div>
</div>
<script src="/regia.js?v=8"></script>
<script src="/regia.js?v=9"></script>