7.8 KiB
laravel-video-extractor
Extract thumbnails, metadata and stream formats from VK (vkvideo.ru / vk.com), Rutube and OK.ru (Odnoklassniki) videos. The platform is auto-detected from the URL — one facade, all services.
use Apoloz\VideoExtractor\Facades\Video;
// Full extraction (metadata + thumbnails + formats)
$info = Video::extract('https://rutube.ru/video/373ba417757981facf06c55ea5c46cb4/');
echo $info->title; // "Первые слова малыша..."
echo $info->bestThumbnail()->url; // https://pic.rtbcdn.ru/video/...jpg
echo $info->duration; // 3756
// Lightweight thumbnail-only path (single request)
Video::thumbnail('https://vkvideo.ru/video-18403220_456239696');
// OK.ru — auto-detected, full metadata + mp4/HLS formats
Video::extract('https://ok.ru/video/15019292494547')->bestThumbnail()->url;
Requirements
- PHP 8.1+
- Laravel 10 / 11 / 12
Installation
The package is hosted in a private Gitea repository
(gitea.office.local:3000/eatvio/laravel-video-extractor). It is not on
Packagist, so the consuming project must register the repository with Composer
and have read access to it.
Option A — via SSH (recommended)
Requires an SSH key with read access to the repo on every machine running
composer install (the key configured during development works on this host).
# In your Laravel project:
composer config repositories.laravel-video-extractor git \
"ssh://git@gitea.office.local:222/eatvio/laravel-video-extractor.git"
composer require apoloz/laravel-video-extractor:^1.0
For other servers / CI, add a Gitea deploy key (repo → Settings → Deploy
Keys) with the machine's public key and pre-seed known_hosts:
ssh-keyscan -p 222 gitea.office.local >> ~/.ssh/known_hosts
Option B — via HTTP with an access token
Useful when SSH key distribution is impractical (CI, restricted networks).
- In Gitea: Settings → Applications → Generate New Token (scope
read:repository). - Configure Composer auth (on each machine):
composer config --global http-basic.gitea.office.local <gitea-login> <token> - Register the repository and install:
composer config repositories.laravel-video-extractor git \ "http://gitea.office.local:3000/eatvio/laravel-video-extractor.git" composer require apoloz/laravel-video-extractor:^1.0
For CI, the auth can be supplied via the COMPOSER_AUTH environment variable
instead of composer config.
After installation
The service provider and Video facade are auto-discovered by Laravel — no
manual registration needed. To publish the config:
php artisan vendor:publish --tag=video-extractor-config
Versioning
Stable releases are git tags (v1.0.0, ...). Use a caret constraint
(^1.0) to pull tagged releases. The @dev / dev-main constraint pulls the
latest main branch tip (no tag required) — convenient during development.
Supported URLs
| Platform | Examples |
|---|---|
| VK | vkvideo.ru/video-{oid}_{id}, vk.com/video-{oid}_{id}, vkvideo.ru/clip-..., vk.com/video_ext.php?oid=...&id=..., m.vk.com/..., vksport.vkvideo.ru/... |
| Rutube | rutube.ru/video/{id}, rutube.ru/live/video/{id}, rutube.ru/video/private/{id}/?p=token, rutube.ru/play/embed/{id} |
| OK.ru | ok.ru/video/{id}, ok.ru/videoembed/{id}, ok.ru/live/{id}, m.ok.ru/video/{id}, odnoklassniki.ru/video/{id}, ok.ru/dk?st.mvId=... |
How it works
The package mirrors the extraction logic of yt-dlp, adapted for idiomatic Laravel:
-
VK uses two endpoints with automatic fallback:
GET https://vk.com/video_ext.php?oid=&id=(primary) — returns a multi-resolutionimage[]array of thumbnails plus full metadata, embedded as JSON in the HTML.POST https://vk.com/al_video.php(fallback, yt-dlp's proven method) — yields a single thumbnail URL but surfaces explicit error codes (3= login required,8= unavailable).
- VK serves responses in Windows-1251; the package transparently re-encodes them to UTF-8.
-
Rutube uses its clean public REST API (UTF-8, no auth, no challenges):
GET https://rutube.ru/api/video/{id}/?format=json— metadata + stablethumbnail_url.GET https://rutube.ru/api/play/options/{id}/?format=json— master HLS playlist + access verdict.
-
OK.ru scrapes the public video page (UTF-8, anonymous access) with a two-tier strategy:
- (primary) Parses the
<div data-options="...">player payload (HTML-entity-decoded then doublejson_decode) formovie.poster,videos[](mp4 qualities),hlsManifestUrlandlikeCount. - (fallback) Open Graph /
ya:ovs<meta>tags — used when the player payload is absent (external/restricted/paid videos) to still recover the thumbnail and basic metadata.
- (primary) Parses the
Usage
Extract everything
$info = Video::extract($url);
$info->title;
$info->description;
$info->duration; // seconds
$info->uploader; // author / channel
$info->timestamp; // unix
$info->viewCount;
$info->isLive;
$info->ageLimit; // 0 or 18
$info->categories; // ["Education"]
$info->thumbnails; // ThumbnailDto[] (multiple resolutions where available)
$info->bestThumbnail(); // highest-resolution ThumbnailDto
$info->thumbnailUrl(); // convenience: just the URL
$info->formats; // FormatDto[] (mp4/hls/dash for VK, HLS for Rutube)
Just the thumbnail (fast path)
$url = Video::thumbnail($url); // string|null — skips the formats request
Dependency injection
use Apoloz\VideoExtractor\VideoExtractor;
public function handle(VideoExtractor $extractor): void
{
$info = $extractor->extract($url);
}
Configuration
Published to config/video-extractor.php:
return [
'http' => [
'timeout' => env('VIDEO_EXTRACTOR_TIMEOUT', 30),
'user_agent' => env('VIDEO_EXTRACTOR_USER_AGENT'),
],
'cache' => [
'enabled' => env('VIDEO_EXTRACTOR_CACHE_ENABLED', true),
'store' => env('VIDEO_EXTRACTOR_CACHE_STORE'), // null = default
'thumbnail_ttl' => env('VIDEO_EXTRACTOR_THUMBNAIL_TTL', 604800), // 7 days
'info_ttl' => env('VIDEO_EXTRACTOR_INFO_TTL', 600), // 10 min
],
];
Caching strategy: thumbnail URLs are stable CDN resources and are cached for a long time (default 7 days). Full extraction results include format URLs that carry an expires=/expire= parameter, so they are cached briefly and the TTL is automatically capped by the soonest format expiry.
Error handling
| Exception | Cause |
|---|---|
PlatformNotSupportedException |
The URL host is neither VK nor Rutube |
VideoNotFoundException |
Video deleted / removed / not found |
GeoBlockedException |
Rutube acl_access denied the request |
LoginRequiredException |
VK returned code 3 (private / age-restricted) |
VideoExtractionException |
Base class for all package errors |
All exceptions extend Apoloz\VideoExtractor\Exceptions\VideoExtractionException.
Known limitations
- VK JS challenge (429 →
/challenge.html): VK occasionally requires solving a JavaScript challenge. yt-dlp solves this with a built-in JS interpreter; that is out of scope for this package, which raises a descriptiveRuntimeExceptioninstead. Public videos normally work without it; retry later or use a VK session cookie for heavy use. - Auth-required content (private / age-restricted VK videos) is not supported without upstream session handling.
- Stream format URLs expire within hours by design (VK
expires=, Rutubeexpire=); always re-extract before playback.
Credits
Extraction logic inspired by and adapted from yt-dlp's VK and Rutube extractors.
License
MIT