Fix: www/ movido a raíz, estructura final

This commit is contained in:
Ricardo Monla
2026-05-04 13:52:29 -03:00
parent 1e13888679
commit 17ff243bfb
337 changed files with 46456 additions and 19 deletions
+28
View File
@@ -0,0 +1,28 @@
# Dependencias
node_modules/
descargar-curso/node_modules/
# Archivos de sesión
descargar-curso/session.json
descargar-curso/.cookies_*.txt
descargar-curso/.mapa_videos.json
descargar-curso/.videos_descargados.txt
descargar-curso/.lista_videos.txt
# Videos descargados (son grandes, mejor no subirlos)
descargar-curso/descargas/videos_offline/
# Temporales
/tmp/
*.tmp
*.part
*.ytdl
# Logs
descargar-curso/descarga_videos.log
# OS
.DS_Store
Thumbs.db
www/assets/resources/*/*.mp4
www/assets/resources/mx_sin-modulo/*.mp4
+49
View File
@@ -0,0 +1,49 @@
<?php
/**
* deploy.php — Webhook de deploy automático para ksaldis-dt
*
* Gitea envía POST a esta URL al hacer push.
* Ejecuta deploy_ksaldis.sh para actualizar el contenido.
*
* Configuración en Gitea:
* Settings → Webhooks → Add webhook (Gitea)
* URL: https://rmonla.duckdns.org/ksaldis-dt/deploy.php
* Content type: application/json
* Secret: ksaldis2026deploy
* Events: Push events
*/
define('WEBHOOK_SECRET', 'ksaldis2026deploy');
// Solo POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
die('Method not allowed');
}
// Verificar firma de Gitea (X-Gitea-Signature usa HMAC SHA256)
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_GITEA_SIGNATURE'] ?? '';
if ($signature) {
$expected = hash_hmac('sha256', $payload, WEBHOOK_SECRET);
if (!hash_equals($expected, $signature)) {
http_response_code(403);
die('Invalid signature');
}
}
// Ejecutar deploy
$scriptPath = __DIR__ . '/deploy_ksaldis.sh';
$output = [];
$returnCode = 0;
exec("bash $scriptPath 2>&1", $output, $returnCode);
// Respuesta
header('Content-Type: application/json');
echo json_encode([
'ok' => $returnCode === 0,
'output' => implode("\n", $output),
'time' => date('Y-m-d H:i:s')
]);
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
# deploy_ksaldis.sh — Script de deploy automático
# Llamado por el webhook de Gitea al hacer push
#
# Clona/actualiza el repo y copia LIBRO_DE_ESTUDIO.html como index.html
set -e
REPO_DIR="/var/www/ksaldis-dt/.repo"
WEB_DIR="/var/www/ksaldis-dt"
REPO_URL="http://10.0.10.205:3000/rmonla/rm-KSALDIS-DT.git"
LOG="$WEB_DIR/deploy.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') | $1" >> "$LOG"
}
log "Deploy iniciado"
# Clonar o actualizar repo
GIT="git -c safe.directory=$REPO_DIR"
if [ -d "$REPO_DIR/.git" ]; then
cd "$REPO_DIR"
$GIT fetch origin 2>&1
$GIT reset --hard origin/main 2>&1
log "git pull OK"
else
git clone "$REPO_URL" "$REPO_DIR" 2>&1
log "git clone OK"
fi
# Copiar el libro como index.html
SRC="$REPO_DIR/descargar-curso/descargas/LIBRO_DE_ESTUDIO.html"
if [ -f "$SRC" ]; then
cp "$SRC" "$WEB_DIR/index.html"
log "index.html actualizado ($(wc -c < "$WEB_DIR/index.html") bytes)"
else
log "ERROR: LIBRO_DE_ESTUDIO.html no encontrado en el repo"
exit 1
fi
log "Deploy completado"
+836
View File
@@ -0,0 +1,836 @@
#!/usr/bin/env node
/**
* COMPLETAR OFFLINE — Versión definitiva
* =======================================
*
* 1. Descarga los 42 videos de Vimeo usando las cookies de sesión
* 2. Los convierte a formato compatible con navegador (MP4)
* 3. Regenera LIBRO_DE_ESTUDIO.html y LIBRO_DE_ESTUDIO.md
* con los videos incrustados como <video> local o referencias
*
* Modo de uso:
* node completar_offline.mjs
*
* Requisitos:
* - yt-dlp instalado (pip install yt-dlp)
* - session.json de Playwright en la carpeta raíz
* - Los HTML descargados (de download_course.mjs)
*
* Archivos de salida:
* descargas/videos_offline/ → videos descargados (.mp4)
* descargas/LIBRO_DE_ESTUDIO.html → libro con videos locales
* descargas/LIBRO_DE_ESTUDIO.md → libro con referencias
*/
import fs from "fs";
import path from "path";
import { execSync, spawn } from "child_process";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DOWNLOADS_DIR = path.join(__dirname, "descargas");
const SECCIONES_DIR = path.join(DOWNLOADS_DIR, "secciones");
const VIDEOS_DIR = path.join(DOWNLOADS_DIR, "videos_offline");
const SESSION_JSON = path.join(__dirname, "session.json");
const COOKIES_TXT = "/tmp/cookies_netscape_voley.txt";
// ── Estructura del curso ──
const MODULOS = [
{
titulo: "Módulo 1 — Planificación",
carpeta: "Modulo_1_Planificacion",
secciones: [
{ carpeta: "Planificacion", titulo: "Planificación" },
{ carpeta: "Preparacion_Atletica", titulo: "Preparación Atlética" },
{ carpeta: "Explicacion_TP", titulo: "Explicación del Trabajo Práctico" },
{ carpeta: "Seguimientos_Equipos", titulo: "Seguimientos de Equipos" },
{ carpeta: "Analisis_Equipos_1", titulo: "Análisis de Equipos 1" },
{ carpeta: "Analisis_Equipos_2", titulo: "Análisis de Equipos 2" },
{ carpeta: "TP_Final_Unidad", titulo: "TP Final de la Unidad" },
],
},
{
titulo: "Módulo 2 — Entrenamiento",
carpeta: "Modulo_2_Entrenamiento",
secciones: [
{ carpeta: "Centrales_y_Oponentes", titulo: "Centrales y Opuestos/as" },
{ carpeta: "Puntas_y_Liberos", titulo: "Puntas y Liberos/as" },
{
carpeta: "Entrenamiento_Armadoras",
titulo: "Entrenamiento de Armadoras",
},
{ carpeta: "Explicacion_TP", titulo: "Explicación del TP" },
],
},
];
// ══════════════════════════════════════════════════════════════
// FUNCIONES AUXILIARES
// ══════════════════════════════════════════════════════════════
function log(msg) {
const ts = new Date().toISOString().replace("T", " ").substring(0, 19);
console.log(`[${ts}] ${msg}`);
}
function ensureDir(dir) {
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}
function sleep(ms) {
return new Promise((r) => setTimeout(r, ms));
}
/**
* Convierte el session.json de Playwright a cookies en formato Netscape
* que yt-dlp pueda usar.
*/
function convertirCookies() {
if (!fs.existsSync(SESSION_JSON)) {
log("⚠ No se encontró session.json. Ejecutá primero download_course.mjs");
return false;
}
const data = JSON.parse(fs.readFileSync(SESSION_JSON, "utf-8"));
const cookies = data.cookies || [];
const lines = ["# Netscape HTTP Cookie File"];
for (const c of cookies) {
const domain = c.domain;
const flag = domain.startsWith(".") ? "TRUE" : "FALSE";
const path = c.path || "/";
const secure = c.secure ? "TRUE" : "FALSE";
const exp = c.expires > 0 ? String(Math.floor(c.expires)) : "1778594896";
const name = c.name;
const value = c.value;
lines.push(
`${domain}\t${flag}\t${path}\t${secure}\t${exp}\t${name}\t${value}`,
);
}
fs.writeFileSync(COOKIES_TXT, lines.join("\n") + "\n");
log(`✓ Cookies convertidas (${cookies.length} cookies)`);
return true;
}
// ══════════════════════════════════════════════════════════════
// PASO 1: EXTRAER TODOS LOS VIDEOS DEL HTML DESCARGADO
// ══════════════════════════════════════════════════════════════
function extraerTodosLosVideos() {
const videos = [];
const vistos = new Set();
for (const modulo of MODULOS) {
for (const seccion of modulo.secciones) {
const htmlPath = path.join(
SECCIONES_DIR,
modulo.carpeta,
seccion.carpeta,
"pagina.html",
);
if (!fs.existsSync(htmlPath)) continue;
const html = fs.readFileSync(htmlPath, "utf-8");
if (html.includes("500 Internal Server Error")) continue;
// Buscar todos los IDs de Vimeo en esta página
const regex = /player\.vimeo\.com\/video\/(\d+)/g;
let match;
while ((match = regex.exec(html)) !== null) {
const id = match[1];
if (vistos.has(id)) continue;
vistos.add(id);
// Extraer el título del tema asociado (buscar hacia atrás)
const before = html.substring(
Math.max(0, match.index - 400),
match.index,
);
const tituloMatch = before.match(/<em>([^<]+)<\/em>/);
const titulo = tituloMatch
? tituloMatch[1]
.trim()
.replace(/[<>:"/\\|?*]+/g, "_")
.substring(0, 80)
: `video_${id}`;
// Extraer la página que embebe el video (para pasarla a yt-dlp)
const paginaURL = `https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/${modulo.carpeta.includes("1") ? "19096" : "19097"}/s/${seccion.carpeta === "Planificacion" ? "34868" : seccion.carpeta === "Preparacion_Atletica" ? "34869" : seccion.carpeta === "Explicacion_TP" && modulo.carpeta.includes("1") ? "34870" : seccion.carpeta === "Seguimientos_Equipos" ? "35523" : seccion.carpeta === "Analisis_Equipos_1" ? "35524" : seccion.carpeta === "Analisis_Equipos_2" ? "35525" : seccion.carpeta === "TP_Final_Unidad" ? "35526" : seccion.carpeta === "Centrales_y_Oponentes" ? "35519" : seccion.carpeta === "Puntas_y_Liberos" ? "35520" : seccion.carpeta === "Entrenamiento_Armadoras" ? "35521" : seccion.carpeta === "Explicacion_TP" && modulo.carpeta.includes("2") ? "35522" : "35519"}/study-resources/list`;
videos.push({
id,
titulo,
pagina: seccion.carpeta,
modulo: modulo.carpeta,
nombreModulo: modulo.titulo,
nombreSeccion: seccion.titulo,
paginaURL,
urlVimeo: `https://player.vimeo.com/video/${id}`,
});
}
}
}
return videos;
}
// ══════════════════════════════════════════════════════════════
// PASO 2: DESCARGA DE VIDEOS
// ══════════════════════════════════════════════════════════════
async function descargarVideos(videos) {
ensureDir(VIDEOS_DIR);
// Verificar yt-dlp
try {
execSync("yt-dlp --version", { stdio: "ignore" });
} catch {
log("✗ yt-dlp no está instalado. Ejecutá: pip install yt-dlp");
return [];
}
if (!fs.existsSync(COOKIES_TXT)) {
if (!convertirCookies()) return [];
}
const descargados = [];
for (let i = 0; i < videos.length; i++) {
const v = videos[i];
const nombreSeguro =
`${v.nombreModulo.replace(/[^a-zA-Z0-9_]/g, "_")}_${v.nombreSeccion.replace(/[^a-zA-Z0-9_]/g, "_")}_${v.titulo.replace(/[^a-zA-Z0-9_]/g, "_")}.mp4`.substring(
0,
120,
);
const rutaSalida = path.join(VIDEOS_DIR, nombreSeguro);
if (fs.existsSync(rutaSalida)) {
log(
` [${i + 1}/${videos.length}] ↺ Ya existe: ${v.titulo.substring(0, 40)}`,
);
descargados.push({ ...v, archivo: nombreSeguro });
continue;
}
log(
` [${i + 1}/${videos.length}] ▶ Descargando: ${v.titulo.substring(0, 50)}...`,
);
try {
// yt-dlp con la página embebedora como URL y cookies
const cmd = [
"yt-dlp",
"--cookies",
COOKIES_TXT,
"-o",
rutaSalida,
"--merge-output-format",
"mp4",
"--no-playlist",
"--progress",
"--print",
"after_move:OK",
v.paginaURL,
].join(" ");
const output = execSync(cmd, {
timeout: 300000, // 5 min por video
stdio: ["ignore", "pipe", "pipe"],
});
// Verificar que se haya descargado
if (fs.existsSync(rutaSalida)) {
const sizeMB = (fs.statSync(rutaSalida).size / 1024 / 1024).toFixed(1);
log(`${sizeMB} MB — ${v.titulo.substring(0, 40)}`);
descargados.push({ ...v, archivo: nombreSeguro });
} else {
log(` ⚠ Archivo no encontrado después de descarga`);
}
} catch (err) {
log(` ✗ Error: ${err.message.substring(0, 100)}`);
}
// Pequeña pausa entre descargas
await sleep(1000);
}
return descargados;
}
// ══════════════════════════════════════════════════════════════
// PASO 3: GENERAR LIBRO DE ESTUDIO CON VIDEOS
// ══════════════════════════════════════════════════════════════
function extraerTemas(filePath) {
const temas = [];
try {
if (!fs.existsSync(filePath)) return temas;
const html = fs.readFileSync(filePath, "utf-8");
if (html.includes("500 Internal Server Error")) return temas;
// Extraer tablas de estudio
const tablas = extraerTablas(html);
for (const tabla of tablas) {
const filas = extraerFilas(tabla);
for (const fila of filas) {
if (!fila.includes("<td")) continue;
const tema = procesarFila(fila);
if (tema) temas.push(tema);
}
}
} catch {}
return temas;
}
function extraerTablas(html) {
const tablas = [];
let pos = 0;
while (true) {
const start = html.indexOf(
'<table class="table table-bordered study-resources"',
pos,
);
if (start === -1) break;
const openEnd = html.indexOf(">", start);
if (openEnd === -1) break;
let depth = 1;
let searchPos = openEnd + 1;
while (depth > 0 && searchPos < html.length) {
const nextOpen = html.indexOf("<table", searchPos);
const nextClose = html.indexOf("</table>", searchPos);
if (nextClose === -1) break;
if (nextOpen !== -1 && nextOpen < nextClose) {
depth++;
searchPos = nextOpen + 6;
} else {
depth--;
searchPos = nextClose + 8;
}
}
tablas.push(html.substring(start, searchPos));
pos = searchPos;
}
return tablas;
}
function extraerFilas(tablaHTML) {
const filas = [];
const tbodyStart = tablaHTML.indexOf("<tbody>");
if (tbodyStart === -1) return filas;
const tbody = tablaHTML.substring(tbodyStart);
let pos = 0;
while (true) {
const trStart = tbody.indexOf("<tr", pos);
if (trStart === -1) break;
let depth = 1;
let tableDepth = 0;
let searchPos = trStart + 3;
while (depth > 0 && searchPos < tbody.length) {
const ops = [
{ type: "trO", pos: tbody.indexOf("<tr", searchPos) },
{ type: "trC", pos: tbody.indexOf("</tr>", searchPos) },
{ type: "tableO", pos: tbody.indexOf("<table", searchPos) },
{ type: "tableC", pos: tbody.indexOf("</table>", searchPos) },
].filter((o) => o.pos !== -1);
if (ops.length === 0) break;
const earliest = ops.sort((a, b) => a.pos - b.pos)[0];
switch (earliest.type) {
case "tableO":
tableDepth++;
searchPos = earliest.pos + 6;
break;
case "tableC":
tableDepth = Math.max(0, tableDepth - 1);
searchPos = earliest.pos + 7;
break;
case "trO":
if (tableDepth === 0) depth++;
searchPos = earliest.pos + 3;
break;
case "trC":
if (tableDepth === 0) depth--;
searchPos = earliest.pos + 5;
break;
}
}
filas.push(tbody.substring(trStart, searchPos));
pos = searchPos;
}
return filas;
}
function procesarFila(filaHTML) {
const tituloMatch = filaHTML.match(/<em>([\s\S]*?)<\/em>/i);
const titulo = tituloMatch
? limpiarHTML(tituloMatch[1]).trim()
: "Sin título";
const vimeoMatch = filaHTML.match(/player\.vimeo\.com\/video\/(\d+)/i);
const vimeoID = vimeoMatch ? vimeoMatch[1] : null;
const recursoMatch = filaHTML.match(
/<a[^>]*href="([^"]*)"[^>]*class="btn[^"]*"[^>]*>(Abrir (?:Archivo|Enlace))<\/a>/i,
);
const recursoURL = recursoMatch ? recursoMatch[1] : null;
const recursoTexto = recursoMatch ? recursoMatch[2] : null;
const contenidoMatch = filaHTML.match(
/<div class="redactor-usr-input">([\s\S]*?)<\/div>/i,
);
let contenidoHTML = contenidoMatch ? contenidoMatch[1].trim() : "";
if (!contenidoHTML && !vimeoID && !recursoURL) return null;
const id = titulo
.toLowerCase()
.replace(/[^a-z0-9áéíóúüñ]+/gi, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
return { id, titulo, contenidoHTML, vimeoID, recursoURL, recursoTexto };
}
function limpiarHTML(html) {
return html
.replace(/<[^>]*>/g, "")
.replace(/&nbsp;/g, " ")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/\s+/g, " ")
.trim();
}
function htmlAMarkdown(html) {
if (!html) return "";
let md = html;
md = md.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "# $1\n\n");
md = md.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "## $1\n\n");
md = md.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "### $1\n\n");
md = md.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "#### $1\n\n");
md = md.replace(/<strong>([\s\S]*?)<\/strong>/gi, "**$1**");
md = md.replace(/<b>([\s\S]*?)<\/b>/gi, "**$1**");
md = md.replace(/<em>([\s\S]*?)<\/em>/gi, "*$1*");
md = md.replace(/<i>([\s\S]*?)<\/i>/gi, "*$1*");
md = md.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, "$1\n\n");
md = md.replace(/<ul[^>]*>([\s\S]*?)<\/ul>/gi, "$1\n");
md = md.replace(/<ol[^>]*>([\s\S]*?)<\/ol>/gi, "$1\n");
md = md.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "- $1\n");
md = md.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, "> $1\n\n");
md = md.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, t) =>
tablaAMarkdown(t),
);
md = md.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, "`$1`");
md = md.replace(/<hr[^>]*>/gi, "\n---\n");
// Entities
const ents = {
"&nbsp;": " ",
"&amp;": "&",
"&lt;": "<",
"&gt;": ">",
"&quot;": '"',
"&#39;": "'",
"&aacute;": "á",
"&eacute;": "é",
"&iacute;": "í",
"&oacute;": "ó",
"&uacute;": "ú",
"&ntilde;": "ñ",
"&Aacute;": "Á",
"&Eacute;": "É",
"&Iacute;": "Í",
"&Oacute;": "Ó",
"&Uacute;": "Ú",
"&Ntilde;": "Ñ",
"&uuml;": "ü",
"&Uuml;": "Ü",
};
for (const [k, v] of Object.entries(ents))
md = md.replace(new RegExp(k, "g"), v);
md = md.replace(/<[^>]*>/g, "");
md = md.replace(/\n{4,}/g, "\n\n\n");
md = md.replace(/[ \t]+\n/g, "\n");
md = md
.split("\n")
.map((l) => l.trim())
.join("\n");
return md.trim();
}
function tablaAMarkdown(tablaHTML) {
const filas = [];
const rowRegex = /<tr[^>]*>([\s\S]*?)<\/tr>/gi;
let match;
while ((match = rowRegex.exec(tablaHTML)) !== null) {
const celdas = [];
const cellRegex = /<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/gi;
let cm;
while ((cm = cellRegex.exec(match[1])) !== null) {
const celda = cm[1]
.replace(/<br\s*\/?>/gi, " ")
.replace(/<[^>]+>/g, "")
.replace(/&nbsp;/g, " ")
.replace(/\s+/g, " ")
.trim();
celdas.push(celda);
}
if (celdas.length > 0) filas.push(celdas);
}
if (filas.length === 0) return "";
const numCols = Math.max(...filas.map((f) => f.length));
let md = "\n";
while (filas[0].length < numCols) filas[0].push("");
md += "| " + filas[0].join(" | ") + " |\n";
md += "| " + filas[0].map(() => "---").join(" | ") + " |\n";
for (let i = 1; i < filas.length; i++) {
while (filas[i].length < numCols) filas[i].push("");
md += "| " + filas[i].join(" | ") + " |\n";
}
md += "\n";
return md;
}
function generarContenido(videosDescargados) {
// Mapa de video ID → archivo local
const mapaVideos = {};
for (const v of videosDescargados) {
mapaVideos[v.id] = v.archivo;
}
const libros = [];
for (const modulo of MODULOS) {
const seccionesConContenido = [];
for (const seccion of modulo.secciones) {
const htmlPath = path.join(
SECCIONES_DIR,
modulo.carpeta,
seccion.carpeta,
"pagina.html",
);
const temas = extraerTemas(htmlPath);
if (temas.length > 0) {
// Asignar videos descargados a los temas
for (const tema of temas) {
if (tema.vimeoID && mapaVideos[tema.vimeoID]) {
tema.videoLocal = mapaVideos[tema.vimeoID];
}
}
seccionesConContenido.push({ titulo: seccion.titulo, temas });
}
}
if (seccionesConContenido.length > 0) {
libros.push({ titulo: modulo.titulo, secciones: seccionesConContenido });
}
}
return libros;
}
// ══════════════════════════════════════════════════════════════
// GENERAR MARKDOWN
// ══════════════════════════════════════════════════════════════
function generarMarkdown(libros) {
const fecha = new Date().toLocaleDateString("es-AR", {
year: "numeric",
month: "long",
day: "numeric",
});
let totalTemas = 0;
let md = `# Curso de Entrenador Nacional de Vóley
## Material de Estudio Completo
*Generado el ${fecha}*
*Incluye contenido de texto + videos descargados offline*
---
`;
for (const modulo of libros) {
md += `# ${modulo.titulo}\n\n`;
for (const seccion of modulo.secciones) {
md += `## ${seccion.titulo}\n\n`;
for (let t = 0; t < seccion.temas.length; t++) {
const tema = seccion.temas[t];
totalTemas++;
md += `### ${t + 1}. ${tema.titulo}\n\n`;
if (tema.videoLocal) {
md += `🎬 **Video:** \`${tema.videoLocal}\` *(archivo local)*\n\n`;
} else if (tema.vimeoID) {
md += `📹 *Video disponible — ID: ${tema.vimeoID}*\n\n`;
}
if (tema.contenidoHTML) {
md += htmlAMarkdown(tema.contenidoHTML);
md += "\n\n";
}
if (tema.recursoURL) {
const icono = tema.recursoTexto === "Abrir Archivo" ? "📄" : "🔗";
md += `${icono} **${tema.recursoTexto}:** ${tema.titulo}\`${tema.recursoURL}\`\n\n`;
}
if (t < seccion.temas.length - 1) md += "---\n\n";
}
}
}
md += `\n---\n*Fin del material. ${totalTemas} temas · ${Object.keys(mapaVideos).filter((k) => mapaVideos[k]).length} videos descargados.*\n`;
return md;
}
// ══════════════════════════════════════════════════════════════
// GENERAR HTML (con videos incrustados)
// ══════════════════════════════════════════════════════════════
let mapaVideos = {};
function generarHTML(libros) {
const fecha = new Date().toLocaleDateString("es-AR", {
year: "numeric",
month: "long",
day: "numeric",
});
let totalModulos = libros.length;
let totalSecciones = 0;
let totalTemas = 0;
for (const m of libros) {
totalSecciones += m.secciones.length;
for (const s of m.secciones) totalTemas += s.temas.length;
}
const totalVidsDesc = Object.values(mapaVideos).filter(Boolean).length;
const videoHTML = (tema) => {
if (tema.videoLocal) {
const rutaRelativa = `../videos_offline/${tema.videoLocal}`;
return `<div class="video-wrapper">
<video controls preload="metadata" width="100%">
<source src="${rutaRelativa}" type="video/mp4">
Tu navegador no soporta video HTML5.
</video>
<div class="video-label">🎬 ${tema.titulo}</div>
</div>`;
}
if (tema.vimeoID) {
return `<p class="video-ref">📹 <em>Video no descargado (ID: ${tema.vimeoID})</em></p>`;
}
return "";
};
const css = `
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', Georgia, serif; background: #f5f3ef; color: #2c2c2c; line-height: 1.8; font-size: 17px; }
.portada { background: linear-gradient(135deg, #0d2b3e 0%, #1a5276 50%, #2980b9 100%); color: white; padding: 5rem 2rem; text-align: center; min-height: 70vh; display: flex; flex-direction: column; justify-content: center; align-items: center; }
.portada h1 { font-size: 2.8rem; margin-bottom: 0.5rem; font-weight: 700; }
.portada h2 { font-size: 1.4rem; font-weight: 300; opacity: 0.9; }
.portada .stats { margin-top: 2rem; display: flex; gap: 2rem; flex-wrap: wrap; justify-content: center; }
.portada .stat { background: rgba(255,255,255,0.12); padding: 0.6rem 1.2rem; border-radius: 8px; font-size: 0.9rem; }
.indice { background: white; padding: 2rem; max-width: 900px; margin: 0 auto; border-bottom: 1px solid #e0e0e0; }
.indice h2 { color: #0d2b3e; margin-bottom: 1rem; }
.indice ul { list-style: none; }
.indice li { padding: 0.3rem 0; }
.indice a { color: #1a5276; text-decoration: none; border-bottom: 1px solid #ddd; }
.indice a:hover { border-bottom-color: #1a5276; }
.indice .mod-tit { font-weight: 600; margin-top: 0.8rem; color: #0d2b3e; font-size: 1.05rem; }
.contenido { max-width: 900px; margin: 0 auto; padding: 2rem; background: white; }
.contenido h1 { color: #0d2b3e; font-size: 2rem; border-bottom: 3px solid #2980b9; padding-bottom: 0.5rem; margin: 3rem 0 1.5rem; }
.contenido h2 { color: #1a5276; font-size: 1.5rem; margin: 2rem 0 1rem; border-left: 4px solid #2980b9; padding-left: 0.8rem; }
.contenido h3 { color: #0d2b3e; font-size: 1.15rem; margin: 1.5rem 0 0.5rem; }
.contenido p { margin: 0.6rem 0; text-align: justify; }
.contenido ul, .contenido ol { margin: 0.6rem 0; padding-left: 1.5rem; }
.contenido li { margin: 0.3rem 0; }
.contenido strong { color: #0d2b3e; }
.contenido hr { border: none; border-top: 1px solid #e0e0e0; margin: 1.5rem 0; }
.contenido table { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.9rem; }
.contenido th, .contenido td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
.contenido th { background: #0d2b3e; color: white; }
.contenido tr:nth-child(even) { background: #f9f9f9; }
.contenido blockquote { border-left: 4px solid #2980b9; margin: 1rem 0; padding: 0.5rem 1rem; background: #f0f5fa; font-style: italic; color: #555; }
.contenido .tema-num { background: #2980b9; color: white; display: inline-block; padding: 0.1rem 0.5rem; border-radius: 4px; font-size: 0.8rem; margin-right: 0.5rem; }
.video-wrapper { margin: 1rem 0; background: #000; border-radius: 8px; overflow: hidden; }
.video-wrapper video { display: block; max-height: 500px; }
.video-label { background: #0d2b3e; color: white; padding: 0.4rem 0.8rem; font-size: 0.85rem; text-align: center; }
.video-ref { color: #888; font-style: italic; font-size: 0.9rem; margin: 0.5rem 0; padding: 0.3rem 0.6rem; border-left: 3px solid #ccc; background: #fafafa; }
.recurso-link { display: inline-block; background: #e8f4f8; border: 1px solid #b3d9e8; border-radius: 4px; padding: 0.2rem 0.6rem; font-size: 0.85rem; margin: 0.3rem 0; color: #1a5276; text-decoration: none; }
.recurso-link:hover { background: #d0ebf5; }
.footer { text-align: center; padding: 3rem 2rem; color: #999; font-size: 0.85rem; max-width: 900px; margin: 0 auto; }
@media (max-width: 600px) {
body { font-size: 15px; }
.portada { padding: 2rem 1rem; min-height: auto; }
.portada h1 { font-size: 1.8rem; }
.contenido { padding: 1rem; }
}
`;
// Índice
let idx = '<div class="indice"><h2>📑 Índice de contenidos</h2><ul>';
for (const modulo of libros) {
idx += `<li class="mod-tit">${modulo.titulo}</li><ul>`;
for (let j = 0; j < modulo.secciones.length; j++) {
const s = modulo.secciones[j];
const anchor = `sec-${modulo.titulo.replace(/[^a-zA-Z0-9]/g, "")}-${s.titulo.replace(/[^a-zA-Z0-9]/g, "")}`;
idx += `<li><a href="#${anchor}">${s.titulo} (${s.temas.length} temas)</a></li>`;
}
idx += "</ul>";
}
idx += "</ul></div>";
// Cuerpo
let body = "";
for (const modulo of libros) {
body += `<h1>${modulo.titulo}</h1>\n`;
for (const seccion of modulo.secciones) {
const anchor = `sec-${modulo.titulo.replace(/[^a-zA-Z0-9]/g, "")}-${seccion.titulo.replace(/[^a-zA-Z0-9]/g, "")}`;
body += `<h2 id="${anchor}">${seccion.titulo}</h2>\n`;
for (let t = 0; t < seccion.temas.length; t++) {
const tema = seccion.temas[t];
body += `<h3><span class="tema-num">${t + 1}</span> ${tema.titulo}</h3>\n`;
body += videoHTML(tema);
if (tema.contenidoHTML) {
body += `<div class="estudio-contenido">${tema.contenidoHTML}</div>\n`;
}
if (tema.recursoURL) {
const icono = tema.recursoTexto === "Abrir Archivo" ? "📄" : "🔗";
const urlCompleta = tema.recursoURL.startsWith("http")
? tema.recursoURL
: `https://voley.onlineeducation.center${tema.recursoURL}`;
body += `<p><a class="recurso-link" href="${urlCompleta}" target="_blank">${icono} ${tema.recursoTexto}: ${tema.titulo}</a></p>\n`;
}
if (t < seccion.temas.length - 1) body += "<hr>\n";
}
}
}
// Footer
body += `<p style="text-align:center;color:#999;margin-top:3rem;font-size:0.85rem;">— Fin del material de estudio —</p>`;
return `<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Curso de Vóley — Material de Estudio Completo</title>
<style>${css}</style>
</head>
<body>
<div class="portada">
<h1>🏐 Curso de Entrenador Nacional de Vóley</h1>
<h2>Material de Estudio Completo</h2>
<p>Contenido + videos descargados — 100% offline</p>
<div class="stats">
<div class="stat">📚 ${totalModulos} módulos</div>
<div class="stat">📖 ${totalSecciones} secciones</div>
<div class="stat">🎯 ${totalTemas} temas</div>
<div class="stat">🎬 ${totalVidsDesc} videos</div>
</div>
</div>
${idx}
<div class="contenido">
${body}
</div>
<div class="footer">
<p>Generado el ${fecha} — Online Education Center</p>
<p>Material de estudio 100% offline</p>
</div>
</body>
</html>`;
}
// ══════════════════════════════════════════════════════════════
// MAIN
// ══════════════════════════════════════════════════════════════
async function main() {
console.log("");
console.log("═══════════════════════════════════════════════════");
console.log(" COMPLETAR OFFLINE — Videos + Libro de Estudio");
console.log("═══════════════════════════════════════════════════");
console.log("");
// Paso 1: Extraer videos del HTML
log("Paso 1: Extrayendo videos del contenido descargado...");
const todosLosVideos = extraerTodosLosVideos();
log(`${todosLosVideos.length} videos únicos encontrados`);
log("");
// Paso 2: Descargar videos
log("Paso 2: Descargando videos de Vimeo...");
log(" (Puede tomar tiempo dependiendo de tu conexión)");
log("");
const videosDescargados = await descargarVideos(todosLosVideos);
log("");
log(
`${videosDescargados.length}/${todosLosVideos.length} videos descargados`,
);
// Construir mapa global
globalThis.mapaVideos = {};
for (const v of videosDescargados) {
globalThis.mapaVideos[v.id] = v.archivo;
}
// Paso 3: Generar libro de estudio
log("");
log("Paso 3: Generando libro de estudio con videos...");
const libros = generarContenido(videosDescargados);
// Markdown
const md = generarMarkdown(libros);
const mdPath = path.join(DOWNLOADS_DIR, "LIBRO_DE_ESTUDIO.md");
fs.writeFileSync(mdPath, md, "utf-8");
log(`${mdPath} (${(md.length / 1024).toFixed(0)} KB)`);
// HTML
const html = generarHTML(libros);
const htmlPath = path.join(DOWNLOADS_DIR, "LIBRO_DE_ESTUDIO.html");
fs.writeFileSync(htmlPath, html, "utf-8");
log(`${htmlPath} (${(html.length / 1024).toFixed(0)} KB)`);
// Estadísticas
let totalPalabras = 0;
let totalTemas = 0;
for (const m of libros) {
for (const s of m.secciones) {
for (const t of s.temas) {
totalTemas++;
if (t.contenidoHTML) {
totalPalabras += t.contenidoHTML
.replace(/<[^>]*>/g, "")
.split(/\s+/).length;
}
}
}
}
log("");
log("═══════════════════════════════════════════════════");
log(" COMPLETADO");
log("═══════════════════════════════════════════════════");
log("");
log(` 📚 ${libros.length} módulos · ${totalTemas} temas`);
log(` 📝 ~${(totalPalabras / 1000).toFixed(1)}k palabras de contenido`);
log(
` 🎬 ${videosDescargados.length} videos descargados (en videos_offline/)`,
);
log("");
log(" Para abrir el libro de estudio:");
log(` file://${htmlPath}`);
log("");
}
main().catch((err) => {
console.error("Error:", err.message);
process.exit(1);
});
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# Copiar recursos (PDFs, etc) de secciones a assets/resources/
cd "$(dirname "$0")/.."
declare -A MAP
MAP["Modulo_1_Planificacion/Planificacion"]="m1_planificacion"
MAP["Modulo_1_Planificacion/Preparacion_Atletica"]="m1_preparacion-atletica"
MAP["Modulo_1_Planificacion/Explicacion_TP"]="m1_explicacion-tp"
MAP["Modulo_1_Planificacion/Seguimientos_Equipos"]="m1_seguimientos-equipos"
MAP["Modulo_1_Planificacion/Analisis_Equipos_1"]="m1_analisis-equipos-1"
MAP["Modulo_1_Planificacion/Analisis_Equipos_2"]="m1_analisis-equipos-2"
MAP["Modulo_1_Planificacion/TP_Final_Unidad"]="m1_tp-final"
MAP["Modulo_2_Entrenamiento/Centrales_y_Oponentes"]="m2_centrales-opuestos"
MAP["Modulo_2_Entrenamiento/Puntas_y_Liberos"]="m2_puntas-liberos"
MAP["Modulo_2_Entrenamiento/Entrenamiento_Armadoras"]="m2_entrenamiento-armadoras"
MAP["Modulo_2_Entrenamiento/Explicacion_TP"]="m2_explicacion-tp"
total=0
for key in "${!MAP[@]}"; do
target="${MAP[$key]}"
src="descargar-curso/descargas/secciones/$key"
count=0
for f in "$src"/*; do
fn=$(basename "$f")
case "$fn" in
pagina.html|contenido.md|videos.md|recursos.json|.gitkeep) ;;
*)
cp "$f" "www-ksaldis-dt/assets/resources/$target/" 2>/dev/null
count=$((count+1))
total=$((total+1))
;;
esac
done
echo " $target: $count archivos"
done
echo ""
echo "✅ Total: $total archivos copiados"
+49
View File
@@ -0,0 +1,49 @@
#!/bin/bash
set -e
REPO_DIR="/var/www/ksaldis-dt/.repo"
WEB_DIR="/var/www/ksaldis-dt"
REPO_URL="http://10.0.10.205:3000/rmonla/rm-KSALDIS-DT.git"
LOG="$WEB_DIR/deploy.log"
GIT="git -c safe.directory=$REPO_DIR"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') | $1" >> "$LOG"
}
log "Deploy iniciado"
if [ -d "$REPO_DIR/.git" ]; then
cd "$REPO_DIR"
$GIT fetch origin 2>&1
$GIT reset --hard origin/main 2>&1
log "git pull OK"
else
rm -rf "$REPO_DIR"
git clone "$REPO_URL" "$REPO_DIR" 2>&1
log "git clone OK"
fi
SRC="$REPO_DIR/descargar-curso/descargas/LIBRO_DE_ESTUDIO.html"
if [ -f "$SRC" ]; then
cp "$SRC" "$WEB_DIR/index.html"
log "index.html actualizado ($(wc -c < "$WEB_DIR/index.html") bytes)"
fi
# Copiar secciones
SECCIONES_SRC="$REPO_DIR/descargar-curso/descargas/secciones"
if [ -d "$SECCIONES_SRC" ]; then
rsync -a --delete "$SECCIONES_SRC/" "$WEB_DIR/secciones/"
log "secciones/ actualizadas"
fi
# Copiar otros assets
for asset in campus imagenes screenshots subpaginas; do
ASSET_SRC="$REPO_DIR/descargar-curso/descargas/$asset"
if [ -d "$ASSET_SRC" ]; then
rsync -a "$ASSET_SRC/" "$WEB_DIR/$asset/"
log "$asset/ actualizado"
fi
done
log "Deploy completado"
+482
View File
@@ -0,0 +1,482 @@
#!/bin/bash
#
# DESCARGAR VIDEOS DEL CURSO DE VOLEY
# ====================================
# Descarga los 42 videos de Vimeo con reanudación automática.
# Puede detenerse y reanudarse cuando quieras.
#
# Modo de uso:
# bash descargar_videos.sh
#
# Opciones:
# bash descargar_videos.sh --resume # Reanuda descargas pendientes
# bash descargar_videos.sh --status # Ver estado de descargas
# bash descargar_videos.sh --clean # Borrar todo y empezar de nuevo
#
set -e
# ─── CONFIGURACIÓN ───────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SESSION_JSON="$SCRIPT_DIR/session.json"
COOKIES_FILE="$SCRIPT_DIR/.cookies_voley.txt"
VIDEOS_DIR="$SCRIPT_DIR/descargas/videos_offline"
REGISTRO="$SCRIPT_DIR/.videos_descargados.txt"
LISTA_VIDEOS="$SCRIPT_DIR/.lista_videos.txt"
DEST_DIR="$SCRIPT_DIR/descargas"
LIMIT_RATE="" # Ej: "5M" para limitar a 5 MB/s, vacío = sin límite
# ─── COLORES ─────────────────────────────────────────────────
VERDE='\033[0;32m'
AMARILLO='\033[1;33m'
ROJO='\033[0;31m'
AZUL='\033[0;34m'
RESET='\033[0m'
ok() { echo -e " ${VERDE}${RESET} $1"; }
warn() { echo -e " ${AMARILLO}${RESET} $1"; }
err() { echo -e " ${ROJO}${RESET} $1"; }
info() { echo -e " ${AZUL}${RESET} $1"; }
# ─── HELPERS ─────────────────────────────────────────────────
timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
log() {
echo "[$(timestamp)] $1"
}
check_dependencies() {
if ! command -v yt-dlp &>/dev/null; then
err "yt-dlp no está instalado."
echo " Instalálo con: pip install yt-dlp"
exit 1
fi
if ! command -v python3 &>/dev/null; then
err "python3 no está instalado."
exit 1
fi
}
# ─── CONVERTIR COOKIES ──────────────────────────────────────
convertir_cookies() {
if [ ! -f "$SESSION_JSON" ]; then
err "No se encontró $SESSION_JSON"
echo " Ejecutá primero: node download_course.mjs"
exit 1
fi
log "Convirtiendo cookies de Playwright a formato Netscape..."
python3 -c "
import json
with open('$SESSION_JSON') as f:
data = json.load(f)
cookies = data.get('cookies', [])
lines = ['# Netscape HTTP Cookie File']
for c in cookies:
domain = c.get('domain', '')
if not domain:
continue
flag = 'TRUE' if domain.startswith('.') else 'FALSE'
path = c.get('path', '/')
secure = 'TRUE' if c.get('secure', False) else 'FALSE'
exp = c.get('expires', 0)
if exp <= 0:
exp = 1778594896 # fecha lejana
name = c.get('name', '')
value = c.get('value', '')
lines.append(f'{domain}\t{flag}\t{path}\t{secure}\t{int(exp)}\t{name}\t{value}')
with open('$COOKIES_FILE', 'w') as f:
f.write('\n'.join(lines) + '\n')
print(f'{len(cookies)}')
" 2>/dev/null
if [ -f "$COOKIES_FILE" ]; then
local count=$(wc -l < "$COOKIES_FILE")
ok "Cookies convertidas ($((count - 1)))"
else
err "Error al convertir cookies"
exit 1
fi
}
# ─── EXTRAER LISTA DE VIDEOS ─────────────────────────────────
extraer_lista_videos() {
log "Extrayendo lista de videos del HTML descargado..."
python3 -c "
import os, re, json
base = '$DEST_DIR/secciones'
modulos = ['Modulo_1_Planificacion', 'Modulo_2_Entrenamiento']
# Mapeo de carpetas a IDs de sección
mapeo_secciones = {
'Planificacion': ('m/19096', 's/34868'),
'Preparacion_Atletica': ('m/19096', 's/34869'),
'Explicacion_TP': ('m/19096', 's/34870'),
'Seguimientos_Equipos': ('m/19096', 's/35523'),
'Analisis_Equipos_1': ('m/19096', 's/35524'),
'Analisis_Equipos_2': ('m/19096', 's/35525'),
'TP_Final_Unidad': ('m/19096', 's/35526'),
'Centrales_y_Oponentes': ('m/19097', 's/35519'),
'Puntas_y_Liberos': ('m/19097', 's/35520'),
'Entrenamiento_Armadoras': ('m/19097', 's/35521'),
'Explicacion_TP': ('m/19097', 's/35522'),
}
# Nombre humano de cada sección
nombres = {
'Planificacion': 'Planificacion',
'Preparacion_Atletica': 'Preparacion_Atletica',
'Explicacion_TP': 'Explicacion_TP',
'Seguimientos_Equipos': 'Seguimientos_Equipos',
'Analisis_Equipos_1': 'Analisis_Equipos_1',
'Analisis_Equipos_2': 'Analisis_Equipos_2',
'TP_Final_Unidad': 'TP_Final_Unidad',
'Centrales_y_Oponentes': 'Centrales_y_Oponentes',
'Puntas_y_Liberos': 'Puntas_y_Liberos',
'Entrenamiento_Armadoras': 'Entrenamiento_Armadoras',
}
videos = []
vistos = set()
for mod in modulos:
mod_path = os.path.join(base, mod)
if not os.path.isdir(mod_path):
continue
for sec in os.listdir(mod_path):
html_path = os.path.join(mod_path, sec, 'pagina.html')
if not os.path.isfile(html_path):
continue
html = open(html_path, 'r', errors='ignore').read()
if '500 Internal Server Error' in html:
continue
for m in re.finditer(r'player\.vimeo\.com/video/(\d+)', html):
vid = m.group(1)
if vid in vistos:
continue
vistos.add(vid)
# Buscar título del tema
idx = m.start()
before = html[max(0, idx - 400):idx]
tm = re.search(r'<em>([^<]+)</em>', before)
titulo = tm.group(1).strip() if tm else f'video_{vid}'
# Limpiar caracteres no válidos para文件名
titulo = re.sub(r'[<>:\"/\\\\|?*]+', '_', titulo)[:80]
# Construir URL de la página embebedora
mapa = mapeo_secciones.get(sec)
if mapa:
mod_id, sec_id = mapa
pagina_url = f'https://voley.onlineeducation.center/es/campus/student/training/e/14197/{mod_id}/{sec_id}/study-resources/list'
else:
pagina_url = ''
videos.append({
'id': vid,
'titulo': titulo,
'seccion': nombres.get(sec, sec),
'modulo': 'M1' if '1' in mod else 'M2',
'url': pagina_url,
})
with open('$LISTA_VIDEOS', 'w') as f:
json.dump(videos, f, ensure_ascii=False, indent=2)
print(len(videos))
" 2>/dev/null
local total=$(wc -l < "$LISTA_VIDEOS" 2>/dev/null || echo 0)
if [ "$total" -gt 0 ] 2>/dev/null; then
ok "Lista de videos generada"
python3 -c "import json; print(f' Total: {len(json.load(open(\"$LISTA_VIDEOS\")))} videos')" 2>/dev/null
else
err "No se encontraron videos en el HTML descargado"
exit 1
fi
}
# ─── DESCARGAR VIDEOS ───────────────────────────────────────
descargar_videos() {
mkdir -p "$VIDEOS_DIR"
touch "$REGISTRO"
# Leer videos ya descargados
local descargados=()
while IFS= read -r line; do
descargados+=("$line")
done < "$REGISTRO"
# Obtener lista completa
local total=$(python3 -c "import json; print(len(json.load(open('$LISTA_VIDEOS'))))" 2>/dev/null || echo 0)
if [ "$total" -eq 0 ]; then
err "Lista de videos vacía"
exit 1
fi
echo ""
echo " Total: $total videos"
echo " Ya descargados: ${#descargados[@]}"
echo " Pendientes: $((total - ${#descargados[@]}))"
echo ""
# Procesar cada video
for i in $(seq 0 $((total - 1))); do
local data=$(python3 -c "
import json
with open('$LISTA_VIDEOS') as f:
v = json.load(f)
v = v[$i]
print(f'{v[\"id\"]}|{v[\"titulo\"]}|{v[\"seccion\"]}|{v[\"modulo\"]}|{v[\"url\"]}')" 2>/dev/null)
local id=$(echo "$data" | cut -d'|' -f1)
local titulo=$(echo "$data" | cut -d'|' -f2)
local seccion=$(echo "$data" | cut -d'|' -f3)
local modulo=$(echo "$data" | cut -d'|' -f4)
local pagina_url=$(echo "$data" | cut -d'|' -f5-)
# Verificar si ya se descargó
if grep -q "^$id|" "$REGISTRO" 2>/dev/null; then
continue
fi
# Nombre del archivo de salida
local safe_name="${modulo}_${seccion}_${titulo}"
safe_name=$(echo "$safe_name" | sed 's/[^a-zA-Z0-9_]/_/g' | cut -c1-120)
local output_path="$VIDEOS_DIR/${safe_name}.mp4"
# Si el archivo ya existe en disco pero no en el registro, agregarlo
if [ -f "$output_path" ]; then
echo "${id}|${safe_name}.mp4" >> "$REGISTRO"
ok "[$((i+1))/$total] Ya existe: ${titulo:0:50}"
continue
fi
echo ""
echo " ─────────────────────────────────────────────"
printf " [%d/%d] 🎬 %s\n" $((i+1)) "$total" "${titulo:0:70}"
echo " ─────────────────────────────────────────────"
echo ""
if [ -z "$pagina_url" ]; then
warn "Sin URL de página embebedora, intentando con URL directa..."
pagina_url="https://player.vimeo.com/video/${id}"
fi
# Construir comando yt-dlp
local cmd="yt-dlp"
cmd+=" --cookies \"$COOKIES_FILE\""
cmd+=" -o \"$output_path\""
cmd+=" --merge-output-format mp4"
cmd+=" --no-playlist"
cmd+=" --no-progress"
cmd+=" --console-title"
cmd+=" --retries 3"
cmd+=" --continue"
if [ -n "$LIMIT_RATE" ]; then
cmd+=" --limit-rate $LIMIT_RATE"
fi
cmd+=" \"$pagina_url\""
# Ejecutar descarga
if eval "$cmd" 2>&1; then
if [ -f "$output_path" ]; then
local size=$(du -h "$output_path" | cut -f1)
echo "${id}|${safe_name}.mp4" >> "$REGISTRO"
ok "Descargado: ${titulo:0:40} ($size)"
else
# Buscar archivo con otro nombre
local found=$(find "$VIDEOS_DIR" -name "*${id}*" -o -name "*${titulo:0:20}*" 2>/dev/null | head -1)
if [ -n "$found" ]; then
local fname=$(basename "$found")
echo "${id}|${fname}" >> "$REGISTRO"
local size=$(du -h "$found" | cut -f1)
ok "Descargado (con otro nombre): ${titulo:0:40} ($size)"
else
warn "Puede que se haya descargado pero no se encontró el archivo esperado."
warn "Ruta buscada: $output_path"
fi
fi
else
err "Error descargando: ${titulo:0:50}"
fi
# Pequeña pausa entre descargas para no saturar
sleep 2
done
echo ""
local pendientes=$((total - $(wc -l < "$REGISTRO" 2>/dev/null || echo 0)))
if [ "$pendientes" -le 0 ]; then
echo " 🎉 ¡Todos los videos descargados!"
else
echo " Quedan $pendientes videos pendientes."
echo " Para reanudar: bash $0"
fi
}
# ─── GENERAR LIBRO CON VIDEOS ───────────────────────────────
generar_libro() {
log "Generando libro de estudio con videos locales..."
node -e "
const fs = require('fs');
const path = require('path');
// Leer registro de videos descargados
const registro = '$REGISTRO';
const videosMap = {};
if (fs.existsSync(registro)) {
const lines = fs.readFileSync(registro, 'utf-8').trim().split('\n');
for (const line of lines) {
const [id, archivo] = line.split('|');
if (id && archivo) videosMap[id] = archivo;
}
}
console.log(' Videos mapeados:', Object.keys(videosMap).length);
// Guardar mapa para que lo use el script principal
const mapaPath = '$SCRIPT_DIR/.mapa_videos.json';
fs.writeFileSync(mapaPath, JSON.stringify(videosMap, null, 2));
" 2>/dev/null
# Ahora regenerar el libro usando el script existente
if [ -f "$SCRIPT_DIR/generar_dashboard.mjs" ]; then
node "$SCRIPT_DIR/generar_dashboard.mjs" 2>&1
else
warn "No se encontró generar_dashboard.mjs"
warn "El libro se generará sin videos la próxima vez que ejecutes node generar_dashboard.mjs"
fi
}
# ─── ESTADO ──────────────────────────────────────────────────
mostrar_estado() {
echo ""
echo " ─── ESTADO DE DESCARGA ───"
echo ""
if [ ! -f "$LISTA_VIDEOS" ]; then
echo " Lista de videos no generada."
echo " Ejecutá primero: bash $0"
return
fi
local total=$(python3 -c "import json; print(len(json.load(open('$LISTA_VIDEOS'))))" 2>/dev/null || echo 0)
local descargados=$(wc -l < "$REGISTRO" 2>/dev/null || echo 0)
local pendientes=$((total - descargados))
echo " Total videos: $total"
echo " Descargados: $descargados"
echo " Pendientes: $pendientes"
echo ""
if [ "$descargados" -gt 0 ] && [ -d "$VIDEOS_DIR" ]; then
local total_size=$(du -sh "$VIDEOS_DIR" 2>/dev/null | cut -f1)
echo " Espacio usado: $total_size"
echo ""
echo " Últimos descargados:"
tail -5 "$REGISTRO" 2>/dev/null | while IFS='|' read -r id archivo; do
if [ -n "$archivo" ] && [ -f "$VIDEOS_DIR/$archivo" ]; then
local size=$(du -h "$VIDEOS_DIR/$archivo" 2>/dev/null | cut -f1)
echo "$archivo ($size)"
fi
done
fi
echo ""
}
# ─── LIMPIAR ─────────────────────────────────────────────────
limpiar() {
echo ""
warn "¿Borrar todos los videos descargados? (s/N): "
read -r respuesta
if [ "$respuesta" = "s" ] || [ "$respuesta" = "S" ]; then
rm -rf "$VIDEOS_DIR" "$REGISTRO" "$LISTA_VIDEOS" "$COOKIES_FILE"
ok "Eliminado. Listo para empezar de nuevo."
else
ok "Operación cancelada."
fi
}
# ─── MAIN ────────────────────────────────────────────────────
main() {
echo ""
echo "═══════════════════════════════════════════════════"
echo " 🏐 DESCARGAR VIDEOS — Curso de Vóley"
echo "═══════════════════════════════════════════════════"
echo ""
check_dependencies
# Procesar argumentos
case "${1:-}" in
--status|-s)
convertir_cookies
extraer_lista_videos
mostrar_estado
exit 0
;;
--clean|-c)
limpiar
exit 0
;;
--resume|-r)
true # simplemente continua
;;
*)
# Primer ejecución: setup completo
if [ ! -f "$COOKIES_FILE" ]; then
convertir_cookies
fi
if [ ! -f "$LISTA_VIDEOS" ]; then
extraer_lista_videos
fi
;;
esac
# Asegurar cookies
if [ ! -f "$COOKIES_FILE" ]; then
convertir_cookies
fi
if [ ! -f "$LISTA_VIDEOS" ]; then
extraer_lista_videos
fi
descargar_videos
echo ""
echo "═══════════════════════════════════════════════════"
echo " Listo."
echo "═══════════════════════════════════════════════════"
echo ""
echo " Videos en: $VIDEOS_DIR"
echo " Para retomar: bash $0 --resume"
echo " Para ver estado: bash $0 --status"
echo ""
}
main "$@"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,227 @@
[
{
"text": "Políticas de cookies",
"href": "https://kb.onlineeducation.center/es/cookies/",
"title": ""
},
{
"text": "Políticas de privacidad",
"href": "https://kb.onlineeducation.center/es/privacy/",
"title": ""
},
{
"text": "Términos de uso",
"href": "https://kb.onlineeducation.center/es/terms/",
"title": ""
},
{
"text": "Aceptar",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list#agree",
"title": ""
},
{
"text": "",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"title": ""
},
{
"text": "",
"href": "https://voley.onlineeducation.center/es/campus",
"title": "Voley"
},
{
"text": "Elva Karina Saldis",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list#",
"title": ""
},
{
"text": "Su cuenta",
"href": "https://account.onlineeducation.center/es/",
"title": ""
},
{
"text": "Configuración regional",
"href": "https://account.onlineeducation.center/es/config?bth=https%3A//voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"title": ""
},
{
"text": "Datos para certificados",
"href": "https://voley.onlineeducation.center/es/campus/student/e/14197/user/account?r=https%3A//voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"title": ""
},
{
"text": "Mesa de ayuda",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/help-desk/tickets",
"title": ""
},
{
"text": "Cerrar sesión",
"href": "https://voley.onlineeducation.center/es/logout",
"title": ""
},
{
"text": "English",
"href": "https://voley.onlineeducation.center/en/campus/student/training/e/14197/m/19097/s/35519/study-resources/list?hl=en&locale_switcher=1",
"title": ""
},
{
"text": "Inicio",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197",
"title": ""
},
{
"text": "Información",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/info",
"title": ""
},
{
"text": "Auditorio",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/auditorium/sessions",
"title": ""
},
{
"text": "Material de estudio",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/study-resources",
"title": ""
},
{
"text": "Foros",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/forum",
"title": ""
},
{
"text": "Evaluaciones",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list#",
"title": ""
},
{
"text": "Trabajos prácticos",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/practice-work",
"title": ""
},
{
"text": "Administración",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"title": ""
},
{
"text": "Calificaciones",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/course-results",
"title": ""
},
{
"text": "Certificaciones",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/certificate/list",
"title": ""
},
{
"text": "Su opinión",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/review/make",
"title": ""
},
{
"text": "Mesa de ayuda",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/help-desk/tickets",
"title": ""
},
{
"text": "Ir a la encuesta",
"href": "https://onlineeducation.center/es/encuesta/modulo/curso-entrenador-nacional-dos-edicion-2022-t-261ca228f64845?email=saldis_karina%40hotmail.com&studentname=Elva%20Karina%20Saldis&editionnumber=5&modulenumber=1",
"title": ""
},
{
"text": "Opinar",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/review/make",
"title": ""
},
{
"text": "Módulo 1",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list#collapse1",
"title": ""
},
{
"text": "PLANIFICACION",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/list",
"title": ""
},
{
"text": "PREPARACION ATLETICA",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/list",
"title": ""
},
{
"text": "EXPLICACION DEL TRABAJO PRACTICO A PRESENTAR",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34870/study-resources/list",
"title": ""
},
{
"text": "SEGUIMIENTOS DE EQUIPOS",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35523/study-resources/list",
"title": ""
},
{
"text": "ANALISIS DE EQUIPOS DE VOLEIBOL",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/list",
"title": ""
},
{
"text": "ANALISIS DE EQUIPOS DE VOLEIBOL 2",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/list",
"title": ""
},
{
"text": "TRABAJO PRACTICO FINAL DE LA UNIDAD",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35526/study-resources/list",
"title": ""
},
{
"text": "Módulo 2",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list#collapse2",
"title": ""
},
{
"text": "ENTRENAMIENTO DE LOS CENTRALES Y OPUESTOS/AS",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"title": ""
},
{
"text": "PUNTAS Y LIBEROS/AS",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35520/study-resources/list",
"title": ""
},
{
"text": "ENTRENAMIENTO DE LOS Y LAS ARMADORAS",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35521/study-resources/list",
"title": ""
},
{
"text": "EXPLICACION DEL TRABAJO PRACTICO A PRESENTAR",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35522/study-resources/list",
"title": ""
},
{
"text": "Abrir Archivo",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/46874/show?me=142960",
"title": ""
},
{
"text": "Abrir Archivo",
"href": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/46805/show?me=142961",
"title": ""
},
{
"text": "Términos de uso",
"href": "https://kb.onlineeducation.center/es/terms/",
"title": ""
},
{
"text": "Política de privacidad",
"href": "https://kb.onlineeducation.center/es/privacy/",
"title": ""
},
{
"text": "Powered by",
"href": "https://onlineeducation.center/",
"title": "powered by Online Education Center"
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

+305
View File
@@ -0,0 +1,305 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>KSALDIS-DT — Curso de Entrenador Nacional de Vóley</title>
<meta name="description" content="Material de estudio offline del Curso de Entrenador Nacional de Vóley — 2 módulos, 11 secciones, 42 videos" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
background: #0a0e1a;
color: #e0e4ef;
min-height: 100vh;
overflow-x: hidden;
}
/* ── HERO ── */
.hero {
position: relative;
min-height: 80vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 3rem 2rem;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 80% 50% at 50% 0%, rgba(41, 128, 185, 0.25) 0%, transparent 60%),
radial-gradient(ellipse 60% 40% at 80% 100%, rgba(142, 68, 173, 0.15) 0%, transparent 50%),
radial-gradient(ellipse 50% 30% at 10% 60%, rgba(52, 152, 219, 0.1) 0%, transparent 50%);
pointer-events: none;
z-index: 0;
}
.hero::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 600px;
height: 600px;
transform: translate(-50%, -50%);
background: radial-gradient(circle, rgba(41, 128, 185, 0.08) 0%, transparent 70%);
animation: pulse-glow 4s ease-in-out infinite;
pointer-events: none;
z-index: 0;
}
@keyframes pulse-glow {
0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.6; }
50% { transform: translate(-50%, -50%) scale(1.15); opacity: 1; }
}
.hero-content {
position: relative;
z-index: 1;
max-width: 700px;
}
.hero-icon {
font-size: 5rem;
margin-bottom: 1.5rem;
filter: drop-shadow(0 0 30px rgba(41, 128, 185, 0.4));
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12px); }
}
.hero h1 {
font-size: 2.8rem;
font-weight: 800;
letter-spacing: -1px;
background: linear-gradient(135deg, #ffffff 0%, #a8c8e8 50%, #7fb3d8 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.8rem;
line-height: 1.15;
}
.hero .subtitle {
font-size: 1.15rem;
font-weight: 300;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 2rem;
letter-spacing: 0.5px;
}
/* ── STATS ── */
.stats-row {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 2.5rem;
}
.stat-badge {
background: rgba(255, 255, 255, 0.06);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.08);
padding: 0.6rem 1.4rem;
border-radius: 100px;
font-size: 0.88rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.75);
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
}
.stat-badge:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(41, 128, 185, 0.3);
transform: translateY(-2px);
}
.stat-badge .num {
font-weight: 700;
color: #5dade2;
}
/* ── CARDS ── */
.cards-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.2rem;
max-width: 960px;
width: 100%;
padding: 0 1rem;
}
.card {
position: relative;
background: rgba(255, 255, 255, 0.04);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16px;
padding: 2rem 1.5rem;
text-decoration: none;
color: #e0e4ef;
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 0.8rem;
}
.card::before {
content: '';
position: absolute;
inset: 0;
border-radius: 16px;
padding: 1px;
background: linear-gradient(135deg, rgba(41, 128, 185, 0.3), transparent 50%);
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
opacity: 0;
transition: opacity 0.35s;
}
.card:hover {
transform: translateY(-6px);
background: rgba(255, 255, 255, 0.08);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3), 0 0 40px rgba(41, 128, 185, 0.1);
}
.card:hover::before { opacity: 1; }
.card.primary {
background: linear-gradient(135deg, rgba(231, 76, 60, 0.15), rgba(192, 57, 43, 0.08));
border-color: rgba(231, 76, 60, 0.2);
}
.card.primary::before {
background: linear-gradient(135deg, rgba(231, 76, 60, 0.5), transparent 50%);
}
.card.primary:hover {
background: linear-gradient(135deg, rgba(231, 76, 60, 0.25), rgba(192, 57, 43, 0.15));
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3), 0 0 40px rgba(231, 76, 60, 0.15);
}
.card-icon {
font-size: 2.2rem;
margin-bottom: 0.2rem;
}
.card-title {
font-size: 1.05rem;
font-weight: 600;
line-height: 1.3;
}
.card-desc {
font-size: 0.82rem;
color: rgba(255, 255, 255, 0.4);
font-weight: 400;
}
/* ── FOOTER ── */
.footer {
text-align: center;
padding: 3rem 2rem;
color: rgba(255, 255, 255, 0.25);
font-size: 0.8rem;
}
.footer a {
color: rgba(255, 255, 255, 0.35);
text-decoration: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.2s;
}
.footer a:hover {
color: rgba(255, 255, 255, 0.6);
border-bottom-color: rgba(41, 128, 185, 0.4);
}
.footer .separator {
margin: 0 0.5rem;
opacity: 0.3;
}
/* ── RESPONSIVE ── */
@media (max-width: 640px) {
.hero { min-height: 60vh; padding: 2rem 1rem; }
.hero h1 { font-size: 1.8rem; }
.hero .subtitle { font-size: 0.95rem; }
.hero-icon { font-size: 3.5rem; }
.cards-grid { grid-template-columns: 1fr 1fr; gap: 0.8rem; }
.card { padding: 1.4rem 1rem; }
.stat-badge { font-size: 0.8rem; padding: 0.5rem 1rem; }
}
@media (max-width: 380px) {
.cards-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<section class="hero" id="hero">
<div class="hero-content">
<div class="hero-icon">🏐</div>
<h1>Curso de Entrenador Nacional de Vóley</h1>
<p class="subtitle">Material de Estudio — Versión Offline</p>
<div class="stats-row">
<div class="stat-badge">📚 <span class="num">2</span> módulos</div>
<div class="stat-badge">📂 <span class="num">11</span> secciones</div>
<div class="stat-badge">🎥 <span class="num">42</span> videos</div>
</div>
<div class="cards-grid">
<a href="LIBRO_DE_ESTUDIO.html" class="card primary" id="link-libro">
<div class="card-icon">📖</div>
<div class="card-title">Libro de Estudio</div>
<div class="card-desc">Contenido completo navegable</div>
</a>
<a href="secciones/" class="card" id="link-secciones">
<div class="card-icon">📂</div>
<div class="card-title">Secciones</div>
<div class="card-desc">Explorar por módulo</div>
</a>
<a href="videos/" class="card" id="link-videos">
<div class="card-icon">🎥</div>
<div class="card-title">Videos</div>
<div class="card-desc">Referencias Vimeo</div>
</a>
<a href="screenshots/" class="card" id="link-screenshots">
<div class="card-icon">🖼️</div>
<div class="card-title">Capturas</div>
<div class="card-desc">Screenshots del campus</div>
</a>
</div>
</div>
</section>
<footer class="footer">
<p>KSALDIS-DT <span class="separator">·</span> Contenido extraído del campus Online Education Center</p>
</footer>
</body>
</html>
@@ -0,0 +1,217 @@
[
{
"type": "pdf-link",
"url": "https://account.onlineeducation.center/es/config?bth=https%3A//voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"text": "Configuración regional"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/e/14197/user/account?r=https%3A//voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"text": "Datos para certificados"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/en/campus/student/training/e/14197/m/19097/s/35519/study-resources/list?hl=en&locale_switcher=1",
"text": "English"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/study-resources",
"text": "Material de estudio"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/list",
"text": "PLANIFICACION"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/list",
"text": "PREPARACION ATLETICA"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34870/study-resources/list",
"text": "EXPLICACION DEL TRABAJO PRACTICO A PRESENTAR"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35523/study-resources/list",
"text": "SEGUIMIENTOS DE EQUIPOS"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/list",
"text": "ANALISIS DE EQUIPOS DE VOLEIBOL"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/list",
"text": "ANALISIS DE EQUIPOS DE VOLEIBOL 2"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35526/study-resources/list",
"text": "TRABAJO PRACTICO FINAL DE LA UNIDAD"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/list",
"text": "ENTRENAMIENTO DE LOS CENTRALES Y OPUESTOS/AS"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35520/study-resources/list",
"text": "PUNTAS Y LIBEROS/AS"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35521/study-resources/list",
"text": "ENTRENAMIENTO DE LOS Y LAS ARMADORAS"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35522/study-resources/list",
"text": "EXPLICACION DEL TRABAJO PRACTICO A PRESENTAR"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/46874/show?me=142960",
"text": "Abrir Archivo"
},
{
"type": "pdf-link",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19097/s/35519/study-resources/46805/show?me=142961",
"text": "Abrir Archivo"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919369433?nocache=1777730642",
"title": "https://player.vimeo.com/video/919369433?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/1179197191?nocache=1777730642",
"title": "https://player.vimeo.com/video/1179197191?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919341704?nocache=1777730642",
"title": "https://player.vimeo.com/video/919341704?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919342668?nocache=1777730642",
"title": "https://player.vimeo.com/video/919342668?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919346562?nocache=1777730642",
"title": "https://player.vimeo.com/video/919346562?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919347866?nocache=1777730642",
"title": "https://player.vimeo.com/video/919347866?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919348493?nocache=1777730642",
"title": "https://player.vimeo.com/video/919348493?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919351892?nocache=1777730642",
"title": "https://player.vimeo.com/video/919351892?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919352848?nocache=1777730642",
"title": "https://player.vimeo.com/video/919352848?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919353448?nocache=1777730642",
"title": "https://player.vimeo.com/video/919353448?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919354134?nocache=1777730642",
"title": "https://player.vimeo.com/video/919354134?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919355668?nocache=1777730642",
"title": "https://player.vimeo.com/video/919355668?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919356423?nocache=1777730642",
"title": "https://player.vimeo.com/video/919356423?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919357109?nocache=1777730642",
"title": "https://player.vimeo.com/video/919357109?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919358607?nocache=1777730642",
"title": "https://player.vimeo.com/video/919358607?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919359386?nocache=1777730642",
"title": "https://player.vimeo.com/video/919359386?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919359855?nocache=1777730642",
"title": "https://player.vimeo.com/video/919359855?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919360318?nocache=1777730642",
"title": "https://player.vimeo.com/video/919360318?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919363158?nocache=1777730642",
"title": "https://player.vimeo.com/video/919363158?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919363728?nocache=1777730642",
"title": "https://player.vimeo.com/video/919363728?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919364522?nocache=1777730642",
"title": "https://player.vimeo.com/video/919364522?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919365485?nocache=1777730642",
"title": "https://player.vimeo.com/video/919365485?nocache=1777730642"
},
{
"type": "iframe",
"url": "https://player.vimeo.com/video/919366263?nocache=1777730642",
"title": "https://player.vimeo.com/video/919366263?nocache=1777730642"
},
{
"type": "image",
"url": "https://voley.onlineeducation.center/uploads/institucion/d15ddc66bd4cd881200ee3c3abacd7396367ddaa.png",
"alt": "Voley"
},
{
"type": "image",
"url": "https://voley.onlineeducation.center/img/user-default.jpg",
"alt": ""
},
{
"type": "image",
"url": "https://voley.onlineeducation.center/img/oec-logo-grey.png",
"alt": "Online Education Center"
}
]
@@ -0,0 +1,68 @@
[
{
"seccion": "Planificacion",
"modulo": "Modulo_1_Planificacion",
"ok": true,
"recursos": 10
},
{
"seccion": "Preparacion_Atletica",
"modulo": "Modulo_1_Planificacion",
"ok": true,
"recursos": 16
},
{
"seccion": "Explicacion_TP",
"modulo": "Modulo_1_Planificacion",
"ok": true,
"recursos": 3
},
{
"seccion": "Seguimientos_Equipos",
"modulo": "Modulo_1_Planificacion",
"ok": true,
"recursos": 0
},
{
"seccion": "Analisis_Equipos_1",
"modulo": "Modulo_1_Planificacion",
"ok": true,
"recursos": 0
},
{
"seccion": "Analisis_Equipos_2",
"modulo": "Modulo_1_Planificacion",
"ok": true,
"recursos": 0
},
{
"seccion": "TP_Final_Unidad",
"modulo": "Modulo_1_Planificacion",
"ok": true,
"recursos": 0
},
{
"seccion": "Centrales_y_Oponentes",
"modulo": "Modulo_2_Entrenamiento",
"ok": true,
"recursos": 26
},
{
"seccion": "Puntas_y_Liberos",
"modulo": "Modulo_2_Entrenamiento",
"ok": true,
"recursos": 14
},
{
"seccion": "Entrenamiento_Armadoras",
"modulo": "Modulo_2_Entrenamiento",
"ok": true,
"recursos": 2
},
{
"seccion": "Explicacion_TP",
"modulo": "Modulo_2_Entrenamiento",
"ok": true,
"recursos": 2
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

@@ -0,0 +1,5 @@
# Analisis Equipos 1
Oops! An Error Occurred
The server returned a "500 Internal Server Error".
Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,166 @@
[
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53294/show?me=142938",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53294/show?me=142938",
"texto": "Abrir Archivo",
"id": "53294"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50262/show?me=142080",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50262/show?me=142080",
"texto": "Abrir Archivo",
"id": "50262"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53180/show?me=142693",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53180/show?me=142693",
"texto": "Abrir Archivo",
"id": "53180"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53185/show?me=142698",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53185/show?me=142698",
"texto": "Abrir Archivo",
"id": "53185"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53181/show?me=142694",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53181/show?me=142694",
"texto": "Abrir Archivo",
"id": "53181"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50276/show?me=142081",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50276/show?me=142081",
"texto": "Abrir Archivo",
"id": "50276"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50236/show?me=142098",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50236/show?me=142098",
"texto": "Abrir Archivo",
"id": "50236"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53080/show?me=142591",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53080/show?me=142591",
"texto": "Abrir Archivo",
"id": "53080"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50233/show?me=142087",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50233/show?me=142087",
"texto": "Abrir Archivo",
"id": "50233"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50234/show?me=142089",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50234/show?me=142089",
"texto": "Abrir Archivo",
"id": "50234"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50235/show?me=142088",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50235/show?me=142088",
"texto": "Abrir Archivo",
"id": "50235"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50237/show?me=142092",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50237/show?me=142092",
"texto": "Abrir Archivo",
"id": "50237"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53182/show?me=142695",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53182/show?me=142695",
"texto": "Abrir Archivo",
"id": "53182"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50278/show?me=142090",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50278/show?me=142090",
"texto": "Abrir Archivo",
"id": "50278"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50280/show?me=142091",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50280/show?me=142091",
"texto": "Abrir Archivo",
"id": "50280"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53079/show?me=142590",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53079/show?me=142590",
"texto": "Abrir Archivo",
"id": "53079"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50282/show?me=142093",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50282/show?me=142093",
"texto": "Abrir Archivo",
"id": "50282"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50283/show?me=142096",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/50283/show?me=142096",
"texto": "Abrir Archivo",
"id": "50283"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53186/show?me=142699",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53186/show?me=142699",
"texto": "Abrir Archivo",
"id": "53186"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53183/show?me=142696",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53183/show?me=142696",
"texto": "Abrir Archivo",
"id": "53183"
},
{
"tipo": "archivo-curso",
"href": "https://voley.onlineeducation.center/es/campus/coord/training/edition/14197/module/19096/subject/35524/study-resources/50260/show?me=142084",
"url": "https://voley.onlineeducation.center/es/campus/coord/training/edition/14197/module/19096/subject/35524/study-resources/50260/show?me=142084",
"texto": "",
"id": "50260"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53184/show?me=142697",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35524/study-resources/53184/show?me=142697",
"texto": "Abrir Archivo",
"id": "53184"
},
{
"tipo": "video-embed",
"url": "https://player.vimeo.com/video/1073484590?nocache=1777790295",
"id": "1073484590"
},
{
"tipo": "video-embed",
"url": "https://player.vimeo.com/video/876705933?nocache=1777790295",
"id": "876705933"
}
]
@@ -0,0 +1,5 @@
# Analisis Equipos 2
Oops! An Error Occurred
The server returned a "500 Internal Server Error".
Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,51 @@
[
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53295/show?me=142939",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53295/show?me=142939",
"texto": "Abrir Archivo",
"id": "53295"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53194/show?me=142710",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53194/show?me=142710",
"texto": "Abrir Archivo",
"id": "53194"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53190/show?me=142706",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53190/show?me=142706",
"texto": "Abrir Archivo",
"id": "53190"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53191/show?me=142707",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53191/show?me=142707",
"texto": "Abrir Archivo",
"id": "53191"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53192/show?me=142708",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53192/show?me=142708",
"texto": "Abrir Archivo",
"id": "53192"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53193/show?me=142709",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53193/show?me=142709",
"texto": "Abrir Archivo",
"id": "53193"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53201/show?me=142717",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/35525/study-resources/53201/show?me=142717",
"texto": "Abrir Archivo",
"id": "53201"
}
]
@@ -0,0 +1,11 @@
# Explicacion TP
> Extraído el 2026-05-02
PLANIFICACION
PREPARACION ATLETICA
EXPLICACION DEL TRABAJO PRACTICO A PRESENTAR
SEGUIMIENTOS DE EQUIPOS
ANALISIS DE EQUIPOS DE VOLEIBOL
ANALISIS DE EQUIPOS DE VOLEIBOL 2
TRABAJO PRACTICO FINAL DE LA UNIDAD
File diff suppressed because one or more lines are too long
@@ -0,0 +1,14 @@
[
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34870/study-resources/53173/show?me=142686",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34870/study-resources/53173/show?me=142686",
"texto": "Abrir Archivo",
"id": "53173"
},
{
"tipo": "video-embed",
"url": "https://player.vimeo.com/video/1173576229?nocache=1777790278",
"id": "1173576229"
}
]
@@ -0,0 +1,12 @@
# Videos - Explicacion TP
> Sección: https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34870/study-resources/list
## Video Vimeo ID: 1173576229
- **Plataforma**: Vimeo
- **URL**: https://player.vimeo.com/video/1173576229?nocache=1777731170
- **ID**: 1173576229
Para descargar: yt-dlp "https://player.vimeo.com/video/1173576229"
@@ -0,0 +1,11 @@
# Planificacion
> Extraído el 2026-05-02
PLANIFICACION
PREPARACION ATLETICA
EXPLICACION DEL TRABAJO PRACTICO A PRESENTAR
SEGUIMIENTOS DE EQUIPOS
ANALISIS DE EQUIPOS DE VOLEIBOL
ANALISIS DE EQUIPOS DE VOLEIBOL 2
TRABAJO PRACTICO FINAL DE LA UNIDAD
File diff suppressed because one or more lines are too long
@@ -0,0 +1,63 @@
[
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50320/show?me=140219",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50320/show?me=140219",
"texto": "Abrir Archivo",
"id": "50320"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/52903/show?me=142139",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/52903/show?me=142139",
"texto": "Abrir Archivo",
"id": "52903"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/46541/show?me=140221",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/46541/show?me=140221",
"texto": "Abrir Enlace",
"id": "46541"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/46723/show?me=140222",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/46723/show?me=140222",
"texto": "Abrir Archivo",
"id": "46723"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/46724/show?me=140223",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/46724/show?me=140223",
"texto": "Abrir Archivo",
"id": "46724"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50142/show?me=140224",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50142/show?me=140224",
"texto": "Abrir Enlace",
"id": "50142"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50145/show?me=140225",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50145/show?me=140225",
"texto": "Abrir Enlace",
"id": "50145"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50144/show?me=140226",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/50144/show?me=140226",
"texto": "Abrir Enlace",
"id": "50144"
},
{
"tipo": "video-embed",
"url": "https://player.vimeo.com/video/1170245111?nocache=1777790245",
"id": "1170245111"
}
]
@@ -0,0 +1,12 @@
# Videos - Planificacion
> Sección: https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34868/study-resources/list
## Video Vimeo ID: 1170245111
- **Plataforma**: Vimeo
- **URL**: https://player.vimeo.com/video/1170245111?nocache=1777731162
- **ID**: 1170245111
Para descargar: yt-dlp "https://player.vimeo.com/video/1170245111"
@@ -0,0 +1,11 @@
# Preparacion Atletica
> Extraído el 2026-05-02
PLANIFICACION
PREPARACION ATLETICA
EXPLICACION DEL TRABAJO PRACTICO A PRESENTAR
SEGUIMIENTOS DE EQUIPOS
ANALISIS DE EQUIPOS DE VOLEIBOL
ANALISIS DE EQUIPOS DE VOLEIBOL 2
TRABAJO PRACTICO FINAL DE LA UNIDAD
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,101 @@
[
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53012/show?me=142522",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53012/show?me=142522",
"texto": "Abrir Archivo",
"id": "53012"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53137/show?me=142648",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53137/show?me=142648",
"texto": "Abrir Archivo",
"id": "53137"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53138/show?me=142649",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53138/show?me=142649",
"texto": "Abrir Archivo",
"id": "53138"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53139/show?me=142650",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/53139/show?me=142650",
"texto": "Abrir Archivo",
"id": "53139"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/46719/show?me=140229",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/46719/show?me=140229",
"texto": "Abrir Archivo",
"id": "46719"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50329/show?me=140232",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50329/show?me=140232",
"texto": "Abrir Archivo",
"id": "50329"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/46726/show?me=140233",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/46726/show?me=140233",
"texto": "Abrir Archivo",
"id": "46726"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50139/show?me=140234",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50139/show?me=140234",
"texto": "Abrir Enlace",
"id": "50139"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/46725/show?me=140235",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/46725/show?me=140235",
"texto": "Abrir Archivo",
"id": "46725"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50140/show?me=140236",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50140/show?me=140236",
"texto": "Abrir Enlace",
"id": "50140"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50143/show?me=140237",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50143/show?me=140237",
"texto": "Abrir Enlace",
"id": "50143"
},
{
"tipo": "archivo-curso",
"href": "/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50141/show?me=140238",
"url": "https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/50141/show?me=140238",
"texto": "Abrir Enlace",
"id": "50141"
},
{
"tipo": "video-embed",
"url": "https://player.vimeo.com/video/1173231315?nocache=1777790263",
"id": "1173231315"
},
{
"tipo": "video-embed",
"url": "https://player.vimeo.com/video/1063655414?nocache=1777790263",
"id": "1063655414"
},
{
"tipo": "video-embed",
"url": "https://player.vimeo.com/video/1063657248?nocache=1777790263",
"id": "1063657248"
}
]
@@ -0,0 +1,28 @@
# Videos - Preparacion Atletica
> Sección: https://voley.onlineeducation.center/es/campus/student/training/e/14197/m/19096/s/34869/study-resources/list
## Video Vimeo ID: 1173231315
- **Plataforma**: Vimeo
- **URL**: https://player.vimeo.com/video/1173231315?nocache=1777731166
- **ID**: 1173231315
Para descargar: yt-dlp "https://player.vimeo.com/video/1173231315"
## Video Vimeo ID: 1063655414
- **Plataforma**: Vimeo
- **URL**: https://player.vimeo.com/video/1063655414?nocache=1777731166
- **ID**: 1063655414
Para descargar: yt-dlp "https://player.vimeo.com/video/1063655414"
## Video Vimeo ID: 1063657248
- **Plataforma**: Vimeo
- **URL**: https://player.vimeo.com/video/1063657248?nocache=1777731166
- **ID**: 1063657248
Para descargar: yt-dlp "https://player.vimeo.com/video/1063657248"
@@ -0,0 +1,5 @@
# Seguimientos Equipos
Oops! An Error Occurred
The server returned a "500 Internal Server Error".
Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused.
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More