Interface web complète : formulaire d'ajout (URL directe ou recherche de chaîne) et panneau de file d'attente avec sondage de la progression. - views/downloadForm/ : sélecteur de mode (vidéo/audio/miniature) partagé, ajout direct par URL, recherche de chaîne (GET /api/channel) avec ajout par résultat. Construction DOM via createElement/textContent (jamais d'innerHTML avec du texte venant de YouTube), pas juste par prudence : ça évite aussi tout risque d'injection depuis un titre de vidéo. - views/queuePanel/ : sonde GET /api/queue toutes les 1,5s, affiche statut/ progression/erreur par élément, annulation et lien de téléchargement natif (<a download>, pas de fetch+blob). - api/ : clients fetch typés (channelApi, queueApi), chaque await dans son propre try/catch avec message d'erreur adapté (réseau vs HTTP vs JSON). - QueueItem gagne un champ title optionnel (purement informatif, jamais utilisé pour construire la commande yt-dlp) pour un affichage plus parlant qu'une URL brute. - client/tsconfig.json + build.mjs : cible ES2022 (nécessaire pour Error.cause, jusque-là ES2020). - Vitest ajouté côté client (fonctions pures de src/format.ts ; le câblage DOM est vérifié manuellement, pas de jsdom pour l'instant). Deux bugs réels trouvés et corrigés en testant dans un vrai navigateur (pas seulement en lisant le code) : - CSS : un attribut `hidden` sur un conteneur `display: flex` se faisait écraser par cette règle (même spécificité, la feuille de l'auteur gagne) - les deux panneaux d'options restaient visibles en même temps. - server/src/routes/channel.ts : ne mappait vers 502 que les YtDlpError (code de sortie non nul), pas les erreurs brutes de spawn (ex. binaire introuvable - ENOENT), qui remontaient donc en 500 générique. Corrigé + test de non-régression. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
57 lines
2 KiB
JavaScript
57 lines
2 KiB
JavaScript
// Build client : TypeScript (+ .html/.scss par vue) -> client/dist, via
|
|
// esbuild. Script à la main plutôt qu'un outil plus lourd (Vite, webpack) :
|
|
// le client reste pour l'instant minimal, et esbuild suffit - y compris
|
|
// pour le Sass (voir le plugin ci-dessous, qui délègue à `sass` puis laisse
|
|
// esbuild regrouper le CSS résultant, importé depuis le JS, dans un
|
|
// app.css voisin).
|
|
//
|
|
// Usage : node build.mjs [--watch]
|
|
// npm run build -> build unique (celui lancé par server/Dockerfile)
|
|
// npm run watch -> reconstruit à chaque sauvegarde, pour le dev local
|
|
import { copyFileSync, mkdirSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import * as esbuild from 'esbuild';
|
|
import * as sass from 'sass';
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const outdir = path.join(here, 'dist');
|
|
|
|
mkdirSync(outdir, { recursive: true });
|
|
copyFileSync(path.join(here, 'src', 'index.html'), path.join(outdir, 'index.html'));
|
|
|
|
/** Plugin esbuild : compile les imports `.scss` en CSS via le paquet `sass`. */
|
|
const sassPlugin = {
|
|
name: 'sass',
|
|
setup(build) {
|
|
build.onLoad({ filter: /\.scss$/ }, async (args) => {
|
|
const result = await sass.compileAsync(args.path);
|
|
return { contents: result.css, loader: 'css' };
|
|
});
|
|
},
|
|
};
|
|
|
|
const buildOptions = {
|
|
entryPoints: [path.join(here, 'src', 'app.ts')],
|
|
outfile: path.join(outdir, 'app.js'),
|
|
bundle: true,
|
|
format: 'esm',
|
|
target: 'es2022',
|
|
sourcemap: true,
|
|
// Chaque vue importe son propre gabarit comme chaîne brute (`import html
|
|
// from './Vue.html'` - voir global.d.ts) et son propre style Sass ; les
|
|
// styles sont regroupés automatiquement par esbuild dans app.css.
|
|
loader: { '.html': 'text' },
|
|
plugins: [sassPlugin],
|
|
};
|
|
|
|
const watch = process.argv.includes('--watch');
|
|
|
|
if (watch) {
|
|
const ctx = await esbuild.context(buildOptions);
|
|
await ctx.watch();
|
|
console.log('[youtubedownloader-client] watching for changes...');
|
|
} else {
|
|
await esbuild.build(buildOptions);
|
|
console.log('[youtubedownloader-client] built to client/dist');
|
|
}
|