feat: lazy ReactPlayer, seed 11 vidéos YouTube (niveaux 0/1/2)
Some checks failed
CI/CD — Build & Deploy / Build (push) Failing after 41s
CI/CD — Build & Deploy / Deploy to VPS (push) Has been skipped

This commit is contained in:
2026-03-14 08:25:41 +01:00
parent 11d9432218
commit 5eb0a43d7f
3 changed files with 180 additions and 9 deletions

166
backend/src/seeds/videos.ts Normal file
View File

@@ -0,0 +1,166 @@
/**
* Seed vidéos YouTube — OriginsDigital
* Usage : npm run seed:videos
*
* Requiert DB_HOST, DB_USER, DB_PASSWORD, DB_NAME dans l'env.
*/
import "reflect-metadata";
import * as dotenv from "dotenv";
dotenv.config();
import { AppDataSource } from "../config/data-source";
import { Video } from "../entities/Video";
const yt = (id: string) => `https://img.youtube.com/vi/${id}/maxresdefault.jpg`;
const VIDEOS: Partial<Video>[] = [
// ── Niveau 0 — libre ──────────────────────────────────────────────────────
{
title: "JavaScript in 100 Seconds",
description: "Tour rapide du JavaScript — syntaxe, event loop, histoire.",
storageType: "youtube",
storageKey: "DHjqpvDnNGE",
thumbnailUrl: yt("DHjqpvDnNGE"),
duration: 100,
requiredLevel: 0,
isPublished: true,
publishedAt: new Date(),
},
{
title: "TypeScript in 100 Seconds",
description: "Pourquoi typer son JavaScript — les bases en 100 secondes.",
storageType: "youtube",
storageKey: "zQnBQ4tB3ZA",
thumbnailUrl: yt("zQnBQ4tB3ZA"),
duration: 100,
requiredLevel: 0,
isPublished: true,
publishedAt: new Date(),
},
{
title: "React in 100 Seconds",
description: "Components, JSX, hooks — React expliqué en 100 secondes.",
storageType: "youtube",
storageKey: "Tn6-PIqc4UM",
thumbnailUrl: yt("Tn6-PIqc4UM"),
duration: 100,
requiredLevel: 0,
isPublished: true,
publishedAt: new Date(),
},
{
title: "CSS in 100 Seconds",
description: "Cascading Style Sheets — sélecteurs, box model, flexbox.",
storageType: "youtube",
storageKey: "OEV8gMkCHXQ",
thumbnailUrl: yt("OEV8gMkCHXQ"),
duration: 100,
requiredLevel: 0,
isPublished: true,
publishedAt: new Date(),
},
{
title: "Git in 100 Seconds",
description: "Versionner son code — commits, branches, merges.",
storageType: "youtube",
storageKey: "hwP7WQkmECE",
thumbnailUrl: yt("hwP7WQkmECE"),
duration: 100,
requiredLevel: 0,
isPublished: true,
publishedAt: new Date(),
},
{
title: "Node.js in 100 Seconds",
description: "JavaScript côté serveur — event loop, npm, streams.",
storageType: "youtube",
storageKey: "ENrzD9HAZK4",
thumbnailUrl: yt("ENrzD9HAZK4"),
duration: 100,
requiredLevel: 0,
isPublished: true,
publishedAt: new Date(),
},
// ── Niveau 1 — basic ──────────────────────────────────────────────────────
{
title: "Docker in 100 Seconds",
description: "Conteneuriser ses applications — images, volumes, compose.",
storageType: "youtube",
storageKey: "gAkwW2tuIqE",
thumbnailUrl: yt("gAkwW2tuIqE"),
duration: 100,
requiredLevel: 1,
isPublished: true,
publishedAt: new Date(),
},
{
title: "Tailwind CSS in 100 Seconds",
description: "Utility-first CSS — pourquoi ça change tout.",
storageType: "youtube",
storageKey: "mr15Xzb1Ook",
thumbnailUrl: yt("mr15Xzb1Ook"),
duration: 100,
requiredLevel: 1,
isPublished: true,
publishedAt: new Date(),
},
{
title: "SQL Explained in 100 Seconds",
description: "Bases de données relationnelles — SELECT, JOIN, index.",
storageType: "youtube",
storageKey: "zsjvFFKnte0",
thumbnailUrl: yt("zsjvFFKnte0"),
duration: 100,
requiredLevel: 1,
isPublished: true,
publishedAt: new Date(),
},
// ── Niveau 2 — pro ────────────────────────────────────────────────────────
{
title: "Linux in 100 Seconds",
description: "Le système d'exploitation qui fait tourner Internet.",
storageType: "youtube",
storageKey: "rrB13utjYV4",
thumbnailUrl: yt("rrB13utjYV4"),
duration: 100,
requiredLevel: 2,
isPublished: true,
publishedAt: new Date(),
},
{
title: "Redis in 100 Seconds",
description: "Cache in-memory — sessions, queues, pub/sub.",
storageType: "youtube",
storageKey: "G1rOthIU-uo",
thumbnailUrl: yt("G1rOthIU-uo"),
duration: 100,
requiredLevel: 2,
isPublished: true,
publishedAt: new Date(),
},
];
async function seed() {
await AppDataSource.initialize();
const repo = AppDataSource.getRepository(Video);
let inserted = 0;
let skipped = 0;
for (const data of VIDEOS) {
const exists = await repo.findOne({
where: { storageType: data.storageType, storageKey: data.storageKey },
});
if (exists) { skipped++; continue; }
await repo.save(repo.create(data));
inserted++;
}
await AppDataSource.destroy();
console.log(`✅ Seed terminé — ${inserted} insérées, ${skipped} ignorées (déjà présentes).`);
}
seed().catch((e) => { console.error("❌ Seed échoué :", e.message); process.exit(1); });