Skip to main content

Overview

Platzi Viewer includes a comprehensive progress tracking system that monitors your learning journey, saves your progress locally, and syncs it to the server for backup and cross-device access.

How Progress Works

Progress States

Each class in every course can have one of three states:
  1. Not Started - Default state for all classes
  2. In Progress - Video has been played but not completed
  3. Complete - Class has been marked as finished

Class Keys

Progress is tracked using unique class identifiers:
const classKey = `${catIdx}|${routeIdx}|${courseIdx}|${modIdx}|${classIdx}`
// Example: "0|1|2|3|4" for specific class
This hierarchical key ensures each class is uniquely identifiable across the entire content library.

Automatic Progress Tracking

Video Playback Tracking

In Progress Marking: When you start playing a video, it’s automatically marked as “in progress”:
{
  status: 'in_progress',
  lastWatched: '2024-03-15T14:30:00.000Z',
  watchTime: 125.5  // seconds
}
Triggers:
  • Video play event fires
  • Current playback time is recorded
  • Timestamp is saved

Auto-Complete on Finish

Classes are automatically marked complete when:
  • Video plays to the end (ended event)
  • Progress is saved with completion timestamp
  • Next class auto-advances after 1.5 seconds
Completion Record:
{
  status: 'complete',
  completedAt: '2024-03-15T14:45:32.000Z'
}

Manual Progress Management

Mark Complete Button

Manually mark a class as complete:
  1. Open any class in the player
  2. Click Completada button below the video
  3. Button updates to show checkmark: ✅ Completada
  4. Sidebar updates to reflect completion
Use Cases:
  • Mark non-video content (readings, HTML) as complete
  • Skip videos you’ve already watched elsewhere
  • Track progress through text-based lessons

Progress Indicators

Course View:
  • Module headers show progress: “5/12 clases”
  • Progress bars show percentage completion
  • Class numbers change to checkmarks (✓) when complete
Player Sidebar:
  • Active class shows play icon (▶)
  • Complete classes show checkmark (✓)
  • Not started classes show class number
Learning View:
  • Progress rings display completion percentage
  • “X% completado” text shows exact progress
  • Completed/remaining class counts

Local Storage (Browser)

localStorage Implementation

Progress is saved in your browser’s localStorage: Storage Key: platzi_progress Data Structure:
{
  "0|1|2|0|0": {
    "status": "complete",
    "completedAt": "2024-03-15T10:30:00.000Z"
  },
  "0|1|2|0|1": {
    "status": "in_progress",
    "lastWatched": "2024-03-15T10:32:00.000Z",
    "watchTime": 45.2
  }
}

Advantages of localStorage

Instant Access - No network latency ✅ Offline Support - Works without server connection ✅ Privacy - Data stays on your device ✅ Fast Updates - No API calls needed

localStorage Limitations

⚠️ Browser-Specific - Not shared across different browsers ⚠️ Device-Specific - Not shared across different devices ⚠️ Can Be Cleared - Browser cache clearing removes progress ⚠️ Storage Limit - Typically 5-10MB per origin

Server Synchronization

Automatic Sync

Progress syncs to the server automatically: Sync Triggers:
  • Any progress change (complete, in-progress)
  • Debounced to 400ms to batch updates
  • Saves to /api/progress endpoint
Server Storage:
  • Saves to progress.json file on server
  • Acts as backup and cross-device sync
  • Survives browser cache clearing

Sync Strategy

Queue-Based Syncing:
// Progress change triggers sync queue
queueServerSync() {
  clearTimeout(this._syncTimer);
  this._syncTimer = setTimeout(() => {
    saveServerProgress();
  }, 400);
}
Benefits:
  • Batches rapid changes (e.g., marking multiple classes complete)
  • Reduces server load
  • Prevents race conditions

Server Progress Backup

Server-side progress.json format:
{
  "0|1|2|0|0": {
    "status": "complete",
    "completedAt": "2024-03-15T10:30:00.000Z"
  },
  "0|1|2|0|1": {
    "status": "in_progress",
    "lastWatched": "2024-03-15T10:32:00.000Z",
    "watchTime": 45.2
  }
}

Progress Merging

Conflict Resolution

When local and server progress conflict, the system uses intelligent merging: Merge Priority:
  1. Status Rank: complete > in_progress > not_started
  2. Timestamp: Newer timestamp wins if status is equal
Merge Algorithm:
mergeProgress(localProgress, serverProgress) {
  // Combine all keys from both sources
  // For each key:
  //   - If only in one source: use that record
  //   - If in both: compare status rank
  //   - If equal rank: use newer timestamp
}
Example Conflict:
// Local: in_progress at 10:30
// Server: complete at 10:25
// Result: complete (higher status rank)

// Local: complete at 10:30
// Server: complete at 10:25  
// Result: complete at 10:30 (newer timestamp)

Initialization Merge

On app load, progress merges automatically:
await state.init();
// 1. Load courses data
// 2. Load local progress from localStorage
// 3. Load server progress from /api/progress
// 4. Merge using conflict resolution
// 5. Save merged result to localStorage
// 6. Don't sync back to server immediately (already merged)

Progress Calculation

Course Progress

Calculated by counting completed classes:
const completed = countCourseCompleted(catIdx, routeIdx, courseIdx);
const total = countCourseClasses(course);
const percent = total > 0 ? (completed / total) * 100 : 0;
Handles:
  • Courses with detailed class arrays
  • Courses with only class counts (bootstrap mode)
  • Empty courses (0% progress)

Route Progress

Aggregates progress across all courses in a route:
getRouteProgress(catIdx, routeIdx) {
  // If route is a course: calculate course progress
  // If route has multiple courses:
  //   - Sum all completed classes across courses
  //   - Sum all total classes across courses
  //   - Calculate overall percentage
}

Overall Progress

Global statistics across all content:
getOverallProgress() {
  return {
    totalClasses,        // Total classes across all courses
    completedClasses,    // Total completed classes
    totalRoutes,         // Total learning paths
    startedRoutes,       // Routes with >0 completed
    completedRoutes,     // Routes with 100% completion
    percent              // Overall completion percentage
  }
}

Progress Views

Course Detail Page

Progress Ring:
     ╭─────╮
    │  75% │   15 completadas
     ╰─────╯   5 restantes
  • Large circular progress indicator
  • Percentage displayed in center
  • Completion stats below
  • Ring turns green when 100% complete
Module Progress:
  • Mini progress bars on each module header
  • “X/Y” class count
  • Checkmark (✅) on module number when 100% complete

Learning View

In-Progress Courses:
┌─────────────────────────────────┐
│ 📊 Desarrollo Web               │
│ Curso de JavaScript Avanzado    │
│                                 │
│    ╭──────╮                     │
│   │  45% │  Continuar →         │
│    ╰──────╯                     │
│ 18/40 clases completadas        │
└─────────────────────────────────┘
Shows only routes with progress > 0.

Player Sidebar

Visual Indicators:
  • Active: ▶ Class Name
  • Complete: ✓ Class Name (styled with completion color)
  • Not Started: 1 Class Name (number badge)
Module Summary:
[1] Module Name
    5/8 clases

Notes and Personal Tracking

Course Notes

Each course has a personal notes section: Features:
  • Large textarea for notes, code snippets, ideas
  • Auto-saves to localStorage on input
  • Persists per course (unique storage key)
  • Supports markdown-style text
Storage Key Pattern:
const notesKey = `platzi_notes_${catIdx}_${routeIdx}_${courseIdx}`;
Use Cases:
  • Take notes during video lessons
  • Save code snippets for reference
  • Track personal insights
  • Create quick reference guides

API Endpoints

Get Progress

GET /api/progress
Response:
{
  "0|1|2|0|0": {
    "status": "complete",
    "completedAt": "2024-03-15T10:30:00.000Z"
  }
}

Save Progress

POST /api/progress
Content-Type: application/json

{
  "0|1|2|0|0": {
    "status": "complete",
    "completedAt": "2024-03-15T10:30:00.000Z"
  }
}
Response: 200 OK or error with code

Cross-Device Progress

Syncing Across Devices

To sync progress across multiple devices:
  1. Same Server Required: All devices must connect to the same server
  2. Automatic Sync: Progress syncs on each change
  3. Merge on Load: Opening the app merges server progress
  4. No Manual Sync Needed: Everything is automatic
Workflow:
Device A: Mark class complete
  → Save to localStorage
  → Sync to server (progress.json)

Device B: Open app
  → Load local progress
  → Load server progress
  → Merge (server wins if newer)
  → Continue learning

Progress Persistence

Survives:
  • Browser restarts
  • Server restarts (saved to progress.json)
  • Network interruptions (localStorage backup)
Does NOT Survive:
  • Browser cache/data clearing (unless synced to server)
  • Changing to different server
  • Deleting progress.json on server

Privacy and Data

What’s Tracked

✓ Class completion status ✓ Completion timestamps
✓ Last watched timestamps ✓ Watch time (seconds) ✓ Personal course notes (localStorage only)

What’s NOT Tracked

✗ Video viewing analytics ✗ Detailed playback statistics ✗ Personal information ✗ Usage patterns/metrics ✗ Search queries

Data Location

  • localStorage: Your browser’s local storage
  • Server: progress.json file in server directory
  • Not Sent: No external services, no analytics, no telemetry

Troubleshooting Progress

Progress Not Saving

Check:
  1. Browser console for errors
  2. localStorage availability (private browsing may block)
  3. Server connectivity (/api/health)
  4. Disk space on server for progress.json

Progress Lost After Browser Clear

Solution:
  • Progress should restore from server on next load
  • If server progress was also lost, it cannot be recovered
  • Recommendation: Don’t clear browser data unnecessarily

Syncing Issues

Check:
  1. /api/progress endpoint returns data
  2. POST requests succeed (check Network tab)
  3. Server has write permissions for progress.json
  4. Check server logs for sync errors

Progress Not Merging

Verify:
  1. Server progress loads successfully on init
  2. No errors during merge (check console)
  3. localStorage has space available
  4. Timestamps are valid ISO 8601 format

Best Practices

  1. Let Auto-Tracking Work: Videos auto-complete on finish
  2. Manual Complete for Non-Video: Use button for readings/HTML content
  3. Use Notes Feature: Track insights and code snippets per course
  4. Check Learning View: Monitor overall progress regularly
  5. Don’t Clear Browser Data: If you must, ensure server sync is working
  6. Multiple Devices: Wait a few seconds after completing to ensure sync

Technical Details

State Management

Progress is managed by the StateService class: File: /js/services/state.js Key Methods:
  • markClassComplete(classKey) - Mark class as complete
  • markClassInProgress(classKey, time) - Mark class as in progress
  • isClassComplete(classKey) - Check completion status
  • getClassStatus(classKey) - Get current status
  • getCourseProgress(catIdx, routeIdx, courseIdx) - Calculate course progress
  • getRouteProgress(catIdx, routeIdx) - Calculate route progress

Progress Data Schema

interface ProgressRecord {
  status: 'complete' | 'in_progress' | 'not_started';
  completedAt?: string;      // ISO 8601 timestamp
  lastWatched?: string;      // ISO 8601 timestamp  
  watchTime?: number;        // seconds
}

interface ProgressData {
  [classKey: string]: ProgressRecord;
}

Performance Considerations

  • Debounced Sync: 400ms delay batches rapid changes
  • Minimal Server Calls: Only on actual progress changes
  • localStorage First: Instant reads, no network latency
  • Efficient Merging: O(n) merge algorithm on initialization
  • No Polling: Server doesn’t poll for updates (push-based)

Build docs developers (and LLMs) love