PHPackages                             rajendra/youtube-progress - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. rajendra/youtube-progress

ActiveLibrary[Image &amp; Media](/categories/media)

rajendra/youtube-progress
=========================

Enterprise-grade YouTube video progress tracking for Laravel with anti-cheat detection, segment-based verification, and LMS-optimized completion tracking.

v1.0.2(yesterday)01↑2900%MITPHP ^8.2

Since Jul 19Compare

[ Source](https://github.com/rajendra-chimala/rajendra-youtube-progress)[ Packagist](https://packagist.org/packages/rajendra/youtube-progress)[ RSS](/packages/rajendra-youtube-progress/feed)WikiDiscussions Synced today

READMEChangelogDependencies (6)Versions (3)Used By (0)

Rajendra YouTube Progress
=========================

[](#rajendra-youtube-progress)

Enterprise-grade YouTube video progress tracking for Laravel with **anti-cheat detection**, segment-based verification, and LMS-optimized completion tracking.

```

```

That single line loads the YouTube IFrame API, tracks watch progress with **verified watched segments**, blocks forward-skip cheating, saves progress to Local Storage, and resumes playback on the viewer's next visit — automatically.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Why v1.0.2](#why-v102)
- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
    - [Blade component](#blade-component)
    - [Anti-cheat options](#anti-cheat-options)
    - [Progress bar component](#progress-bar-component)
    - [Helper function](#helper-function)
    - [Facade](#facade)
    - [Multiple videos on one page](#multiple-videos-on-one-page)
- [How anti-cheat tracking works](#how-anti-cheat-tracking-works)
- [Storage format](#storage-format)
- [Events](#events)
- [Developer callbacks](#developer-callbacks)
- [Public JavaScript API](#public-javascript-api)
- [Configuration reference](#configuration-reference)
- [Security](#security)
- [Accessibility](#accessibility)
- [Performance](#performance)
- [Browser &amp; platform support](#browser--platform-support)
- [Migration from v1.0.0](#migration-from-v100)
- [Example app](#example-app)
- [Testing](#testing)
- [FAQ](#faq)
- [Troubleshooting](#troubleshooting)
- [License](#license)

---

Why v1.0.2
----------

[](#why-v102)

In v1.0.0, a student could cheat by dragging the YouTube seek bar to the end of a video, instantly receiving course completion. v1.0.2 solves this with a comprehensive anti-cheat system:

ProblemSolutionStudent drags seek bar to end**Forward seek prevention** — player snaps back to the farthest watched pointCompletion based on current position**Segment-based completion** — only unique watched time countsStudent plays at 16x speed**Speed monitoring** — tracking pauses above configured limitStudent skips 5-minute chunks**Watched segments** — gaps in watched time are never countedCompletion now requires **actually watching** the required percentage of the video. No workarounds, no shortcuts.

---

Requirements
------------

[](#requirements)

RequirementVersionPHP^8.2Laravel^11.0 / ^12.0---

Installation
------------

[](#installation)

```
composer require rajendra/youtube-progress
```

Laravel's package auto-discovery registers the service provider and facade automatically.

Publish the config file and the compiled JS/CSS assets:

```
php artisan vendor:publish --tag=youtube-progress
```

You can publish each piece independently:

```
php artisan vendor:publish --tag=youtube-progress-config
php artisan vendor:publish --tag=youtube-progress-assets
php artisan vendor:publish --tag=youtube-progress-views
```

---

Configuration
-------------

[](#configuration)

```
// config/youtube-progress.php
return [
    'save_interval'          => 10000,     // ms between autosaves
    'completion_percentage'  => 95,        // % unique watched to complete
    'storage_prefix'         => 'rajendra_progress_',
    'auto_resume'            => true,
    'auto_load_api'          => true,
    'debug'                  => false,

    // Anti-cheat (new in v1.0.2)
    'prevent_forward_seek'   => true,
    'maximum_allowed_speed'  => 2,
    'seek_tolerance'         => 1.5,
    'toast_duration'         => 3000,
    'toast_message'          => 'You must watch this part before continuing.',

    // Dimensions
    'default_width'          => '100%',
    'default_height'         => '480',
];
```

Every key can also be set via `.env`:

```
YOUTUBE_PROGRESS_SAVE_INTERVAL=10000
YOUTUBE_PROGRESS_COMPLETION_PERCENTAGE=95
YOUTUBE_PROGRESS_PREVENT_FORWARD_SEEK=true
YOUTUBE_PROGRESS_MAXIMUM_ALLOWED_SPEED=2
YOUTUBE_PROGRESS_SEEK_TOLERANCE=1.5
YOUTUBE_PROGRESS_TOAST_DURATION=3000
YOUTUBE_PROGRESS_TOAST_MESSAGE=You must watch this part before continuing.
YOUTUBE_PROGRESS_DEBUG=false
```

---

Usage
-----

[](#usage)

### Blade component

[](#blade-component)

```

```

With options:

```

```

`video` accepts a bare 11-character ID or a full YouTube URL (`watch?v=`, `youtu.be/`, `embed/`, `shorts/`) — it's normalized to an ID internally.

### Anti-cheat options

[](#anti-cheat-options)

```
{{-- Enable forward seek prevention (default) --}}

{{-- Show inline progress bar inside the player --}}

{{-- Show real-time debug overlay --}}

{{-- Light theme --}}

{{-- Developer callbacks --}}

    function handleComplete(detail) {
        console.log('Video completed!', detail.videoId, detail.percentage + '%');
    }
    function handleSeekBlocked(detail) {
        console.log('Seek blocked at', detail.attempted, 's');
    }
    function handleProgress(detail) {
        console.log('Progress saved:', detail.percentage + '%');
    }

```

### Progress bar component

[](#progress-bar-component)

A standalone indicator that syncs with the player via events:

```

{{-- Without the percentage label --}}

```

### Helper function

[](#helper-function)

```
{!! youtube_progress('dQw4w9WgXcQ') !!}

{!! youtube_progress('dQw4w9WgXcQ', ['height' => 360, 'prevent-seek' => true]) !!}
```

### Facade

[](#facade)

```
use Rajendra\YoutubeProgress\Facades\YoutubeProgress;

$videoId = YoutubeProgress::resolveVideoId('https://youtu.be/dQw4w9WgXcQ');
$key = YoutubeProgress::storageKey($videoId);
```

### Multiple videos on one page

[](#multiple-videos-on-one-page)

```

```

Each gets a unique generated player ID and fully independent progress tracking. The IFrame API is loaded exactly once.

---

How anti-cheat tracking works
-----------------------------

[](#how-anti-cheat-tracking-works)

### Segment-based progress

[](#segment-based-progress)

Instead of storing a single `currentTime` number, the package stores an array of **watched segments** — time ranges the viewer has actually watched:

```
{
    "segments": [[0, 120], [120, 240], [240, 360]]
}
```

Overlapping segments are merged automatically. The **unique watched duration**is the sum of all segment lengths.

### Completion algorithm

[](#completion-algorithm)

```
unique watched duration / video duration × 100 = completion percentage

```

A video is marked completed only when this percentage reaches `completion_percentage` (default 95%). Dragging the seek bar to the end does **not** count as watched.

### Forward seek prevention

[](#forward-seek-prevention)

When `prevent_forward_seek` is enabled (default), the player tracks `maxWatchedTime` — the farthest point reached through normal playback. Any attempt to seek beyond this boundary:

1. Automatically snaps the player back to `maxWatchedTime`
2. Displays a toast notification
3. Dispatches a `youtube-progress:blocked` event

### Playback verification

[](#playback-verification)

Watched time is only counted while the YouTube player state is `PLAYING`. The following states are **ignored**:

- `BUFFERING` — no time counted during buffer stalls
- `PAUSED` — no time counted while paused
- `UNSTARTED` — no time counted before first play
- `ENDED` — segment finalized from last position to duration

### Speed monitoring

[](#speed-monitoring)

If playback speed exceeds `maximum_allowed_speed` (default 2x), the tracking system pauses segment recording and dispatches a `youtube-progress:speed-warning`event. Speed is checked on every tracking tick.

---

Storage format
--------------

[](#storage-format)

Everything lives in `localStorage`. Each video gets a key: `{prefix}{videoId}`.

```
{
    "version": "1.0.2",
    "videoId": "dQw4w9WgXcQ",
    "currentTime": 830,
    "maxWatchedTime": 820,
    "duration": 1200,
    "segments": [[0, 820]],
    "percentage": 68,
    "completed": false,
    "lastUpdated": "2026-07-19T12:30:20Z"
}
```

Saved automatically:

- Every `save_interval` milliseconds while playing
- On pause (includes final segment before pause)
- On video end (segment finalized to duration)
- When the tab becomes hidden (`visibilitychange`)
- On page unload/refresh/close (`beforeunload` and `pagehide`)

---

Events
------

[](#events)

All events are dispatched on `document` as native `CustomEvent`s:

```
document.addEventListener('youtube-progress:saved', (e) => {
    console.log(e.detail.videoId, e.detail.percentage + '%');
});
```

EventDescription`youtube-progress:loaded`Player finished initializing`youtube-progress:resume`Playback seeked to a saved position`youtube-progress:saved`Progress written to Local Storage`youtube-progress:pause`Video was paused`youtube-progress:completed`Unique watched duration reached the threshold`youtube-progress:seek`A seek was detected (forward or backward)`youtube-progress:blocked`A forward seek was blocked by anti-cheat`youtube-progress:speed-warning`Playback speed exceeded the configured maximum`youtube-progress:segment-added`A new segment was recorded (fires on each tracking tick)---

Developer callbacks
-------------------

[](#developer-callbacks)

Pass JavaScript function names as Blade attributes:

```

    function myCompleteHandler(detail) {
        // detail contains: videoId, percentage, segments, completed, etc.
        markLessonComplete(detail.videoId);
    }

    function myBlockedHandler(detail) {
        // detail contains: from, attempted, snappedTo
        showWarning('You skipped ahead!');
    }

    function myProgressHandler(detail) {
        // Fires on every save — throttle if needed
        updateSidebarProgress(detail.videoId, detail.percentage);
    }

```

---

Public JavaScript API
---------------------

[](#public-javascript-api)

Access via `window.YouTubeProgress`:

```
// Get stored progress for a video
const progress = YouTubeProgress.getProgress('dQw4w9WgXcQ');

// Get watched segments
const segments = YouTubeProgress.getSegments('dQw4w9WgXcQ');

// Check if a video is completed
const done = YouTubeProgress.isCompleted('dQw4w9WgXcQ');

// Clear progress for a single video
YouTubeProgress.clearProgress('dQw4w9WgXcQ');

// Clear all progress
YouTubeProgress.clearAll();

// Export all progress as JSON string
const json = YouTubeProgress.export();

// Import progress from a JSON string
YouTubeProgress.import(jsonString);

// Get aggregate statistics
const stats = YouTubeProgress.stats();
// { totalVideos: 5, completed: 2, averageProgress: 68, totalWatchedSeconds: 4200 }
```

---

Configuration reference
-----------------------

[](#configuration-reference)

KeyTypeDefaultDescription`save_interval`int (ms)`10000`Autosave frequency while playing`completion_percentage`int`95`Unique watched % required for completion`storage_prefix`string`rajendra_progress_`Local Storage key prefix`auto_resume`bool`true`Seek to saved position on load`auto_load_api`bool`true`Inject the YouTube IFrame API script`debug`bool`false`Console-log lifecycle events`prevent_forward_seek`bool`true`Block seeking beyond maxWatchedTime`maximum_allowed_speed`int`2`Max playback rate before tracking pauses`seek_tolerance`float`1.5`Seconds — threshold for seek detection`toast_duration`int (ms)`3000`How long the blocked-seek toast displays`toast_message`string*(see config)*Text shown when a forward seek is blocked`default_width`string`100%`Fallback player width`default_height`string|int`480`Fallback player height### Blade component attributes

[](#blade-component-attributes)

AttributeTypeDefaultDescription`video`string*(required)*Video ID or YouTube URL`width`string|intconfig defaultPlayer width`height`string|intconfig defaultPlayer height`autoplay`bool`false`Auto-play on load`controls`bool`true`Show YouTube controls`muted`bool`false`Mute on load`loop`bool`false`Loop when ended`prevent-seek`bool`true`Per-player seek prevention override`show-progress`bool`false`Show inline progress bar`show-debug`bool`false`Show debug overlay`theme`string`"dark"``"light"` or `"dark"``on-complete`string`null`JS function name called on completion`on-seek-blocked`string`null`JS function name called when seek blocked`on-progress`string`null`JS function name called on each save---

Security
--------

[](#security)

- Video IDs/URLs are validated against `^[A-Za-z0-9_-]{11}$` before reaching the DOM; invalid values throw `InvalidArgumentException`.
- All Blade output goes through automatic `{{ }}` escaping.
- No `eval()`, no `innerHTML` with untrusted input, no inline handlers.
- No data leaves the browser — no backend endpoint, no database, no network call other than the YouTube IFrame API itself.

---

Accessibility
-------------

[](#accessibility)

- Player wrapper has `role="region"` and `aria-label="YouTube video player"`.
- Progress bars include `role="progressbar"` with `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, and `aria-label`.
- Toast notifications use `role="alert"` and `aria-live="assertive"` for screen reader announcements.
- Debug overlay includes `aria-label` for context.
- Keyboard focus styling on the player wrapper (`:focus-within` outline).
- YouTube's built-in keyboard controls (spacebar, arrows) work natively.

---

Performance
-----------

[](#performance)

- Single YouTube IFrame API instance per page regardless of player count.
- Lazy initialization — players only initialize when the API is ready.
- Debounced localStorage writes via configurable `save_interval`.
- Segment merge algorithm runs in O(n log n) due to sort; typically O(n) since segments arrive in order and rarely need re-sorting.
- Minimal DOM manipulation — only updates progress bar width/text and debug overlay text content.

---

Browser &amp; platform support
------------------------------

[](#browser--platform-support)

**Desktop:** Chrome, Firefox, Safari, Edge, Opera. **Mobile:** Android and iOS (responsive layout via CSS `aspect-ratio`).

---

Migration from v1.0.0
---------------------

[](#migration-from-v100)

**Automatic.** When the package reads an old `localStorage` record (no `version`field), it automatically migrates it to the v1.0.2 segment format:

- Old `currentTime` becomes the initial segment `[[0, currentTime]]`
- `maxWatchedTime` is set to `currentTime`
- The migrated record is saved immediately

No data is lost. Students retain their existing progress. The migration happens transparently on the first page load after upgrading.

If you need to handle this in your own code:

```
document.addEventListener('youtube-progress:loaded', (e) => {
    const progress = YouTubeProgress.getProgress(e.detail.videoId);
    if (progress) {
        console.log('Segments:', progress.segments);
    }
});
```

---

Example app
-----------

[](#example-app)

See [`example-app/`](example-app) for a minimal consumer app demonstrating: anti-cheat player, debug overlay, light theme, free-seeking mode, standalone progress bar, multiple players, public API usage, and live event logging.

---

Testing
-------

[](#testing)

```
composer install
vendor/bin/pest
```

The suite covers:

- Video ID resolution and validation (bare IDs, watch URLs, youtu.be, embed, shorts)
- Storage key generation with custom prefixes
- All `jsConfig()` keys including anti-cheat options
- Anti-cheat config defaults and custom values
- Blade component rendering with all new attributes
- Prevent-seek, show-debug, theme, callback data attributes
- Inline progress bar rendering
- ARIA accessibility attributes
- Backward compatibility with minimal component usage
- Unique player ID generation
- The `youtube_progress()` helper function
- Config merging with expected defaults

---

FAQ
---

[](#faq)

**Does this require a database?**No. All progress lives in the visitor's browser via `localStorage`.

**Does progress sync across devices?**No — by design. Use `youtube-progress:saved` to persist to your own server.

**Can students still cheat?**The anti-cheat system makes cheating significantly harder. Forward seeks are blocked, completion requires actual watched time, and speed abuse is detected. Dedicated attackers with browser dev tools could theoretically modify `localStorage` directly — for server-side verification, listen to `youtube-progress:saved` and validate against your own records.

**Can I use this with Livewire, Inertia, or Turbo?**Yes. Dispatch `youtube-progress:rescan` on `document` after injecting new players into the DOM:

```
document.dispatchEvent(new CustomEvent('youtube-progress:rescan'));
```

**What happens if `localStorage` is unavailable?**Reads/writes are wrapped in `try/catch`; playback still works, it just won't persist progress.

**Can I style the player or progress bar?**Override `.yt-progress-wrapper`, `.yt-progress-player`, `.yt-progress-bar-track`, `.yt-progress-bar-fill`, `.yt-progress-bar-label`, `.yt-progress-toast`, or `.yt-progress-debug` in your CSS, or publish the views to change the markup.

---

Troubleshooting
---------------

[](#troubleshooting)

SymptomFixPlayer never appearsCheck console for invalid `video` value — must resolve to an 11-char IDProgress doesn't resumeConfirm `auto_resume` is `true` and `localStorage` isn't blockedAssets 404Run `php artisan vendor:publish --tag=youtube-progress-assets`Multiple players interfereEnsure published/bundled JS is loaded — don't hardcode duplicate IDsToast doesn't appearCheck `prevent_forward_seek` is `true` and the CSS is loadedDebug overlay missingEnsure `show-debug` attribute is `true` on the componentSafari doesn't save on closeSafari Private Browsing may block `localStorage` entirelySeek tolerance too sensitiveIncrease `seek_tolerance` in config (e.g., `2.0` or `3.0`)---

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a0b9acc8f37a881e43630ba77a97cd047f3b23bab399cf88c9d5e1a79e43f0cf?d=identicon)[rajendra-chimala](/maintainers/rajendra-chimala)

---

Tags

laravelvideoyoutubelocal storageprogressresumee-learninglmsiframe apianti-cheatsegment-tracking

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/rajendra-youtube-progress/health.svg)

```
[![Health](https://phpackages.com/badges/rajendra-youtube-progress/health.svg)](https://phpackages.com/packages/rajendra-youtube-progress)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[moonshine/moonshine

Laravel administration panel

1.3k253.1k86](/packages/moonshine-moonshine)[tallstackui/tallstackui

TallStackUI is a powerful suite of Blade components that elevate your workflow of Livewire applications.

726176.2k14](/packages/tallstackui-tallstackui)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k30.2M150](/packages/laravel-cashier)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M136](/packages/laravel-pulse)[illuminate/console

The Illuminate Console package.

13046.0M6.8k](/packages/illuminate-console)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
