20 KiB
Phase 3: Creator Tools & VOD - Complete Implementation Guide
Duration: Weeks 9-13 | Complexity: High | Team Size: 3 devs
Executive Summary
Phase 3 empowers creators with professional tools for content management. VOD archival enables creators to build libraries of content. Clip creation allows viral moments to be shared. Advanced analytics help creators understand their audience.
Phase 3 Goals
Primary Goals
- Automatic VOD archival from live streams
- Clip creation and sharing system
- Advanced creator analytics dashboard
- Stream health monitoring
- VOD library management and playback
Success Criteria
- 📹 100+ VODs archived
- 🎬 50+ clips created and shared
- 📊 Analytics dashboard with 10+ metrics
- 📈 95% stream uptime with health metrics
- 🎥 <2 minute clip generation time
Sprint Breakdown
Sprint 3.1: VOD Archives
Duration: Days 1-5 | Team: 2 devs
User Story 3.1.1: Automatic Stream Recording
AS A creator
I WANT my streams automatically archived
SO THAT I don't lose content and can build a VOD library
Tasks:
- Enable stream recording on Cloudflare/Mux
- Store recordings in R2 storage
- Index VODs in database
- Create VOD metadata storage
- Auto-cleanup old test recordings
Recording Configuration:
// config/streaming.ts
export const recordingConfig = {
enabled: true,
minDurationMinutes: 1, // Only record streams >1 min
deleteAfterDays: 365, // Keep for 1 year
allowUserDelete: true,
storage: {
provider: 'r2', // Cloudflare R2
bucket: 'aethex-vods',
prefix: '{channelId}/{streamId}/',
},
formats: ['hls', 'mp4'], // HLS for streaming, MP4 for download
}
Database Schema:
model VOD {
id String @id @default(cuid())
streamId String @unique
stream Stream @relation(fields: [streamId], references: [id], onDelete: Cascade)
channelId String
channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade)
title String // From stream
description String?
thumbnail String?
// Storage paths
hlsUrl String // HLS stream URL
mp4Url String? // MP4 download link
// Duration & stats
duration Int // Minutes
fileSize Int // Bytes
resolution String @default("1080p") // Max resolution
// Metadata
viewCount Int @default(0)
likeCount Int @default(0)
clipCount Int @default(0)
// Visibility
isPublic Boolean @default(true)
isDeleted Boolean @default(false)
deletedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clips Clip[]
}
model Clip {
id String @id @default(cuid())
vodId String
vod VOD @relation(fields: [vodId], references: [id], onDelete: Cascade)
creatorId String
creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade)
title String
description String?
// Trim points (in seconds from start of VOD)
startTime Int
endTime Int
duration Int // endTime - startTime
// Clip URL and metadata
clipUrl String
thumbnail String?
// Engagement
viewCount Int @default(0)
likeCount Int @default(0)
shareCount Int @default(0)
// Status
status ClipStatus @default(PROCESSING) // PROCESSING, READY, FAILED
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum ClipStatus {
PROCESSING
READY
FAILED
DELETED
}
API Routes:
GET /api/channels/:id/vods- List creator's VODsGET /api/vods/:id- Get VOD detailsPUT /api/vods/:id- Update VOD (title, description, visibility)DELETE /api/vods/:id- Delete VODGET /api/vods/:id/comments- VOD comments (Phase 5)
Background Jobs:
- Record stream when LIVE status set
- Stop recording when stream ENDED
- Generate thumbnail at 25% mark
- Index VOD metadata
Webhook from Cloudflare/Mux:
// pages/api/webhooks/recording-ready.ts
export async function POST(req) {
const event = req.body
if (event.type === 'video.ready') {
// Update VOD status to READY
await db.vod.update({
where: { streamId: event.streamId },
data: {
hlsUrl: event.hlsUrl,
mp4Url: event.downloadUrl,
duration: event.duration,
}
})
}
}
Deliverables:
- Recording enabled on all streams
- VODs indexed in database
- VOD list page functional
- Webhook processing
User Story 3.1.2: VOD Playback & Library
AS A viewer
I WANT to watch VODs
SO THAT I can catch up on streams I missed
Tasks:
- VOD library page (
/channel/{username}/vods) - VOD playback player (reuse HLS player)
- VOD comments (Phase 5)
- VOD download link (optional)
- Progress tracking (watch time)
Page Structure:
/channel/[username]/vods
├── VOD List
│ ├── Grid/List toggle
│ ├── Sort: newest, trending, watched
│ ├── Filter: date range
│ └── Search VODs
├── VOD Cards
│ ├── Thumbnail
│ ├── Title
│ ├── Duration
│ ├── View count
│ ├── Upload date
│ └── Like button
└── Pagination (12 per page)
VOD Playback Page:
/vods/[vodId]
├── Player (full width, sticky controls)
├── Sidebar:
│ ├── VOD Info (title, date, views)
│ ├── Creator Card + Follow
│ ├── Like/Share buttons
│ └── Comments (Phase 5)
└── Related VODs (same channel)
Watch Progress Tracking:
model WatchProgress {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
vodId String
vod VOD @relation(fields: [vodId], references: [id], onDelete: Cascade)
lastPosition Int // Seconds watched
percentage Float // 0-100
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([userId, vodId])
}
API Routes:
GET /api/channels/:id/vods- List VODsGET /api/vods/:id- Get VOD detailsPOST /api/watch-progress- Save watch progressGET /api/watch-progress- Get user's watch history
Components:
components/vod/
├── VODLibrary.tsx
├── VODGrid.tsx
├── VODCard.tsx
├── VODPlayer.tsx
├── VODInfo.tsx
├── WatchProgressBar.tsx
└── RelatedVODs.tsx
Deliverables:
- VOD library page
- VOD playback working
- Watch progress tracking
- Search/filter working
User Story 3.1.3: Watch History & Recommendations
AS A viewer
I WANT to access my watch history
SO THAT I can resume videos and get recommendations
Tasks:
- Watch history page
- Resume button (jump to last position)
- Clear history option
- Recommended VODs from followed creators
- "Continue Watching" section
Routes:
GET /api/watch-history- User's watch historyDELETE /api/watch-history/:vodId- Remove from historyPOST /api/watch-history/clear- Clear all
Deliverables:
- Watch history page
- Resume functionality
- Continue watching section
Sprint 3.2: Clip Creation
Duration: Days 6-10 | Team: 2 devs
User Story 3.2.1: In-Stream Clip Button
AS A viewer
I WANT to create clips while watching
SO THAT I can share viral moments with others
Tasks:
- Add clip button to stream player
- Auto-capture last 30 seconds (configurable)
- Show clip creation feedback
- Redirect to clip editor after creation
- Display creation status
Clip Capture Logic:
// When user clicks clip button
const clipStream = async (streamId: string) => {
const now = Date.now()
// Clip last 30 seconds of HLS stream
const clipData = {
vodId: streamId, // Use stream as VOD
startTime: Math.max(0, currentPlaybackPosition - 30),
endTime: currentPlaybackPosition,
title: `Clip from ${creatorName}'s stream`,
}
// Queue clip generation job
await fetch('/api/clips', {
method: 'POST',
body: JSON.stringify(clipData)
})
}
Deliverables:
- Clip button visible on stream player
- Clip auto-capture working
- User feedback on clip creation
User Story 3.2.2: Clip Editor
AS A creator
I WANT to edit and publish clips
SO THAT I can curate my best moments
Tasks:
- Build clip editor page (
/clips/[clipId]/edit) - Timeline with trim handles
- Preview scrubber
- Title/description editor
- Thumbnail editor (select frame)
- One-click publish to Twitter/TikTok (Phase 3B)
Page Structure:
/clips/[clipId]/edit
├── Video Preview
│ ├── Timeline (full VOD)
│ ├── Clip section highlighted
│ ├── Trim handles (drag to adjust)
│ └── Play button
├── Edit Panel
│ ├── Title input
│ ├── Description textarea
│ ├── Thumbnail selector (frame picker)
│ ├── Tags input (Phase 2B)
│ └── Visibility toggle
└── Publish Action
├── Preview button
├── Save draft button
└── Publish button
Components:
components/clips/
├── ClipEditor.tsx
├── ClipTimeline.tsx
├── TrimHandles.tsx
├── FrameSelector.tsx
├── PreviewPlayer.tsx
└── PublishPanel.tsx
API Routes:
PUT /api/clips/:id- Update clip metadataPOST /api/clips/:id/generate- Generate clip videoPOST /api/clips/:id/publish- Publish clipPOST /api/clips/:id/thumbnail- Update thumbnail
Background Job:
- FFmpeg or Cloudflare Stream API to cut video segment
- Generate MP4 and thumbnail
- Upload to R2
- Update clip status to READY
Deliverables:
- Clip editor page
- Trim functionality
- Preview working
- Publish button functional
User Story 3.2.3: Clip Sharing & Discovery
AS A creator
I WANT my clips shared widely
SO THAT I grow my audience
Tasks:
- Clip sharing page (
/clips/:id) - Social media share buttons
- Embed code for websites
- Clip analytics
- Trending clips section
Clip Page Structure:
/clips/[clipId]
├── Header: Clip title + creator
├── Video Player
├── Share Buttons (Twitter, TikTok, Discord, etc)
├── Embed Code (iframe)
├── Stats (views, likes, shares, datetime)
├── Creator Card + Channel Link
├── Comments (Phase 5)
└── Related Clips (trending from channel)
Routes:
GET /api/clips/trending- Trending clipsGET /api/clips/:id/stats- Clip analyticsGET /api/channels/:id/clips- Creator's clipsPOST /api/clips/:id/like- Like clip (Phase 5)
Deliverables:
- Clip sharing page
- Share buttons working
- Trending clips showing
- Embed working
Sprint 3.3: Advanced Analytics
Duration: Days 11-15 | Team: 2 devs
User Story 3.3.1: Analytics Dashboard
AS A creator
I WANT detailed analytics
SO THAT I can understand and grow my audience
Tasks:
- Build analytics dashboard (
/dashboard/analytics) - Implement data collection
- Graphs and charts
- Filter by date range
- Export analytics (CSV)
Analytics Metrics:
Stream-level:
- Stream duration
- Peak concurrent viewers
- Average viewers
- Total views
- Chat messages
- Follow gains
- Conversion rate (viewers → followers)
Channel-level:
- Total hours streamed
- Total views (all time)
- Average peak viewers
- Growth (weekly/monthly)
- Most watched category
- Best performing streams
- Top clips
Database Schema:
model StreamAnalytics {
id String @id @default(cuid())
streamId String @unique
stream Stream @relation(fields: [streamId], references: [id], onDelete: Cascade)
// Viewers
peakConcurrent Int
avgConcurrent Int
uniqueViewers Int
totalWatchMinutes Int
// Engagement
chatMessages Int
newFollowers Int
conversionRate Float // followers gained / unique viewers
// VOD
vodViews Int @default(0)
vodEngagement Float @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ViewCountSnapshot {
id String @id @default(cuid())
streamId String
stream Stream @relation(fields: [streamId], references: [id], onDelete: Cascade)
viewCount Int
timestamp DateTime
@@index([streamId, timestamp])
}
Chart Components (using Recharts or Chart.js):
components/analytics/
├── AnalyticsDashboard.tsx
├── ViewersOverTime.tsx
├── PeakViewersChart.tsx
├── EngagementChart.tsx
├── TopStreamsTable.tsx
├── ChannelGrowthChart.tsx
└── ConversionRateMetric.tsx
API Routes:
GET /api/analytics/streams/:id- Stream analyticsGET /api/analytics/channels/:id- Channel analyticsGET /api/analytics/charts/viewers- Viewer count time seriesPOST /api/analytics/export- CSV export
Data Collection:
// Collect viewer count every minute
setInterval(async () => {
const activeStreams = await db.stream.findMany({
where: { status: 'LIVE' }
})
for (let stream of activeStreams) {
await db.viewCountSnapshot.create({
data: {
streamId: stream.id,
viewCount: stream.viewerCount,
timestamp: new Date(),
}
})
}
}, 60000)
Deliverables:
- Analytics dashboard built
- Charts displaying correctly
- Data collection working
- Date range filtering
User Story 3.3.2: Stream Health Monitoring
AS A creator
I WANT to see stream health metrics
SO THAT I can fix quality issues during live streaming
Tasks:
- Display bitrate, FPS, resolution
- Connection quality indicator
- Bitrate graph
- Issue alerts (bitrate drop, connection loss)
- Guide to fix issues
Metrics:
interface StreamHealth {
uptime: number // % uptime
bitrate: number // Kbps
fps: number // Frames per second
resolution: string // 1920x1080
latency: number // ms from ingest to edge
packetLoss: number // % packets lost
bufferingEvents: number // Number of times player buffered
}
Dashboard Widget:
Stream Health Status
├── Connection: Excellent (green indicator)
├── Bitrate: 5000 kbps (target 5500) [graph]
├── FPS: 60 [icon good]
├── Resolution: 1920x1080
├── Latency: 1.2s (acceptable range)
└── Alerts: None
Real-time Updates (via WebSocket):
socket.on('stream:health-update', (health) => {
updateHealthMetrics(health)
})
Deliverables:
- Health metrics displayed correctly
- Real-time updates working
- Alerts triggering
- Helpful tooltips
User Story 3.3.3: VOD & Clip Analytics
AS A creator
I WANT to see how my VODs and clips perform
SO THAT I can create more of the best content
Tasks:
- VOD view count tracking
- Clip performance metrics
- Trending clips dashboard
- Best performing content
Metrics:
- VOD views, engagement, average watch time
- Clip views, likes, shares, embeds
- Which streams generate most clips
- Which clips drive most traffic back to channel
API Routes:
GET /api/vods/:id/analyticsGET /api/clips/:id/analyticsGET /api/channels/:id/top-vodsGET /api/channels/:id/top-clips
Deliverables:
- VOD analytics working
- Clip analytics showing
- Top content identified
Sprint 3.4: Polish & Optimization
Duration: Days 16-20 | Team: 2 devs
Performance Optimization
- Optimize analytics queries (database indexes)
- Cache analytics data (Redis)
- Lazy load charts
- Implement pagination on clip lists
Mobile Responsiveness
- Mobile VOD player
- Mobile analytics dashboard
- Mobile clip editor
- Touch-friendly controls
Testing
- Unit tests for analytics calculations
- E2E tests: record stream → VOD created → viewable
- Clip creation and playback tests
- Analytics accuracy validation
Database Schema - Phase 3 Updates
New Models:
model VOD { /* ... */ }
model Clip { /* ... */ }
model StreamAnalytics { /* ... */ }
model ViewCountSnapshot { /* ... */ }
model WatchProgress { /* ... */ }
Updated Models:
model Stream {
// Add VOD relation
vod VOD?
}
model Channel {
// Add VOD and clip relations
vods VOD[]
}
model User {
// Add clip relation
clips Clip[]
}
API Routes - Phase 3 Additions
VODs
GET /api/channels/:id/vodsGET /api/vods/:idPUT /api/vods/:idDELETE /api/vods/:idPOST /api/watch-progressGET /api/watch-progress
Clips
POST /api/clips(create from stream/VOD)GET /api/clips/:idPUT /api/clips/:idDELETE /api/clips/:idPOST /api/clips/:id/generateGET /api/clips/trendingGET /api/channels/:id/clips
Analytics
GET /api/analytics/streams/:idGET /api/analytics/channels/:idGET /api/analytics/charts/viewersPOST /api/analytics/export
Components - Phase 3 Additions
VOD
components/vod/
├── VODLibrary.tsx
├── VODCard.tsx
├── VODPlayer.tsx
└── WatchProgressBar.tsx
Clips
components/clips/
├── ClipEditor.tsx
├── ClipTimeline.tsx
├── TrimHandles.tsx
├── ClipCard.tsx
└── TrendingClips.tsx
Analytics
components/analytics/
├── AnalyticsDashboard.tsx
├── ViewersChart.tsx
├── EngagementChart.tsx
├── StreamHealth.tsx
└── TopContentTable.tsx
Third-Party Services
Video Processing
- Cloudflare Stream or Mux: Recording/clipping
- FFmpeg (self-hosted): Optional for additional processing
Charts
- Recharts or Chart.js: Graphing library
Storage
- Cloudflare R2: VOD and clip storage (already in Phase 1)
Success Metrics
VOD Metrics
- 100+ VODs created
- 50k+ total VOD views
- 10+ min average VOD watch time
- 30% VOD engagement rate
Clip Metrics
- 50+ clips created
- 10k+ total clip views
- 5% of viewers create clips
- 100+ clips shared to social media
Analytics Metrics
- 90%+ creators use analytics dashboard
- Charts load in <2s
- 0 analytics data loss
- Trending clips updated hourly
Performance Metrics
- VOD playback <2s latency
- Clip generation <2 minutes
- Analytics queries <1s
- Database stays <100GB
Phase 3 Timeline
| Week | Sprint | Focus |
|---|---|---|
| 9 | 3.1 | VOD archival & playback |
| 10 | 3.2 | Clip creation & editing |
| 11 | 3.3 | Advanced analytics |
| 12-13 | 3.4 | Polish & optimization |
Phase 3 Completion Checklist
- VOD archival working automatically
- VOD playback page functional
- Clip creation from stream working
- Clip editor with trim working
- Clip sharing page built
- Analytics dashboard showing data
- Stream health metrics displaying
- Mobile responsive
- Performance optimized
- Tests passing
- Deployed to production
- 100+ VODs archived
- 50+ clips created
Phase 3 Estimated Completion: Week 13 (by April 18, 2025)
Next Phase: Phase 4 - Monetization (April 21, 2025)
See PHASE_4_MONETIZATION.md for Phase 4 details.