Aggiunge modulo Replay, Garage dev e YouTube a livello società.

Pipeline registrazione/upload con storage S3, archivio web e app, proxy replay
per il browser, OAuth YouTube sulla pagina club (Premium Full) e footer legale.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 07:53:11 +02:00
parent a87cda156b
commit 1f273f849d
87 changed files with 2952 additions and 195 deletions

View File

@@ -0,0 +1,123 @@
require "open-uri"
require "aws-sdk-s3"
module Recordings
class Storage
class Error < StandardError; end
def initialize
@backend = MatchLiveTv.replay_storage_local? ? LocalBackend.new : S3Backend.new
end
def upload(local_path:, key:, content_type: "video/mp4")
@backend.upload(local_path: local_path, key: key, content_type: content_type)
end
def delete(key:)
@backend.delete(key: key)
end
def download_to_temp(key:)
local = local_path_for_key(key)
return local if local && File.exist?(local)
url = presigned_get_url(key: key, expires_in: 600)
raise Error, "Impossibile scaricare file dallo storage" if url.blank?
dest = File.join(Dir.tmpdir, "replay-dl-#{SecureRandom.hex(8)}.mp4")
URI.open(url) do |remote|
File.binwrite(dest, remote.read)
end
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)
end
def local_path_for_key(key)
@backend.local_path_for_key(key)
end
def backend_name
@backend.name
end
class LocalBackend
def name
"local"
end
def upload(local_path:, key:, content_type:)
dest = local_path_for_key(key)
FileUtils.mkdir_p(File.dirname(dest))
FileUtils.cp(local_path, dest)
File.size(dest)
end
def delete(key:)
path = local_path_for_key(key)
FileUtils.rm_f(path)
true
end
def presigned_get_url(key:, expires_in:)
nil
end
def local_path_for_key(key)
File.join(MatchLiveTv.recordings_local_path, "replays", key)
end
end
class S3Backend
def name
"s3"
end
def upload(local_path:, key:, content_type:)
client.put_object(
bucket: MatchLiveTv.replay_storage_bucket,
key: key,
body: File.open(local_path, "rb"),
content_type: content_type
)
File.size(local_path)
end
def delete(key:)
client.delete_object(bucket: MatchLiveTv.replay_storage_bucket, key: key)
true
rescue Aws::S3::Errors::ServiceError => e
raise Error, e.message
end
def presigned_get_url(key:, expires_in:)
signer = Aws::S3::Presigner.new(client: client)
signer.presigned_url(
:get_object,
bucket: MatchLiveTv.replay_storage_bucket,
key: key,
expires_in: expires_in
)
end
def local_path_for_key(_key)
nil
end
private
def client
@client ||= Aws::S3::Client.new(
access_key_id: MatchLiveTv.replay_storage_access_key_id,
secret_access_key: MatchLiveTv.replay_storage_secret_access_key,
endpoint: MatchLiveTv.replay_storage_endpoint,
region: MatchLiveTv.replay_storage_region,
force_path_style: MatchLiveTv.replay_storage_force_path_style?,
response_checksum_validation: "WHEN_REQUIRED"
)
end
end
end
end