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:
@@ -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,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
|
||||
@@ -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>
|
||||
|
||||
@@ -441,6 +441,8 @@ de:
|
||||
preview_waiting: Vorschau wartet auf Signal…
|
||||
pause: Pausieren
|
||||
resume: Livestream fortsetzen
|
||||
mute: Audio stummschalten
|
||||
unmute: Audio einschalten
|
||||
stop: Livestream beenden
|
||||
modal:
|
||||
set_won_title: Satz gewonnen
|
||||
@@ -476,9 +478,12 @@ de:
|
||||
unknown_period: "—"
|
||||
resumed: Übertragung fortgesetzt
|
||||
paused_cover: "Übertragung pausiert — Deckbild auf Sendung"
|
||||
muted: "Audio stumm — YouTube empfängt das Mikrofon nicht"
|
||||
unmuted: "Mikrofon-Audio wieder aktiv"
|
||||
closed: Übertragung beendet
|
||||
resume_error: Fehler beim Fortsetzen der Übertragung
|
||||
pause_error: Fehler beim Pausieren der Übertragung
|
||||
mute_error: Fehler beim Stummschalten
|
||||
close_error: Fehler beim Beenden
|
||||
close_confirm: Übertragung endgültig beenden?
|
||||
link_unavailable: Link nicht verfügbar
|
||||
|
||||
@@ -436,6 +436,8 @@ en:
|
||||
preview_waiting: Waiting for signal…
|
||||
pause: Pause stream
|
||||
resume: Resume stream
|
||||
mute: Mute audio
|
||||
unmute: Unmute audio
|
||||
stop: End stream
|
||||
modal:
|
||||
set_won_title: Set won
|
||||
@@ -471,9 +473,12 @@ en:
|
||||
unknown_period: "—"
|
||||
resumed: Stream resumed
|
||||
paused_cover: "Stream paused — cover on air"
|
||||
muted: "Audio muted — YouTube is not receiving the microphone"
|
||||
unmuted: "Microphone audio restored"
|
||||
closed: Stream closed
|
||||
resume_error: Error resuming the stream
|
||||
pause_error: Error pausing the stream
|
||||
mute_error: Error muting audio
|
||||
close_error: Error closing
|
||||
close_confirm: Permanently close the live stream?
|
||||
link_unavailable: Link not available
|
||||
|
||||
@@ -441,6 +441,8 @@ es:
|
||||
preview_waiting: Vista previa a la espera de señal…
|
||||
pause: Pausar directo
|
||||
resume: Reanudar directo
|
||||
mute: Silenciar audio
|
||||
unmute: Reactivar audio
|
||||
stop: Finalizar directo
|
||||
modal:
|
||||
set_won_title: Set ganado
|
||||
@@ -476,9 +478,12 @@ es:
|
||||
unknown_period: "—"
|
||||
resumed: Retransmisión reanudada
|
||||
paused_cover: "Retransmisión en pausa — cortinilla al aire"
|
||||
muted: "Audio silenciado — YouTube no recibe el micrófono"
|
||||
unmuted: "Audio del micrófono reactivado"
|
||||
closed: Retransmisión finalizada
|
||||
resume_error: Error al reanudar la retransmisión
|
||||
pause_error: Error al pausar la retransmisión
|
||||
mute_error: Error al silenciar el audio
|
||||
close_error: Error al cerrar
|
||||
close_confirm: "¿Cerrar definitivamente la retransmisión?"
|
||||
link_unavailable: Enlace no disponible
|
||||
|
||||
@@ -441,6 +441,8 @@ fr:
|
||||
preview_waiting: Aperçu en attente du signal…
|
||||
pause: Mettre en pause
|
||||
resume: Reprendre le direct
|
||||
mute: Couper l'audio
|
||||
unmute: Réactiver l'audio
|
||||
stop: Terminer le direct
|
||||
modal:
|
||||
set_won_title: Set gagné
|
||||
@@ -476,9 +478,12 @@ fr:
|
||||
unknown_period: "—"
|
||||
resumed: Direct repris
|
||||
paused_cover: "Direct en pause — habillage à l'antenne"
|
||||
muted: "Audio coupé — YouTube ne reçoit pas le micro"
|
||||
unmuted: "Audio du micro rétabli"
|
||||
closed: Direct terminé
|
||||
resume_error: Erreur lors de la reprise du direct
|
||||
pause_error: Erreur lors de la mise en pause du direct
|
||||
mute_error: Erreur lors de la coupure audio
|
||||
close_error: Erreur de clôture
|
||||
close_confirm: Clôturer définitivement le direct ?
|
||||
link_unavailable: Lien non disponible
|
||||
|
||||
@@ -436,6 +436,8 @@ it:
|
||||
preview_waiting: Anteprima in attesa del segnale…
|
||||
pause: Metti in pausa
|
||||
resume: Riprendi diretta
|
||||
mute: Silenzia audio
|
||||
unmute: Riattiva audio
|
||||
stop: Chiudi diretta
|
||||
modal:
|
||||
set_won_title: Set vinto
|
||||
@@ -471,9 +473,12 @@ it:
|
||||
unknown_period: "—"
|
||||
resumed: Diretta ripresa
|
||||
paused_cover: "Diretta in pausa — copertina in onda"
|
||||
muted: "Audio silenziato — YouTube non riceve il microfono"
|
||||
unmuted: "Audio microfono riattivato"
|
||||
closed: Diretta chiusa
|
||||
resume_error: Errore ripresa diretta
|
||||
pause_error: Errore pausa diretta
|
||||
mute_error: Errore silenziamento audio
|
||||
close_error: Errore chiusura
|
||||
close_confirm: Chiudere definitivamente la diretta?
|
||||
link_unavailable: Link non disponibile
|
||||
|
||||
@@ -47,6 +47,7 @@ Rails.application.routes.draw do
|
||||
patch :stop
|
||||
patch :pause
|
||||
patch :resume
|
||||
patch :audio_mute
|
||||
patch :score
|
||||
post :score_action
|
||||
get :events
|
||||
@@ -142,6 +143,7 @@ Rails.application.routes.draw do
|
||||
patch "regia/:token/score", to: "public/regia#score", as: :public_regia_score
|
||||
post "regia/:token/pause", to: "public/regia#pause", as: :public_regia_pause
|
||||
post "regia/:token/resume", to: "public/regia#resume", as: :public_regia_resume
|
||||
post "regia/:token/audio_mute", to: "public/regia#audio_mute", as: :public_regia_audio_mute
|
||||
post "regia/:token/stop", to: "public/regia#stop", as: :public_regia_stop
|
||||
|
||||
root to: "public/pages#home"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddAudioMutedToStreamSessions < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
add_column :stream_sessions, :audio_muted, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
||||
Generated
+2
-1
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.2].define(version: 2026_06_12_120000) do
|
||||
ActiveRecord::Schema[7.2].define(version: 2026_08_12_220000) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pgcrypto"
|
||||
enable_extension "plpgsql"
|
||||
@@ -280,6 +280,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_06_12_120000) do
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "regia_token_digest"
|
||||
t.datetime "regia_token_expires_at"
|
||||
t.boolean "audio_muted", default: false, null: false
|
||||
t.index ["match_id"], name: "index_stream_sessions_on_match_id"
|
||||
t.index ["publish_token"], name: "index_stream_sessions_on_publish_token", unique: true
|
||||
t.index ["regia_token_digest"], name: "index_stream_sessions_on_regia_token_digest", unique: true
|
||||
|
||||
@@ -165,6 +165,10 @@
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.regia-btn--outline.is-muted {
|
||||
border-color: var(--regia-yellow);
|
||||
color: var(--regia-yellow);
|
||||
}
|
||||
.regia-btn--danger {
|
||||
background: transparent;
|
||||
border: 1px solid var(--regia-red);
|
||||
|
||||
@@ -25,9 +25,12 @@
|
||||
partialsPrefix: "Parziali:",
|
||||
resumed: "Diretta ripresa",
|
||||
pausedCover: "Diretta in pausa — copertina in onda",
|
||||
muted: "Audio silenziato — YouTube non riceve il microfono",
|
||||
unmuted: "Audio microfono riattivato",
|
||||
closed: "Diretta chiusa",
|
||||
resumeError: "Errore ripresa diretta",
|
||||
pauseError: "Errore pausa diretta",
|
||||
muteError: "Errore silenziamento audio",
|
||||
closeError: "Errore chiusura",
|
||||
closeConfirm: "Chiudere definitivamente la diretta?",
|
||||
linkUnavailable: "Link non disponibile",
|
||||
@@ -39,6 +42,8 @@
|
||||
streamEnded: "Diretta terminata",
|
||||
resumeLabel: "Riprendi diretta",
|
||||
pauseLabel: "Metti in pausa",
|
||||
muteLabel: "Silenzia audio",
|
||||
unmuteLabel: "Riattiva audio",
|
||||
endedBadge: "Terminata",
|
||||
pausedBadge: "In pausa",
|
||||
liveBadge: "In onda",
|
||||
@@ -76,6 +81,7 @@
|
||||
scoreUrl: root.dataset.scoreUrl,
|
||||
pauseUrl: root.dataset.pauseUrl,
|
||||
resumeUrl: root.dataset.resumeUrl,
|
||||
audioMuteUrl: root.dataset.audioMuteUrl,
|
||||
stopUrl: root.dataset.stopUrl,
|
||||
cableUrl: root.dataset.cableUrl,
|
||||
hlsUrl: root.dataset.hlsUrl,
|
||||
@@ -103,6 +109,7 @@
|
||||
preview: document.getElementById("regia-preview"),
|
||||
previewPlaceholder: document.getElementById("regia-preview-placeholder"),
|
||||
btnPause: document.getElementById("btn-pause"),
|
||||
btnAudioMute: document.getElementById("btn-audio-mute"),
|
||||
subtitle: document.getElementById("regia-subtitle")
|
||||
};
|
||||
|
||||
@@ -111,6 +118,7 @@
|
||||
let previewStarted = false;
|
||||
let wasPausedPreview = false;
|
||||
let streamPaused = root.dataset.initialPaused === "true";
|
||||
let audioMuted = root.dataset.initialAudioMuted === "true";
|
||||
|
||||
function hlsUrlFresh() {
|
||||
const sep = cfg.hlsUrl.includes("?") ? "&" : "?";
|
||||
@@ -246,8 +254,18 @@
|
||||
els.btnPause.textContent = paused ? I18N.resumeLabel : I18N.pauseLabel;
|
||||
}
|
||||
|
||||
function syncMuteButton(data) {
|
||||
if (!els.btnAudioMute) return;
|
||||
if (typeof data.audio_muted === "boolean") {
|
||||
audioMuted = data.audio_muted;
|
||||
}
|
||||
els.btnAudioMute.textContent = audioMuted ? I18N.unmuteLabel : I18N.muteLabel;
|
||||
els.btnAudioMute.classList.toggle("is-muted", audioMuted);
|
||||
}
|
||||
|
||||
function setBadge(data) {
|
||||
syncPauseButton(data);
|
||||
syncMuteButton(data);
|
||||
if (data.stream_closed) {
|
||||
els.badge.textContent = I18N.endedBadge;
|
||||
els.badge.className = "regia-badge regia-badge--ended";
|
||||
@@ -458,6 +476,24 @@
|
||||
toast(wasPaused ? I18N.resumed : I18N.pausedCover);
|
||||
}
|
||||
|
||||
async function toggleMuteAudio() {
|
||||
if (!cfg.audioMuteUrl) return;
|
||||
const nextMuted = !audioMuted;
|
||||
const res = await fetch(cfg.audioMuteUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
||||
body: JSON.stringify({ muted: nextMuted })
|
||||
});
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
throw new Error(body.error || I18N.muteError);
|
||||
}
|
||||
const data = await res.json();
|
||||
applyScorePayload(data);
|
||||
setBadge(data);
|
||||
toast(audioMuted ? I18N.muted : I18N.unmuted);
|
||||
}
|
||||
|
||||
async function stopStream() {
|
||||
const ok = confirm(I18N.closeConfirm);
|
||||
if (!ok) return null;
|
||||
@@ -471,6 +507,7 @@
|
||||
}
|
||||
|
||||
els.btnPause?.addEventListener("click", () => togglePauseStream().catch((e) => toast(e.message)));
|
||||
els.btnAudioMute?.addEventListener("click", () => toggleMuteAudio().catch((e) => toast(e.message)));
|
||||
document.getElementById("btn-stop")?.addEventListener("click", () => stopStream().catch((e) => toast(e.message)));
|
||||
|
||||
async function shareLink(url, title, text) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Api::V1 stream session audio mute", type: :request do
|
||||
let!(:user) { User.create!(email: "mute-api@test.it", name: "U", password: "Password123", role: "coach") }
|
||||
let!(:club) { Club.create!(name: "C", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
|
||||
let!(:owner) { club.club_memberships.create!(user: user, role: "owner") }
|
||||
let!(:team) { club.teams.create!(name: "T", sport: "volleyball") }
|
||||
let!(:match) { team.matches.create!(opponent_name: "Opp", sport: "volleyball") }
|
||||
let!(:session) { StreamSession.create!(match: match, user: user, platform: "youtube", status: "live") }
|
||||
|
||||
def auth_headers
|
||||
post "/api/v1/auth/login", params: { email: user.email, password: "Password123" }
|
||||
token = response.parsed_body["access_token"]
|
||||
{ "Authorization" => "Bearer #{token}", "Content-Type" => "application/json" }
|
||||
end
|
||||
|
||||
it "include audio_muted nello show della sessione" do
|
||||
get "/api/v1/sessions/#{session.id}", headers: auth_headers
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.parsed_body["audio_muted"]).to eq(false)
|
||||
end
|
||||
|
||||
it "silenzia e riattiva l'audio via PATCH" do
|
||||
patch "/api/v1/sessions/#{session.id}/audio_mute",
|
||||
params: { muted: true }.to_json,
|
||||
headers: auth_headers
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.parsed_body["audio_muted"]).to eq(true)
|
||||
expect(session.reload.audio_muted).to eq(true)
|
||||
|
||||
patch "/api/v1/sessions/#{session.id}/audio_mute",
|
||||
params: { muted: false }.to_json,
|
||||
headers: auth_headers
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.parsed_body["audio_muted"]).to eq(false)
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,7 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Public regia", type: :request do
|
||||
let!(:user) { User.create!(email: "regia@test.it", name: "U", password: "password123", role: "coach") }
|
||||
let!(:user) { User.create!(email: "regia@test.it", name: "U", password: "Password123", role: "coach") }
|
||||
let!(:club) { Club.create!(name: "C", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
|
||||
let!(:team) { club.teams.create!(name: "T", sport: "volleyball") }
|
||||
let!(:match) { team.matches.create!(opponent_name: "Opp", sport: "volleyball") }
|
||||
@@ -148,6 +148,29 @@ RSpec.describe "Public regia", type: :request do
|
||||
expect(session.reload.status).to eq("connecting")
|
||||
end
|
||||
|
||||
it "silenzia e riattiva l'audio della diretta" do
|
||||
token = Sessions::RegiaAccess.new(session).issue_token!
|
||||
expect(session.audio_muted).to eq(false)
|
||||
|
||||
post public_regia_audio_mute_path(token), params: { muted: true }, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.parsed_body["audio_muted"]).to eq(true)
|
||||
expect(session.reload.audio_muted).to eq(true)
|
||||
|
||||
post public_regia_audio_mute_path(token), params: { muted: false }, as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.parsed_body["audio_muted"]).to eq(false)
|
||||
expect(session.reload.audio_muted).to eq(false)
|
||||
end
|
||||
|
||||
it "mostra il pulsante mute sulla pagina regia" do
|
||||
token = Sessions::RegiaAccess.new(session).issue_token!
|
||||
get public_regia_path(token)
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include("Silenzia audio")
|
||||
expect(response.body).to include("btn-audio-mute")
|
||||
end
|
||||
|
||||
it "emette token valido anche oltre le 8 ore dalla partenza" do
|
||||
session.update!(started_at: 9.hours.ago)
|
||||
token = Sessions::RegiaAccess.new(session).issue_token!
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe Sessions::SetAudioMute do
|
||||
let!(:user) { User.create!(email: "mute@test.it", name: "U", password: "Password123", role: "coach") }
|
||||
let!(:club) { Club.create!(name: "C", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
|
||||
let!(:team) { club.teams.create!(name: "T", sport: "volleyball") }
|
||||
let!(:match) { team.matches.create!(opponent_name: "Opp", sport: "volleyball") }
|
||||
let!(:session) { StreamSession.create!(match: match, user: user, platform: "matchlivetv", status: "live") }
|
||||
|
||||
it "attiva il mute e persiste lo stato" do
|
||||
described_class.new(session, muted: true).call
|
||||
expect(session.reload.audio_muted).to eq(true)
|
||||
expect(session.stream_events.order(:occurred_at).last.event_type).to eq("audio_muted")
|
||||
end
|
||||
|
||||
it "disattiva il mute" do
|
||||
session.update!(audio_muted: true)
|
||||
described_class.new(session, muted: false).call
|
||||
expect(session.reload.audio_muted).to eq(false)
|
||||
expect(session.stream_events.order(:occurred_at).last.event_type).to eq("audio_unmuted")
|
||||
end
|
||||
|
||||
it "è idempotente se lo stato è già quello richiesto" do
|
||||
described_class.new(session, muted: false).call
|
||||
expect(session.stream_events.count).to eq(0)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user