App: scelta canale YouTube MLTV/società, privacy e condivisione link.

Premium Full può forzare il canale piattaforma; visibilità non in elenco o privato;
share_url unificato per Match Live TV e YouTube.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-02 22:52:07 +02:00
parent 6ac4db98bb
commit 1fb5cd5aa2
14 changed files with 362 additions and 68 deletions

View File

@@ -118,7 +118,7 @@ module Api
end
def session_params
params.permit(:platform, :privacy_status, :quality_preset, :target_bitrate, :target_fps)
params.permit(:platform, :privacy_status, :quality_preset, :target_bitrate, :target_fps, :youtube_channel)
end
def session_json(session, detail: false)
@@ -130,6 +130,8 @@ module Api
rtmp_ingest_url: session.rtmp_ingest_url,
hls_playback_url: session.hls_playback_url,
watch_page_url: session.watch_page_url,
share_url: session.share_url,
youtube_watch_url: session.youtube_watch_url,
youtube_broadcast_id: session.youtube_broadcast_id,
privacy_status: session.privacy_status,
quality_preset: session.quality_preset,

View File

@@ -71,6 +71,7 @@ module Api
def team_json(team, detail: false)
ent = team.entitlements
yt = Youtube::TeamStatus.new(team)
resolver = Youtube::CredentialResolver.new(team)
data = {
id: team.id,
name: team.name,
@@ -84,6 +85,9 @@ module Api
youtube_selectable: yt.selectable?,
youtube_channel_title: yt.channel_title,
youtube_uses_platform_channel: yt.uses_platform_channel?,
youtube_platform_available: resolver.platform_available?,
youtube_team_channel_available: resolver.team_channel_available?,
youtube_team_channel_title: team.youtube_credential&.channel_title,
plan_slug: ent.plan.slug,
plan_name: ent.plan.name,
premium_active: ent.premium_active?,

View File

@@ -90,6 +90,21 @@ class StreamSession < ApplicationRecord
"#{MatchLiveTv.app_public_url.chomp('/')}/live/#{id}"
end
def youtube_watch_url
return nil if youtube_broadcast_id.blank?
return nil if youtube_broadcast_id.to_s.start_with?("mock_")
"https://www.youtube.com/watch?v=#{youtube_broadcast_id}"
end
def share_url
if platform == "youtube"
youtube_watch_url.presence || watch_page_url
else
watch_page_url
end
end
def terminal?
status.in?(%w[ended error])
end

View File

@@ -24,7 +24,9 @@ module Sessions
)
if session.platform == "youtube"
attach_youtube_broadcast!(session)
youtube_channel = @params[:youtube_channel].presence
assert_youtube_channel!(youtube_channel)
attach_youtube_broadcast!(session, youtube_channel: youtube_channel)
end
StreamSession.transaction do
@@ -39,8 +41,18 @@ module Sessions
private
def attach_youtube_broadcast!(session)
yt = Youtube::BroadcastService.new(session.match.team)
def assert_youtube_channel!(youtube_channel)
resolver = Youtube::CredentialResolver.new(@match.team, channel: youtube_channel)
if resolver.resolve.blank?
raise Teams::EntitlementError.new(
"Canale YouTube selezionato non disponibile",
code: "youtube_channel_unavailable"
)
end
end
def attach_youtube_broadcast!(session, youtube_channel: nil)
yt = Youtube::BroadcastService.new(session.match.team, youtube_channel: youtube_channel)
title = "#{session.match.team.name} vs #{session.match.opponent_name}"
broadcast = yt.create_broadcast!(
title: title,

View File

@@ -2,9 +2,9 @@ module Youtube
class BroadcastService
class Error < StandardError; end
def initialize(team)
def initialize(team, youtube_channel: nil)
@team = team
@resolver = CredentialResolver.new(team)
@resolver = CredentialResolver.new(team, channel: youtube_channel)
@credential = @resolver.resolve
end

View File

@@ -1,25 +1,61 @@
module Youtube
class CredentialResolver
def self.for_team(team)
new(team).resolve
VALID_CHANNELS = %w[platform team].freeze
def self.for_team(team, channel: nil)
new(team, channel: channel).resolve
end
def initialize(team)
def initialize(team, channel: nil)
@team = team
@status = TeamStatus.new(team)
@mode = team.entitlements.plan.youtube_mode
@channel = channel.to_s.presence
@channel = nil unless VALID_CHANNELS.include?(@channel)
@ent = team.entitlements
@mode = @ent.plan.youtube_mode
end
def resolve
if @status.uses_platform_channel?
PlatformCredential.new
elsif @mode == "team"
case effective_channel
when :platform
PlatformCredential.configured? ? PlatformCredential.new : nil
when :team
@team.youtube_credential
end
end
def uses_platform_channel?
@status.uses_platform_channel?
effective_channel == :platform
end
def platform_available?
@ent.youtube_enabled? && PlatformCredential.configured?
end
def team_channel_available?
@ent.youtube_enabled? && @ent.premium_full? && @mode == "team" && @team.youtube_credential.present?
end
def effective_channel
if @channel == "platform"
return :platform if platform_available?
elsif @channel == "team"
return :team if team_channel_available?
end
auto_channel
end
private
def auto_channel
if @mode == "matchlivetv_light"
return :platform if platform_available?
elsif @mode == "team"
return :team if team_channel_available?
return :platform if platform_available?
end
nil
end
end
end