Replay fuori da Puma, fix ops cron e firma release Android.

Dopo auth Rails redirect a URL presigned su /media/ (edge → Garage); aumenta
RAILS_MAX_THREADS, corregge ingest log ops via stdin e keystore Play Console.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-13 22:59:15 +02:00
parent e1636139a9
commit 13390d8a3c
14 changed files with 214 additions and 11 deletions

View File

@@ -52,6 +52,15 @@ module Public
private
def serve_video(disposition:)
expires_in = disposition == "attachment" ? MatchLiveTv.replay_download_url_ttl_seconds : MatchLiveTv.replay_presigned_url_ttl_seconds
redirect_url = Recordings::PublicMediaUrl.new(
key: @recording.storage_key,
expires_in: expires_in,
disposition: disposition,
filename: disposition == "attachment" ? download_filename : nil
).call
return redirect_to redirect_url, allow_other_host: true if redirect_url.present?
Recordings::ServeFromStorage.new(
key: @recording.storage_key,
content_type: "video/mp4",
@@ -66,6 +75,9 @@ module Public
key = @recording.thumbnail_storage_key
return head :not_found if key.blank?
redirect_url = Recordings::PublicMediaUrl.new(key: key, expires_in: MatchLiveTv.replay_presigned_url_ttl_seconds).call
return redirect_to redirect_url, allow_other_host: true if redirect_url.present?
Recordings::ServeFromStorage.new(
key: key,
content_type: "image/jpeg",

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
module Recordings
# URL presigned S3 riscritto sul path pubblico /media/ (edge → Garage).
# Rails fa solo auth; il browser scarica/streama senza occupare Puma.
class PublicMediaUrl
def initialize(key:, expires_in: MatchLiveTv.replay_presigned_url_ttl_seconds, disposition: "inline", filename: nil)
@key = key
@expires_in = expires_in
@disposition = disposition
@filename = filename
end
def call
return nil if @key.blank?
return nil unless MatchLiveTv.replay_media_redirect_enabled?
raw = Recordings::Storage.new.presigned_get_url(
key: @key,
expires_in: @expires_in,
response_content_disposition: content_disposition_param
)
return nil if raw.blank?
rewrite_to_public(raw)
end
private
def rewrite_to_public(raw)
uri = URI.parse(raw)
base = MatchLiveTv.replay_media_public_base_url.chomp("/")
query = uri.query.presence
path = uri.path.start_with?("/") ? uri.path : "/#{uri.path}"
query ? "#{base}#{path}?#{query}" : "#{base}#{path}"
end
def content_disposition_param
return nil unless @disposition == "attachment" && @filename.present?
%(attachment; filename="#{@filename.gsub('"', '\\"')}")
end
end
end

View File

@@ -31,8 +31,12 @@ module Recordings
dest
end
def presigned_get_url(key:, expires_in: MatchLiveTv.replay_presigned_url_ttl_seconds)
@backend.presigned_get_url(key: key, expires_in: expires_in)
def presigned_get_url(key:, expires_in: MatchLiveTv.replay_presigned_url_ttl_seconds, response_content_disposition: nil)
@backend.presigned_get_url(
key: key,
expires_in: expires_in,
response_content_disposition: response_content_disposition
)
end
def local_path_for_key(key)
@@ -61,7 +65,7 @@ module Recordings
true
end
def presigned_get_url(key:, expires_in:)
def presigned_get_url(key:, expires_in:, response_content_disposition: nil)
nil
end
@@ -92,14 +96,15 @@ module Recordings
raise Error, e.message
end
def presigned_get_url(key:, expires_in:)
def presigned_get_url(key:, expires_in:, response_content_disposition: nil)
signer = Aws::S3::Presigner.new(client: client)
signer.presigned_url(
:get_object,
params = {
bucket: MatchLiveTv.replay_storage_bucket,
key: key,
expires_in: expires_in
)
}
params[:response_content_disposition] = response_content_disposition if response_content_disposition.present?
signer.presigned_url(:get_object, **params)
end
def local_path_for_key(_key)

View File

@@ -141,6 +141,16 @@ module MatchLiveTv
ENV.fetch("REPLAY_DOWNLOAD_URL_TTL_SECONDS", "900").to_i
end
def replay_media_public_base_url
ENV.fetch("REPLAY_MEDIA_PUBLIC_BASE_URL") { "#{app_public_url.chomp('/')}/media" }
end
def replay_media_redirect_enabled?
return false if replay_storage_local?
ENV.fetch("REPLAY_MEDIA_REDIRECT", "true") == "true"
end
def google_analytics_measurement_id
ENV["GOOGLE_ANALYTICS_MEASUREMENT_ID"].presence
end

View File

@@ -0,0 +1,51 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe Recordings::PublicMediaUrl do
before do
allow(MatchLiveTv).to receive(:replay_storage_local?).and_return(false)
allow(MatchLiveTv).to receive(:replay_media_redirect_enabled?).and_return(true)
allow(MatchLiveTv).to receive(:replay_media_public_base_url).and_return("https://www.matchlivetv.it/media")
allow(MatchLiveTv).to receive(:replay_presigned_url_ttl_seconds).and_return(7200)
end
it "riscrive l'URL presigned Garage sul path pubblico /media/" do
storage = instance_double(Recordings::Storage)
allow(Recordings::Storage).to receive(:new).and_return(storage)
allow(storage).to receive(:presigned_get_url).and_return(
"http://garage:3900/matchlivetv-replays/teams/abc/video.mp4?X-Amz-Signature=abc"
)
url = described_class.new(key: "teams/abc/video.mp4").call
expect(url).to eq(
"https://www.matchlivetv.it/media/matchlivetv-replays/teams/abc/video.mp4?X-Amz-Signature=abc"
)
end
it "restituisce nil se il redirect è disabilitato" do
allow(MatchLiveTv).to receive(:replay_media_redirect_enabled?).and_return(false)
expect(described_class.new(key: "teams/abc/video.mp4").call).to be_nil
end
it "passa content-disposition per i download" do
storage = instance_double(Recordings::Storage)
allow(Recordings::Storage).to receive(:new).and_return(storage)
allow(storage).to receive(:presigned_get_url).and_return("http://garage:3900/bucket/key.mp4?sig=1")
described_class.new(
key: "key.mp4",
disposition: "attachment",
filename: "partita.mp4"
).call
expect(storage).to have_received(:presigned_get_url).with(
hash_including(
key: "key.mp4",
response_content_disposition: %(attachment; filename="partita.mp4")
)
)
end
end