veza/docs/archive/V0_703_RELEASE_SCOPE.md
senke 63867f1d09
Some checks failed
Backend API CI / test-unit (push) Failing after 0s
Backend API CI / test-integration (push) Failing after 0s
Frontend CI / test (push) Failing after 0s
Storybook Audit / Build & audit Storybook (push) Failing after 0s
feat(v0.703): Go Live & Streaming Complet
- Backend: room creation for live streams, permissions CanJoin/CanSend/CanRead for stream rooms
- LiveViewChat: useLiveStreamChat hook, WebSocket connection, stream_id as room
- LiveViewPlayer: real-time viewer count via polling (5s)
- Media Session: seekbackward/seekforward handlers (10s step)
- GoLiveView.stories.tsx: Default, Loading, Error, StreamKeyVisible
- Docs: API_REFERENCE, CHANGELOG, PROJECT_STATE, FEATURE_STATUS, RETROSPECTIVE_V0703
- SCOPE_CONTROL, .cursorrules: update to v0.801
- Archive V0_703_RELEASE_SCOPE.md
2026-02-25 09:35:22 +01:00

18 KiB
Raw Permalink Blame History

V0.703 Release Scope — Go Live & Streaming Complet

Statut : En cours Phase : 7 (Production Readiness — Finale) Prérequis : v0.702 (taguée) Date cible : TBD Estimation : ~3 sprints (15 jours ouvrés) Précédente : v0.702


1. Objectif

Dernière version de la Phase 7. Complète le module Live Streaming en ajoutant la fonctionnalité Go Live (démarrage de stream), actuellement bloquée par un toast "coming soon" dans la Navbar. Ajoute la gestion de stream keys, les endpoints start/stop/schedule, le chat live connecté WebSocket, et des améliorations player (vitesse de lecture, Media Session API).


2. État actuel (post-v0.702)

Composant État Détail
LiveStream model Livré live_streams table, GORM model avec StreamKey (json:"-")
GET /live/streams Livré Liste publique, filtre ?is_live=true
GET /live/streams/:id Livré Détail d'un stream
POST /live/streams Livré Création (auth required), mais pas de stream key generation
Stream events handler Livré POST /internal/stream-events (stream_started, stream_ended, listener_joined, listener_left)
LiveView (frontend) Livré Visualisation de streams (player, info, recommended, chat mock)
Go Live (frontend) Absent Navbar "Go Live" → toast "coming soon"
Stream key generation Absent StreamKey field exists mais jamais généré ni exposé
GET /live/streams/my/key Absent Endpoint pour récupérer sa stream key
PUT /live/streams/:id Absent Mise à jour métadonnées pendant le live
Stream scheduling Absent Pas de scheduled_at pour planifier un stream
Live chat WebSocket Absent LiveViewChat utilise des données mock statiques
VOD (replays) Absent Streams terminés non consultables
Playback speed Absent Pas de contrôle 0.5x2x
Media Session API Absent Pas de contrôles OS (notification area)

3. Lots

Lot GL1 — Go Live Backend

Objectif : Compléter l'API live streaming avec stream key, mise à jour, scheduling et VOD.

# Tâche Fichiers impactés Effort
GL1-01 Migration 117_live_streams_go_live.sql — ajout colonnes scheduled_at, stream_url, is_vod ; update stream_key NOT NULL avec default UUID migrations/117_live_streams_go_live.sql S
GL1-02 Mise à jour modèle LiveStream — ajout champs ScheduledAt, StreamURL, IsVOD internal/models/live_stream.go S
GL1-03 GenerateStreamKey dans service — génère un UUID v4 unique pour stream_key lors du Create internal/services/live_stream_service.go S
GL1-04 GET /live/streams/me — liste les streams de l'utilisateur authentifié (incluant stream_key) internal/handlers/live_stream_handler.go, routes M
GL1-05 GET /live/streams/me/key — retourne la stream key de l'utilisateur (crée un stream draft si nécessaire) internal/handlers/live_stream_handler.go, routes S
GL1-06 POST /live/streams/me/key/regenerate — régénère la stream key internal/handlers/live_stream_handler.go, routes S
GL1-07 PUT /live/streams/:id — mise à jour titre/description/category/tags pendant ou avant le live internal/handlers/live_stream_handler.go, routes S
GL1-08 GET /live/streams?is_vod=true — listing des streams terminés (VOD replays) internal/services/live_stream_service.go, repository S
GL1-09 Prometheus metrics — veza_live_streams_active, veza_live_stream_viewers_total internal/monitoring/metrics.go S
GL1-10 Tests unitaires — stream key generation, CRUD complet, ownership check, VOD filter internal/services/live_stream_service_test.go (nouveau) M

Lot GL2 — Go Live Frontend

Objectif : Page Go Live pour configurer et démarrer un stream, afficher la stream key, instructions OBS.

# Tâche Fichiers impactés Effort
GL2-01 GoLiveView — formulaire (titre, description, catégorie, tags, thumbnail), affichage stream key avec bouton copy, instructions OBS/Streamlabs apps/web/src/features/live/pages/go-live-page/GoLiveView.tsx (nouveau) L
GL2-02 GoLivePage — wrapper avec fetch stream key, state management apps/web/src/features/live/pages/GoLivePage.tsx (nouveau) M
GL2-03 Lazy export LazyGoLive — createLazyComponent pour GoLivePage apps/web/src/components/ui/lazy-component/lazyExports.ts S
GL2-04 Route /live/go-live dans routeConfig — protégée auth apps/web/src/router/routeConfig.tsx S
GL2-05 Navbar "Go Live" → navigate /live/go-live au lieu du toast apps/web/src/components/layout/Navbar.tsx S
GL2-06 Service frontend liveServicegetMyStreams, getMyStreamKey, regenerateStreamKey, updateStream, createStream apps/web/src/services/liveService.ts (modifier) M
GL2-07 MSW handlers — mock GET /live/streams/me, GET /live/streams/me/key, POST regenerate, PUT :id apps/web/src/mocks/handlers-live.ts (nouveau) M
GL2-08 Story GoLiveView.stories.tsx — états Default, Loading, Error, StreamKeyVisible apps/web/src/features/live/pages/go-live-page/GoLiveView.stories.tsx (nouveau) M

Lot GL3 — Live Chat & Interaction

Objectif : Connecter le chat live aux WebSocket existants pour un chat temps réel par stream.

# Tâche Fichiers impactés Effort
GL3-01 Chat room par stream — utiliser le système de conversation WebSocket existant (JoinConversation avec stream_id comme room) internal/websocket/chat/handlers.go M
GL3-02 LiveViewChat connecté WebSocket — remplacer les données mock par le hook useChat existant apps/web/src/features/live/pages/live-page/LiveViewChat.tsx M
GL3-03 Viewer count temps réel — écouter les events listener_joined/listener_left via WebSocket apps/web/src/features/live/pages/live-page/LiveViewPlayer.tsx S
GL3-04 Tests — chat join/leave stream room, message broadcast Tests M

Lot GL4 — Player Enhancements

Objectif : Vitesse de lecture et intégration Media Session API pour contrôles OS.

# Tâche Fichiers impactés Effort
GL4-01 Playback speed control — bouton 0.5x, 0.75x, 1x, 1.25x, 1.5x, 2x dans le player apps/web/src/features/player/components/audio-player/ M
GL4-02 Media Session API — navigator.mediaSession avec metadata (title, artist, artwork), action handlers (play, pause, seekbackward, seekforward, previoustrack, nexttrack) apps/web/src/features/player/hooks/ M
GL4-03 Tests — playback speed persistence, media session metadata update Tests S

Lot QA1 — Tests & Release

# Tâche Fichiers impactés Effort
QA1-01 Smoke test v0.703 — checklist complète docs/SMOKE_TEST_V0703.md S
QA1-02 Mise à jour PROJECT_STATE, FEATURE_STATUS, CHANGELOG docs/ S
QA1-03 Rétrospective v0.703, archivage scope, placeholder v0.801, tag docs/, Git S

4. Hors scope v0.703

Élément Version cible
2FA SMS / Passkeys v0.104
Themes (high contrast, compact) v0.801
Accessibilité WCAG AA v0.801
Cloud Storage avancé (versioning, backup) v0.802
OpenAPI/Swagger auto-gen v0.803
Wishlist marketplace v0.901
Promotions dynamiques (flash sales) v0.901
Dispute / réclamation client v0.901
Multi-camera support v2.0
Donations/tips live v2.0
Clipping moments v2.0

5. Détail technique — Lot GL1

5.1 Migration 117_live_streams_go_live.sql

ALTER TABLE live_streams
    ADD COLUMN IF NOT EXISTS scheduled_at TIMESTAMPTZ,
    ADD COLUMN IF NOT EXISTS stream_url TEXT DEFAULT '',
    ADD COLUMN IF NOT EXISTS is_vod BOOLEAN NOT NULL DEFAULT false;

UPDATE live_streams SET stream_key = gen_random_uuid()::text WHERE stream_key = '' OR stream_key IS NULL;

ALTER TABLE live_streams ALTER COLUMN stream_key SET NOT NULL;
ALTER TABLE live_streams ALTER COLUMN stream_key SET DEFAULT gen_random_uuid()::text;

CREATE INDEX IF NOT EXISTS idx_live_streams_user_id ON live_streams(user_id);
CREATE INDEX IF NOT EXISTS idx_live_streams_is_live ON live_streams(is_live) WHERE is_live = true;
CREATE INDEX IF NOT EXISTS idx_live_streams_scheduled ON live_streams(scheduled_at) WHERE scheduled_at IS NOT NULL;

5.2 Modèle LiveStream mis à jour

type LiveStream struct {
    // ... champs existants ...
    ScheduledAt *time.Time `json:"scheduled_at,omitempty" db:"scheduled_at"`
    StreamURL   string     `gorm:"type:text;default:''" json:"stream_url,omitempty" db:"stream_url"`
    IsVOD       bool       `gorm:"default:false" json:"is_vod" db:"is_vod"`
}

5.3 Stream Key Generation

func (s *LiveStreamService) Create(ctx context.Context, userID uuid.UUID, stream *models.LiveStream) (*models.LiveStream, error) {
    if stream.Title == "" {
        return nil, errors.New("title is required")
    }
    stream.UserID = userID
    stream.StreamKey = uuid.New().String()
    if stream.StreamerName == "" {
        stream.StreamerName = "Streamer"
    }
    if err := s.repo.Create(ctx, stream); err != nil {
        return nil, err
    }
    return stream, nil
}

5.4 GET /live/streams/me/key

func (h *LiveStreamHandler) GetMyStreamKey(c *gin.Context) {
    userID, ok := GetUserIDUUID(c)
    if !ok { return }

    streams, err := h.service.ListByUser(c.Request.Context(), userID)
    if err != nil {
        RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get streams", err))
        return
    }

    var streamKey string
    if len(streams) > 0 {
        streamKey = streams[0].StreamKey
    } else {
        draft := &models.LiveStream{Title: "My Stream"}
        created, err := h.service.Create(c.Request.Context(), userID, draft)
        if err != nil {
            RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to create stream", err))
            return
        }
        streamKey = created.StreamKey
    }

    RespondSuccess(c, http.StatusOK, gin.H{
        "stream_key": streamKey,
        "rtmp_url":   "rtmp://stream.veza.app/live",
    })
}

5.5 VOD — Streams terminés

Quand SetIsLive(false) est appelé (stream_ended event), le stream reste en DB avec ended_at set. On ajoute un filtre is_vod qui retourne les streams avec ended_at IS NOT NULL AND is_live = false.

5.6 Scheduled Streams

Le champ scheduled_at permet de planifier un stream futur. L'endpoint POST /live/streams accepte scheduled_at dans le body. Le frontend affiche les streams planifiés avec un badge "Scheduled" et un countdown.


6. Détail technique — Lot GL2

6.1 GoLiveView

interface GoLiveViewProps {
  streamKey: string | null;
  rtmpUrl: string;
  onCreateStream: (data: CreateStreamData) => Promise<void>;
  onRegenerateKey: () => Promise<void>;
  isLoading: boolean;
}

interface CreateStreamData {
  title: string;
  description: string;
  category: string;
  tags: string[];
  thumbnailUrl?: string;
  scheduledAt?: string;
}

Sections :

  1. Stream Configuration — formulaire titre/description/category/tags
  2. Stream Key — affichage masqué par défaut, bouton reveal, bouton copy, bouton regenerate
  3. Connection Info — RTMP URL + stream key, instructions OBS/Streamlabs
  4. Preview — thumbnail upload, scheduled_at picker
  5. Actions — bouton "Go Live Now" ou "Schedule Stream"

6.2 Navbar update

// Avant (toast)
onClick={() => toast.info('Live streaming feature coming soon')}

// Après (navigation)
onClick={() => { onNavigate('go-live'); setShowUserMenu(false); }}

7. Détail technique — Lot GL4

7.1 Playback Speed

const SPEED_OPTIONS = [0.5, 0.75, 1, 1.25, 1.5, 2];

function PlaybackSpeedButton({ currentSpeed, onSpeedChange }: Props) {
    // Cycle through speeds on click, or dropdown on hover
}

7.2 Media Session API

function useMediaSession(track: Track | null, actions: MediaSessionActions) {
    useEffect(() => {
        if (!('mediaSession' in navigator) || !track) return;

        navigator.mediaSession.metadata = new MediaMetadata({
            title: track.title,
            artist: track.artist,
            artwork: track.coverUrl ? [{ src: track.coverUrl, sizes: '512x512' }] : [],
        });

        navigator.mediaSession.setActionHandler('play', actions.onPlay);
        navigator.mediaSession.setActionHandler('pause', actions.onPause);
        navigator.mediaSession.setActionHandler('previoustrack', actions.onPrevious);
        navigator.mediaSession.setActionHandler('nexttrack', actions.onNext);
        navigator.mediaSession.setActionHandler('seekbackward', actions.onSeekBackward);
        navigator.mediaSession.setActionHandler('seekforward', actions.onSeekForward);
    }, [track, actions]);
}

8. Fichiers impactés (récapitulatif)

Backend Go (nouveau)

Fichier Action
migrations/117_live_streams_go_live.sql Nouveau — colonnes scheduled_at, stream_url, is_vod
internal/services/live_stream_service_test.go Nouveau — tests unitaires live stream

Backend Go (modifier)

Fichier Action
internal/models/live_stream.go Ajout ScheduledAt, StreamURL, IsVOD
internal/services/live_stream_service.go Stream key generation, ListByUser, RegenerateKey
internal/handlers/live_stream_handler.go GET me, GET me/key, POST regenerate, PUT :id
internal/api/routes_live.go Ajout routes protégées (me, key, regenerate, PUT)
internal/repositories/live_stream_repository.go ListByUserID, filtre VOD
internal/monitoring/metrics.go Métriques live streams

Frontend (nouveau)

Fichier Action
apps/web/src/features/live/pages/GoLivePage.tsx Nouveau — page wrapper
apps/web/src/features/live/pages/go-live-page/GoLiveView.tsx Nouveau — formulaire Go Live
apps/web/src/features/live/pages/go-live-page/GoLiveView.stories.tsx Nouveau — stories
apps/web/src/mocks/handlers-live.ts Nouveau — MSW handlers live

Frontend (modifier)

Fichier Action
apps/web/src/components/layout/Navbar.tsx Remplacer toast par navigation
apps/web/src/services/liveService.ts Ajout getMyStreams, getMyStreamKey, regenerateStreamKey, updateStream
apps/web/src/router/routeConfig.tsx Route /live/go-live
apps/web/src/components/ui/lazy-component/lazyExports.ts LazyGoLive
apps/web/src/features/live/pages/live-page/LiveViewChat.tsx Connecter WebSocket
apps/web/src/features/live/pages/live-page/LiveViewPlayer.tsx Viewer count real-time
apps/web/src/features/player/components/audio-player/ Playback speed control
apps/web/src/features/player/hooks/ Media Session API hook

Documentation

Fichier Action
docs/API_REFERENCE.md Ajout section Live Streaming
docs/SMOKE_TEST_V0703.md Nouveau — checklist
docs/PROJECT_STATE.md Section v0.703
docs/FEATURE_STATUS.md Mise à jour
CHANGELOG.md Section v0.703
docs/RETROSPECTIVE_V0703.md Nouveau (fin de release)

9. Critères d'acceptation globaux

  • Stream key générée automatiquement lors du POST /live/streams
  • GET /live/streams/me/key retourne stream key + RTMP URL (auth required)
  • POST /live/streams/me/key/regenerate génère une nouvelle stream key
  • PUT /live/streams/:id met à jour les métadonnées (ownership check)
  • GET /live/streams?is_vod=true retourne les streams terminés
  • Frontend GoLivePage : formulaire, stream key visible/copiable, instructions OBS
  • Navbar "Go Live" navigue vers /live/go-live au lieu du toast
  • LiveViewChat connecté WebSocket (messages en temps réel)
  • Viewer count mis à jour en temps réel via events
  • Playback speed : 6 vitesses (0.5x2x), persistance
  • Media Session API : metadata (title, artist, artwork), action handlers
  • Stories GoLiveView : Default, Loading, Error, StreamKeyVisible
  • MSW handlers live : GET me, GET me/key, POST regenerate, PUT :id
  • Tests unitaires backend : stream key, CRUD, ownership, VOD
  • Tag v0.703 créé

10. Risques

Risque Mitigation
Stream key exposée en clair dans le frontend Masquée par défaut, reveal on click, HTTPS obligatoire
RTMP server non configuré (stream server absent) Les endpoints backend sont prêts, stream server Rust à configurer séparément
LiveViewChat charge excessive avec beaucoup de viewers Rate limiting WebSocket existant (v0.503), pagination historique
Media Session API non supportée par tous les navigateurs Feature detection avec if ('mediaSession' in navigator)
VOD storage (streams enregistrés) v0.703 = métadonnées uniquement, pas de stockage vidéo réel (future: S3 segments)

11. Architecture

graph TD
    subgraph backend [Backend Go]
        API[API Routes /live/*]
        SVC[LiveStreamService]
        REPO[LiveStreamRepository]
        DB[(PostgreSQL)]
        WS[WebSocket Hub]
    end

    subgraph frontend [Frontend React]
        GL[GoLivePage]
        LV[LiveView]
        LC[LiveViewChat]
        PL[Player]
    end

    subgraph external [External]
        OBS[OBS/Streamlabs]
        SS[Stream Server Rust]
    end

    GL -->|"POST /live/streams"| API
    GL -->|"GET /live/streams/me/key"| API
    LV -->|"GET /live/streams"| API
    LC -->|"WebSocket /ws"| WS
    PL -->|"Media Session API"| PL

    OBS -->|"RTMP stream_key"| SS
    SS -->|"POST /internal/stream-events"| API
    API --> SVC
    SVC --> REPO
    REPO --> DB
    WS -->|"chat messages"| LC

12. Références

  • RETROSPECTIVE_V0702.md
  • V0_702_RELEASE_SCOPE.md
  • SCOPE_CONTROL.md
  • veza-backend-api/internal/handlers/live_stream_handler.go (handlers existants)
  • veza-backend-api/internal/services/live_stream_service.go (service existant)
  • veza-backend-api/internal/models/live_stream.go (modèle existant)
  • apps/web/src/features/live/pages/live-page/LiveView.tsx (composant vue)
  • apps/web/src/components/layout/Navbar.tsx L223 (toast "coming soon")