Repository navigation
App Web
Branche : web-converter
Code : dossier public/
En ligne : bloopstudio.github.io/Anyform
App statique 100% client-side : aucun serveur, aucun backend, aucune build step. Toutes
les librairies tierces (ImageTracer.js, SheetJS, heic2any, UTIF.js, ffmpeg.wasm) sont
vendorisées localement dans public/vendor/ — aucun appel réseau externe, même pour la
vidéo/l'audio ou le décodage HEIC. Le thème (clair/sombre) suit automatiquement les
préférences de l'appareil (prefers-color-scheme, pas de bouton manuel).
- Choisir le mode en haut : Convertisseur, Compresseur (voir Compresseur), Inspecteur (voir Inspecteur) ou Comparateur (voir Comparateur)
- Convertisseur/Compresseur : choisir le type de fichier via les onglets (Image / Données / Audio / Vidéo / Sous-titres — seuls Image, Audio et Vidéo restent visibles en mode Compresseur). Inspecteur/Comparateur n'ont pas cet onglet : la catégorie est déduite du fichier déposé.
- En mode Convertisseur, choisir le format d'entrée et de sortie
- Déposer un fichier (glisser-déposer ou clic) — deux dropzones côte à côte en mode Comparateur (fichier A / fichier B)
- Cliquer sur le bouton d'action ("Convertir", "Compresser", "Inspecter" ou "Comparer")
Pendant le traitement : le bouton affiche un spinner, une barre de progression suit
l'avancement réel pour l'audio/vidéo (événement progress de ffmpeg.wasm). Le résultat
s'affiche dans une carte dédiée (nom, taille, bouton télécharger, bouton nouveau fichier),
en plus du téléchargement automatique. En mode Compresseur, la taille affiche
avant → après et le pourcentage gagné. Un encart sous le résultat garde les 5 derniers
fichiers traités (voir Historique local), et si l'onglet est en arrière-plan à la fin
d'une conversion audio/vidéo, une notification native prévient.
-
Images : SVG, PNG, JPG, WebP, GIF, BMP, HEIC en entrée ⇄ PNG/JPG/WebP/AVIF/ICO/TIFF/SVG
en sortie. Vectorisation raster → SVG via ImageTracer.js. Décodage HEIC via
heic2any/libheif WASM. - Audio : WAV, MP3, OGG, M4A, FLAC, AAC, WMA, Opus — moteur ffmpeg.wasm.
- Vidéo : MP4, WebM, MOV, MKV, AVI, FLV, OGV en entrée ⇄ les mêmes + GIF animé en sortie — moteur ffmpeg.wasm (partagé avec l'audio, chargé une seule fois par session).
- Données : CSV ⇄ JSON ⇄ XLSX via SheetJS (build patché, sans vulnérabilité connue).
-
Sous-titres : SRT ⇄ VTT ⇄ ASS, en pur JavaScript (aucune dépendance, pas de moteur à
charger). Le style ASS (police, couleur, position) n'est pas préservé — non représentable
en SRT/VTT, perdu dans les deux sens. Code :
public/subtitles.js.
Mode séparé (onglet en haut de l'interface) : réduit la taille d'un fichier sans changer son format, contrairement au Convertisseur. Limité aux images, à l'audio et aux vidéos — pas de notion de "compression sans changer de format" pour les données ou les sous-titres (fichiers texte). Trois niveaux : Léger, Moyen, Fort.
-
Images (PNG, JPG, WebP, HEIC) — réencodage via Canvas. GIF et BMP ne sont pas
compressibles :
canvas.toBlob()ne sait pas les encoder dans Chromium (il retombe silencieusement sur du PNG), donc ils sont exclus du Compresseur (ils restent convertibles en entrée dans le mode Convertisseur) :- JPG/WebP ont un curseur de qualité natif → qualité réduite (0.82 / 0.6 / 0.4 selon le niveau).
- PNG n'a pas de curseur de qualité → redimensionnement à la place (100% / 85% / 65% selon le niveau).
-
Audio (WAV, MP3, OGG, M4A, FLAC, AAC, WMA, Opus) — ffmpeg.wasm :
- Formats à bitrate réglable (MP3, OGG, M4A, AAC, Opus, WMA) : bitrate réduit à 192 / 128 / 96 kbps selon le niveau.
- FLAC (sans perte) : pas de curseur de qualité qui aurait du sens —
-compression_level5 / 8 / 12 à la place (taille réduite, qualité inchangée, encodage plus lent). - WAV (PCM brut, aucune compression possible) : fréquence d'échantillonnage réduite à 44100 / 32000 / 22050 Hz, comme le redimensionnement pour le PNG.
-
Vidéo (MP4, WebM, MOV, MKV, AVI, FLV, OGV) — ffmpeg.wasm, CRF réduit sur le même
codec/conteneur d'origine (pas de réencodage vers un autre codec) :
- H.264 (mp4/mov/mkv/avi/flv) : CRF 26 / 30 / 35
- VP9 (webm) : CRF 26 / 30 / 35 avec
-b:v 0(mode qualité constante) - Theora (ogv) :
-qscale:v6 / 10 / 18 - L'audio n'est pas retouché (
-c:a copy) — la vidéo domine presque toujours la taille du fichier.
Code : public/compress.js.
Mode séparé : lit les propriétés d'un fichier sans le modifier ni produire de fichier de
sortie. Pas d'onglet Type de fichier — la catégorie est déduite de l'extension du fichier
déposé (categoryOfExt). Volontairement léger : la durée/résolution audio/vidéo vient des
métadonnées natives du navigateur (éléments <audio>/<video>, événement
loadedmetadata), pas d'un décodage complet via ffmpeg.wasm — pas de moteur à charger,
résultat quasi instantané.
-
Images : dimensions, ratio. Pour un SVG, les attributs
width/height/viewBoxdéclarés dans le fichier plutôt qu'un rendu (pas de rasterisation juste pour inspecter). - Audio/Vidéo : durée, résolution (vidéo), débit moyen estimé (taille du fichier / durée).
- Données : nombre de feuilles, lignes et colonnes de la première feuille, en-têtes.
- Sous-titres : nombre de sous-titres, premier/dernier timecode, plage couverte.
Code : public/inspect.js.
Seul mode à deux entrées (fichier A / fichier B) au lieu d'une seule — deux dropzones côte à côte remplacent la dropzone unique. Les deux fichiers doivent être de la même catégorie (déduite de leur extension), sinon une erreur explicite s'affiche.
- Images : diff pixel par pixel sur la zone commune (les deux images redimensionnées aux plus petites dimensions communes si elles diffèrent). Pixels identiques affichés en gris atténué, pixels qui changent (écart R+G+B > 30, pour ignorer le bruit de compression JPEG) en rouge. Résultat : pourcentage de pixels identiques + image de diff téléchargeable.
-
Données/Sous-titres : diff ligne à ligne par plus longue sous-séquence commune (LCS),
la méthode standard derrière
git diff— coût O(n×m) en temps et en mémoire, donc repli automatique sur une comparaison d'empreinte SHA-256 au-delà de 3000 lignes plutôt que de figer l'onglet. -
Reste (audio, vidéo, XLSX...) : comparaison par empreinte SHA-256 uniquement via
crypto.subtle— identique ou différent, sans détail sur ce qui change.
Code : public/compare.js.
Les 5 derniers fichiers convertis/compressés (nom, taille, bouton "Télécharger") restent
accessibles sous le résultat, stockés dans IndexedDB (jamais envoyé nulle part, cohérent
avec le reste d'Anyform) — un bouton "Vider" efface tout. Un ajout au-delà de 5 fait sortir
le plus ancien. Code : public/history.js.
Les conversions audio/vidéo peuvent prendre du temps ; si l'onglet n'est plus visible à la
fin, l'API Notification du navigateur prévient plutôt que de compter sur l'utilisateur
pour revenir vérifier. La permission est demandée au premier clic sur "Convertir" pour une
catégorie audio/vidéo (geste utilisateur requis par les navigateurs), pas au chargement de
la page. Silencieux si la permission est refusée ou déjà utilisée (l'onglet est visible).
Le workflow .github/workflows/deploy-pages.yml déploie automatiquement public/ à
chaque push sur web-converter (pas besoin de merger vers main, qui n'est qu'une
page d'accueil). Il applique aussi un cache-busting (?v=<sha>) sur les scripts/styles
pour que les navigateurs ne servent jamais une version périmée.
Prérequis côté dépôt (déjà fait, à titre de référence) :
- Settings → Pages → source "GitHub Actions"
-
Settings → Environments → github-pages → Deployment branches → autoriser
web-converter(sinon le déploiement échoue silencieusement avec une erreur de protection d'environnement)
Un second workflow (bump-version.yml) incrémente automatiquement VERSION à chaque
push.
Pas de build nécessaire :
npx serve public-
public/app.js— interface (mode, onglets, formats, glisser-déposer, progression, résultat, notifications, historique) -
public/convert.js— conversion d'images (Canvas API + ImageTracer.js + heic2any + UTIF.js) -
public/compress.js— compression d'images/audio/vidéos (même format en sortie) -
public/data.js— conversion de données (CSV/JSON/XLSX) -
public/subtitles.js— conversion de sous-titres SRT/VTT/ASS (texte pur) -
public/history.js— historique local des 5 derniers fichiers (IndexedDB) -
public/inspect.js— lecture des propriétés d'un fichier, sans le modifier -
public/compare.js— diff entre deux fichiers (image, texte ligne à ligne, ou empreinte) -
public/ffmpeg-engine.js— chargement partagé du moteur ffmpeg.wasm -
public/audio.js/public/video.js— conversion audio/vidéo -
public/privacy.html— politique de confidentialité -
public/vendor/— librairies vendorisées
-
[hidden]vsdisplay: flex: une règle CSS.preview-row { display: flex }a priorité sur l'attributhiddennatif — nécessite.preview-row[hidden] { display: none }explicite. -
<select>désynchronisé au premier chargement mobile : peupler les options via JS avant le premier paint pouvait laisser le picker natif incohérent. Fix : options statiques dans le HTML, JS seulement pour les changements déclenchés par l'utilisateur. -
canvas.toBlob()fallback silencieux : pour un format non supporté par le navigateur (ex. AVIF), certains navigateurs renvoient un PNG avec le mime-type demandé au lieu d'échouer. Détecté en comparantblob.typeau mime attendu. -
Encore
[hidden]vsdisplay: grid: même piège que.preview-row, cette fois sur#categoryTabs(.tabs { display: grid }neutralisait son attributhiddenen mode Inspecteur/Comparateur). Fix : masquer le<div class="field">englobant (#categoryField, qui a déjà la règle.field[hidden] { display: none }) plutôt que#categoryTabslui-même.
Anyform — développé par BloopStudio · Essayer en ligne · Dépôt · Confidentialité