feat: lazy ReactPlayer, seed 11 vidéos YouTube (niveaux 0/1/2)
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
"typeorm": "ts-node -e \"require('typeorm/cli')\"",
|
||||
"migration:generate": "npm run typeorm -- migration:generate",
|
||||
"migration:run": "npm run typeorm -- migration:run",
|
||||
"migration:revert": "npm run typeorm -- migration:revert"
|
||||
"migration:revert": "npm run typeorm -- migration:revert",
|
||||
"seed:videos": "ts-node --transpile-only src/seeds/videos.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.1",
|
||||
|
||||
166
backend/src/seeds/videos.ts
Normal file
166
backend/src/seeds/videos.ts
Normal 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); });
|
||||
Reference in New Issue
Block a user