@jjlmoya/utils-audiovisual 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/package.json +60 -0
  2. package/src/category/i18n/en.ts +198 -0
  3. package/src/category/i18n/es.ts +198 -0
  4. package/src/category/i18n/fr.ts +198 -0
  5. package/src/category/index.ts +17 -0
  6. package/src/category/seo.astro +15 -0
  7. package/src/components/PreviewNavSidebar.astro +116 -0
  8. package/src/components/PreviewToolbar.astro +143 -0
  9. package/src/data.ts +4 -0
  10. package/src/env.d.ts +5 -0
  11. package/src/index.ts +32 -0
  12. package/src/layouts/PreviewLayout.astro +117 -0
  13. package/src/pages/[locale]/[slug].astro +146 -0
  14. package/src/pages/[locale].astro +251 -0
  15. package/src/pages/index.astro +4 -0
  16. package/src/tests/faq_count.test.ts +19 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/mocks/astro_mock.js +2 -0
  19. package/src/tests/no_h1_in_components.test.ts +48 -0
  20. package/src/tests/seo_length.test.ts +22 -0
  21. package/src/tests/tool_validation.test.ts +17 -0
  22. package/src/tool/chromaticLens/bibliography.astro +17 -0
  23. package/src/tool/chromaticLens/component.astro +178 -0
  24. package/src/tool/chromaticLens/i18n/en.ts +246 -0
  25. package/src/tool/chromaticLens/i18n/es.ts +244 -0
  26. package/src/tool/chromaticLens/i18n/fr.ts +244 -0
  27. package/src/tool/chromaticLens/index.ts +43 -0
  28. package/src/tool/chromaticLens/logic.ts +87 -0
  29. package/src/tool/chromaticLens/seo.astro +15 -0
  30. package/src/tool/chromaticLens/style.css +308 -0
  31. package/src/tool/chromaticLens/ui.ts +109 -0
  32. package/src/tool/collageMaker/bibliography.astro +17 -0
  33. package/src/tool/collageMaker/component.astro +302 -0
  34. package/src/tool/collageMaker/i18n/en.ts +233 -0
  35. package/src/tool/collageMaker/i18n/es.ts +231 -0
  36. package/src/tool/collageMaker/i18n/fr.ts +231 -0
  37. package/src/tool/collageMaker/index.ts +51 -0
  38. package/src/tool/collageMaker/logic.ts +134 -0
  39. package/src/tool/collageMaker/seo.astro +15 -0
  40. package/src/tool/collageMaker/style.css +386 -0
  41. package/src/tool/exifCleaner/bibliography.astro +18 -0
  42. package/src/tool/exifCleaner/component.astro +162 -0
  43. package/src/tool/exifCleaner/i18n/en.ts +277 -0
  44. package/src/tool/exifCleaner/i18n/es.ts +277 -0
  45. package/src/tool/exifCleaner/i18n/fr.ts +277 -0
  46. package/src/tool/exifCleaner/index.ts +57 -0
  47. package/src/tool/exifCleaner/logic.ts +135 -0
  48. package/src/tool/exifCleaner/seo.astro +18 -0
  49. package/src/tool/exifCleaner/style.css +289 -0
  50. package/src/tool/exifCleaner/ui.ts +117 -0
  51. package/src/tool/imageCompressor/bibliography.astro +17 -0
  52. package/src/tool/imageCompressor/component.astro +262 -0
  53. package/src/tool/imageCompressor/i18n/en.ts +232 -0
  54. package/src/tool/imageCompressor/i18n/es.ts +230 -0
  55. package/src/tool/imageCompressor/i18n/fr.ts +230 -0
  56. package/src/tool/imageCompressor/index.ts +50 -0
  57. package/src/tool/imageCompressor/logic.ts +79 -0
  58. package/src/tool/imageCompressor/seo.astro +15 -0
  59. package/src/tool/imageCompressor/style.css +503 -0
  60. package/src/tool/printQualityCalculator/bibliography.astro +18 -0
  61. package/src/tool/printQualityCalculator/component.astro +318 -0
  62. package/src/tool/printQualityCalculator/i18n/en.ts +247 -0
  63. package/src/tool/printQualityCalculator/i18n/es.ts +245 -0
  64. package/src/tool/printQualityCalculator/i18n/fr.ts +245 -0
  65. package/src/tool/printQualityCalculator/index.ts +56 -0
  66. package/src/tool/printQualityCalculator/logic.ts +53 -0
  67. package/src/tool/printQualityCalculator/seo.astro +18 -0
  68. package/src/tool/printQualityCalculator/style.css +491 -0
  69. package/src/tool/printQualityCalculator/ui.ts +122 -0
  70. package/src/tool/privacyBlur/bibliography.astro +17 -0
  71. package/src/tool/privacyBlur/component.astro +230 -0
  72. package/src/tool/privacyBlur/i18n/en.ts +238 -0
  73. package/src/tool/privacyBlur/i18n/es.ts +236 -0
  74. package/src/tool/privacyBlur/i18n/fr.ts +236 -0
  75. package/src/tool/privacyBlur/index.ts +49 -0
  76. package/src/tool/privacyBlur/logic.ts +249 -0
  77. package/src/tool/privacyBlur/seo.astro +15 -0
  78. package/src/tool/privacyBlur/style.css +332 -0
  79. package/src/tool/privacyBlur/ui.ts +124 -0
  80. package/src/tool/subtitleSync/bibliography.astro +17 -0
  81. package/src/tool/subtitleSync/component.astro +187 -0
  82. package/src/tool/subtitleSync/i18n/en.ts +241 -0
  83. package/src/tool/subtitleSync/i18n/es.ts +241 -0
  84. package/src/tool/subtitleSync/i18n/fr.ts +241 -0
  85. package/src/tool/subtitleSync/index.ts +49 -0
  86. package/src/tool/subtitleSync/logic.ts +91 -0
  87. package/src/tool/subtitleSync/seo.astro +15 -0
  88. package/src/tool/subtitleSync/style.css +325 -0
  89. package/src/tool/subtitleSync/ui.ts +152 -0
  90. package/src/tool/timelapseCalculator/bibliography.astro +15 -0
  91. package/src/tool/timelapseCalculator/component.astro +148 -0
  92. package/src/tool/timelapseCalculator/i18n/en.ts +169 -0
  93. package/src/tool/timelapseCalculator/i18n/es.ts +169 -0
  94. package/src/tool/timelapseCalculator/i18n/fr.ts +169 -0
  95. package/src/tool/timelapseCalculator/index.ts +52 -0
  96. package/src/tool/timelapseCalculator/logic.ts +46 -0
  97. package/src/tool/timelapseCalculator/seo.astro +18 -0
  98. package/src/tool/timelapseCalculator/style.css +285 -0
  99. package/src/tool/tvDistance/bibliography.astro +17 -0
  100. package/src/tool/tvDistance/component.astro +178 -0
  101. package/src/tool/tvDistance/i18n/en.ts +223 -0
  102. package/src/tool/tvDistance/i18n/es.ts +223 -0
  103. package/src/tool/tvDistance/i18n/fr.ts +223 -0
  104. package/src/tool/tvDistance/index.ts +49 -0
  105. package/src/tool/tvDistance/logic.ts +47 -0
  106. package/src/tool/tvDistance/seo.astro +15 -0
  107. package/src/tool/tvDistance/style.css +435 -0
  108. package/src/tool/tvDistance/ui.ts +66 -0
  109. package/src/tool/videoFrameExtractor/bibliography.astro +17 -0
  110. package/src/tool/videoFrameExtractor/component.astro +285 -0
  111. package/src/tool/videoFrameExtractor/i18n/en.ts +235 -0
  112. package/src/tool/videoFrameExtractor/i18n/es.ts +235 -0
  113. package/src/tool/videoFrameExtractor/i18n/fr.ts +235 -0
  114. package/src/tool/videoFrameExtractor/index.ts +53 -0
  115. package/src/tool/videoFrameExtractor/logic.ts +49 -0
  116. package/src/tool/videoFrameExtractor/seo.astro +15 -0
  117. package/src/tool/videoFrameExtractor/style.css +426 -0
  118. package/src/tool/videoFrameExtractor/ui.ts +179 -0
  119. package/src/tools.ts +25 -0
  120. package/src/types.ts +72 -0
@@ -0,0 +1,235 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { VideoFrameExtractorUI, VideoFrameExtractorLocaleContent } from '../index';
3
+
4
+ const slug = 'extractor-frames-video';
5
+ const title = 'Extractor de Frames de Vídeo - Captura fotogramas en alta resolución';
6
+ const description = 'Extrae imágenes individuales de tus vídeos con precisión de frame. Captura momentos perfectos en HD de forma local y gratuita.';
7
+
8
+ const ui: VideoFrameExtractorUI = {
9
+ uploadTitle: "Sube un archivo de vídeo",
10
+ uploadFormats: "MP4, WebM, MOV o MKV (Máx. 500MB)",
11
+ privacyNote: "El vídeo no se sube a Internet, se procesa en tu navegador.",
12
+ playLabel: "Play",
13
+ pauseLabel: "Pausa",
14
+ captureBtn: "Capturar Frame",
15
+ prevFrame: "-1F",
16
+ nextFrame: "+1F",
17
+ batchTitle: "Extracción Automática",
18
+ batchEvery: "Cada",
19
+ batchStart: "Iniciar Secuencia",
20
+ batchProcessing: "Extrayendo...",
21
+ galleryTitle: "Frames Capturados",
22
+ galleryEmpty: "Las capturas aparecerán aquí conforme las realices.",
23
+ downloadAll: "Descargar Todo",
24
+ downloadHD: "Descargar Imagen HD",
25
+ resetBtn: "Subir otro vídeo"
26
+ };
27
+
28
+ const faq: VideoFrameExtractorLocaleContent['faq'] = [
29
+ {
30
+ question: "¿Puedo extraer frames de vídeos largos?",
31
+ answer: "Sí, siempre que tu navegador tenga suficiente memoria RAM para cargar el vídeo. Recomendamos archivos de hasta 500MB para un rendimiento óptimo.",
32
+ },
33
+ {
34
+ question: "¿En qué resolución se guardan las capturas?",
35
+ answer: "La captura se realiza a la resolución nativa del vídeo original. Si tu vídeo es 4K, obtendrás una imagen 4K de alta calidad.",
36
+ },
37
+ ];
38
+
39
+ const howTo: VideoFrameExtractorLocaleContent['howTo'] = [
40
+ {
41
+ name: "Carga tu vídeo",
42
+ text: "Selecciona el archivo de vídeo desde tu dispositivo. No lo subiremos a ningún servidor.",
43
+ },
44
+ {
45
+ name: "Navega hasta el momento exacto",
46
+ text: "Usa la barra de tiempo o los botones de ±1 frame para mayor precisión quirúrgica.",
47
+ },
48
+ {
49
+ name: "Captura el fotograma",
50
+ text: "Pulsa el botón de captura para guardar el momento en la galería inferior.",
51
+ },
52
+ {
53
+ name: "Descarga en alta calidad",
54
+ text: "Descarga capturas individuales o toda la sesión en formato WebP optimizado.",
55
+ },
56
+ ];
57
+
58
+ const bibliography: VideoFrameExtractorLocaleContent['bibliography'] = [
59
+ {
60
+ name: "Captura de fotogramas con HTML5 Video API",
61
+ url: "https://developer.mozilla.org/es/docs/Web/HTML/Element/video",
62
+ },
63
+ ];
64
+
65
+ const seo: VideoFrameExtractorLocaleContent['seo'] = [
66
+ {
67
+ type: 'summary',
68
+ title: 'Extracción Profesional de Fotogramas de Vídeo',
69
+ items: [
70
+ 'Precisión de fotograma único (±1 frame) para captura perfecta',
71
+ 'Soporta MP4, WebM, MOV, MKV hasta 500MB',
72
+ 'Resolución nativa del vídeo preservada (SD, HD, 4K)',
73
+ 'Extracción por lotes automática a intervalos personalizados'
74
+ ]
75
+ },
76
+ { type: 'title', text: 'Extracción de Fotogramas: Congelando el Tiempo en Vídeo', level: 2 },
77
+ { type: 'paragraph', html: 'A veces, una imagen vale más que mil palabras. Pero encontrar esa imagen perfecta dentro de un vídeo de 10 minutos puede ser frustrante. Nuestra herramienta utiliza la potencia local de tu navegador para extraer fotogramas de precisión sin necesidad de software profesional.' },
78
+
79
+ { type: 'stats', items: [
80
+ { value: '±1', label: 'Precisión Frame Único', icon: 'mdi:target' },
81
+ { value: '100%', label: 'Resolución Nativa', icon: 'mdi:video-high-definition' },
82
+ { value: '500MB', label: 'Archivos Soportados', icon: 'mdi:file-video' }
83
+ ], columns: 3 },
84
+
85
+ { type: 'title', text: 'Casos de Uso Profesionales', level: 3 },
86
+ { type: 'comparative', items: [
87
+ {
88
+ title: 'Cine y Fotografía',
89
+ description: 'Capturar fotogramas como referencia visual o composición',
90
+ icon: 'mdi:film',
91
+ points: [
92
+ 'Extraer stills para marketing de películas',
93
+ 'Referencias de composición de escenas',
94
+ 'Análisis de fotograma a fotograma'
95
+ ]
96
+ },
97
+ {
98
+ title: 'Contenido Digital',
99
+ description: 'Crear miniaturas y portadas para redes sociales',
100
+ icon: 'mdi:youtube',
101
+ points: [
102
+ 'Miniaturas de YouTube en alta resolución',
103
+ 'Portadas para redes sociales',
104
+ 'Thumbs para presentaciones'
105
+ ],
106
+ highlight: true
107
+ },
108
+ {
109
+ title: 'Documentación Técnica',
110
+ description: 'Extraer fotogramas de tutoriales y demostraciones',
111
+ icon: 'mdi:book-open',
112
+ points: [
113
+ 'Screenshots de vídeos tutoriales',
114
+ 'Documentación visual step-by-step',
115
+ 'Análisis de movimiento en tiempo real'
116
+ ]
117
+ },
118
+ {
119
+ title: 'Deportes y Acción',
120
+ description: 'Capturar el instante preciso de máxima acción',
121
+ icon: 'mdi:dumbbell',
122
+ points: [
123
+ 'Análisis de técnica deportiva frame-by-frame',
124
+ 'Captura de momentos heroicos',
125
+ 'Estudio de movimiento'
126
+ ]
127
+ }
128
+ ], columns: 2 },
129
+
130
+ { type: 'title', text: 'Formatos de Vídeo Soportados', level: 3 },
131
+ { type: 'table', headers: ['Formato', 'Extensión', 'Compatibilidad', 'Notas'], rows: [
132
+ ['MPEG-4', 'MP4', 'Universal (100%)', 'Mejor compresión, ampliamente utilizado'],
133
+ ['WebM', 'WebM', 'Navegadores modernos', 'Compresión superior, menor tamaño'],
134
+ ['QuickTime', 'MOV', 'Safari, algunos reproductores', 'Estándar Apple'],
135
+ ['Matroska', 'MKV', 'Navegadores modernos', 'Contenedor flexible, calidad variable']
136
+ ] },
137
+
138
+ { type: 'card', title: 'Precisión de Fotograma Único', html: 'Moverse un solo frame adelante o atrás (±1 frame) es vital para capturar el instante perfecto: un salto, una sonrisa, un gesto, un instante científico. A 24 fps, cada fotograma dura apenas 41 milisegundos. Nuestra herramienta te da control milimétrico.' },
139
+
140
+ { type: 'proscons', items: [
141
+ {
142
+ pro: 'Privacidad total: el vídeo se procesa 100% localmente en tu navegador',
143
+ con: 'Limitado a tamaño de memoria RAM disponible (~500MB recomendado)'
144
+ },
145
+ {
146
+ pro: 'Resolución nativa preservada: SD, HD, 4K sin re-compresión',
147
+ con: 'Requiere navegador moderno con soporte HTML5 Video'
148
+ },
149
+ {
150
+ pro: 'Extracción por lotes automática a intervalos personalizados',
151
+ con: 'Para edición avanzada (trim, cortes) necesitas editor de vídeo'
152
+ },
153
+ {
154
+ pro: 'Exporta fotogramas en WebP optimizado o PNG sin compresión',
155
+ con: 'Un fotograma a la vez (no exporta secuencias GIF automatizadas)'
156
+ }
157
+ ], proTitle: 'Ventajas', conTitle: 'Limitaciones' },
158
+
159
+ { type: 'diagnostic', variant: 'info', title: 'Resolución y Frame Rate', icon: 'mdi:information', badge: 'Técnico', html: 'La resolución final del fotograma depende del vídeo original. Si el vídeo es 4K (3840x2160), extraerás fotogramas 4K. Si es 720p, obtendrás 720p. Sin upscaling inteligente: preservamos la información nativa del vídeo.' },
160
+
161
+ { type: 'glossary', items: [
162
+ {
163
+ term: 'Frame (Fotograma)',
164
+ definition: 'Una imagen individual en una secuencia de vídeo. Un vídeo a 24 fps contiene 24 fotogramas por segundo.'
165
+ },
166
+ {
167
+ term: 'FPS (Frames Per Second)',
168
+ definition: 'Fotogramas por segundo. 24 fps (cine), 30 fps (vídeo web), 60 fps (video fluido), 120 fps (super slow-mo).'
169
+ },
170
+ {
171
+ term: 'Codec de Vídeo',
172
+ definition: 'Algoritmo de compresión: H.264 (MPEG-4), VP9 (WebM), HEVC. Determina tamaño de archivo y calidad.'
173
+ },
174
+ {
175
+ term: 'Bitrate (Velocidad de Bits)',
176
+ definition: 'Cantidad de datos procesados por segundo (Mbps). Mayor bitrate = mayor calidad pero archivos más grandes.'
177
+ },
178
+ {
179
+ term: 'Resolución de Vídeo',
180
+ definition: 'Dimensiones en píxeles: 720p (1280x720), 1080p (1920x1080), 4K (3840x2160), 8K (7680x4320).'
181
+ }
182
+ ] },
183
+
184
+ { type: 'message', title: 'Extracción Profesional de Fotogramas', ariaLabel: 'Información técnica sobre extracción de vídeo', html: 'No necesitas convertidores online complejos o software profesional. Un fotograma perfecto está a solo 3 clics: carga vídeo, navega, captura. Privacidad total, resolución nativa, descarga instantánea.' },
185
+
186
+ { type: 'title', text: 'Congelando Momentos de Vídeo', level: 3 },
187
+ { type: 'paragraph', html: 'Cada vídeo contiene cientos de fotogramas. Muchos de ellos son oro puro esperando a ser descubiertos. Utiliza esta herramienta para extraer esos momentos perfectos sin comprometer calidad ni privacidad.' }
188
+ ];
189
+
190
+ const faqSchema: WithContext<FAQPage> = {
191
+ '@context': 'https://schema.org',
192
+ '@type': 'FAQPage',
193
+ mainEntity: faq.map((item) => ({
194
+ '@type': 'Question',
195
+ name: item.question,
196
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
197
+ })),
198
+ };
199
+
200
+ const howToSchema: WithContext<HowTo> = {
201
+ '@context': 'https://schema.org',
202
+ '@type': 'HowTo',
203
+ name: title,
204
+ description,
205
+ step: howTo.map((step) => ({
206
+ '@type': 'HowToStep',
207
+ name: step.name,
208
+ text: step.text,
209
+ })),
210
+ };
211
+
212
+ const appSchema: WithContext<SoftwareApplication> = {
213
+ '@context': 'https://schema.org',
214
+ '@type': 'SoftwareApplication',
215
+ name: title,
216
+ description,
217
+ applicationCategory: 'UtilitiesApplication',
218
+ operatingSystem: 'Web',
219
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
220
+ inLanguage: 'es',
221
+ };
222
+
223
+ export const content: VideoFrameExtractorLocaleContent = {
224
+ slug,
225
+ title,
226
+ description,
227
+ ui,
228
+ seo,
229
+ faq,
230
+ faqTitle: 'Preguntas frecuentes sobre extracción de vídeo',
231
+ bibliography,
232
+ bibliographyTitle: 'Estándares técnicos de captura de vídeo',
233
+ howTo,
234
+ schemas: [faqSchema as any, howToSchema as any, appSchema],
235
+ };
@@ -0,0 +1,235 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { VideoFrameExtractorUI, VideoFrameExtractorLocaleContent } from '../index';
3
+
4
+ const slug = 'extracteur-frames-video-en-ligne-capturer-images-hd';
5
+ const title = 'Extracteur de Frames de Vidéo - Capturez des images haute résolution';
6
+ const description = 'Extrayez des images individuelles de vos vidéos avec une précision à l\'image près. Capturez des moments parfaits en HD localement et gratuitement.';
7
+
8
+ const ui: VideoFrameExtractorUI = {
9
+ uploadTitle: "Importez un fichier vidéo",
10
+ uploadFormats: "MP4, WebM, MOV ou MKV (Max. 500 Mo)",
11
+ privacyNote: "La vidéo n'est pas téléchargée sur Internet, elle est traitée dans votre navigateur.",
12
+ playLabel: "Lecture",
13
+ pauseLabel: "Pause",
14
+ captureBtn: "Capturer l'image",
15
+ prevFrame: "-1F",
16
+ nextFrame: "+1F",
17
+ batchTitle: "Extraction automatique",
18
+ batchEvery: "Chaque",
19
+ batchStart: "Lancer la séquence",
20
+ batchProcessing: "Extraction en cours...",
21
+ galleryTitle: "Images capturées",
22
+ galleryEmpty: "Les captures apparaîtront ici au fur et à mesure.",
23
+ downloadAll: "Tout télécharger",
24
+ downloadHD: "Télécharger en HD",
25
+ resetBtn: "Importer une autre vidéo"
26
+ };
27
+
28
+ const faq: VideoFrameExtractorLocaleContent['faq'] = [
29
+ {
30
+ question: "Puis-je extraire des images de vidéos longues ?",
31
+ answer: "Oui, tant que votre navigateur dispose d'assez de RAM. Nous recommandons des fichiers jusqu'à 500 Mo pour des performances optimales.",
32
+ },
33
+ {
34
+ question: "Dans quelle résolution les captures sont-elles enregistrées ?",
35
+ answer: "La capture se fait à la résolution native de la vidéo originale. Si votre vidéo est en 4K, vous obtiendrez une image 4K de haute qualité.",
36
+ },
37
+ ];
38
+
39
+ const howTo: VideoFrameExtractorLocaleContent['howTo'] = [
40
+ {
41
+ name: "Importez votre vidéo",
42
+ text: "Sélectionnez le fichier vidéo depuis votre appareil. Il n'est pas envoyé sur nos serveurs.",
43
+ },
44
+ {
45
+ name: "Naviguez jusqu'au moment exact",
46
+ text: "Utilisez la barre de navigation ou les boutons ±1 image pour une précision chirurgicale.",
47
+ },
48
+ {
49
+ name: "Capturez l'image",
50
+ text: "Appuyez sur le bouton de capture pour enregistrer l'instant dans la galerie ci-dessous.",
51
+ },
52
+ {
53
+ name: "Téléchargez en haute qualité",
54
+ text: "Téléchargez vos captures individuelles ou toute la session au format WebP optimisé.",
55
+ },
56
+ ];
57
+
58
+ const bibliography: VideoFrameExtractorLocaleContent['bibliography'] = [
59
+ {
60
+ name: "Capture d'images avec l'API HTML5 Video",
61
+ url: "https://developer.mozilla.org/fr/docs/Web/HTML/Element/video",
62
+ },
63
+ ];
64
+
65
+ const seo: VideoFrameExtractorLocaleContent['seo'] = [
66
+ {
67
+ type: 'summary',
68
+ title: 'Extraction Professionnelle d\'Images de Vidéo',
69
+ items: [
70
+ 'Précision à l\'image près (±1 image) pour une capture parfaite',
71
+ 'Supporte MP4, WebM, MOV, MKV jusqu\'à 500 Mo',
72
+ 'Résolution native de la vidéo préservée (SD, HD, 4K)',
73
+ 'Extraction par lots automatique à intervalles personnalisés'
74
+ ]
75
+ },
76
+ { type: 'title', text: 'Extraction d\'Images : Figer le Temps en Vidéo', level: 2 },
77
+ { type: 'paragraph', html: 'Une image vaut parfois mille mots. Mais trouver l\'image parfaite peut être frustrant. Notre outil utilise la puissance locale de votre navigateur pour extraire des images de précision sans logiciel professionnel.' },
78
+
79
+ { type: 'stats', items: [
80
+ { value: '±1', label: 'Précision à l\'image', icon: 'mdi:target' },
81
+ { value: '100 %', label: 'Résolution Native', icon: 'mdi:video-high-definition' },
82
+ { value: '500 Mo', label: 'Fichiers Supportés', icon: 'mdi:file-video' }
83
+ ], columns: 3 },
84
+
85
+ { type: 'title', text: 'Cas d\'Utilisation Professionnels', level: 3 },
86
+ { type: 'comparative', items: [
87
+ {
88
+ title: 'Cinéma et Photographie',
89
+ description: 'Capturer des images pour référence visuelle ou composition',
90
+ icon: 'mdi:film',
91
+ points: [
92
+ 'Extraire des stills pour le marketing de films',
93
+ 'Références de composition de scènes',
94
+ 'Analyse image par image'
95
+ ]
96
+ },
97
+ {
98
+ title: 'Contenu Numérique',
99
+ description: 'Créer des miniatures et des couvertures pour les réseaux sociaux',
100
+ icon: 'mdi:youtube',
101
+ points: [
102
+ 'Miniatures YouTube haute résolution',
103
+ 'Couvertures pour réseaux sociaux',
104
+ 'Vignettes pour présentations'
105
+ ],
106
+ highlight: true
107
+ },
108
+ {
109
+ title: 'Documentation Technique',
110
+ description: 'Extraire des images de tutoriels et démonstrations',
111
+ icon: 'mdi:book-open',
112
+ points: [
113
+ 'Captures d\'écran de vidéos tutoriels',
114
+ 'Documentation visuelle étape par étape',
115
+ 'Analyse de mouvement en temps réel'
116
+ ]
117
+ },
118
+ {
119
+ title: 'Sports et Action',
120
+ description: 'Capturer l\'instant précis de l\'action maximale',
121
+ icon: 'mdi:dumbbell',
122
+ points: [
123
+ 'Analyse technique sportive image par image',
124
+ 'Capture de moments héroïques',
125
+ 'Étude du mouvement'
126
+ ]
127
+ }
128
+ ], columns: 2 },
129
+
130
+ { type: 'title', text: 'Formats Vidéo Supportés', level: 3 },
131
+ { type: 'table', headers: ['Format', 'Extension', 'Compatibilité', 'Notes'], rows: [
132
+ ['MPEG-4', 'MP4', 'Universel (100 %)', 'Meilleure compression, très utilisé'],
133
+ ['WebM', 'WebM', 'Navigateurs modernes', 'Compression supérieure, taille réduite'],
134
+ ['QuickTime', 'MOV', 'Safari, certains lecteurs', 'Standard Apple'],
135
+ ['Matroska', 'MKV', 'Navigateurs modernes', 'Conteneur flexible, qualité variable']
136
+ ] },
137
+
138
+ { type: 'card', title: 'Précision à l\'Image Près', html: 'Se déplacer d\'une seule image en avant ou en arrière (±1 image) est vital pour capturer l\'instant parfait : un saut, un sourire, un geste. Notre outil vous donne un contrôle millimétrique.' },
139
+
140
+ { type: 'proscons', items: [
141
+ {
142
+ pro: 'Confidentialité totale : la vidéo est traitée à 100 % localement',
143
+ con: 'Limité par la mémoire RAM disponible (~500 Mo recommandés)'
144
+ },
145
+ {
146
+ pro: 'Résolution native préservée : SD, HD, 4K sans re-compression',
147
+ con: 'Nécessite un navigateur supportant HTML5 Video'
148
+ },
149
+ {
150
+ pro: 'Extraction par lots automatique à intervalles personnalisés',
151
+ con: 'Pour un montage avancé (coupes, trim), un éditeur est requis'
152
+ },
153
+ {
154
+ pro: 'Exportation en WebP optimisé ou PNG sans compression',
155
+ con: 'Une image à la fois (pas d\'exportation GIF animée)'
156
+ }
157
+ ], proTitle: 'Avantages', conTitle: 'Limitations' },
158
+
159
+ { type: 'diagnostic', variant: 'info', title: 'Résolution et Fréquence d\'Images', icon: 'mdi:information', badge: 'Technique', html: 'La résolution finale dépend de la vidéo d\'origine. Si la vidéo est en 4K (3840x2160), vous obtiendrez des images 4K. Nous préservons l\'information native sans upscaling.' },
160
+
161
+ { type: 'glossary', items: [
162
+ {
163
+ term: 'Frame (Image)',
164
+ definition: 'Une image individuelle dans une séquence vidéo. Une vidéo à 24 fps contient 24 images par seconde.'
165
+ },
166
+ {
167
+ term: 'FPS (Images par seconde)',
168
+ definition: 'Vitesse de défilement : 24 fps (cinéma), 30 fps (vidéo web), 60 fps (fluide).'
169
+ },
170
+ {
171
+ term: 'Codec Vidéo',
172
+ definition: 'Algorithme de compression : H.264 (MP4), VP9 (WebM). Détermine la taille et la qualité.'
173
+ },
174
+ {
175
+ term: 'Bitrate',
176
+ definition: 'Quantité de données par seconde (Mbps). Un bitrate élevé = meilleure qualité.'
177
+ },
178
+ {
179
+ term: 'Résolution Vidéo',
180
+ definition: 'Dimensions en pixels : 720p (1280x720), 1080p (1920x1080), 4K (3840x2160).'
181
+ }
182
+ ] },
183
+
184
+ { type: 'message', title: 'Extraction Professionnelle d\'Images', ariaLabel: 'Informations techniques', html: 'L\'image parfaite est à portée de clic : importez, naviguez, capturez. Confidentialité totale, résolution native, téléchargement instantané.' },
185
+
186
+ { type: 'title', text: 'Figer des Moments Vidéo', level: 3 },
187
+ { type: 'paragraph', html: 'Chaque vidéo contient des centaines d\'images. Beaucoup sont des pépites. Utilisez cet outil pour extraire ces moments parfaits.' }
188
+ ];
189
+
190
+ const faqSchema: WithContext<FAQPage> = {
191
+ '@context': 'https://schema.org',
192
+ '@type': 'FAQPage',
193
+ mainEntity: faq.map((item) => ({
194
+ '@type': 'Question',
195
+ name: item.question,
196
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
197
+ })),
198
+ };
199
+
200
+ const howToSchema: WithContext<HowTo> = {
201
+ '@context': 'https://schema.org',
202
+ '@type': 'HowTo',
203
+ name: title,
204
+ description,
205
+ step: howTo.map((step) => ({
206
+ '@type': 'HowToStep',
207
+ name: step.name,
208
+ text: step.text,
209
+ })),
210
+ };
211
+
212
+ const appSchema: WithContext<SoftwareApplication> = {
213
+ '@context': 'https://schema.org',
214
+ '@type': 'SoftwareApplication',
215
+ name: title,
216
+ description,
217
+ applicationCategory: 'UtilitiesApplication',
218
+ operatingSystem: 'Web',
219
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
220
+ inLanguage: 'fr',
221
+ };
222
+
223
+ export const content: VideoFrameExtractorLocaleContent = {
224
+ slug,
225
+ title,
226
+ description,
227
+ ui,
228
+ seo,
229
+ faq,
230
+ faqTitle: 'Foire Aux Questions sur l\'Extraction de Fotogrammes Vidéo',
231
+ bibliography,
232
+ bibliographyTitle: 'Normes Techniques pour la Capture Vidéo',
233
+ howTo,
234
+ schemas: [faqSchema as any, howToSchema as any, appSchema],
235
+ };
@@ -0,0 +1,53 @@
1
+ import type { AudiovisualToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
2
+ import VideoFrameExtractor from './component.astro';
3
+ import VideoFrameExtractorSEO from './seo.astro';
4
+ import VideoFrameExtractorBibliography from './bibliography.astro';
5
+
6
+ export interface VideoFrameExtractorUI {
7
+ uploadTitle: string;
8
+ uploadFormats: string;
9
+ privacyNote: string;
10
+ playLabel: string;
11
+ pauseLabel: string;
12
+ captureBtn: string;
13
+ prevFrame: string;
14
+ nextFrame: string;
15
+ batchTitle: string;
16
+ batchEvery: string;
17
+ batchStart: string;
18
+ batchProcessing: string;
19
+ galleryTitle: string;
20
+ galleryEmpty: string;
21
+ downloadAll: string;
22
+ downloadHD: string;
23
+ resetBtn: string;
24
+ [key: string]: string;
25
+ }
26
+
27
+ export type VideoFrameExtractorLocaleContent = ToolLocaleContent<VideoFrameExtractorUI>;
28
+
29
+ import { content as es } from './i18n/es';
30
+ import { content as en } from './i18n/en';
31
+ import { content as fr } from './i18n/fr';
32
+
33
+ export const videoFrameExtractor: AudiovisualToolEntry<VideoFrameExtractorUI> = {
34
+ id: 'extractor-frames-video',
35
+ icons: {
36
+ bg: 'mdi:video-vintage',
37
+ fg: 'mdi:camera-plus',
38
+ },
39
+ i18n: {
40
+ es: async () => es as unknown as VideoFrameExtractorLocaleContent,
41
+ en: async () => en as unknown as VideoFrameExtractorLocaleContent,
42
+ fr: async () => fr as unknown as VideoFrameExtractorLocaleContent,
43
+ },
44
+ };
45
+
46
+ export { VideoFrameExtractor, VideoFrameExtractorSEO, VideoFrameExtractorBibliography };
47
+
48
+ export const VIDEO_FRAME_EXTRACTOR_TOOL: ToolDefinition = {
49
+ entry: videoFrameExtractor as unknown as AudiovisualToolEntry,
50
+ Component: VideoFrameExtractor,
51
+ SEOComponent: VideoFrameExtractorSEO,
52
+ BibliographyComponent: VideoFrameExtractorBibliography,
53
+ };
@@ -0,0 +1,49 @@
1
+ export interface CapturedFrame {
2
+ id: string;
3
+ url: string;
4
+ timestamp: number;
5
+ }
6
+
7
+ export function formatTime(seconds: number): string {
8
+ const date = new Date(seconds * 1000);
9
+ const mm = date.getUTCMinutes().toString().padStart(2, "0");
10
+ const ss = date.getUTCSeconds().toString().padStart(2, "0");
11
+ const ms = Math.floor(date.getUTCMilliseconds()).toString().padStart(3, "0");
12
+ return `${mm}:${ss}.${ms}`;
13
+ }
14
+
15
+ export function generateId(): string {
16
+ return Math.random().toString(36).substring(2, 9);
17
+ }
18
+
19
+ export function captureFrameFromVideo(video: HTMLVideoElement): CapturedFrame | null {
20
+ if (!video.videoWidth || !video.videoHeight) return null;
21
+
22
+ const canvas = document.createElement("canvas");
23
+ canvas.width = video.videoWidth;
24
+ canvas.height = video.videoHeight;
25
+ const ctx = canvas.getContext("2d");
26
+
27
+ if (!ctx) return null;
28
+
29
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
30
+ const url = canvas.toDataURL("image/webp", 1.0);
31
+
32
+ return {
33
+ id: generateId(),
34
+ url,
35
+ timestamp: video.currentTime
36
+ };
37
+ }
38
+
39
+ export async function captureFrameAtTime(video: HTMLVideoElement, time: number): Promise<CapturedFrame | null> {
40
+ return new Promise((resolve) => {
41
+ const onSeeked = () => {
42
+ const frame = captureFrameFromVideo(video);
43
+ video.removeEventListener("seeked", onSeeked);
44
+ resolve(frame);
45
+ };
46
+ video.addEventListener("seeked", onSeeked);
47
+ video.currentTime = time;
48
+ });
49
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { videoFrameExtractor } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'es' } = Astro.props;
11
+ const content = await videoFrameExtractor.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ <SEORenderer content={{ locale, sections: content.seo || [] }} />