Qué incluye
Seis capacidades, un SDK. Sube, transcodifica, distribuye, subtitula, reproduce y protege tu media.
Upload
Tus usuarios suben. Nosotros nos encargamos del resto.
URLs pre-firmadas directo desde Cloudflare R2, los archivos nunca tocan tu servidor. Tracking de progreso, validación de tipo y límites de tamaño por tipo de media incluidos.
- URLs pre-firmadas PUT generadas por archivo con expiración configurable
- Componente React UploadZone con drag-and-drop, o completamente headless
- Progreso en tiempo real via hook useProgress y ProgressProvider
- Validación de archivos: tipo, tamaño y dimensiones verificados antes del upload
- Uploads multipart para archivos grandes con chunking automático
import { UploadZone } from "@devultur/react";
<UploadZone
accept="video/*"
maxSize="2GB"
onUploadUrl={async (file) => {
const { url, key } = await media.createUploadUrl({
filename: file.name,
contentType: file.type,
});
return { url, key };
}}
onComplete={(result) => {
console.log("Uploaded:", result.key);
}}
/>Transcode
Video crudo entra, streaming HLS sale.
FFmpeg corre en máquinas Fly.io por segundo. Sube un video, recibe un playlist HLS. Adaptive bitrate, auto-thumbnails, y notificaciones webhook cuando el job termina.
- HLS single-bitrate o adaptativo con presets configurables
- Facturación por segundo en Fly.io Machines (sin costos idle)
- Thumbnail auto-generado en timestamp configurable
- Callback webhook cuando el transcode completa o falla
- 720p (Free), 1080p (Starter), 4K (Pro) resolución máxima
const job = await media.transcode({
key: "videos/lesson-1.mp4",
preset: "hls-adaptive",
callbackUrl: "https://yourapp.com/api/webhooks",
});
// Poll status
const status = await media.getTranscodeStatus(job.id);
// { state: "completed", playlist: "videos/lesson-1/playlist.m3u8" }Deliver
Cero egress. En serio.
Cloudflare R2 almacena tus archivos con $0 en fees de egress. Cada archivo tiene una URL firmada con expiración configurable. Range requests y CDN caching funcionan sin configuración.
- Cloudflare R2 storage: $0 bandwidth, siempre
- URLs firmadas HMAC-SHA256 con expiración configurable
- Soporte de range requests para video seeking
- CDN caching via la red global de Cloudflare
- Custom domain opcional (tier Pro)
const url = await media.getMediaUrl({
key: "videos/lesson-1/playlist.m3u8",
expiresIn: 3600, // 1 hour
});
// Signed URL with HMAC verification
// https://media.devultur.com/v1/media/videos/lesson-1/playlist.m3u8
// ?expires=1719000000&signature=abc123...Captions
Transcripción en cualquier idioma, automáticamente.
Envía un video, recibe archivos VTT con timestamps por oración. Groq Whisper transcribe, Mistral traduce. Todo automático.
- Speech-to-text via Groq Whisper (228x realtime)
- Output VTT almacenado junto al video
- Transcripción multi-idioma (99+ idiomas)
- Traducciones automáticas via Mistral
- Progreso en tiempo real via webhooks
const caption = await media.requestCaptions({
key: "videos/lesson-1.mp4",
locale: "es-CO",
});
// Check status
const status = await media.getCaptionsStatus(caption.id);
// Download VTT
const vtt = await media.getCaptionsVtt(caption.id);Player
Agrega un video player con un componente.
hls.js por debajo, estilizado por defecto o completamente headless. Atajos de teclado, selector de captions, y reanudar desde última posición incluidos.
- HLS adaptive streaming via hls.js
- Selector de captions con soporte multi-idioma
- Reanudar desde última posición con callback onProgress
- Atajos de teclado (espacio, flechas, f para pantalla completa)
- Estilizado por defecto, o renderiza tu propio UI en modo headless
import { VideoPlayer } from "@devultur/react";
<VideoPlayer
src={playlistUrl}
captions={[
{ locale: "es-CO", label: "Español", src: spanishVtt },
{ locale: "en", label: "English", src: englishVtt },
]}
onProgress={(time) => saveProgress(lessonId, time)}
initialTime={savedProgress}
/>Security
Tokens JWT vinculados a la sesión.
Cuatro niveles de protección de contenido, desde URLs firmadas hasta segmentos HLS encriptados con AES-128. Los tokens se vinculan a IP y huella de sesión con expiración configurable.
- Nivel 1: URLs firmadas HMAC-SHA256 con expiración
- Nivel 2: Tokens JWT con vinculación por IP y huella de sesión
- Nivel 3: JWT + URLs de segmentos de corta duración (Pro)
- Nivel 4: Segmentos HLS encriptados AES-128 (Pro)
- Expiración configurable por tipo de token
const token = await media.issueToken({
key: "videos/lesson-1/playlist.m3u8",
expiresIn: 7200,
claims: {
userId: user.id,
ip: request.ip,
},
});
// Token is verified by the Worker on each segment request