Files
meet-hub/adilo-ai-agent-quick-ref.md
dwindown 60baf32f73 Display bootcamp lesson chapters on Product Detail page as marketing content
This commit implements displaying lesson chapters/timeline as marketing content
on the Product Detail page for bootcamp products, helping potential buyers
understand the detailed breakdown of what they'll learn.

## Changes

### Product Detail Page (src/pages/ProductDetail.tsx)
- Updated Lesson interface to include optional chapters property
- Modified fetchCurriculum to fetch chapters along with lessons
- Enhanced renderCurriculumPreview to display chapters as nested content under lessons
- Chapters shown with timestamps and titles, clickable to navigate to bootcamp access page
- Visual hierarchy: Module → Lesson → Chapters with proper indentation and styling

### Review System Fixes
- Fixed review prompt re-appearing after submission (before admin approval)
- Added hasSubmittedReview check to prevent showing prompt when review exists
- Fixed edit review functionality to pre-populate form with existing data
- ReviewModal now handles both INSERT (new) and UPDATE (edit) operations
- Edit resets is_approved to false requiring re-approval

### Video Player Enhancements
- Implemented Adilo/Video.js integration for M3U8/HLS playback
- Added video progress tracking with refs pattern for reliability
- Implemented chapter navigation for both Adilo and YouTube players
- Added keyboard shortcuts (Space, Arrows, F, M, J, L)
- Resume prompt for returning users with saved progress

### Database Migrations
- Added Adilo video support fields (m3u8_url, mp4_url, video_host)
- Created video_progress table for tracking user watch progress
- Fixed consulting slots user_id foreign key
- Added chapters support to products and bootcamp_lessons tables

### Documentation
- Added Adilo implementation plan and quick reference docs
- Cleaned up transcript analysis files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 23:54:32 +07:00

9.1 KiB

Adilo Video Player - Quick AI Agent Reference

For Your Windsurf/IDE AI Agent

Copy this into your .codebase instructions or share with AI agent:


Project: LearnHub - Adilo M3U8 Video Player with Custom Chapters

Problem Statement

Build a React video player that:

  • Streams video from Adilo using M3U8 (HLS) direct URL
  • Displays custom chapter navigation
  • Allows click-to-jump to chapters
  • Tracks user progress
  • Saves completion data to Supabase

Tech Stack

  • React 18+ (Hooks, Context)
  • HLS.js - for M3U8 streaming
  • Supabase - for progress tracking
  • HTML5 Video API - native controls
  • CSS Modules - styling

Quick Command Reference

Install Dependencies

npm install hls.js @supabase/supabase-js

Project Structure to Create

src/
├── components/
│   ├── AdiloVideoPlayer.jsx          # Main component
│   ├── ChapterNavigation.jsx         # Chapter sidebar
│   └── ProgressBar.jsx               # Progress indicator
├── hooks/
│   ├── useAdiloPlayer.js             # HLS streaming logic
│   └── useChapterTracking.js         # Chapter tracking
├── services/
│   ├── adiloService.js               # Adilo API calls
│   └── progressService.js            # Supabase progress
├── styles/
│   └── AdiloVideoPlayer.module.css
└── types/
    └── video.types.js

Implementation Phases (In Order)

PHASE 1: useAdiloPlayer Hook

Goal: Get HLS.js working with M3U8 URL

What to build:

  • React hook that initializes HLS.js instance
  • Return: videoRef, isReady, isPlaying, currentTime, duration
  • Handle browser compatibility (Safari vs HLS.js)
  • Clean up HLS instance on unmount
  • Emit callbacks: onTimeUpdate, onEnded, onError

Test with:

const { videoRef, currentTime, isReady } = useAdiloPlayer({
  m3u8Url: "https://adilo.bigcommand.com/m3u8/...",
  autoplay: false,
  onTimeUpdate: (time) => console.log(time)
});

PHASE 2: useChapterTracking Hook

Goal: Determine which chapter is currently active

What to build:

  • React hook that tracks active chapter
  • Input: chapters array, currentTime
  • Return: activeChapter, activeChapterId, chapterProgress
  • Detect chapter transitions
  • Calculate progress percentage

Chapter data structure:

{
  id: "ch1",
  startTime: 0,
  endTime: 120,
  title: "Introduction",
  description: "Welcome to the course"
}

Test with:

const { activeChapter, chapterProgress } = useChapterTracking({
  chapters: [...],
  currentTime: 45
});
// activeChapter should be chapter with startTime ≤ 45 < endTime

PHASE 3: AdiloVideoPlayer Component

Goal: Main player combining both hooks

What to build:

  • Component that uses both hooks
  • Renders:
  • Props: m3u8Url, videoId, chapters, autoplay, showChapters
  • Methods: jumpToChapter(), play(), pause()
  • Callbacks: onChapterChange, onVideoComplete, onProgressUpdate

Usage example:

<AdiloVideoPlayer
  m3u8Url="https://adilo.bigcommand.com/m3u8/..."
  chapters={[{id: "1", startTime: 0, endTime: 120, title: "Intro"}]}
  onVideoComplete={() => markComplete()}
  onChapterChange={(ch) => console.log(ch.title)}
/>

PHASE 4: ChapterNavigation Component

Goal: Display chapters user can click to jump

What to build:

  • Sidebar/timeline showing all chapters
  • Highlight current active chapter
  • Show time for each chapter
  • Click handler to jump to chapter
  • Progress bar for each chapter

Props:

  • chapters: Chapter[]
  • activeChapterId: string
  • currentTime: number
  • onChapterClick: (startTime: number) => void
  • completedChapters: string[]

PHASE 5: Supabase Integration

Goal: Save video progress to database

Database schema needed:

CREATE TABLE video_progress (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id uuid NOT NULL,
  video_id uuid NOT NULL,
  last_position int,
  completed_chapters text[],
  watched_percentage int,
  is_completed boolean DEFAULT false,
  completed_at timestamp,
  created_at timestamp DEFAULT now(),
  updated_at timestamp DEFAULT now(),
  UNIQUE(user_id, video_id)
);

Functions to implement:

  • saveProgress(userId, videoId, currentTime, completedChapters)
  • getLastPosition(userId, videoId) - resume from last position
  • markVideoComplete(userId, videoId)
  • getVideoAnalytics(userId, videoId)

PHASE 6: Styling

Goal: Make it look good and responsive

Key CSS classes needed:

  • .adilo-player - main container
  • .video-container - video wrapper
  • .chapters-sidebar - chapter list
  • .chapter-item - individual chapter
  • .chapter-item.active - highlight active
  • .progress-bar - progress visualization

Responsive breakpoints:

  • Desktop: sidebar on right
  • Tablet: sidebar below video
  • Mobile: horizontal timeline under video

Key Implementation Details

HLS.js Initialization Pattern

if (Hls.isSupported()) {
  const hls = new Hls();
  hls.loadSource(m3u8Url);
  hls.attachMedia(videoElement);
  hls.on(Hls.Events.MANIFEST_PARSED, () => {
    videoElement.play();
  });
} else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
  // Safari native HLS support
  videoElement.src = m3u8Url;
}

Chapter Jump Implementation

const jumpToChapter = (startTime) => {
  if (videoRef.current) {
    videoRef.current.currentTime = startTime;
    videoRef.current.play();
  }
};

Track Current Chapter Pattern

const current = chapters.find(
  ch => currentTime >= ch.startTime && currentTime < ch.endTime
);
setActiveChapter(current?.id);

Debounce Progress Saves

// Save progress every 5 seconds, not on every timeupdate
const saveProgressDebounced = debounce(
  (userId, videoId, time) => saveProgress(userId, videoId, time),
  5000
);

Common Tasks for AI Agent

When asking your AI agent to implement:

Task: "Create useAdiloPlayer hook"

Should generate:

  • Import HLS from 'hls.js'
  • useRef for video element
  • useEffect to initialize HLS
  • useCallback for event handlers
  • Clean up logic in return

Task: "Add chapter jump functionality"

Should implement:

  • Button click handler
  • Call jumpToChapter(startTime)
  • Update videoRef.current.currentTime
  • Play the video

Task: "Save progress to Supabase"

Should implement:

  • Create/update row in video_progress table
  • Include: user_id, video_id, last_position, completed_chapters
  • Handle conflicts (UPSERT)
  • Error handling

Task: "Make chapters responsive"

Should implement:

  • CSS Grid for desktop (sidebar)
  • Flex column for mobile
  • Media query at 768px breakpoint
  • Adjust spacing and font sizes

Testing Checklist

Unit Tests

  • useAdiloPlayer hook returns correct refs/values
  • useChapterTracking calculates active chapter correctly
  • jumpToChapter updates video.currentTime
  • Progress saves to Supabase

Integration Tests

  • Video plays when component mounts
  • Chapter changes highlight in UI
  • Clicking chapter jumps player
  • Progress saves on interval
  • Completion triggers callback

Browser Tests

  • Works on Chrome/Edge (HLS.js)
  • Works on Firefox (HLS.js)
  • Works on Safari (native HLS)
  • Works on mobile browsers

Edge Cases

  • Bad M3U8 URL shows error
  • Network interruption handled
  • Video paused mid-chapter
  • Page refresh preserves position

Environment Variables Needed

VITE_SUPABASE_URL=your_supabase_url
VITE_SUPABASE_ANON_KEY=your_supabase_key

Debugging Tips

HLS.js not loading?

  • Check M3U8 URL is correct from Adilo
  • Verify CORS headers from Adilo
  • Check browser console for HLS.js errors
  • Try hls.on(Hls.Events.ERROR, console.error)

Chapter not highlighting?

  • Add console.log(currentTime, chapters) to track values
  • Verify chapter startTime/endTime are correct
  • Check activeChapter state is updating

Progress not saving?

  • Verify Supabase connection works
  • Check user_id and video_id are defined
  • Add error logs to saveProgress function
  • Check database table schema matches

Performance Optimization Tips

  1. Memoize chapters list to prevent re-renders

    const chapters = useMemo(() => chaptersData, [chaptersData]);
    
  2. Debounce timeupdate events (fires 60x per second!)

    const updateChapter = debounce(() => {...}, 100);
    video.addEventListener('timeupdate', updateChapter);
    
  3. Lazy load chapter images/thumbnails

    <img loading="lazy" src={chapter.thumbnail} />
    
  4. Use React.memo for ChapterNavigation

    export default React.memo(ChapterNavigation);
    

Resources


Status: Ready to implement! 🚀

Start with PHASE 1 (useAdiloPlayer hook), then PHASE 2-6 in order.