Video Streaming Platform (YouTube / Hotstar / Netflix / Prime) High-level System Design

High-Level Design: Video Streaming Platform (YouTube-like)

Table of Contents

  1. Problem Statement & Requirements
  2. High-Level Architecture
  3. Component Architecture
  4. Data Flow
  5. API Design & Communication Protocols
  6. Database Design
  7. Caching Strategy
  8. State Management
  9. Performance Optimization
  10. Error Handling & Edge Cases
  11. Interview Cross-Questions
  12. Trade-offs & Design Decisions
  13. Accessibility (a11y)
  14. Security & Content Protection
  15. Mobile & Touch Interactions
  16. Testing Strategy
  17. Offline/PWA Capabilities
  18. Video Player Deep Dive
  19. Internationalization (i18n)
  20. Analytics & Monitoring
  21. Notification System
  22. Live Streaming Deep Dive

1. Problem Statement & Requirements

Functional Requirements

  • Video Upload: Users can upload videos in various formats and sizes
  • Video Streaming: Users can watch videos with adaptive quality
  • Video Player Controls: Play, pause, seek, volume, quality selection, speed control
  • Comments: Users can post, read, edit, and delete comments
  • Recommendations: Personalized video suggestions based on viewing history
  • Search: Search videos by title, tags, description
  • Thumbnails: Auto-generate and custom upload thumbnails
  • View Counting: Track video views accurately (eventual consistency acceptable)
  • Live Streaming: Support for real-time video streaming (optional)
  • Subscriptions: Users can subscribe to channels
  • Likes/Dislikes: User engagement metrics

Non-Functional Requirements

  • Scalability: Support millions of concurrent viewers
  • Availability: 99.9% uptime for streaming service
  • Low Latency: Video start time < 2 seconds, buffering minimal
  • Consistency: Eventual consistency acceptable for views, likes
  • Durability: Videos should never be lost (high durability)
  • Performance: Support adaptive bitrate streaming (240p to 4K)
  • Cost Efficiency: Optimize storage and bandwidth costs
  • Global Reach: CDN-based delivery for worldwide users

Scale Estimations

  • Users: 500M daily active users
  • Videos: 500 hours of video uploaded per minute
  • Concurrent Viewers: 10M concurrent video streams
  • Storage: 1PB+ for video storage (multi-resolution)
  • Bandwidth: 100+ Gbps for video delivery
  • Metadata: Billions of video records, comments, likes

2. High-Level Architecture

┌─────────────────────────────────────────────────────────────────────────┐
│                            CLIENT LAYER                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                  │
│  │   Web App    │  │  Mobile App  │  │   Smart TV   │                  │
│  │  (React/Vue) │  │ (iOS/Android)│  │     App      │                  │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘                  │
└─────────┼──────────────────┼──────────────────┼──────────────────────────┘
          │                  │                  │
          └──────────────────┼──────────────────┘
                             │
                    ┌────────▼────────┐
                    │   API Gateway   │
                    │  (Rate Limiting,│
                    │   Auth, Routing)│
                    └────────┬────────┘
                             │
          ┌──────────────────┼──────────────────┐
          │                  │                  │
   ┌──────▼──────┐    ┌─────▼──────┐    ┌─────▼──────┐
   │   Video     │    │  Metadata  │    │   User     │
   │  Upload     │    │  Service   │    │  Service   │
   │  Service    │    │            │    │            │
   └──────┬──────┘    └─────┬──────┘    └─────┬──────┘
          │                 │                  │
          │                 │                  │
   ┌──────▼──────┐    ┌─────▼──────┐    ┌─────▼──────┐
   │  Transcode  │    │  Comment   │    │ Recommend. │
   │   Service   │    │  Service   │    │  Service   │
   │  (Queue)    │    │            │    │   (ML)     │
   └──────┬──────┘    └─────┬──────┘    └─────┬──────┘
          │                 │                  │
          │                 │                  │
┌─────────▼─────────────────▼──────────────────▼─────────────┐
│                     DATA LAYER                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐  │
│  │   SQL    │  │  NoSQL   │  │  Object  │  │  Cache   │  │
│  │   (RDS)  │  │(Cassandra│  │ Storage  │  │ (Redis)  │  │
│  │          │  │/DynamoDB)│  │   (S3)   │  │          │  │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    CDN LAYER                                 │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐  │
│  │  CDN Edge│  │  CDN Edge│  │  CDN Edge│  │  CDN Edge│  │
│  │   (US)   │  │   (EU)   │  │  (APAC)  │  │  (Others)│  │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                  BACKGROUND JOBS                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐  │
│  │Thumbnail │  │  View    │  │Analytics │  │  CDN     │  │
│  │Generator │  │ Counter  │  │Processor │  │ Warmer   │  │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘  │
└─────────────────────────────────────────────────────────────┘

2.0.a System Invariants & Frontend Consistency Model

A video streaming frontend is a distributed system between:

  • Browser
  • CDN
  • Player runtime
  • Backend control plane

The UI must enforce correctness even when these components are temporarily inconsistent.

These invariants must never be violated.

2.0.b Playback State Invariants

Invariant Why it must hold
UI must never show “Playing” if no media bytes are being consumed Prevents fake playback
UI must never advance timestamp if buffer is empty Prevents desync
Video time must be monotonic except during user seeks Prevents jumps
Pause must always stop network fetch Prevents waste + billing errors
A stalled network must never lock UI Prevents dead-UI

These are enforced by the player state machine.

2.0.c Buffering Invariants

The buffer is the frontend’s source of truth for playback.

Rules:

  • Playback can start only when buffer ≥ minimum threshold
  • Playback must pause automatically when buffer = 0
  • Video must never outrun buffer
  • Buffer eviction must never remove currently playing segment

Violation of these rules causes:

  • Infinite spinner bugs
  • Ghost playback
  • Desync between audio and video

2.0.d Manifest Consistency

The manifest is the contract between backend and player.

The frontend enforces:

Rule Why
Manifest must always match codec in buffer Prevents decode failure
Manifest refresh must be idempotent Prevents rewind bugs
Manifest updates must not invalidate already-buffered segments Prevents playback drop

Frontend always treats manifest as append-only during active playback.

2.0.e UI vs Network Truth

The UI does not trust the network.

The UI derives state from:

  • Player buffer
  • Decoder state
  • Playback clock

Not from:

  • CDN responses
  • Backend playback metadata

This prevents:

  • “Video says playing but nothing plays”
  • Broken pause/play buttons
  • Wrong seek bar

2.0.f Live Stream Invariants

For live video:

  • The playback head must never go beyond live edge
  • The UI must expose latency vs quality tradeoff
  • Rebuffering must not rewind live edge

Live video correctness is harder than VOD.

The frontend enforces:

  • Drift control
  • Jitter smoothing
  • Live-edge correction

5. Frontend Failure Model

The frontend assumes failure is normal.

5.1 CDN Failure

If a video segment fails to load:

  • Retry same CDN
  • Try alternate CDN
  • Drop to lower bitrate
  • Pause playback if buffer hits zero

The UI never shows “playing” unless buffer is moving.

5.2 Manifest Failure

If manifest refresh fails:

  • Continue playing buffered segments
  • Freeze bitrate adaptation
  • Retry in background

Playback should not drop just because control plane failed.

5.3 Network Degradation

The frontend continuously measures:

  • Download speed
  • Buffer growth
  • Rebuffer frequency

It dynamically:

  • Reduces quality
  • Increases buffer targets
  • Disables prefetch

This keeps playback stable instead of pretty.

5.4 Tab Suspension / Mobile App Backgrounding

If browser or app is suspended:

  • Freeze playback clock
  • Save buffer state
  • Resume without reloading

Avoids restarting video.

5.5 User Actions During Failure

Users may:

  • Seek while buffering
  • Pause during network loss
  • Switch quality mid-segment

The frontend serializes these actions and applies them when safe.

No user input is lost.

3. Component Architecture

3.1 Frontend Components

┌────────────────────────────────────────────────────────┐
│                  VIDEO PLAYER COMPONENT                 │
│                                                         │
│  ┌───────────────────────────────────────────────┐    │
│  │         Video Rendering Canvas                 │    │
│  │      (HTML5 Video / WebRTC for live)          │    │
│  └───────────────────────────────────────────────┘    │
│                                                         │
│  ┌───────────────────────────────────────────────┐    │
│  │              Player Controls                   │    │
│  │  [Play/Pause] [Timeline] [Volume] [Quality]   │    │
│  │  [Speed] [Fullscreen] [Captions] [Settings]   │    │
│  └───────────────────────────────────────────────┘    │
│                                                         │
│  ┌───────────────────────────────────────────────┐    │
│  │          Adaptive Bitrate Logic                │    │
│  │  - Monitor bandwidth & buffer health           │    │
│  │  - Switch quality based on network             │    │
│  │  - Preload next segments                       │    │
│  └───────────────────────────────────────────────┘    │
│                                                         │
│  ┌───────────────────────────────────────────────┐    │
│  │            Buffer Management                   │    │
│  │  - Maintain 10-30s buffer ahead                │    │
│  │  - Handle network interruptions                │    │
│  │  - Smart preloading                            │    │
│  └───────────────────────────────────────────────┘    │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│              RECOMMENDATION COMPONENT                   │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐            │
│  │  Video   │  │  Video   │  │  Video   │            │
│  │ Thumbnail│  │ Thumbnail│  │ Thumbnail│  ...       │
│  │  + Meta  │  │  + Meta  │  │  + Meta  │            │
│  └──────────┘  └──────────┘  └──────────┘            │
│  - Personalized based on watch history                │
│  - Trending videos                                     │
│  - Category-based recommendations                     │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│               COMMENTS COMPONENT                        │
│  ┌──────────────────────────────────────────────┐     │
│  │  Comment Input (with mentions, emojis)       │     │
│  └──────────────────────────────────────────────┘     │
│  ┌──────────────────────────────────────────────┐     │
│  │  Comment List (paginated/infinite scroll)    │     │
│  │  - Top comments                              │     │
│  │  - Newest first                              │     │
│  │  - Threaded replies                          │     │
│  └──────────────────────────────────────────────┘     │
│  - Real-time updates (WebSocket/Polling)              │
│  - Like/Dislike comments                              │
└────────────────────────────────────────────────────────┘

3.1.a Frontend Failure Model

The frontend assumes failure is normal.

3.1.b CDN Failure

If a video segment fails to load:

  • Retry same CDN
  • Try alternate CDN
  • Drop to lower bitrate
  • Pause playback if buffer hits zero

The UI never shows “playing” unless buffer is moving.

3.1.c Manifest Failure

If manifest refresh fails:

  • Continue playing buffered segments
  • Freeze bitrate adaptation
  • Retry in background

Playback should not drop just because control plane failed.

3.1.d Network Degradation

The frontend continuously measures:

  • Download speed
  • Buffer growth
  • Rebuffer frequency

It dynamically:

  • Reduces quality
  • Increases buffer targets
  • Disables prefetch

This keeps playback stable instead of pretty.

3.1.e Tab Suspension / Mobile App Backgrounding

If browser or app is suspended:

  • Freeze playback clock
  • Save buffer state
  • Resume without reloading

Avoids restarting video.

3.1.f User Actions During Failure

Users may:

  • Seek while buffering
  • Pause during network loss
  • Switch quality mid-segment

The frontend serializes these actions and applies them when safe.

No user input is lost.

3.2 Backend Services

Video Upload Service

┌─────────────────────────────────────────┐
│      VIDEO UPLOAD SERVICE               │
│                                         │
│  1. Validate upload (format, size)     │
│  2. Generate unique video ID           │
│  3. Chunk upload to Object Storage     │
│  4. Create metadata entry              │
│  5. Trigger transcoding job            │
│  6. Generate placeholder thumbnail     │
│                                         │
│  Technologies:                          │
│  - Multipart upload (chunks)           │
│  - Resumable uploads                   │
│  - S3/GCS for raw video storage        │
└─────────────────────────────────────────┘

Transcoding Service

┌─────────────────────────────────────────┐
│      TRANSCODING SERVICE                │
│                                         │
│  Input: Raw video file                 │
│                                         │
│  Process:                               │
│  1. Read from Object Storage           │
│  2. Transcode to multiple resolutions: │
│     - 4K (2160p)                       │
│     - 1080p                            │
│     - 720p                             │
│     - 480p                             │
│     - 360p                             │
│     - 240p                             │
│  3. Encode with H.264/H.265/VP9        │
│  4. Generate HLS/DASH manifests        │
│  5. Extract thumbnails (keyframes)     │
│  6. Store segments in Object Storage   │
│  7. Update metadata with URLs          │
│  8. Warm CDN cache                     │
│                                         │
│  Technologies:                          │
│  - FFmpeg/Elastic Transcoder           │
│  - Worker queue (SQS/RabbitMQ)         │
│  - Distributed workers (Auto-scaling)  │
└─────────────────────────────────────────┘

Streaming Service

┌─────────────────────────────────────────┐
│       STREAMING SERVICE                 │
│                                         │
│  1. Receive video request              │
│  2. Check user authentication          │
│  3. Fetch manifest file (HLS/DASH)     │
│  4. Serve via CDN                      │
│  5. Track playback events              │
│  6. Log analytics                      │
│                                         │
│  Protocols:                             │
│  - HLS (HTTP Live Streaming)           │
│  - DASH (Dynamic Adaptive Streaming)   │
│  - WebRTC (for live streaming)         │
└─────────────────────────────────────────┘

4. Data Flow

4.1 Video Upload Flow

┌─────────┐
│  User   │
└────┬────┘
     │ 1. Upload Video
     ▼
┌─────────────────┐
│  Upload Service │
└────┬────────────┘
     │ 2. Chunk & Store Raw Video
     ▼
┌─────────────────┐
│  Object Storage │
│      (S3)       │
└────┬────────────┘
     │ 3. Trigger Transcode
     ▼
┌─────────────────┐
│  Message Queue  │
│   (SQS/Kafka)   │
└────┬────────────┘
     │ 4. Pick Job
     ▼
┌─────────────────┐
│   Transcoder    │
│   Workers       │
└────┬────────────┘
     │ 5. Generate Multiple Resolutions
     ├──────────┬──────────┬──────────┐
     ▼          ▼          ▼          ▼
  [4K.m3u8] [1080p.m3u8] [720p.m3u8] [360p.m3u8]
     │          │          │          │
     └──────────┴──────────┴──────────┘
                    │
                    ▼
          ┌─────────────────┐
          │ Object Storage  │
          │  (Segmented)    │
          └────┬────────────┘
               │ 6. Update Metadata
               ▼
          ┌─────────────────┐
          │   Database      │
          │  (video_id,     │
          │   resolutions)  │
          └────┬────────────┘
               │ 7. Warm CDN
               ▼
          ┌─────────────────┐
          │      CDN        │
          │  Edge Servers   │
          └─────────────────┘

4.2 Video Streaming Flow

┌─────────┐
│  User   │
└────┬────┘
     │ 1. Request Video
     ▼
┌─────────────────┐
│   API Gateway   │
└────┬────────────┘
     │ 2. Auth & Validate
     ▼
┌─────────────────┐
│ Metadata Service│
└────┬────────────┘
     │ 3. Fetch Video Info
     ▼
┌─────────────────┐
│    Database     │
└────┬────────────┘
     │ 4. Return Manifest URL
     ▼
┌─────────────────┐
│   Client        │
│  (Video Player) │
└────┬────────────┘
     │ 5. Request Manifest (master.m3u8)
     ▼
┌─────────────────┐
│   CDN Edge      │
└────┬────────────┘
     │ 6. Serve Manifest
     ▼
┌─────────────────┐
│   Client        │
│  (Parse Quality)│
└────┬────────────┘
     │ 7. Request Segments (720p_001.ts, 720p_002.ts...)
     ▼
┌─────────────────┐
│   CDN Edge      │
│  (Cache Hit)    │
└────┬────────────┘
     │ 8. Stream Video Segments
     ▼
┌─────────────────┐
│  Video Player   │
│  (Buffer & Play)│
└─────────────────┘

4.3 Adaptive Bitrate Flow

Video Player Logic:
┌─────────────────────────────────────────┐
│                                         │
│  while (video playing):                 │
│    monitor_bandwidth()                  │
│    monitor_buffer_health()              │
│                                         │
│    if bandwidth_high && buffer_healthy: │
│        switch_to_higher_quality()       │
│    if bandwidth_low || buffer_starving: │
│        switch_to_lower_quality()        │
│                                         │
│    preload_next_segments()              │
│    update_playback_stats()              │
│                                         │
└─────────────────────────────────────────┘

Quality Ladder:
4K (2160p) ─┐
1080p      ─┤
720p       ─┼─── Switch based on:
480p       ─┤     - Network speed
360p       ─┤     - Buffer status
240p       ─┘     - Device capability

4.4 View Counting Flow

┌─────────┐
│  User   │
│ Watches │
└────┬────┘
     │ 1. Video Play Event (>30s watched)
     ▼
┌─────────────────┐
│ Analytics       │
│ Service         │
└────┬────────────┘
     │ 2. Write to Stream
     ▼
┌─────────────────┐
│  Kafka/Kinesis  │
│   (Event Log)   │
└────┬────────────┘
     │ 3. Aggregate Views
     ▼
┌─────────────────┐
│  Stream         │
│  Processor      │
│ (Flink/Spark)   │
└────┬────────────┘
     │ 4. Batch Update (every 5 min)
     ▼
┌─────────────────┐
│  Redis Counter  │
│  (Fast Reads)   │
└────┬────────────┘
     │ 5. Periodic Flush
     ▼
┌─────────────────┐
│  Database       │
│  (Persistent)   │
└─────────────────┘

Note: Eventual consistency is acceptable
Views may be slightly delayed (5-10 min)

5. API Design & Communication Protocols

5.1 REST APIs

Video Metadata APIs

GET /api/v1/videos/{videoId}
Response:
{
  "videoId": "abc123",
  "title": "Sample Video",
  "description": "...",
  "duration": 600,
  "views": 1000000,
  "likes": 50000,
  "dislikes": 1000,
  "channelId": "channel123",
  "uploadDate": "2025-01-15T10:00:00Z",
  "thumbnailUrl": "https://cdn.example.com/thumbnails/abc123.jpg",
  "manifestUrl": "https://cdn.example.com/videos/abc123/master.m3u8",
  "resolutions": ["2160p", "1080p", "720p", "480p", "360p", "240p"]
}

POST /api/v1/videos/upload
Headers:
  Authorization: Bearer 
Body (multipart):
  file: 
  title: "Video Title"
  description: "..."
  tags: ["tag1", "tag2"]
Response:
{
  "videoId": "abc123",
  "uploadStatus": "processing",
  "uploadUrl": "https://upload.example.com/abc123"
}

GET /api/v1/videos/{videoId}/recommendations
Response:
{
  "videos": [
    {"videoId": "xyz789", "title": "...", "thumbnailUrl": "..."},
    ...
  ]
}

Comment APIs

POST /api/v1/videos/{videoId}/comments
Headers:
  Authorization: Bearer <token>
Body:
{
  "text": "Great video!",
  "parentCommentId": null
}
Response:
{
  "commentId": "comment123",
  "userId": "user456",
  "text": "Great video!",
  "createdAt": "2025-01-15T10:00:00Z"
}

GET /api/v1/videos/{videoId}/comments?limit=20&offset=0&sort=top
Response:
{
  "comments": [
    {
      "commentId": "comment123",
      "userId": "user456",
      "userName": "John Doe",
      "text": "Great video!",
      "likes": 100,
      "replies": 5,
      "createdAt": "2025-01-15T10:00:00Z"
    },
    ...
  ],
  "nextOffset": 20
}

User Engagement APIs

POST /api/v1/videos/{videoId}/like
POST /api/v1/videos/{videoId}/dislike
POST /api/v1/channels/{channelId}/subscribe
POST /api/v1/videos/{videoId}/watch
  Body: { "timestamp": 120, "quality": "720p" }

5.2 Streaming Protocols

HLS (HTTP Live Streaming)

Master Playlist (master.m3u8):
#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=8000000,RESOLUTION=3840x2160
2160p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
1080p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=854x480
480p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p/index.m3u8

Quality Playlist (720p/index.m3u8):
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXTINF:10.0,
segment_001.ts
#EXTINF:10.0,
segment_002.ts
#EXTINF:10.0,
segment_003.ts
...
#EXT-X-ENDLIST

DASH (Dynamic Adaptive Streaming over HTTP)

MPD (Media Presentation Description):


  
     mimeType="video/mp4">
       bandwidth="8000000" width="3840" height="2160">
        2160p/
         />
      
       bandwidth="5000000" width="1920" height="1080">
        1080p/
         />
      
      ...
    
  

5.3 WebSocket (Real-time Updates)

// Comment updates
WS /ws/videos/{videoId}/comments

Client -> Server:
{
  "type": "subscribe",
  "videoId": "abc123"
}

Server -> Client (new comment):
{
  "type": "new_comment",
  "comment": {
    "commentId": "comment789",
    "userId": "user123",
    "text": "Just posted!",
    "createdAt": "2025-01-15T10:05:00Z"
  }
}

// Live view count updates
Server -> Client (every 10s):
{
  "type": "view_count_update",
  "views": 1000543
}

6. Database Design

6.1 SQL Database (MySQL/PostgreSQL) – Metadata

Videos Table

CREATE TABLE videos (
    video_id VARCHAR(36) PRIMARY KEY,
    channel_id VARCHAR(36) NOT NULL,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    duration INT NOT NULL, -- in seconds
    upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status ENUM('processing', 'ready', 'failed') DEFAULT 'processing',
    category_id INT,
    privacy ENUM('public', 'unlisted', 'private') DEFAULT 'public',
    manifest_url VARCHAR(512),
    thumbnail_url VARCHAR(512),
    raw_video_url VARCHAR(512),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_channel_id (channel_id),
    INDEX idx_upload_date (upload_date),
    INDEX idx_status (status),
    INDEX idx_category (category_id)
);

CREATE TABLE video_resolutions (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    video_id VARCHAR(36) NOT NULL,
    resolution VARCHAR(10) NOT NULL, -- '720p', '1080p', etc.
    video_url VARCHAR(512) NOT NULL,
    bitrate INT,
    file_size BIGINT,
    FOREIGN KEY (video_id) REFERENCES videos(video_id),
    INDEX idx_video_id (video_id)
);

CREATE TABLE video_stats (
    video_id VARCHAR(36) PRIMARY KEY,
    view_count BIGINT DEFAULT 0,
    like_count INT DEFAULT 0,
    dislike_count INT DEFAULT 0,
    comment_count INT DEFAULT 0,
    share_count INT DEFAULT 0,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (video_id) REFERENCES videos(video_id)
);

Channels Table

CREATE TABLE channels (
    channel_id VARCHAR(36) PRIMARY KEY,
    user_id VARCHAR(36) NOT NULL,
    channel_name VARCHAR(100) NOT NULL,
    description TEXT,
    subscriber_count BIGINT DEFAULT 0,
    total_views BIGINT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_id (user_id)
);

CREATE TABLE subscriptions (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id VARCHAR(36) NOT NULL,
    channel_id VARCHAR(36) NOT NULL,
    subscribed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    notifications_enabled BOOLEAN DEFAULT TRUE,
    UNIQUE KEY unique_subscription (user_id, channel_id),
    INDEX idx_user_id (user_id),
    INDEX idx_channel_id (channel_id)
);

Users Table

CREATE TABLE users (
    user_id VARCHAR(36) PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    display_name VARCHAR(100),
    profile_picture_url VARCHAR(512),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_login TIMESTAMP,
    INDEX idx_email (email),
    INDEX idx_username (username)
);

6.2 NoSQL Database (Cassandra/DynamoDB) – Comments & Engagement

Comments Table (Cassandra Schema)

CREATE TABLE comments (
    video_id TEXT,
    comment_id TIMEUUID,
    user_id TEXT,
    parent_comment_id TIMEUUID,
    text TEXT,
    like_count INT,
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    PRIMARY KEY (video_id, comment_id)
) WITH CLUSTERING ORDER BY (comment_id DESC);

-- Secondary index for user comments
CREATE TABLE user_comments (
    user_id TEXT,
    comment_id TIMEUUID,
    video_id TEXT,
    text TEXT,
    created_at TIMESTAMP,
    PRIMARY KEY (user_id, comment_id)
) WITH CLUSTERING ORDER BY (comment_id DESC);

-- Index for comment replies
CREATE TABLE comment_replies (
    parent_comment_id TIMEUUID,
    reply_id TIMEUUID,
    user_id TEXT,
    text TEXT,
    created_at TIMESTAMP,
    PRIMARY KEY (parent_comment_id, reply_id)
) WITH CLUSTERING ORDER BY (reply_id DESC);

Watch History (Cassandra Schema)

CREATE TABLE watch_history (
    user_id TEXT,
    watched_at TIMESTAMP,
    video_id TEXT,
    watch_duration INT, -- seconds watched
    total_duration INT, -- video total duration
    quality TEXT, -- resolution watched
    PRIMARY KEY (user_id, watched_at, video_id)
) WITH CLUSTERING ORDER BY (watched_at DESC);

-- For recommendations
CREATE TABLE user_preferences (
    user_id TEXT PRIMARY KEY,
    favorite_categories SET<TEXT>,
    disliked_categories SET<TEXT>,
    preferred_languages SET<TEXT>,
    watch_time_total BIGINT,
    last_updated TIMESTAMP
);

Video Analytics (Time-Series Data)

CREATE TABLE video_analytics (
    video_id TEXT,
    time_bucket TIMESTAMP, -- hourly/daily buckets
    metric_type TEXT, -- 'views', 'watch_time', 'engagement'
    value BIGINT,
    PRIMARY KEY ((video_id, metric_type), time_bucket)
) WITH CLUSTERING ORDER BY (time_bucket DESC);

6.3 Redis (Caching Layer)

# Video metadata cache
Key: video:{videoId}
Value: {JSON of video metadata}
TTL: 1 hour

# Video stats cache (hot data)
Key: video:stats:{videoId}
Value: {views: 1000000, likes: 50000, ...}
TTL: 5 minutes

# Trending videos cache
Key: trending:videos:{region}:{category}
Value: [videoId1, videoId2, ...]
TTL: 10 minutes

# User session cache
Key: user:session:{sessionId}
Value: {userId, preferences, ...}
TTL: 24 hours

# View count buffer (before batch update)
Key: video:views:{videoId}
Value: counter (incremented on each view)
Flush to DB: every 5 minutes

# Comment count cache
Key: video:comments:{videoId}
Value: sorted set of recent comments
TTL: 15 minutes

# Recommendation cache
Key: recommendations:{userId}
Value: [videoId1, videoId2, ...]
TTL: 30 minutes

7. Caching Strategy

7.1 Multi-Layer Caching Architecture

┌──────────────────────────────────────────────────────┐
│                  CLIENT LAYER                         │
│  Browser Cache: Video segments (24h)                 │
│  IndexedDB: Offline video chunks                     │
└─────────────────┬────────────────────────────────────┘
                  │
                  ▼
┌──────────────────────────────────────────────────────┐
│                   CDN EDGE CACHE                      │
│  - Video segments (HLS/DASH): 7 days                 │
│  - Thumbnails: 30 days                               │
│  - Popular videos: Indefinite (with LRU)             │
│  - Cache hit ratio target: >95%                      │
└─────────────────┬────────────────────────────────────┘
                  │ (Cache miss)
                  ▼
┌──────────────────────────────────────────────────────┐
│               APPLICATION CACHE (Redis)               │
│  - Video metadata: 1 hour                            │
│  - User sessions: 24 hours                           │
│  - Trending videos: 10 minutes                       │
│  - View counts: 5 minutes                            │
│  - Recommendations: 30 minutes                       │
└─────────────────┬────────────────────────────────────┘
                  │ (Cache miss)
                  ▼
┌──────────────────────────────────────────────────────┐
│                  DATABASE LAYER                       │
│  SQL: Metadata, User data                            │
│  NoSQL: Comments, Analytics                          │
│  Object Storage: Video files                         │
└──────────────────────────────────────────────────────┘

7.2 CDN Strategy

Cache-Control Headers

Video Segments (*.ts, *.m4s):
  Cache-Control: public, max-age=604800, immutable
  (7 days, never changes once created)

Manifest Files (*.m3u8, *.mpd):
  Cache-Control: public, max-age=60
  (1 minute, can update for live streams)

Thumbnails:
  Cache-Control: public, max-age=2592000
  (30 days)

Video Metadata API:
  Cache-Control: public, max-age=300
  (5 minutes)

CDN Optimization Techniques

1. Geo-Distributed Edge Servers
   - User routed to nearest edge location
   - Reduces latency from ~200ms to ~20ms

2. Cache Warming
   - Pre-populate CDN with popular videos
   - Triggered on viral video detection

3. Range Requests
   - Support HTTP byte-range requests
   - Enable seeking without downloading entire file

4. Compression
   - Gzip/Brotli for manifests and metadata
   - Video already compressed (H.264/H.265)

5. Cache Tiering
   - Hot tier: Most popular videos (SSD)
   - Warm tier: Moderately popular (HDD)
   - Cold tier: Long-tail (Origin fetch)

7.3 Cache Invalidation Strategy

1. Time-Based Expiration (TTL)
   - Most common approach
   - Acceptable for view counts, stats

2. Event-Based Invalidation
   - Video deleted -> Invalidate all CDN cache
   - Video updated -> Invalidate metadata cache
   - Comment posted -> Invalidate comment cache

3. Write-Through Cache
   - Update cache immediately on write
   - Used for critical data (likes, subscriptions)

4. Cache-Aside Pattern
   - Application checks cache first
   - On miss, fetch from DB and populate cache
   - Used for video metadata

8. State Management

8.1 Client-Side State (Video Player)

// Video Player State Machine
{
  playbackState: 'playing' | 'paused' | 'buffering' | 'ended',
  currentTime: 120.5, // seconds
  duration: 600,
  bufferedRanges: [[0, 150], [200, 250]], // buffered segments
  currentQuality: '720p',
  availableQualities: ['2160p', '1080p', '720p', '480p'],
  volume: 0.8,
  playbackRate: 1.0,
  isFullscreen: false,
  isMuted: false,
  captionsEnabled: true,

  // Adaptive bitrate state
  networkBandwidth: 5000000, // bps
  bufferHealth: 0.8, // 80% healthy
  qualitySwitchPending: false,

  // Analytics
  watchedSegments: [0, 30, 60, 90], // timestamps watched
  totalWatchTime: 150, // seconds
  bufferingEvents: 2,
  qualitySwitches: 3
}

8.2 Server-Side State

Session State (Redis)

{
  "sessionId": "sess_abc123",
  "userId": "user_456",
  "videoId": "video_789",
  "currentPosition": 120,
  "quality": "720p",
  "lastHeartbeat": "2025-01-15T10:05:00Z",
  "device": "web",
  "location": "US-WEST"
}

Transcoding Job State (DynamoDB)

{
  "jobId": "job_xyz",
  "videoId": "video_789",
  "status": "processing",
  "progress": 45,
  "currentResolution": "720p",
  "completedResolutions": ["240p", "360p", "480p"],
  "pendingResolutions": ["1080p", "2160p"],
  "startedAt": "2025-01-15T10:00:00Z",
  "estimatedCompletion": "2025-01-15T10:15:00Z"
}

8.3 State Synchronization

User watches video on Mobile -> Switches to TV

1. Mobile app sends position update:
   POST /api/v1/videos/{videoId}/progress
   { "position": 120, "timestamp": "..." }

2. Server updates Redis:
   SET user:video:progress:{userId}:{videoId} "120"

3. TV app polls for progress:
   GET /api/v1/videos/{videoId}/progress
   Response: { "position": 120 }

4. TV resumes from 120 seconds

9. Performance Optimization

9.1 Video Player Optimizations

Preloading Strategy

// Intelligent preloading
function preloadStrategy(currentTime, duration, bufferHealth) {
  const timeRemaining = duration - currentTime;

  // Preload more if user is likely to finish video
  if (timeRemaining < 60 && bufferHealth > 0.7) {
    preloadAhead(30); // 30 seconds ahead
  } else if (bufferHealth > 0.8) {
    preloadAhead(20);
  } else if (bufferHealth < 0.3) {
    preloadAhead(10); // Conservative if buffer is low
  }

  // Preload next video in playlist
  if (timeRemaining < 10) {
    preloadNextVideo();
  }
}

Adaptive Bitrate Algorithm

function selectQuality(bandwidth, bufferHealth, currentQuality) {
  // Quality ladder (bitrates in bps)
  const qualities = [
    { name: "240p", bitrate: 300000 },
    { name: "360p", bitrate: 800000 },
    { name: "480p", bitrate: 1400000 },
    { name: "720p", bitrate: 2800000 },
    { name: "1080p", bitrate: 5000000 },
    { name: "2160p", bitrate: 8000000 },
  ];

  // Conservative switching (bandwidth * 0.8 safety factor)
  const safeBandwidth = bandwidth * 0.8;

  // Upscale if buffer is healthy and bandwidth supports
  if (bufferHealth > 0.7) {
    for (let i = qualities.length - 1; i >= 0; i--) {
      if (safeBandwidth >= qualities[i].bitrate) {
        return qualities[i].name;
      }
    }
  }

  // Downscale immediately if buffering
  if (bufferHealth < 0.3) {
    const currentIndex = qualities.findIndex((q) => q.name === currentQuality);
    return qualities[Math.max(0, currentIndex - 1)].name;
  }

  return currentQuality;
}

Buffering Strategy

┌─────────────────────────────────────────────────┐
│         Buffer Management Strategy              │
│                                                 │
│  Initial Buffer:  5 seconds                    │
│  Target Buffer:   15-30 seconds                │
│  Max Buffer:      60 seconds                   │
│                                                 │
│  Rebuffering Threshold: < 3 seconds            │
│  Quality Switch Threshold: < 10 seconds        │
│                                                 │
│  Buffer States:                                
│  [0-3s]   -> Critical (downscale quality)      │
│  [3-10s]  -> Low (maintain quality)            │
│  [10-30s] -> Healthy (consider upscale)        │
│  [30-60s] -> Optimal (upscale if possible)     │
│  [60s+]   -> Pause preloading                  │
└─────────────────────────────────────────────────┘

9.2 Thumbnail Optimization

1. Generate Multiple Thumbnails
   - Sprite sheet (for seek preview)
   - Default thumbnail (720p)
   - Small thumbnail (360p for lists)
   - WebP format (smaller size)

2. Lazy Loading
   - Load thumbnails only when in viewport
   - Intersection Observer API
   - Placeholder blur image

3. Responsive Images
   <img
     srcset="thumb_360.webp 360w, thumb_720.webp 720w"
     sizes="(max-width: 600px) 360px, 720px"
     loading="lazy"
   />

9.3 Database Optimizations

Read Replicas

┌─────────────┐
│   Master    │ (Writes: Upload, Update)
│  Database   │
└──────┬──────┘
       │
       │ Replication
       ├───────────┬───────────┬───────────┐
       ▼           ▼           ▼           ▼
  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
  │ Read    │ │ Read    │ │ Read    │ │ Read    │
  │ Replica │ │ Replica │ │ Replica │ │ Replica │
  │  (US)   │ │  (EU)   │ │ (APAC)  │ │ (Other) │
  └─────────┘ └─────────┘ └─────────┘ └─────────┘
       ▲           ▲           ▲           ▲
       │           │           │           │
  [Read] [Read] [Read] [Read] [Read] [Read]
  Video metadata, comments, recommendations

Database Indexing

-- Composite indexes for common queries
CREATE INDEX idx_video_channel_date
  ON videos(channel_id, upload_date DESC);

CREATE INDEX idx_video_category_views
  ON videos(category_id, view_count DESC);

-- Full-text search index
CREATE FULLTEXT INDEX idx_video_search
  ON videos(title, description, tags);

-- Covering index (includes all columns needed)
CREATE INDEX idx_video_list
  ON videos(status, category_id, upload_date DESC)
  INCLUDE (video_id, title, thumbnail_url, duration);

Query Optimization

-- Bad: N+1 query problem
SELECT * FROM videos WHERE channel_id = 'xyz';
-- Then for each video:
SELECT * FROM video_stats WHERE video_id = ?;

-- Good: Single JOIN query
SELECT v.*, vs.view_count, vs.like_count
FROM videos v
LEFT JOIN video_stats vs ON v.video_id = vs.video_id
WHERE v.channel_id = 'xyz'
ORDER BY v.upload_date DESC
LIMIT 20;

-- Use pagination with cursor
SELECT * FROM videos
WHERE upload_date < '2025-01-15T10:00:00Z'
ORDER BY upload_date DESC
LIMIT 20;

9.4 Backend Optimizations

API Response Compression

Gzip compression for JSON responses
Reduces response size by 70-90%

Before: 50KB JSON
After: 5KB Gzipped

Headers:
  Content-Encoding: gzip
  Accept-Encoding: gzip, deflate, br

Database Connection Pooling

Connection Pool Settings:
  Min Connections: 10
  Max Connections: 100
  Connection Timeout: 30s
  Idle Timeout: 600s

Reuse connections instead of creating new ones
Reduces latency from ~100ms to ~5ms per query

Async Processing

Synchronous Upload Flow (Slow):
User -> Upload -> Transcode -> Store -> Return
Total: 10+ minutes

Asynchronous Upload Flow (Fast):
User -> Upload -> Queue Job -> Return
                    |
                    v
               Background Worker
                    |
                    v
          Transcode -> Store -> Notify
Total user wait: < 5 seconds

10. Error Handling & Edge Cases

10.1 Video Player Errors

// Comprehensive error handling
class VideoPlayerErrorHandler {
  handleError(error) {
    switch (error.code) {
      case "MEDIA_ERR_ABORTED":
        // User aborted playback
        this.logError("User aborted", error);
        break;

      case "MEDIA_ERR_NETWORK":
        // Network error during download
        this.retryWithBackoff();
        this.switchToLowerQuality();
        this.showUserMessage("Network error. Retrying...");
        break;

      case "MEDIA_ERR_DECODE":
        // Video decode error
        this.switchToAlternateCodec();
        this.reportCorruptVideo();
        break;

      case "MEDIA_ERR_SRC_NOT_SUPPORTED":
        // Unsupported video format
        this.showUserMessage("Video format not supported");
        this.fallbackToFlashPlayer(); // Legacy support
        break;

      case "MANIFEST_LOAD_ERROR":
        // HLS/DASH manifest failed to load
        this.retryManifestLoad();
        break;

      case "SEGMENT_LOAD_ERROR":
        // Individual segment failed
        this.skipSegment();
        this.continuePlayback();
        break;

      default:
        this.showGenericError();
        this.reportToMonitoring(error);
    }
  }

  retryWithBackoff() {
    const delays = [1000, 2000, 5000, 10000]; // ms
    let attempt = 0;

    const retry = () => {
      if (attempt < delays.length) {
        setTimeout(() => {
          this.reloadVideo();
          attempt++;
        }, delays[attempt]);
      } else {
        this.showUserMessage("Unable to load video. Please try again later.");
      }
    };

    retry();
  }
}

10.2 Upload Failures

Upload Error Scenarios:

1. File Too Large (>10GB)
   - Reject with clear error message
   - Suggest video compression
   - Return 413 Payload Too Large

2. Unsupported Format
   - Check file extension and MIME type
   - Return 415 Unsupported Media Type
   - Provide list of supported formats

3. Network Interruption
   - Resume upload from last chunk
   - Store upload progress in Redis
   - Support multipart upload resume

4. Storage Full
   - Return 507 Insufficient Storage
   - Queue for retry when space available
   - Notify admins

5. Virus/Malware Detection
   - Scan uploaded file
   - Quarantine suspicious files
   - Notify user and reject upload

10.3 Transcoding Failures

Transcoding Error Recovery:

1. Worker Failure
   - Job returns to queue
   - Another worker picks up
   - Max retries: 3

2. Corrupted Video File
   - Attempt repair with FFmpeg
   - If repair fails, notify user
   - Mark video as failed

3. Partial Transcoding Success
   - 720p succeeds, 1080p fails
   - Publish available resolutions
   - Retry failed resolutions

4. Timeout (>1 hour)
   - Split large video into chunks
   - Transcode chunks in parallel
   - Merge transcoded chunks

5. Resource Exhaustion
   - Scale up worker pool
   - Prioritize important videos
   - Queue low-priority videos

10.4 CDN Failures

CDN Failure Scenarios:

1. Edge Server Down
   - DNS failover to next nearest edge
   - Fallback to origin server
   - Auto-healing and alerting

2. Cache Corruption
   - Invalidate corrupted cache
   - Serve from origin
   - Re-warm cache

3. Origin Server Unreachable
   - Serve stale content (if acceptable)
   - Use backup origin server
   - Alert operations team

4. DDoS Attack
   - Rate limiting at edge
   - CAPTCHA for suspicious traffic
   - Geo-blocking if needed

10.5 Edge Cases

Concurrent Video Edits

Problem: User uploads video, then immediately updates title/description

Solution:
1. Lock video metadata during initial processing
2. Queue metadata updates
3. Apply updates after processing completes
4. Use optimistic locking with version numbers

Deleted Video Still Cached

Problem: Video deleted but still accessible via CDN

Solution:
1. Immediate cache invalidation on delete
2. Purge CDN cache (max propagation: 5 min)
3. Add "video not found" check in origin
4. Return 404 even if cached (with short TTL)

View Count Inconsistency

Problem: Different view counts across regions

Solution:
1. Accept eventual consistency
2. Batch updates every 5 minutes
3. Use distributed counter (Redis)
4. Periodic reconciliation job
5. Show approximate counts ("1M+" instead of exact)

Live Stream to VOD Transition

Problem: Live stream ends, should become video-on-demand

Solution:
1. Detect stream end event
2. Concatenate live segments
3. Transcode to standard VOD formats
4. Update manifest from live to VOD
5. Archive chat replay alongside video

Seek in Unbuffered Region

Problem: User seeks to 5:00 but only 0:00-1:00 buffered

Solution:
1. Clear existing buffer
2. Request manifest for 5:00 timestamp
3. Load segments starting from 5:00
4. Show loading spinner during seek
5. Resume playback when buffered

11. Interview Cross-Questions

11.1 Scalability Questions

Q: How would you handle 10x traffic spike (e.g., breaking news)?

A: Multi-pronged approach:

  1. Auto-scaling: Horizontally scale services (API, transcoders, DB read replicas)
  2. CDN: Most traffic absorbed by CDN edge caches (95%+ hit rate)
  3. Rate Limiting: Protect backend services from overload
  4. Graceful Degradation:

    • Disable non-critical features (recommendations, comments)
    • Serve lower quality videos
    • Queue non-urgent operations
  5. Load Shedding: Reject requests with 503 when overloaded
  6. Pre-warming: If spike is predictable, pre-populate CDN caches

Q: How do you handle millions of concurrent uploads?

A:

  1. Chunked Uploads: Break into 5MB chunks, upload in parallel
  2. Upload Service Cluster: Horizontal scaling with load balancer
  3. Queue-Based Transcoding: Decouple upload from processing
  4. Priority Queue: Prioritize verified channels, smaller videos
  5. Backpressure: Slow down uploads if queue is full
  6. Direct S3 Upload: Generate pre-signed URLs, client uploads directly to S3

11.2 Performance Questions

Q: Video start time is 5 seconds. How to reduce to <2 seconds?

A:

  1. Reduce Initial Manifest Size: Serve only first 30s of manifest
  2. Preload First Segment: Embed first segment in HTML (inline)
  3. Adaptive Initial Quality: Start with 360p, upgrade after buffering
  4. CDN Edge Optimization: Ensure nearest edge has content
  5. HTTP/2 Server Push: Push manifest + first segment together
  6. Reduce DNS Lookup: Use DNS prefetching, HTTP keep-alive
  7. Optimize Encoding: Use faster codec profiles for first segments

Q: How do you optimize for mobile devices with limited bandwidth?

A:

  1. Aggressive Quality Downscaling: Start with 240p on slow networks
  2. Reduce Segment Size: 2-second segments instead of 10-second
  3. Thumbnail Sprites: Single image with all seek thumbnails
  4. Data Saver Mode: Lower quality, disable autoplay
  5. Offline Download: Allow download for offline viewing
  6. Adaptive Preloading: Reduce preload buffer on mobile
  7. Video Compression: Use H.265/VP9 for better compression

11.3 Consistency & Reliability Questions

Q: How do you ensure view counts are accurate?

A:

  • Challenge: Exact accuracy is expensive at scale
  • Solution: Approximate counting with eventual consistency

    1. Client sends view event after 30s of watch time
    2. Event logged to Kafka/Kinesis
    3. Stream processor (Flink) aggregates events in 5-min windows
    4. Batch update to Redis counter
    5. Periodic flush to database (every hour)
    6. Tolerate 5-10 min delay in count updates
  • De-duplication: Use session ID + video ID to prevent double-counting
  • Bot Detection: Filter out bot traffic, suspicious IPs

Q: What happens if transcoding service crashes mid-job?

A:

  1. Job Queue with Retry: Job remains in queue until acknowledged
  2. Worker Heartbeat: Workers send heartbeat every 30s
  3. Job Timeout: If no heartbeat for 2 min, job returns to queue
  4. Max Retries: Retry up to 3 times, then mark as failed
  5. Checkpoint State: Save transcoding progress every 20%
  6. Resume from Checkpoint: New worker resumes from last checkpoint
  7. Dead Letter Queue: Failed jobs go to DLQ for manual investigation

11.4 Data Modeling Questions

Q: Why use both SQL and NoSQL databases?

A:

  • SQL (MySQL/PostgreSQL):

    • Structured data with strong relationships
    • ACID transactions (e.g., user subscriptions)
    • Complex queries (e.g., search, recommendations)
    • Examples: Videos, Users, Channels
  • NoSQL (Cassandra/DynamoDB):

    • High write throughput (comments, analytics)
    • Flexible schema (user-generated content)
    • Time-series data (watch history, view counts)
    • Scalability (billions of comments)
    • Examples: Comments, Watch History, Analytics

Q: How do you handle video deletion while ensuring no orphaned data?

A:

  1. Soft Delete: Mark video as deleted, don’t remove immediately
  2. Background Cleanup Job:

    • Delete all resolutions from S3
    • Delete thumbnails
    • Delete comments (Cassandra)
    • Delete analytics data
    • Remove from CDN cache
    • Remove from search index
  3. Cascading Delete: Use database foreign keys with ON DELETE CASCADE
  4. Async Queue: Queue delete operations for background processing
  5. Audit Log: Keep deletion record for compliance
  6. Grace Period: 30-day trash period before permanent deletion

11.5 Cost Optimization Questions

Q: Video storage and bandwidth costs are very high. How to optimize?

A:

  1. Storage Optimization:
  • Delete rarely watched videos (after warning user)
  • Archive old videos to cheaper cold storage (Glacier)
  • De-duplicate identical videos
  • Remove redundant resolutions (e.g., skip 1440p)
  • Use better compression (H.265, VP9, AV1)
  1. Bandwidth Optimization:
  • Aggressive CDN caching (reduce origin bandwidth)
  • Smart preloading (don’t preload if user won’t watch)
  • Disable autoplay on mobile
  • Lower default quality on slow networks
  • Use P2P delivery for live streams (WebRTC mesh)
  1. Transcoding Optimization:

    • Adaptive transcoding (don’t generate 4K for short videos)
    • On-demand transcoding (only transcode when requested)
    • Use cheaper GPU instances for encoding
    • Batch transcode jobs during off-peak hours

Q: How do you decide which videos to cache on CDN?

A:

  • Multi-factor scoring:
  1. Popularity: View count, trending score
  2. Recency: Newly uploaded videos
  3. Geography: Popular in specific regions
  4. Channel: Verified channels, high subscriber count
  5. Content Type: Music videos, viral content
  • Cache Tiers:

    • Hot Tier (SSD, all edges): Top 1% most popular
    • Warm Tier (HDD, major edges): Top 10%
    • Cold Tier (origin fetch): Long-tail content
  • Eviction Policy: LRU with weighted scoring

11.6 Real-Time Features Questions

Q: How would you implement live streaming?

A:

  1. Ingest:
  • Streamer uses RTMP/WebRTC to push to ingest server
  • Ingest server in nearest region
  1. Transcoding:
  • Real-time transcoding to multiple qualities
  • Low-latency encoding (<3s delay)
  1. Distribution:
  • HLS for regular live (10-30s delay acceptable)
  • WebRTC for ultra-low latency (<1s delay)
  • CDN edge caching of live segments
  1. Playback:
  • Adaptive bitrate streaming
  • Live DVR (rewind live stream)
  • Chat synchronization
  1. Fallback:

    • Automatic archive to VOD after stream ends

Q: How do you implement real-time comment updates?

A:

  1. WebSocket Connection: Persistent connection for real-time updates
  2. Pub/Sub System: Redis Pub/Sub or Kafka

    • User posts comment -> Publish to topic
    • All connected clients subscribed to topic receive update
  3. Scaling:

    • Multiple WebSocket servers behind load balancer
    • Sticky sessions for connection persistence
    • Redis for cross-server message passing
  4. Fallback: HTTP long-polling if WebSocket unavailable
  5. Optimization: Only send updates for visible comments (top 50)

11.7 Security Questions

Q: How do you prevent unauthorized video access?

A:

  1. Authentication: JWT tokens for user identity
  2. Authorization: Check video privacy settings

    • Public: Anyone can watch
    • Unlisted: Only with direct link
    • Private: Only owner/invited users
  3. Signed URLs: Time-limited, encrypted video URLs
   https://cdn.example.com/video.m3u8?token=xyz&expires=1234567890
  1. Token Rotation: Short-lived tokens (5-15 min)
  2. DRM: Encrypted video with Widevine/FairPlay for premium content
  3. Geo-Blocking: Restrict content by region if required

Q: How do you prevent video piracy?

A:

  1. DRM Encryption: Widevine, FairPlay, PlayReady
  2. Watermarking: Visible/invisible watermarks with user ID
  3. HDCP: Prevent screen recording (hardware-level)
  4. Forensic Watermarking: Trace leaked videos back to source
  5. Download Restrictions: Disable right-click, inspect element
  6. Rate Limiting: Prevent bulk downloading
  7. Legal: DMCA takedown process, content ID matching

12. Trade-offs & Design Decisions

SQL vs NoSQL for Comments

Decision: Use NoSQL (Cassandra)

  • Pro: Better write scalability for high-volume comments
  • Pro: Time-ordered retrieval (TIMEUUID)
  • Con: Limited query flexibility
  • Con: Eventual consistency

HLS vs DASH

Decision: Support both, prefer HLS

  • HLS: Wider browser support (Safari, iOS)
  • DASH: Open standard, better features
  • Solution: Serve HLS to Apple devices, DASH to others

Synchronous vs Asynchronous Transcoding

Decision: Asynchronous with job queue

  • Pro: Fast upload response (<5s)
  • Pro: Decouple upload from processing
  • Con: Video not immediately available
  • Mitigation: Show processing status, estimate completion time

CDN vs Self-Hosted Streaming

Decision: Use CDN (CloudFront, Akamai)

  • Pro: Global edge caching, low latency
  • Pro: DDoS protection, high availability
  • Con: Expensive for high traffic
  • Mitigation: Aggressive caching, P2P for live streams

Exact vs Approximate View Counting

Decision: Approximate counting with 5-min delay

  • Pro: Massive scalability improvement
  • Pro: Reduced database write load
  • Con: Slight delay in count updates
  • Why: Users tolerate small delays for view counts

Summary

This design provides a scalable, performant, and reliable video streaming platform similar to YouTube. Key highlights:

  1. Scalability: Horizontally scalable services, CDN for global reach
  2. Performance: Adaptive bitrate streaming, multi-layer caching, <2s start time
  3. Reliability: Queue-based transcoding, retry mechanisms, graceful degradation
  4. Cost Efficiency: Aggressive caching, smart preloading, compression
  5. User Experience: Smooth playback, real-time comments, personalized recommendations

The architecture handles millions of concurrent users, supports live and VOD streaming, and provides a YouTube-like experience with adaptive quality, comments, and recommendations.

13. Accessibility (a11y)

Video Player Keyboard Controls

┌─────────────────────────────────────────────────────────────────────────────┐
│                    VIDEO PLAYER KEYBOARD SHORTCUTS                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  Playback Controls:                                                         │
│  ──────────────────                                                          │
│  Space / K         Play / Pause                                            │
│  J                 Rewind 10 seconds                                        │
│  L                 Fast forward 10 seconds                                  │
│  ← / →             Seek backward/forward 5 seconds                         │
│  Home              Go to beginning                                          │
│  End               Go to end                                                │
│  0-9               Jump to 0%-90% of video                                 │
│                                                                              │
│  Volume Controls:                                                           │
│  ────────────────                                                            │
│  M                 Mute / Unmute                                            │
│  ↑ / ↓             Increase / Decrease volume 5%                           │
│                                                                              │
│  Display Controls:                                                          │
│  ─────────────────                                                           │
│  F                 Toggle fullscreen                                        │
│  Escape            Exit fullscreen                                          │
│  C                 Toggle captions                                          │
│  < / >             Decrease / Increase playback speed                      │
│  I                 Toggle mini-player                                       │
│  T                 Toggle theater mode                                      │
│                                                                              │
│  Navigation:                                                                │
│  ───────────                                                                 │
│  Tab               Navigate between controls                               │
│  Shift+N           Next video in playlist                                  │
│  Shift+P           Previous video in playlist                              │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Accessible Video Player Implementation

// AccessibleVideoPlayer.tsx
const AccessibleVideoPlayer = ({ videoId, captions }: VideoPlayerProps) => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [volume, setVolume] = useState(1);
  const [isMuted, setIsMuted] = useState(false);
  const [captionsEnabled, setCaptionsEnabled] = useState(false);
  const announcer = useRef<HTMLDivElement>(null);

  // Screen reader announcements
  const announce = (message: string) => {
    if (announcer.current) {
      announcer.current.textContent = message;
      // Clear after announcement
      setTimeout(() => {
        if (announcer.current) announcer.current.textContent = "";
      }, 1000);
    }
  };

  // Keyboard handler
  const handleKeyDown = (e: KeyboardEvent) => {
    const video = videoRef.current;
    if (!video) return;

    // Don't handle if typing in input
    if (e.target instanceof HTMLInputElement) return;

    switch (e.key) {
      case " ":
      case "k":
        e.preventDefault();
        togglePlay();
        break;

      case "j":
        e.preventDefault();
        seekBy(-10);
        announce("Rewound 10 seconds");
        break;

      case "l":
        e.preventDefault();
        seekBy(10);
        announce("Fast forwarded 10 seconds");
        break;

      case "ArrowLeft":
        e.preventDefault();
        seekBy(-5);
        announce("Rewound 5 seconds");
        break;

      case "ArrowRight":
        e.preventDefault();
        seekBy(5);
        announce("Fast forwarded 5 seconds");
        break;

      case "ArrowUp":
        e.preventDefault();
        adjustVolume(0.05);
        break;

      case "ArrowDown":
        e.preventDefault();
        adjustVolume(-0.05);
        break;

      case "m":
        e.preventDefault();
        toggleMute();
        break;

      case "f":
        e.preventDefault();
        toggleFullscreen();
        break;

      case "c":
        e.preventDefault();
        toggleCaptions();
        break;

      default:
        // Number keys 0-9 for percentage seek
        if (e.key >= "0" && e.key <= "9") {
          e.preventDefault();
          const percent = parseInt(e.key) * 10;
          seekToPercent(percent);
          announce(`Jumped to ${percent}%`);
        }
    }
  };

  const togglePlay = () => {
    const video = videoRef.current;
    if (!video) return;

    if (video.paused) {
      video.play();
      setIsPlaying(true);
      announce("Playing");
    } else {
      video.pause();
      setIsPlaying(false);
      announce("Paused");
    }
  };

  const adjustVolume = (delta: number) => {
    const video = videoRef.current;
    if (!video) return;

    const newVolume = Math.max(0, Math.min(1, video.volume + delta));
    video.volume = newVolume;
    setVolume(newVolume);
    announce(`Volume ${Math.round(newVolume * 100)}%`);
  };

  const toggleCaptions = () => {
    setCaptionsEnabled(!captionsEnabled);
    announce(captionsEnabled ? "Captions off" : "Captions on");
  };

  return (
    <div
      className="video-player-container"
      role="application"
      aria-label="Video player"
      onKeyDown={handleKeyDown}
      tabIndex={0}
    >
      {/* Screen reader announcements */}
      <div
        ref={announcer}
        role="status"
        aria-live="polite"
        aria-atomic="true"
        className="sr-only"
      />

      <video
        ref={videoRef}
        aria-label={`Video: ${title}`}
        onTimeUpdate={() => setCurrentTime(videoRef.current?.currentTime || 0)}
        onLoadedMetadata={() => setDuration(videoRef.current?.duration || 0)}
      >
        {captionsEnabled &&
          captions.map((caption) => (
            <track
              key={caption.language}
              kind="captions"
              src={caption.url}
              srcLang={caption.language}
              label={caption.label}
              default={caption.isDefault}
            />
          ))}
      </video>

      {/* Accessible controls */}
      <div
        className="video-controls"
        role="toolbar"
        aria-label="Video controls"
      >
        <button
          aria-label={isPlaying ? "Pause" : "Play"}
          aria-pressed={isPlaying}
          onClick={togglePlay}
        >
          {isPlaying ? <PauseIcon /> : <PlayIcon />}
        </button>

        <div className="timeline-container">
          <input
            type="range"
            aria-label="Video timeline"
            aria-valuemin={0}
            aria-valuemax={duration}
            aria-valuenow={currentTime}
            aria-valuetext={formatTime(currentTime)}
            value={currentTime}
            max={duration}
            onChange={(e) => seekTo(parseFloat(e.target.value))}
          />
          <span className="sr-only">
            {formatTime(currentTime)} of {formatTime(duration)}
          </span>
        </div>

        <button
          aria-label={isMuted ? "Unmute" : "Mute"}
          aria-pressed={isMuted}
          onClick={toggleMute}
        >
          {isMuted ? <MuteIcon /> : <VolumeIcon />}
        </button>

        <input
          type="range"
          aria-label="Volume"
          aria-valuemin={0}
          aria-valuemax={100}
          aria-valuenow={Math.round(volume * 100)}
          value={volume * 100}
          max={100}
          onChange={(e) => setVolume(parseFloat(e.target.value) / 100)}
        />

        <button
          aria-label={
            captionsEnabled ? "Turn off captions" : "Turn on captions"
          }
          aria-pressed={captionsEnabled}
          onClick={toggleCaptions}
        >
          <CaptionsIcon />
        </button>

        <button aria-label="Enter fullscreen" onClick={toggleFullscreen}>
          <FullscreenIcon />
        </button>
      </div>
    </div>
  );
};

Captions & Subtitles

// CaptionManager.tsx
interface Caption {
  startTime: number;
  endTime: number;
  text: string;
}

const CaptionManager = ({
  captions,
  currentTime,
  enabled,
  style,
}: CaptionManagerProps) => {
  const [activeCaption, setActiveCaption] = useState<Caption | null>(null);

  useEffect(() => {
    if (!enabled) {
      setActiveCaption(null);
      return;
    }

    const caption = captions.find(
      (c) => currentTime >= c.startTime && currentTime <= c.endTime
    );

    setActiveCaption(caption || null);
  }, [currentTime, captions, enabled]);

  if (!activeCaption) return null;

  return (
    <div
      className="caption-container"
      role="region"
      aria-label="Video captions"
      aria-live="off" // Don't announce each caption
      style={{
        backgroundColor: style.backgroundColor,
        color: style.textColor,
        fontSize: style.fontSize,
        fontFamily: style.fontFamily,
      }}
    >
      {activeCaption.text}
    </div>
  );
};

// Caption settings panel
const CaptionSettings = ({ onStyleChange }: CaptionSettingsProps) => {
  return (
    <div
      role="dialog"
      aria-label="Caption settings"
      className="caption-settings"
    >
      <h3 id="caption-settings-title">Caption Settings</h3>

      <div role="group" aria-labelledby="font-size-label">
        <label id="font-size-label">Font Size</label>
        <select
          aria-describedby="font-size-label"
          onChange={(e) => onStyleChange("fontSize", e.target.value)}
        >
          <option value="75%">75%</option>
          <option value="100%">100% (Default)</option>
          <option value="150%">150%</option>
          <option value="200%">200%</option>
        </select>
      </div>

      <div role="group" aria-labelledby="font-family-label">
        <label id="font-family-label">Font Family</label>
        <select onChange={(e) => onStyleChange("fontFamily", e.target.value)}>
          <option value="sans-serif">Sans-serif</option>
          <option value="serif">Serif</option>
          <option value="monospace">Monospace</option>
        </select>
      </div>

      <div role="group" aria-labelledby="bg-color-label">
        <label id="bg-color-label">Background</label>
        <select
          onChange={(e) => onStyleChange("backgroundColor", e.target.value)}
        >
          <option value="rgba(0,0,0,0.75)">Black (Default)</option>
          <option value="rgba(255,255,255,0.75)">White</option>
          <option value="transparent">Transparent</option>
        </select>
      </div>
    </div>
  );
};

Focus Management

// useFocusTrap.ts - Trap focus within video player settings
const useFocusTrap = (
  isActive: boolean,
  containerRef: RefObject<HTMLElement>
) => {
  useEffect(() => {
    if (!isActive || !containerRef.current) return;

    const container = containerRef.current;
    const focusableElements = container.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    );

    const firstElement = focusableElements[0] as HTMLElement;
    const lastElement = focusableElements[
      focusableElements.length - 1
    ] as HTMLElement;

    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key !== "Tab") return;

      if (e.shiftKey) {
        if (document.activeElement === firstElement) {
          e.preventDefault();
          lastElement.focus();
        }
      } else {
        if (document.activeElement === lastElement) {
          e.preventDefault();
          firstElement.focus();
        }
      }
    };

    container.addEventListener("keydown", handleKeyDown);
    firstElement?.focus();

    return () => {
      container.removeEventListener("keydown", handleKeyDown);
    };
  }, [isActive, containerRef]);
};

// VideoSettingsDialog.tsx
const VideoSettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => {
  const dialogRef = useRef<HTMLDivElement>(null);
  const previouslyFocusedRef = useRef<HTMLElement | null>(null);

  useFocusTrap(isOpen, dialogRef);

  useEffect(() => {
    if (isOpen) {
      // Store previously focused element
      previouslyFocusedRef.current = document.activeElement as HTMLElement;
    } else {
      // Restore focus when closing
      previouslyFocusedRef.current?.focus();
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      ref={dialogRef}
      role="dialog"
      aria-modal="true"
      aria-labelledby="settings-title"
      className="settings-dialog"
    >
      <h2 id="settings-title">Video Settings</h2>

      {/* Settings content */}

      <button onClick={onClose} aria-label="Close settings">
        Close
      </button>
    </div>
  );
};

Screen Reader Optimizations

┌─────────────────────────────────────────────────────────────────────────────┐
│                    SCREEN READER ANNOUNCEMENTS                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  Playback Events:                                                           │
│  ────────────────                                                            │
│  • "Playing" / "Paused"                                                    │
│  • "Video ended"                                                            │
│  • "Buffering..." / "Playback resumed"                                     │
│  • "Rewound 10 seconds"                                                    │
│  • "Fast forwarded 10 seconds"                                             │
│                                                                              │
│  Quality Changes:                                                           │
│  ────────────────                                                            │
│  • "Quality changed to 1080p"                                              │
│  • "Auto quality: switching to 720p"                                       │
│                                                                              │
│  Volume:                                                                    │
│  ───────                                                                     │
│  • "Volume 50%"                                                             │
│  • "Muted" / "Unmuted"                                                      │
│                                                                              │
│  Captions:                                                                  │
│  ─────────                                                                   │
│  • "Captions on: English"                                                  │
│  • "Captions off"                                                           │
│                                                                              │
│  Navigation:                                                                │
│  ───────────                                                                 │
│  • "Jumped to 50%"                                                          │
│  • "Now playing: [Video Title]"                                            │
│  • "Entered fullscreen" / "Exited fullscreen"                              │
│                                                                              │
│  Implementation:                                                            │
│  ───────────────                                                             │
│  
│ │ {announcement} │ │
│ │ │ └─────────────────────────────────────────────────────────────────────────────┘

Video Grid Accessibility

// AccessibleVideoGrid.tsx
const AccessibleVideoGrid = ({ videos }: VideoGridProps) => {
  return (
    <section aria-label="Video recommendations">
      <h2 id="recommendations-heading">Recommended Videos</h2>

      <ul
        role="list"
        aria-labelledby="recommendations-heading"
        className="video-grid"
      >
        {videos.map((video, index) => (
          <li key={video.id}>
            <article
              className="video-card"
              aria-labelledby={`video-title-${video.id}`}
            >
              <a
                href={`/watch?v=${video.id}`}
                aria-describedby={`video-meta-${video.id}`}
              >
                <img
                  src={video.thumbnailUrl}
                  alt="" // Decorative, title provides context
                  loading="lazy"
                />

                <div className="video-duration" aria-hidden="true">
                  {formatDuration(video.duration)}
                </div>
              </a>

              <div className="video-info">
                <h3 id={`video-title-${video.id}`}>
                  <a href={`/watch?v=${video.id}`}>{video.title}</a>
                </h3>

                <p id={`video-meta-${video.id}`} className="video-meta">
                  <span>{video.channelName}</span>
                  <span aria-label={`${video.views} views`}>
                    {formatViews(video.views)} views
                  </span>
                  <span aria-label={`uploaded ${video.uploadedAt}`}>
                    {formatRelativeTime(video.uploadedAt)}
                  </span>
                  <span className="sr-only">
                    Duration: {formatDurationAccessible(video.duration)}
                  </span>
                </p>
              </div>
            </article>
          </li>
        ))}
      </ul>
    </section>
  );
};

// Accessible duration formatting
const formatDurationAccessible = (seconds: number): string => {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = seconds % 60;

  const parts = [];
  if (hours > 0) parts.push(`${hours} hour${hours > 1 ? "s" : ""}`);
  if (minutes > 0) parts.push(`${minutes} minute${minutes > 1 ? "s" : ""}`);
  if (secs > 0) parts.push(`${secs} second${secs > 1 ? "s" : ""}`);

  return parts.join(" ");
};

14. Security & Content Protection

DRM (Digital Rights Management) Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                    DRM ENCRYPTION FLOW                                       │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  Video Upload → Encode → Encrypt → Store                                   │
│                                                                              │
│   ┌──────────┐                                                              │
│   │  Raw     │                                                              │
│   │  Video   │                                                              │
│   └────┬─────┘                                                              │
│        │                                                                     │
│        ▼                                                                     │
│   ┌──────────────────────────────────────────────────────────────────┐     │
│   │                  ENCODING & ENCRYPTION                            │     │
│   │                                                                    │     │
│   │  1. Transcode to multiple resolutions                            │     │
│   │  2. Generate encryption keys (per video)                         │     │
│   │  3. Encrypt each segment with AES-128-CTR                        │     │
│   │  4. Create DRM licenses:                                         │     │
│   │     • Widevine (Chrome, Android)                                 │     │
│   │     • FairPlay (Safari, iOS)                                     │     │
│   │     • PlayReady (Edge, Windows)                                  │     │
│   │  5. Store encrypted segments + license server URLs               │     │
│   │                                                                    │     │
│   └──────────────────────────────────────────────────────────────────┘     │
│        │                                                                     │
│        ▼                                                                     │
│   ┌──────────┐   ┌──────────┐   ┌──────────┐                              │
│   │ Encrypted│   │  License │   │   Key    │                              │
│   │ Segments │   │  Server  │   │  Server  │                              │
│   │   (S3)   │   │          │   │          │                              │
│   └──────────┘   └──────────┘   └──────────┘                              │
│                                                                              │
│  Playback Flow:                                                             │
│  ───────────────                                                             │
│  1. Client requests manifest (encrypted video reference)                   │
│  2. Client requests license from License Server                           │
│  3. License Server validates user subscription/rental                     │
│  4. License Server returns decryption key                                 │
│  5. Client CDM (Content Decryption Module) decrypts video                │
│  6. Decrypted video played in protected path                             │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Widevine DRM Implementation

// DRMPlayer.tsx - Multi-DRM Video Player
const DRMPlayer = ({ videoId, manifestUrl }: DRMPlayerProps) => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const initDRM = async () => {
      const video = videoRef.current;
      if (!video) return;

      // Detect DRM support
      const drmConfig = await detectDRMSupport();

      if (!drmConfig) {
        setError("DRM not supported on this browser");
        return;
      }

      try {
        // Initialize Shaka Player with DRM
        const shaka = await import("shaka-player");
        shaka.polyfill.installAll();

        const player = new shaka.Player(video);

        player.configure({
          drm: {
            servers: {
              "com.widevine.alpha": `${API_URL}/drm/widevine/license?videoId=${videoId}`,
              "com.apple.fps.1_0": `${API_URL}/drm/fairplay/license?videoId=${videoId}`,
              "com.microsoft.playready": `${API_URL}/drm/playready/license?videoId=${videoId}`,
            },
          },
        });

        // FairPlay requires certificate
        if (drmConfig.keySystem === "com.apple.fps.1_0") {
          const cert = await fetchFairPlayCertificate();
          player.configure(
            "drm.advanced.com\.apple\.fps\.1_0.serverCertificate",
            cert
          );
        }

        await player.load(manifestUrl);
      } catch (err) {
        console.error("DRM initialization failed:", err);
        setError("Failed to load protected content");
      }
    };

    initDRM();
  }, [videoId, manifestUrl]);

  return (
    <div className="drm-player">
      {error && (
        <div className="drm-error" role="alert">
          <p>{error}</p>
          <p>Try using Chrome, Safari, or Edge for protected content.</p>
        </div>
      )}
      <video ref={videoRef} controls />
    </div>
  );
};

// Detect which DRM system is supported
const detectDRMSupport = async (): Promise<DRMConfig | null> => {
  const keySystems = [
    { keySystem: "com.widevine.alpha", name: "Widevine" },
    { keySystem: "com.apple.fps.1_0", name: "FairPlay" },
    { keySystem: "com.microsoft.playready", name: "PlayReady" },
  ];

  for (const config of keySystems) {
    try {
      const result = await navigator.requestMediaKeySystemAccess(
        config.keySystem,
        [
          {
            initDataTypes: ["cenc"],
            videoCapabilities: [
              {
                contentType: 'video/mp4; codecs="avc1.42E01E"',
              },
            ],
          },
        ]
      );

      if (result) {
        return config;
      }
    } catch (e) {
      // This DRM not supported, try next
    }
  }

  return null;
};

Signed URL Implementation

// signedUrl.ts - Generate time-limited signed URLs
interface SignedUrlParams {
  videoId: string;
  userId: string;
  expiresIn: number; // seconds
  ipAddress?: string;
}

// Server-side: Generate signed URL
const generateSignedUrl = (params: SignedUrlParams): string => {
  const expires = Math.floor(Date.now() / 1000) + params.expiresIn;

  const dataToSign = [
    params.videoId,
    params.userId,
    expires.toString(),
    params.ipAddress || "",
  ].join(":");

  const signature = crypto
    .createHmac("sha256", process.env.URL_SIGNING_SECRET!)
    .update(dataToSign)
    .digest("hex");

  const queryParams = new URLSearchParams({
    videoId: params.videoId,
    expires: expires.toString(),
    signature,
    ...(params.ipAddress && { ip: params.ipAddress }),
  });

  return `${CDN_BASE_URL}/videos/${params.videoId}/manifest.m3u8?${queryParams}`;
};

// Client-side: Request signed URL before playback
const getVideoUrl = async (videoId: string): Promise<string> => {
  const response = await fetch(`/api/v1/videos/${videoId}/play`, {
    headers: {
      Authorization: `Bearer ${getAuthToken()}`,
    },
  });

  const { signedUrl, expiresAt } = await response.json();

  // Store expiration for refresh
  videoUrlCache.set(videoId, { url: signedUrl, expiresAt });

  return signedUrl;
};

// Auto-refresh signed URL before expiration
const useSignedUrl = (videoId: string) => {
  const [signedUrl, setSignedUrl] = useState<string | null>(null);
  const refreshTimer = useRef<NodeJS.Timeout>();

  useEffect(() => {
    const fetchAndRefresh = async () => {
      const response = await getVideoUrl(videoId);
      setSignedUrl(response.url);

      // Refresh 1 minute before expiration
      const refreshIn = response.expiresAt - Date.now() - 60000;
      refreshTimer.current = setTimeout(fetchAndRefresh, refreshIn);
    };

    fetchAndRefresh();

    return () => {
      if (refreshTimer.current) {
        clearTimeout(refreshTimer.current);
      }
    };
  }, [videoId]);

  return signedUrl;
};
┌─────────────────────────────────────────────────────────────────────────────┐
│                    CONTENT ID MATCHING FLOW                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  Upload Flow with Content ID Check:                                        │
│  ──────────────────────────────────                                          │
│                                                                              │
│   User Upload                                                               │
│       │                                                                      │
│       ▼                                                                      │
│   ┌──────────────────┐                                                      │
│   │  Extract Audio/  │                                                      │
│   │  Video Fingerprint│                                                     │
│   └────────┬─────────┘                                                      │
│            │                                                                 │
│            ▼                                                                 │
│   ┌──────────────────┐                                                      │
│   │  Compare Against │                                                      │
│   │  Reference DB    │                                                      │
│   │  (100M+ tracks)  │                                                      │
│   └────────┬─────────┘                                                      │
│            │                                                                 │
│     ┌──────┴──────┐                                                         │
│     │             │                                                          │
│   Match?        No Match                                                    │
│     │             │                                                          │
│     ▼             ▼                                                          │
│  ┌─────────┐   ┌─────────┐                                                  │
│  │ Check   │   │ Publish │                                                  │
│  │ Policy  │   │ Video   │                                                  │
│  └────┬────┘   └─────────┘                                                  │
│       │                                                                      │
│       ├─────────────┬─────────────┬─────────────┐                          │
│       │             │             │             │                           │
│       ▼             ▼             ▼             ▼                           │
│    Block         Monetize      Track Only    Allow                         │
│  (Takedown)   (Ads for owner)  (Analytics)  (Licensed)                    │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Age-Restricted Content UI

// AgeVerification.tsx
const AgeVerification = ({ videoId, onVerified }: AgeVerificationProps) => {
  const [birthDate, setBirthDate] = useState<string>("");
  const [error, setError] = useState<string | null>(null);

  const verifyAge = () => {
    const birth = new Date(birthDate);
    const today = new Date();
    const age = Math.floor(
      (today.getTime() - birth.getTime()) / (365.25 * 24 * 60 * 60 * 1000)
    );

    if (age >= 18) {
      // Store verification in session
      sessionStorage.setItem("ageVerified", "true");
      onVerified();
    } else {
      setError("You must be 18 or older to view this content.");
    }
  };

  return (
    <div className="age-verification" role="dialog" aria-labelledby="age-title">
      <div className="age-content">
        <WarningIcon />
        <h2 id="age-title">Age-Restricted Content</h2>

        <p>
          This video may be inappropriate for some users. Please confirm your
          age to continue.
        </p>

        <div className="age-form">
          <label htmlFor="birthdate">Date of Birth</label>
          <input
            id="birthdate"
            type="date"
            value={birthDate}
            onChange={(e) => setBirthDate(e.target.value)}
            max={new Date().toISOString().split("T")[0]}
            aria-describedby={error ? "age-error" : undefined}
          />

          {error && (
            <p id="age-error" className="error" role="alert">
              {error}
            </p>
          )}

          <button onClick={verifyAge} disabled={!birthDate}>
            Confirm Age
          </button>
        </div>

        <p className="privacy-notice">
          We don't store your date of birth.
          Learn more
        

); }; // AgeRestrictedWrapper.tsx const AgeRestrictedWrapper = ({ video, children, }: AgeRestrictedWrapperProps) => { const [isVerified, setIsVerified] = useState(false); useEffect(() => { // Check if already verified this session const verified = sessionStorage.getItem("ageVerified") === "true"; setIsVerified(verified || !video.isAgeRestricted); }, ); if (!isVerified) { return ( setIsVerified(true)} /> ); } return <>{children}; };

Content Moderation UI

// ReportContent.tsx
const ReportContent = ({ videoId, onClose }: ReportContentProps) => {
  const [reason, setReason] = useState<string>("");
  const [details, setDetails] = useState<string>("");
  const [timestamp, setTimestamp] = useState<number | null>(null);
  const [isSubmitting, setIsSubmitting] = useState(false);

  const reportReasons = [
    { value: "sexual", label: "Sexual content" },
    { value: "violent", label: "Violent or graphic content" },
    { value: "hateful", label: "Hateful or abusive content" },
    { value: "harassment", label: "Harassment or bullying" },
    { value: "spam", label: "Spam or misleading" },
    { value: "copyright", label: "Copyright infringement" },
    { value: "privacy", label: "Privacy violation" },
    { value: "dangerous", label: "Dangerous acts" },
    { value: "child_safety", label: "Child safety concern" },
    { value: "other", label: "Other" },
  ];

  const submitReport = async () => {
    setIsSubmitting(true);

    try {
      await fetch("/api/v1/reports", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          videoId,
          reason,
          details,
          timestamp,
          reportedAt: new Date().toISOString(),
        }),
      });

      onClose();
      showToast(
        "Report submitted. Thank you for helping keep our platform safe."
      );
    } catch (error) {
      showToast("Failed to submit report. Please try again.", "error");
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <div className="report-dialog" role="dialog" aria-labelledby="report-title">
      <h2 id="report-title">Report Video</h2>

      <fieldset>
        <legend>What's the issue?
        {reportReasons.map((r) => (
          
        ))}
      

      

Previous Post

Under the Hood: Universal Commerce Protocol (UCP)

Next Post

try-rs: Control your experiment and project folders.

Related Posts