From 1677df7f867d538b6eb9fd46c8fc23eb4353b90e Mon Sep 17 00:00:00 2001 From: Emiliano Frascaro Date: Tue, 2 Jun 2026 23:00:34 +0200 Subject: [PATCH] =?UTF-8?q?Visibilit=C3=A0=20pubblico/link-only=20su=20tut?= =?UTF-8?q?te=20le=20piattaforme=20di=20streaming.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default pubblico; opzione unica privato/non in elenco per Match Live TV, YouTube piattaforma e canale società. Elenco dirette solo sessioni pubbliche. Co-authored-by: Cursor --- .../app/controllers/public/live_controller.rb | 1 + backend/app/models/stream_session.rb | 17 +++++++ backend/app/services/sessions/create.rb | 6 ++- .../app/services/youtube/broadcast_service.rb | 2 +- .../setup_wizard/step_transmission.dart | 48 +++++++------------ mobile/lib/shared/api_client.dart | 2 +- mobile/lib/shared/models/stream_session.dart | 4 +- 7 files changed, 45 insertions(+), 35 deletions(-) diff --git a/backend/app/controllers/public/live_controller.rb b/backend/app/controllers/public/live_controller.rb index ab37b0e..8395f1b 100644 --- a/backend/app/controllers/public/live_controller.rb +++ b/backend/app/controllers/public/live_controller.rb @@ -11,6 +11,7 @@ module Public sessions = StreamSession .broadcasting + .publicly_listed .includes(:score_state, match: { team: :club }) sessions = sessions.joins(:match).where(matches: { team_id: team_ids }) if team_ids.present? diff --git a/backend/app/models/stream_session.rb b/backend/app/models/stream_session.rb index 31666dd..d4be156 100644 --- a/backend/app/models/stream_session.rb +++ b/backend/app/models/stream_session.rb @@ -3,6 +3,7 @@ class StreamSession < ApplicationRecord PLATFORMS = %w[matchlivetv youtube facebook twitch].freeze STATUSES = %w[idle connecting live reconnecting paused ended error].freeze + PRIVACY_STATUSES = %w[public unlisted private].freeze belongs_to :match belongs_to :user @@ -12,10 +13,13 @@ class StreamSession < ApplicationRecord validates :platform, inclusion: { in: PLATFORMS } validates :status, inclusion: { in: STATUSES } + validates :privacy_status, inclusion: { in: PRIVACY_STATUSES } + before_validation :normalize_privacy_status before_validation :ensure_publish_token, on: :create scope :broadcasting, -> { where(status: %w[live connecting reconnecting]) } + scope :publicly_listed, -> { where(privacy_status: "public") } scope :search_by_team_or_opponent, lambda { |query| q = query.to_s.strip return all if q.blank? @@ -105,6 +109,14 @@ class StreamSession < ApplicationRecord end end + def link_only? + privacy_status.in?(%w[unlisted private]) + end + + def publicly_listed? + privacy_status == "public" + end + def terminal? status.in?(%w[ended error]) end @@ -151,6 +163,11 @@ class StreamSession < ApplicationRecord self.publish_token ||= SecureRandom.urlsafe_base64(32) end + def normalize_privacy_status + self.privacy_status = "public" if privacy_status.blank? + self.privacy_status = "unlisted" if privacy_status == "private" + end + def record_ended_timestamps! now = Time.current update!(ended_at: now) if ended_at.nil? diff --git a/backend/app/services/sessions/create.rb b/backend/app/services/sessions/create.rb index a643c83..e69823f 100644 --- a/backend/app/services/sessions/create.rb +++ b/backend/app/services/sessions/create.rb @@ -1,5 +1,9 @@ module Sessions class Create + def self.normalize_privacy(value) + value.to_s == "public" ? "public" : "unlisted" + end + def initialize(user:, match:, params:) @user = user @match = match @@ -16,7 +20,7 @@ module Sessions match: @match, user: @user, platform: platform, - privacy_status: @params[:privacy_status] || "unlisted", + privacy_status: Sessions::Create.normalize_privacy(@params[:privacy_status]), quality_preset: @params[:quality_preset] || "720p_30_2.5mbps", target_bitrate: @params[:target_bitrate] || 2_500_000, target_fps: @params[:target_fps] || 30, diff --git a/backend/app/services/youtube/broadcast_service.rb b/backend/app/services/youtube/broadcast_service.rb index 4c68f56..c95e959 100644 --- a/backend/app/services/youtube/broadcast_service.rb +++ b/backend/app/services/youtube/broadcast_service.rb @@ -8,7 +8,7 @@ module Youtube @credential = @resolver.resolve end - def create_broadcast!(title:, privacy_status: "unlisted", scheduled_at: nil) + def create_broadcast!(title:, privacy_status: "public", scheduled_at: nil) return mock_broadcast if @credential.blank? || missing_oauth_config? client = authorized_client diff --git a/mobile/lib/features/setup_wizard/step_transmission.dart b/mobile/lib/features/setup_wizard/step_transmission.dart index 9cc055e..75e0307 100644 --- a/mobile/lib/features/setup_wizard/step_transmission.dart +++ b/mobile/lib/features/setup_wizard/step_transmission.dart @@ -30,7 +30,7 @@ class StepTransmission extends ConsumerStatefulWidget { class _StepTransmissionState extends ConsumerState { String _platform = 'matchlivetv'; - String _privacy = 'unlisted'; + bool _linkOnly = false; String? _youtubeChannel; bool _creating = false; @@ -60,7 +60,7 @@ class _StepTransmissionState extends ConsumerState { widget.match.id, platform: _platform, youtubeChannel: _platform == 'youtube' ? _youtubeChannel : null, - privacyStatus: _privacy, + privacyStatus: _linkOnly ? 'unlisted' : 'public', qualityPreset: '720p_30_2.5mbps', targetBitrate: 2500000, targetFps: 30, @@ -213,39 +213,27 @@ class _StepTransmissionState extends ConsumerState { ), ], const SizedBox(height: 24), - Text('Visibilità video', style: theme.textTheme.titleLarge), + Text('Visibilità', style: theme.textTheme.titleLarge), const SizedBox(height: 8), Text( - 'Solo per YouTube. Su Match Live TV la pagina diretta segue le impostazioni del sito.', + 'Vale per Match Live TV e YouTube (canale nostro o della società). ' + 'Di default la diretta è pubblica.', style: theme.textTheme.bodySmall, ), - const SizedBox(height: 12), - SegmentedButton( - segments: const [ - ButtonSegment( - value: 'unlisted', - label: Text('Non in elenco'), - icon: Icon(Icons.link), - ), - ButtonSegment( - value: 'private', - label: Text('Privato'), - icon: Icon(Icons.lock_outline), - ), - ], - selected: {_privacy}, - onSelectionChanged: _platform == 'youtube' - ? (s) => setState(() => _privacy = s.first) - : null, - ), - if (_platform != 'youtube') - Padding( - padding: const EdgeInsets.only(top: 8), - child: Text( - 'Seleziona YouTube Live per impostare la visibilità del video.', - style: theme.textTheme.bodySmall?.copyWith(color: AppTheme.textSecondary), - ), + const SizedBox(height: 8), + SwitchListTile( + contentPadding: EdgeInsets.zero, + value: _linkOnly, + onChanged: (v) => setState(() => _linkOnly = v), + title: const Text('Privato / non in elenco'), + subtitle: Text( + _platform == 'matchlivetv' + ? 'Non compare nell\'elenco dirette del sito. Solo chi ha il link.' + : _platform == 'youtube' + ? 'Non compare su YouTube in ricerca o nel canale. Solo chi ha il link.' + : 'Visibile solo a chi ha il link, non in elenco pubblico.', ), + ), const SizedBox(height: 24), Text('Qualità', style: theme.textTheme.titleLarge), const SizedBox(height: 12), diff --git a/mobile/lib/shared/api_client.dart b/mobile/lib/shared/api_client.dart index 9267284..e589b6e 100644 --- a/mobile/lib/shared/api_client.dart +++ b/mobile/lib/shared/api_client.dart @@ -154,7 +154,7 @@ class ApiClient { String matchId, { String platform = 'matchlivetv', String? youtubeChannel, - String privacyStatus = 'unlisted', + String privacyStatus = 'public', String qualityPreset = '720p_30_2.5mbps', int? targetBitrate, int? targetFps, diff --git a/mobile/lib/shared/models/stream_session.dart b/mobile/lib/shared/models/stream_session.dart index 9f59509..e2c4dca 100644 --- a/mobile/lib/shared/models/stream_session.dart +++ b/mobile/lib/shared/models/stream_session.dart @@ -13,7 +13,7 @@ class StreamSession { this.shareUrl, this.youtubeWatchUrl, this.youtubeBroadcastId, - this.privacyStatus = 'unlisted', + this.privacyStatus = 'public', this.qualityPreset = '720p_30_2.5mbps', this.targetBitrate = 2500000, this.startedAt, @@ -62,7 +62,7 @@ class StreamSession { shareUrl: json['share_url'] as String?, youtubeWatchUrl: json['youtube_watch_url'] as String?, youtubeBroadcastId: json['youtube_broadcast_id'] as String?, - privacyStatus: json['privacy_status'] as String? ?? 'unlisted', + privacyStatus: json['privacy_status'] as String? ?? 'public', qualityPreset: json['quality_preset'] as String? ?? '720p_30_2.5mbps', targetBitrate: json['target_bitrate'] as int? ?? 2500000, startedAt: json['started_at'] != null