Mantiene la copertina YouTube se il telefono cade e avvia ffmpeg sul nodo, non via SSH.

Il relay HLS→RTMPS resta sul worker/agent del nodo assegnato, così tre dirette contemporanee restano in onda sul sito e sul canale della società senza schermo nero.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-16 20:41:46 +02:00
co-authored by Cursor
parent bfb3115a64
commit baa6283a15
36 changed files with 1444 additions and 141 deletions
+73 -7
View File
@@ -4,6 +4,7 @@ require "rails_helper"
RSpec.describe "HLS proxy", type: :request do
let(:mediamtx_hls) { "http://mediamtx.test:8888" }
let(:session_id) { "cc7e90b2-c672-4401-b5d3-51fdcdc7a214" }
let(:playlist) do
<<~M3U8
#EXTM3U
@@ -13,24 +14,89 @@ RSpec.describe "HLS proxy", type: :request do
/live/match_#{session_id}/segment0.ts
M3U8
end
let(:session_id) { "cc7e90b2-c672-4401-b5d3-51fdcdc7a214" }
def stub_hls_get(url, status:, body: "", headers: {}, location: nil)
response_headers = headers.dup
response_headers["location"] = location if location
faraday_response = instance_double(
Faraday::Response,
status: status,
body: body,
headers: response_headers
)
conn = instance_double(Faraday::Connection)
allow_any_instance_of(HlsProxyController).to receive(:hls_conn).and_return(conn)
allow(conn).to receive(:get) do |requested, &block|
expect(requested).to start_with(url.split("?").first)
block&.call(instance_double(Faraday::Request, headers: {}))
faraday_response
end
end
before do
allow(MatchLiveTv).to receive(:mediamtx_hls_url).and_return(mediamtx_hls)
end
describe "GET /live/match_:session_id/*" do
it "proxies MediaMTX redirect and rewrites playlist paths to /hls/" do
stub_request(:get, "#{mediamtx_hls}/live/match_#{session_id}/index.m3u8")
.to_return(status: 302, headers: { "Location" => "/live/match_#{session_id}/index.m3u8?cookieCheck=1" })
stub_request(:get, "#{mediamtx_hls}/live/match_#{session_id}/index.m3u8?cookieCheck=1")
.to_return(status: 200, body: playlist, headers: { "Content-Type" => "application/vnd.apple.mpegurl" })
it "rewrites playlist paths to /hls/" do
stub_hls_get(
"#{mediamtx_hls}/live/match_#{session_id}/index.m3u8",
status: 200,
body: playlist,
headers: { "content-type" => "application/vnd.apple.mpegurl" }
)
get "/live/match_#{session_id}/index.m3u8"
expect(response).to have_http_status(:ok)
expect(response.body).to include("/hls/live/match_#{session_id}/segment0.ts")
expect(response.body).not_to include("/live/match_#{session_id}/segment0.ts")
end
end
describe "GET /hls/live/match_:id on a lab node" do
let!(:lab) do
StreamNode.create!(
slug: "ingest-lab-hls-spec",
hostname: "ingest-lab-hls-spec.lab.mltv-stream.net",
role: "lab",
status: "ready",
provider: "local",
rtmp_base_url: "rtmp://127.0.0.1:11935",
hls_base_url: "http://localhost:3000/hls",
api_base_url: "http://mediamtx_lab:9997",
internal_rtmp_url: "rtmp://mediamtx_lab:1935",
internal_hls_url: "http://mediamtx_lab:8888"
)
end
before do
user = User.create!(email: "hls-lab@test.com", name: "H", password: "Password123", role: "coach")
club = Club.create!(name: "HlsLab", sport: "volleyball")
team = club.teams.create!(name: "T", sport: "volleyball", slug: "hls-lab-t")
match = team.matches.create!(opponent_name: "X")
StreamSession.create!(id: session_id, match: match, user: user, platform: "matchlivetv", stream_node: lab)
end
it "proxies HLS to the session MediaMTX, not home" do
requested = []
faraday_response = instance_double(
Faraday::Response,
status: 200,
body: playlist,
headers: { "content-type" => "application/vnd.apple.mpegurl" }
)
conn = instance_double(Faraday::Connection)
allow_any_instance_of(HlsProxyController).to receive(:hls_conn).and_return(conn)
allow(conn).to receive(:get) do |url, &block|
requested << url
block&.call(instance_double(Faraday::Request, headers: {}))
faraday_response
end
get "/hls/live/match_#{session_id}/index.m3u8"
expect(response).to have_http_status(:ok)
expect(requested.first).to eq("http://mediamtx_lab:8888/live/match_#{session_id}/index.m3u8")
end
end
end