Subir Videos
Sección titulada «Subir Videos»TalentBricksAI soporta dos proveedores de almacenamiento de video. La variable STORAGE_PROVIDER en
el servidor controla cuál se usa (aws por defecto).
Arquitectura
Sección titulada «Arquitectura»Opción A — AWS S3 + CloudFront:
┌────────────────┐ ┌────────────────┐ ┌────────────────┐│ Usuario │────▶│ CloudFront │────▶│ S3 Bucket ││ (Video Player)│ │ (CDN) │ │ (Storage) │└────────────────┘ └────────────────┘ └────────────────┘ │ ▼ CloudFront Signed URLs (Acceso Temporizado)Opción B — Azure Blob Storage:
┌────────────────┐ ┌──────────────────────┐│ Usuario │────▶│ Azure Blob Storage ││ (Video Player)│ │ (Container privado) │└────────────────┘ └──────────────────────┘ │ ▼ SAS URLs Temporales (Acceso Temporizado)Configuración — Opción A: AWS S3 + CloudFront
Sección titulada «Configuración — Opción A: AWS S3 + CloudFront»1. Crear Bucket S3
Sección titulada «1. Crear Bucket S3»# Crear bucketaws s3 mb s3://talentbricksai-videos --región us-east-1
# Configurar como privado (por defecto)aws s3api put-public-access-block \ --bucket talentbricksai-videos \ --public-access-block-configuration \ "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"2. Estructura de Carpetas
Sección titulada «2. Estructura de Carpetas»talentbricksai-videos/├── courses/│ ├── fundamentos-data-engineering/│ │ ├── 01-introduccion.mp4│ │ ├── 02-conceptos.mp4│ │ └── thumbnail.jpg│ └── ml-básico/│ ├── 01-intro.mp4│ └── thumbnail.jpg└── assets/ └── common/3. Politica IAM
Sección titulada «3. Politica IAM»Crear un usuario IAM con permisos minimos:
{ "versión": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::talentbricksai-videos/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::talentbricksai-videos" } ]}Subir Videos
Sección titulada «Subir Videos»opción 1: AWS CLI
Sección titulada «opción 1: AWS CLI»# Subir un videoaws s3 cp ./video.mp4 s3://talentbricksai-videos/courses/mi-curso/01-intro.mp4
# Subir carpeta completaaws s3 sync ./videos/ s3://talentbricksai-videos/courses/mi-curso/opción 2: Consola AWS
Sección titulada «opción 2: Consola AWS»- Ir a S3 en la consola de AWS
- Seleccionar el bucket
- Click en “Upload”
- Arrastrar archivos
opción 3: Desde la aplicación (Admin)
Sección titulada «opción 3: Desde la aplicación (Admin)»La aplicación incluye funcionalidad de upload usando presigned URLs:
// El admin panel genera URLs de subidaconst { uploadUrl, fileUrl } = await generateUploadUrl({ fileName: "video.mp4", contentType: "video/mp4",});
// Subir directamente a S3await fetch(uploadUrl, { method: "PUT", body: videoFile, headers: { "Content-Type": "video/mp4" },});Configuración — Opción B: Azure Blob Storage
Sección titulada «Configuración — Opción B: Azure Blob Storage»1. Crear Storage Account y Container
Sección titulada «1. Crear Storage Account y Container»Ver instrucciones detalladas en Fase 3.B del despliegue.
2. Subir Videos a Azure
Sección titulada «2. Subir Videos a Azure»Opción 1: Azure CLI
Sección titulada «Opción 1: Azure CLI»# Instalar Azure CLI si no lo tienes (macOS)brew install azure-cli
# Iniciar sesiónaz login
# Subir un videoaz storage blob upload \ --account-name talentbricksai \ --container-name videos \ --name courses/mi-curso/01-intro.mp4 \ --file ./video.mp4 \ --auth-mode key
# Subir carpeta completaaz storage blob upload-batch \ --account-name talentbricksai \ --destination videos \ --source ./videos \ --pattern "courses/mi-curso/*" \ --auth-mode keyOpción 2: Azure Storage Explorer
Sección titulada «Opción 2: Azure Storage Explorer»Descargar Azure Storage Explorer — interfaz visual para gestionar blobs sin línea de comandos.
Opción 3: Desde la aplicación (Admin)
Sección titulada «Opción 3: Desde la aplicación (Admin)»El admin panel usa la misma interfaz que con AWS — el proveedor activo se detecta automáticamente
según STORAGE_PROVIDER:
// La app usa getStorageProvider() automáticamenteconst { uploadUrl, fileUrl } = await generateUploadUrl({ fileName: "video.mp4", contentType: "video/mp4",});
// Subir con PUT (tanto AWS como Azure usan PUT)await fetch(uploadUrl, { method: "PUT", body: videoFile, headers: { "Content-Type": "video/mp4" },});3. Variables de Entorno para Azure
Sección titulada «3. Variables de Entorno para Azure»Agregar a .env.server:
# Proveedor activoSTORAGE_PROVIDER=azure
# Azure Blob StorageAZURE_STORAGE_ACCOUNT_NAME=talentbricksaiAZURE_STORAGE_ACCOUNT_KEY=tu-account-key-base64AZURE_STORAGE_CONTAINER_NAME=archivosConfiguración de CloudFront (solo AWS)
Sección titulada «Configuración de CloudFront (solo AWS)»1. Crear Distribucion
Sección titulada «1. Crear Distribucion»En la consola de AWS CloudFront:
- Click “Create Distribution”
- Origin Domain:
talentbricksai-videos.s3.us-east-1.amazonaws.com - Origin Access: “Origin access control settings (recommended)”
- Crear nuevo OAC (Origin Access Control)
2. Configurar Comportamientos
Sección titulada «2. Configurar Comportamientos»| Setting | Valor |
|---|---|
| Viewer Protocol Policy | Redirect HTTP to HTTPS |
| Allowed HTTP Methods | GET, HEAD |
| Cache Policy | CachingOptimized |
| Compress Objects | Yes |
3. URLs Firmadas (Signed URLs)
Sección titulada «3. URLs Firmadas (Signed URLs)»Para contenido privado, usar URLs firmadas:
import { getSignedUrl } from "@aws-sdk/cloudfront-signer";
export function generateVideoUrl(videoPath: string): string { const url = `https://d123abc.cloudfront.net/${videoPath}`;
return getSignedUrl({ url, keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID!, privateKey: process.env.CLOUDFRONT_PRIVATE_KEY!, dateLessThan: new Date(Date.now() + 3600 * 1000).toISOString(), // 1 hora });}4. Actualizar Politica del Bucket
Sección titulada «4. Actualizar Politica del Bucket»Permitir acceso desde CloudFront:
{ "versión": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::talentbricksai-videos/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID" } } } ]}Formatos de Video Recomendados
Sección titulada «Formatos de Video Recomendados»| Setting | Valor Recomendado |
|---|---|
| Formato | MP4 (H.264) |
| Resolucion | 1080p (1920x1080) |
| Bitrate | 5-8 Mbps |
| Audio | AAC, 128-192 kbps |
| Frame Rate | 30 fps |
Compresion con FFmpeg
Sección titulada «Compresion con FFmpeg»# Comprimir video para webffmpeg -i input.mov \ -c:v libx264 -preset slow -crf 22 \ -c:a aac -b:a 128k \ -movflags +faststart \ output.mp4Costos Estimados
Sección titulada «Costos Estimados»| Servicio | Costo |
|---|---|
| S3 Storage | ~$0.023/GB/mes |
| S3 Requests | ~$0.0004/1000 GET |
| CloudFront Transfer | ~$0.085/GB (primeros 10TB) |
| CloudFront Requests | ~$0.0075/10000 |
Ejemplo: 100GB de videos, 1000 visualizaciones/mes
Sección titulada «Ejemplo: 100GB de videos, 1000 visualizaciones/mes»- Storage: 100GB × $0.023 = $2.30/mes
- Transfer: ~50GB × $0.085 = $4.25/mes
- Total: ~$6.55/mes
Variables de Entorno
Sección titulada «Variables de Entorno»Opción A — AWS S3 + CloudFront
Sección titulada «Opción A — AWS S3 + CloudFront»Agregar a .env.server:
# Proveedor activo (valor por defecto — puede omitirse)STORAGE_PROVIDER=aws
# AWS S3AWS_S3_IAM_ACCESS_KEY=AKIA...AWS_S3_IAM_SECRET_KEY=...AWS_S3_FILES_BUCKET=talentbricksai-videosAWS_S3_REGION=us-east-1
# CloudFront (para URLs firmadas)CLOUDFRONT_DOMAIN=d123abc.cloudfront.netCLOUDFRONT_KEY_PAIR_ID=K123...CLOUDFRONT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n..."Opción B — Azure Blob Storage
Sección titulada «Opción B — Azure Blob Storage»Agregar a .env.server:
# Proveedor activoSTORAGE_PROVIDER=azure
# Azure Blob StorageAZURE_STORAGE_ACCOUNT_NAME=talentbricksaiAZURE_STORAGE_ACCOUNT_KEY=tu-account-key-base64AZURE_STORAGE_CONTAINER_NAME=archivosintegración con el Video Player
Sección titulada «integración con el Video Player»El componente VideoPlayer usa las URLs firmadas:
export function VideoPlayer({ lesson }: { lesson: Lesson }) { const { data: videoUrl } = useQuery(getSignedVideoUrl, { lessonId: lesson.id, });
return <video src={videoUrl} controls onTimeUpdate={handleProgress} />;}