[{"data":1,"prerenderedAt":371},["ShallowReactive",2],{"\u002Fprojects\u002Foffline-converter":3,"posts":269},{"id":4,"title":5,"body":6,"description":193,"extension":194,"featured":195,"images":196,"links":221,"meta":231,"navigation":195,"order":232,"path":233,"seo":234,"stack":235,"stats":252,"stem":265,"tag":266,"url":267,"__hash__":268},"projects\u002Fprojects\u002Foffline-converter.md","ABR Offline Converter",{"type":7,"value":8,"toc":180},"minimark",[9,14,82,86,91,99,103,110,114,117,121,132,136],[10,11,13],"h2",{"id":12},"qué-hace","Qué hace",[15,16,17,25,31,37,43,49,55,61,76],"ul",{},[18,19,20,24],"li",{},[21,22,23],"strong",{},"Imágenes."," PNG, JPG, WebP, GIF, BMP, ICO y TIFF entre sí, y AVIF y fotos HEIC del iPhone como entrada. Calidad, ancho y alto con proporción bloqueada, y ajustes distintos para cada archivo si hace falta.",[18,26,27,30],{},[21,28,29],{},"Vídeo y audio."," MP4, WebM, AVI, MKV, MOV y OGV entre sí, vídeo a GIF animado, y sacar el audio de un vídeo a MP3, WAV, OGG, FLAC, AAC, M4A u Opus. Con recorte por inicio y fin.",[18,32,33,36],{},[21,34,35],{},"PDF."," Cada página a PNG, JPG o WebP, o varias imágenes a un PDF en A4, Carta o al tamaño de la imagen.",[18,38,39,42],{},[21,40,41],{},"Fuentes, subtítulos, texto y datos."," TTF, OTF, WOFF y WOFF2; SRT, VTT y ASS; Markdown, HTML, TXT y EPUB; CSV y JSON.",[18,44,45,48],{},[21,46,47],{},"Comparar antes y después"," con un deslizador sobre la imagen convertida.",[18,50,51,54],{},[21,52,53],{},"Por lotes."," Sueltas o pegas (Ctrl+V) varios archivos y los descargas todos en un ZIP.",[18,56,57,60],{},[21,58,59],{},"Taller del libro."," Sueltas los capítulos de un libro en Markdown y sale un EPUB con portada, índice, dedicatoria, epígrafe, créditos, numeración automática y tipografía corregida.",[18,62,63,66,67,71,72,75],{},[21,64,65],{},"Una página para cada conversión",", de ",[68,69,70],"code",{},"\u002Fconvertir\u002Fjpg-a-png"," a ",[68,73,74],{},"\u002Fconvertir\u002Fcsv-a-json",", en español e inglés.",[18,77,78,81],{},[21,79,80],{},"Instalable y sin conexión",", con tema claro u oscuro.",[10,83,85],{"id":84},"cómo-está-hecho","Cómo está hecho",[87,88,90],"h3",{"id":89},"todo-en-el-navegador","Todo en el navegador",[92,93,94,95,98],"p",{},"Nuxt 4 genera un sitio estático, y todo el trabajo lo hacen el navegador y WebAssembly. Ningún archivo sale del dispositivo. El motor de vídeo, ",[68,96,97],{},"ffmpeg.wasm",", se descarga la primera vez que se usa y queda guardado en la caché de la PWA.",[87,100,102],{"id":101},"un-worker-por-tipo-de-trabajo","Un worker por tipo de trabajo",[92,104,105,106,109],{},"Las imágenes se convierten en un Web Worker con ",[68,107,108],{},"OffscreenCanvas",", y el vídeo y el audio en otro con ffmpeg. La interfaz no se congela aunque conviertas un vídeo largo.",[87,111,113],{"id":112},"codificadores-a-mano","Codificadores a mano",[92,115,116],{},"El canvas del navegador sabe exportar PNG, JPG y WebP, pero no BMP, ICO ni TIFF. Esos tres los escribí yo, byte a byte, con sus tests.",[87,118,120],{"id":119},"epub3-sin-pandoc","EPUB3 sin pandoc",[92,122,123,124,127,128,131],{},"El Taller del libro genera el XHTML de cada capítulo, el índice navegable (",[68,125,126],{},"nav"," y NCX), los metadatos y una portada dibujada en canvas, y lo empaqueta con ",[68,129,130],{},"client-zip",".",[10,133,135],{"id":134},"problemas-que-resolví","Problemas que resolví",[15,137,138,148,158,164,170],{},[18,139,140,143,144,147],{},[21,141,142],{},"HEIC no funcionaba dentro del worker."," La librería que decodifica HEIC necesita ",[68,145,146],{},"document",", que no existe en un worker. La decodificación pasó al hilo principal y el resto sigue en el worker.",[18,149,150,153,154,157],{},[21,151,152],{},"Las páginas por conversión no se generaban."," Un ",[68,155,156],{},"await setLocale"," al principio de los componentes rompía el prerender de Nitro: fallaban unas 400 rutas que en desarrollo iban bien. Con un cambio de idioma síncrono se generan las 811.",[18,159,160,163],{},[21,161,162],{},"CSV del mundo real."," Saltos de línea al final, archivos de una sola columna y JSON con claves distintas en cada fila. Ahora se aprovechan todas las columnas y solo paran la conversión los errores de verdad.",[18,165,166,169],{},[21,167,168],{},"ffmpeg acumulaba listeners."," Cada conversión añadía otro listener de progreso. Ahora se guarda la referencia y se quita al acabar.",[18,171,172,175,176,179],{},[21,173,174],{},"Un EPUB que valga para cualquier lector."," ",[68,177,178],{},"mimetype"," primero y sin comprimir, NCX para lectores antiguos, portada marcada como tal y comillas latinas anidadas sin tocar el código y respetando los apóstrofos.",{"title":181,"searchDepth":182,"depth":182,"links":183},"",2,[184,185,192],{"id":12,"depth":182,"text":13},{"id":84,"depth":182,"text":85,"children":186},[187,189,190,191],{"id":89,"depth":188,"text":90},3,{"id":101,"depth":188,"text":102},{"id":112,"depth":188,"text":113},{"id":119,"depth":188,"text":120},{"id":134,"depth":182,"text":135},"Conversor de archivos que funciona entero en el navegador. Imágenes, vídeo, audio, PDF, fuentes, subtítulos, texto y datos, con 192 páginas de conversión, y un Taller del libro que monta un EPUB completo a partir de capítulos en Markdown. Sin servidores, instalable y usable sin conexión.","md",true,[197,202,207,211,216],{"src":198,"alt":199,"width":200,"height":201},"\u002Fimages\u002Fposts\u002Foffline-converter\u002Fportada.webp","Cuatro imágenes PNG convertidas a WebP en la cola, con el formato, la calidad y el tamaño de salida",1425,900,{"src":203,"alt":204,"width":205,"height":206},"\u002Fimages\u002Fposts\u002Foffline-converter\u002Fcomparar.webp","Comparación antes y después de una imagen convertida, con un deslizador en el centro",785,820,{"src":208,"alt":209,"width":201,"height":210},"\u002Fimages\u002Fposts\u002Foffline-converter\u002Fvideo-gif.webp","Un vídeo WebM a punto de convertirse en GIF animado, con el panel de recorte abierto",985,{"src":212,"alt":213,"width":214,"height":215},"\u002Fimages\u002Fposts\u002Foffline-converter\u002Ftaller.webp","El Taller del libro con cuatro capítulos, la portada tipográfica, las páginas extra, el estilo y la vista previa de un capítulo",1140,1710,{"src":217,"alt":218,"width":219,"height":220},"\u002Fimages\u002Fposts\u002Foffline-converter\u002Flistado.webp","El listado de todas las conversiones, agrupadas por imágenes, vídeo, audio, PDF, fuentes, subtítulos, texto y datos",925,2134,[222,225,228],{"label":223,"url":224},"Cómo se usa","\u002Fposts\u002Foffline-converter-convierte-sin-subir-nada",{"label":226,"url":227},"Por dentro","\u002Fposts\u002Foffline-converter-por-dentro",{"label":229,"url":230},"Taller del libro","https:\u002F\u002Fabdell1999.github.io\u002Foffline-converter-public\u002Flibro",{},8,"\u002Fprojects\u002Foffline-converter",{"title":5,"description":193},[236,237,238,239,240,241,108,97,242,243,244,245,246,247,248,249,130,250,251],"Nuxt 4","Vue 3","TypeScript","Tailwind CSS","PWA","Web Workers","gifenc","heic2any","pdf.js","pdf-lib","fonteditor-core","Papa Parse","marked","Turndown","EPUB3","Vitest",[253,256,259,262],{"value":254,"label":255},"0","servidores",{"value":257,"label":258},"192","páginas de conversión",{"value":260,"label":261},"8","tipos de archivo",{"value":263,"label":264},"305","tests (Vitest)","projects\u002Foffline-converter","offline-converter","https:\u002F\u002Fabdell1999.github.io\u002Foffline-converter-public\u002F","g_U-DHXm9_Vn0P9f1BpwkUwZRqNJG92_2YYg85dmfWw",[270,281,290,299,304,313,321,326,333,338,346,352,358,364],{"title":271,"description":272,"date":273,"path":274,"tags":275,"type":280},"Simple TTS: escucha cualquier artículo de la web en voz alta, sin salir de tu navegador","Una extensión gratuita para Chrome y Edge que lee en voz alta lo que selecciones o el artículo entero con las voces del propio navegador. Sin cuentas, sin servidores y sin APIs externas. Qué hace y cómo se usa.","2026-10-23","\u002Fposts\u002Fsimple-tts-escucha-cualquier-web",[276,277,278,279],"simple-tts","privacidad","productividad","proyectos","guia",{"title":282,"description":283,"date":273,"path":284,"tags":285,"type":289},"Simple TTS por dentro: mi primera extensión, de JavaScript a WXT con Vue","Cómo pasó Simple TTS de tres archivos de JavaScript a una extensión Manifest V3 con WXT, Vue 3 y TypeScript. Qué me hizo cambiar en cada etapa, cómo se mantiene vivo un service worker durante una lectura larga, por qué leía la frase del día anterior y cómo se consigue que el resaltado vaya a la par de la voz.","\u002Fposts\u002Fsimple-tts-por-dentro",[276,286,287,288],"arquitectura","typescript","vue","tecnico",{"title":291,"description":292,"date":293,"path":294,"tags":295,"type":289},"TranscribeLocal por dentro: Whisper en el navegador y cómo dejó de inventarse frases","Cómo ejecuta TranscribeLocal Whisper, la separación de hablantes y un modelo de lenguaje dentro del navegador con Transformers.js, por qué Whisper se inventaba frases en los silencios, qué hace falta para transcribir audios largos y por qué un service worker cortaba las descargas a los cinco minutos.","2026-10-22","\u002Fposts\u002Ftranscribe-local-por-dentro",[296,297,286,287,298],"transcribe-local","ia","nuxt",{"title":300,"description":301,"date":293,"path":302,"tags":303,"type":280},"TranscribeLocal: transcribe y saca subtítulos de cualquier vídeo, sin salir de tu navegador","Una app web que convierte audio y vídeo en texto y subtítulos con Whisper, distingue quién habla y hace resúmenes, todo en tu ordenador. Sin subir nada a ningún servidor, sin límites de duración y gratis. Qué hace y cómo se usa.","\u002Fposts\u002Ftranscribe-local-subtitulos-en-tu-navegador",[296,297,277,279],{"title":305,"description":306,"date":307,"path":308,"tags":309,"type":280},"PokeStore: un tema de WordPress a medida para tiendas de cartas coleccionables","Un tema de WordPress y WooCommerce hecho desde cero para tiendas de Pokémon y One Piece TCG. Un recorrido por la tienda como la ve quien compra, de la portada a la lista de deseos, y por lo que puede ajustar quien la lleva.","2026-10-21","\u002Fposts\u002Fpokestore-tema-wordpress-para-tiendas-tcg",[310,311,312,279],"pokestore","wordpress","woocommerce",{"title":314,"description":315,"date":316,"path":317,"tags":318,"type":280},"YouTube Like Shortcut: da like con una tecla, como en TikTok","Una extensión de Chrome que da like al vídeo o Short de YouTube que estás viendo al pulsar la L. Cómo se instala, qué opciones tiene y por qué no se dispara mientras escribes.","2026-10-20","\u002Fposts\u002Fyoutube-like-shortcut-like-con-una-tecla",[319,320,278,279],"youtube-like-shortcut","youtube",{"title":322,"description":323,"date":316,"path":324,"tags":325,"type":289},"YouTube Like Shortcut por dentro: pulsar un botón en una web que cambia sin avisar","Cómo una extensión de una sola función llega antes que el reproductor de YouTube, encuentra el botón de like entre variantes ocultas y layouts que cambian con el ancho de la ventana, y se asegura de que el like ha entrado aunque YouTube esté reconstruyendo la página.","\u002Fposts\u002Fyoutube-like-shortcut-por-dentro",[319,320,286,287],{"title":327,"description":328,"date":329,"path":330,"tags":331,"type":280},"AI Upscaler: amplía imágenes y quita fondos con IA, sin salir de tu navegador","Una extensión de Chrome que amplía imágenes ×2 o ×4 y elimina fondos con inteligencia artificial, todo en tu equipo. Sin subir nada a ningún servidor, sin cuentas y gratis. Qué hace y cómo se usa.","2026-10-19","\u002Fposts\u002Fai-upscaler-imagenes-con-ia-en-tu-navegador",[332,297,277,279],"ai-upscaler",{"title":334,"description":335,"date":329,"path":336,"tags":337,"type":289},"AI Upscaler por dentro: dos redes neuronales en una extensión de Chrome","Cómo ejecuta AI Upscaler Real-ESRGAN y U²-Net dentro del navegador con ONNX Runtime Web, por qué la imagen se procesa en mosaicos con margen, qué hubo que parchear en el modelo de quitar fondos y qué obstáculos pone una extensión Manifest V3 a los workers y al WebAssembly.","\u002Fposts\u002Fai-upscaler-por-dentro",[332,297,286,287,288],{"title":339,"description":340,"date":341,"path":342,"tags":343,"type":280},"ABR Cipher: cifrado seguro, completamente en tu navegador","Una suite de cifrado que no envía nada a ningún servidor. Cifrar textos y archivos, compartir secretos con un enlace que caduca, guardar contraseñas y destruir archivos. Qué hace y cómo se usa.","2026-10-18","\u002Fposts\u002Fabr-cipher-cifrado-en-tu-navegador",[344,345,277,279],"cipher","seguridad",{"title":347,"description":348,"date":341,"path":349,"tags":350,"type":289},"ABR Cipher por dentro: criptografía sin servidor, de la URL al disco","Por qué ABR Cipher solo usa la Web Crypto API, cómo viaja un secreto en el fragmento de una URL, cómo se cifra un archivo de varios GB en el navegador y qué hace falta para destruir un archivo de verdad, con un compañero en Rust que lee NTFS.","\u002Fposts\u002Fabr-cipher-por-dentro",[344,345,286,287,351],"rust",{"title":353,"description":354,"date":341,"path":355,"tags":356,"type":289},"ABReader por dentro: el lector que no encontré, construido a mi medida","Voces neuronales que se ejecutan en el navegador, sincronización entre dispositivos sin servidor y once formatos detrás de un mismo contrato. Las decisiones técnicas de ABReader y por qué las tomé.","\u002Fposts\u002Fabreader-por-dentro",[357,286,287,298],"abreader",{"title":359,"description":360,"date":341,"path":361,"tags":362,"type":280},"ABReader: tu biblioteca de libros electrónicos en el navegador","Un lector de libros electrónicos que funciona en el navegador, sin cuentas ni servidores. Qué hace, cómo se usa y cómo probarlo en un minuto.","\u002Fposts\u002Fabreader-tu-biblioteca-en-el-navegador",[357,363,279],"lectura",{"title":365,"description":366,"date":341,"path":367,"tags":368,"type":289},"Cómo construí este blog: estático, sin servidor y sin coste de hosting","Por qué elegí Nuxt y Markdown en lugar de WordPress, cómo separo el código privado del sitio publicado y qué se rompió en el primer despliegue.","\u002Fposts\u002Farquitectura-de-este-blog",[298,288,369,370],"devops","meta",1792718564255]