<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mario</title>
    <description>The latest articles on Forem by Mario (@whario).</description>
    <link>https://forem.com/whario</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F716015%2F20fa97d7-b598-4f22-a990-1947f955f9ba.jpeg</url>
      <title>Forem: Mario</title>
      <link>https://forem.com/whario</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/whario"/>
    <language>en</language>
    <item>
      <title>Mi flujo ultra rápido para limpiar audios (WhatsApp, reuniones, vídeos) solo con el navegador</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Thu, 05 Feb 2026 12:14:49 +0000</pubDate>
      <link>https://forem.com/whario/mi-flujo-ultra-rapido-para-limpiar-audios-whatsapp-reuniones-videos-solo-con-el-navegador-58jf</link>
      <guid>https://forem.com/whario/mi-flujo-ultra-rapido-para-limpiar-audios-whatsapp-reuniones-videos-solo-con-el-navegador-58jf</guid>
      <description>&lt;p&gt;Grabamos audio &lt;strong&gt;en todas partes&lt;/strong&gt;: WhatsApp, reuniones de trabajo, clases online, Loom, vídeos de YouTube, notas de voz del móvil…&lt;/p&gt;

&lt;p&gt;El problema viene después:&lt;br&gt;&lt;br&gt;
formatos raros (&lt;code&gt;.opus&lt;/code&gt;, &lt;code&gt;.amr&lt;/code&gt;, &lt;code&gt;.m4a&lt;/code&gt;), fragmentos sobrantes, archivos gigantes que no puedes enviar por correo o subir a tu web.&lt;/p&gt;

&lt;p&gt;En este post te cuento &lt;strong&gt;el flujo real&lt;/strong&gt; que uso ahora para “limpiar” audios rápidos sin abrir un DAW ni instalar nada extra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;convertir a MP3 cualquier cosa que me llegue
&lt;/li&gt;
&lt;li&gt;recortar lo que no sirve
&lt;/li&gt;
&lt;li&gt;mezclar trozos sueltos
&lt;/li&gt;
&lt;li&gt;comprimir para que pese poco
&lt;/li&gt;
&lt;li&gt;(opcional) sacar texto de las notas de voz&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo lo hago desde una sola web:&lt;br&gt;&lt;br&gt;
&lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;Convertir Audio a MP3&lt;/a&gt; – un pequeño “swiss army knife” de edición de audio en el navegador.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;No es un post patrocinado 😅, simplemente es la herramienta que me monté para resolver justo estos problemas y ahora la uso a diario.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. El kit mínimo para sobrevivir al caos de audios
&lt;/h2&gt;

&lt;p&gt;Si trabajas con audio &lt;strong&gt;sin ser técnico de sonido&lt;/strong&gt;, normalmente no necesitas 200 pistas ni 80 plugins.&lt;br&gt;&lt;br&gt;
Casi siempre basta con estas 4 cosas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Convertir formato&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;De &lt;code&gt;.opus&lt;/code&gt;, &lt;code&gt;.ogg&lt;/code&gt;, &lt;code&gt;.amr&lt;/code&gt;, &lt;code&gt;.m4a&lt;/code&gt;, etc. a un MP3 normalito.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recortar&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Quitar silencios del principio, errores o partes que no quieres enviar.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprimir&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Pasar de un archivo de 20–30 MB a algo razonable para compartir.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mezclar&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Unir varias notas de voz en un único MP3 (por ejemplo preguntas de alumnos).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Con eso ya puedes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preparar intros/outros de podcast simples,&lt;/li&gt;
&lt;li&gt;mandar audios más profesionales a clientes,&lt;/li&gt;
&lt;li&gt;dejar grabaciones de reuniones listas para subir a Notion, Confluence o donde sea.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. De nota de voz de WhatsApp a MP3 limpio (flujo completo)
&lt;/h2&gt;

&lt;p&gt;Vamos a verlo con un ejemplo realista: alguien te manda una &lt;strong&gt;nota de voz en WhatsApp&lt;/strong&gt; con feedback sobre un proyecto, y quieres:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;guardarla como MP3 “normal”,&lt;/li&gt;
&lt;li&gt;recortar un par de “espera, ¿se escucha?”,&lt;/li&gt;
&lt;li&gt;comprimirla para subirla a tu documentación interna.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Paso 1: convertir cualquier cosa a MP3
&lt;/h3&gt;

&lt;p&gt;Entro en la home de&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;Convertir Audio a MP3&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;y arrastro el archivo que he descargado de WhatsApp.&lt;/p&gt;

&lt;p&gt;La gracia es que no tengo que preocuparme del formato:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.opus&lt;/code&gt; de WhatsApp
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.m4a&lt;/code&gt; del iPhone
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.ogg&lt;/code&gt; de Telegram
&lt;/li&gt;
&lt;li&gt;audio extraído de un vídeo…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El backend se encarga con FFmpeg y lo deja en MP3 con el bitrate que elija (128k casi siempre).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Para devs: internamente es una API en FastAPI que llama a FFmpeg con algo muy parecido a:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; input.opus &lt;span class="nt"&gt;-vn&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;:a libmp3lame &lt;span class="nt"&gt;-b&lt;/span&gt;:a 128k output.mp3
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Pero si no quieres tocar consola, subes el archivo y listo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Paso 2: recortar el ruido de “antes” y “después”
&lt;/h3&gt;

&lt;p&gt;Una vez tengo el MP3, paso al &lt;strong&gt;recortador&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subo el MP3 convertido.
&lt;/li&gt;
&lt;li&gt;Marco visualmente el inicio y el final que me interesan.
&lt;/li&gt;
&lt;li&gt;Descargo solo ese fragmento.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Esto me ahorra muchísimo tiempo comparado con abrir un editor grande solo para cortar 5 segundos al principio y 10 al final.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 3: comprimir sin matar la calidad
&lt;/h3&gt;

&lt;p&gt;Si el archivo pesa demasiado (por ejemplo, grabaciones largas de reuniones), uso el &lt;strong&gt;compresor&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bajo el bitrate a 96k o 64k,&lt;/li&gt;
&lt;li&gt;mantengo el sample rate típico (44.1k),&lt;/li&gt;
&lt;li&gt;escucho un preview rápido y descargo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para voz suele ser más que suficiente y el tamaño cae bastante.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Cuando tienes 10 notas de voz: mezclarlas y olvidarte
&lt;/h2&gt;

&lt;p&gt;Otro caso habitual: varias notas de voz cortas que quieres &lt;strong&gt;unir en un solo archivo&lt;/strong&gt; para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;escuchar de una tirada,&lt;/li&gt;
&lt;li&gt;enviar a alguien,&lt;/li&gt;
&lt;li&gt;guardarlas como “preguntas de usuarios” o feedback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En el mezclador:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subo todos los MP3/OGG/OPUS que quiero juntar.
&lt;/li&gt;
&lt;li&gt;Los ordeno (arrastrar y soltar).
&lt;/li&gt;
&lt;li&gt;Exporto un único MP3 con todas las piezas.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No hay crossfade ni efectos locos: es mezcla &lt;strong&gt;simple y rápida&lt;/strong&gt;, que es lo que suele hacer falta para uso “de oficina”.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Extra: transcribir audios a texto para documentación
&lt;/h2&gt;

&lt;p&gt;Último paso opcional de mi flujo: sacar el texto.&lt;/p&gt;

&lt;p&gt;Cuando el audio es una reunión o feedback largo, me viene genial tener también &lt;strong&gt;texto para buscar&lt;/strong&gt; (Ctrl+F en Notion, Confluence, Google Docs…).&lt;/p&gt;

&lt;p&gt;Uso la herramienta de transcripción:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subo el MP3 recortado y comprimido.
&lt;/li&gt;
&lt;li&gt;Elijo modelo (tiny/base suele ir bien para español).
&lt;/li&gt;
&lt;li&gt;Espero unos segundos y copio el texto.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Luego pego el resultado en:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;issues de GitHub,
&lt;/li&gt;
&lt;li&gt;documentación,
&lt;/li&gt;
&lt;li&gt;o un resumen manual que envío al equipo.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. ¿Por qué hacerlo en web y no con una app de escritorio?
&lt;/h2&gt;

&lt;p&gt;Como dev podría hacerlo todo con FFmpeg en local. De hecho muchas veces sigo usando scripts. Pero tener un &lt;strong&gt;panel web&lt;/strong&gt; con todo junto me da varias ventajas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Funciona igual en cualquier equipo (Windows, Mac, Linux, Chromebook…).
&lt;/li&gt;
&lt;li&gt;No tengo que explicar a gente no técnica cómo instalar FFmpeg.
&lt;/li&gt;
&lt;li&gt;Puedo compartir la misma URL con clientes o compañeros.
&lt;/li&gt;
&lt;li&gt;Puedo usarlo desde el móvil cuando me mandan algo fuera de horario de “escritorio”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Y, sobre todo, puedo tener &lt;strong&gt;un único flujo&lt;/strong&gt; que me sé de memoria:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Subir → convertir → recortar → comprimir → (opcional: transcribir)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Ideas si quieres montarte algo parecido como dev
&lt;/h2&gt;

&lt;p&gt;Si te apetece cacharrear y crear tus propias herramientas internas, algunas ideas técnicas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FastAPI o tu framework favorito en Python.
&lt;/li&gt;
&lt;li&gt;FFmpeg para todo lo multimedia (audio y vídeo).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cualquier stack web que te guste: HTMX, React, Vue…
&lt;/li&gt;
&lt;li&gt;Para la vista de recorte, un &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; HTML5 + un timeline simple genera mucho valor sin necesidad de un DAW completo.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;UX&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag &amp;amp; drop para subir archivos.
&lt;/li&gt;
&lt;li&gt;Progreso visible mientras convierte.
&lt;/li&gt;
&lt;li&gt;Limpieza de temporales en servidor para no acumular GB de audio.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Aunque no quieras abrirlo al público, solo con tener algo así en tu intranet o para tu equipo de producto puedes ahorrar horas de trabajo tonto con audios.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Resumen
&lt;/h2&gt;

&lt;p&gt;Mi flujo actual para domar audios feos de WhatsApp, reuniones y vídeos es:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subo cualquier formato a
👉 &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;Convertir Audio a MP3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Convierto a MP3 normal.
&lt;/li&gt;
&lt;li&gt;Recorto lo que sobra.
&lt;/li&gt;
&lt;li&gt;Comprimo si pesa demasiado.
&lt;/li&gt;
&lt;li&gt;Si es algo importante, lo transcribo a texto para documentarlo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No sustituye a un estudio de sonido, pero para el &lt;strong&gt;99% de tareas diarias&lt;/strong&gt; me basta.&lt;/p&gt;

&lt;p&gt;Si también vives rodeado de notas de voz y audios improvisados, quizá este tipo de flujo te ahorre tanto tiempo como a mí. Y si eres dev, igual te da ideas para montar tus propias utilidades internas encima de FFmpeg.&lt;/p&gt;

&lt;p&gt;¿Tienes algún truco o herramienta similar para trabajar con audio sin complicarte? Me encantará leerlo en los comentarios 👇&lt;/p&gt;

</description>
      <category>audio</category>
      <category>productividad</category>
      <category>herramientas</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Construyendo un extractor de audio (YouTube MP3) con FastAPI, yt-dlp y ffmpeg</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Thu, 08 Jan 2026 23:05:26 +0000</pubDate>
      <link>https://forem.com/whario/construyendo-un-extractor-de-audio-youtube-mp3-con-fastapi-yt-dlp-y-ffmpeg-47n4</link>
      <guid>https://forem.com/whario/construyendo-un-extractor-de-audio-youtube-mp3-con-fastapi-yt-dlp-y-ffmpeg-47n4</guid>
      <description>&lt;p&gt;Extraer el audio de un vídeo para escucharlo como podcast o repasar una clase es un caso de uso muy típico. El problema viene cuando quieres hacerlo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sin instalar nada raro en el ordenador&lt;/li&gt;
&lt;li&gt;Desde el móvil&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En este post cuento cómo está montado por dentro el extractor de audio que usamos en&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;convertiraudioamp3.com&lt;/a&gt;&lt;br&gt;
 y en la herramienta de&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/convertir_video_a_mp3" rel="noopener noreferrer"&gt;vídeo a MP3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No explico el código entero, pero sí las ideas clave: FastAPI, yt-dlp, ffmpeg, reCAPTCHA y algunos trucos para que funcione razonablemente bien con YouTube (incluidos Shorts).&lt;/p&gt;
&lt;h2&gt;
  
  
  Flujo general: de URL a MP3 descargable
&lt;/h2&gt;

&lt;p&gt;El flujo del endpoint “URL → MP3” es algo así:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;El usuario envía un POST con:&lt;/li&gt;
&lt;li&gt;source_url: enlace de YouTube (u otra plataforma compatible)&lt;/li&gt;
&lt;li&gt;calidad_mp3: 192k, 128k o 64k&lt;/li&gt;
&lt;li&gt;&lt;p&gt;recaptcha_token: para evitar abuso/robots&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validamos el reCAPTCHA y la URL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Usamos yt-dlp para inspeccionar los formatos disponibles y elegir el mejor stream de audio.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Descargamos solo el audio (o un stream progresivo de vídeo+audio si no queda otra).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pasamos ese archivo a ffmpeg para convertirlo a MP3 con el bitrate solicitado.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Devolvemos un JSON con:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;download_url para un endpoint de descarga tipo /descargar/{filename}&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limpiamos archivos temporales.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A nivel de usuario, todo eso es un formulario muy simple en&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/convertir_video_a_mp3" rel="noopener noreferrer"&gt;convertiraudioamp3.com/convertir_video_a_mp3&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Capa HTTP: FastAPI + reCAPTCHA
&lt;/h2&gt;

&lt;p&gt;La parte web es un endpoint clásico de FastAPI con formulario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@router.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/convertir_video_a_mp3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convertir_enlace_a_mp3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
    &lt;span class="n"&gt;calidad_mp3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# 1) Log de request (request_id, IP, path, etc.)
&lt;/span&gt;    &lt;span class="c1"&gt;# 2) Verificar reCAPTCHA con el token del formulario
&lt;/span&gt;    &lt;span class="c1"&gt;# 3) Validar/normalizar la URL
&lt;/span&gt;    &lt;span class="c1"&gt;# 4) Lanzar la lógica de descarga + conversión
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cosas a destacar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reCAPTCHA: la verificación se hace antes de gastar CPU/red con yt-dlp.&lt;/li&gt;
&lt;li&gt;Normalización de URL: usamos una función normalize_youtube_url(...) que:&lt;/li&gt;
&lt;li&gt;fuerza https://&lt;/li&gt;
&lt;li&gt;limpia parámetros prescindibles en URLs de YouTube&lt;/li&gt;
&lt;li&gt;request id (rid): cada petición lleva un UUID en logs, lo que ayuda muchísimo a depurar problemas con vídeos concretos.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  yt-dlp: elegir bien el stream de audio
&lt;/h2&gt;

&lt;p&gt;yt-dlp da mucha información de formatos. En lugar de usar un simple "format": "bestaudio/best", nos interesaba:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Evitar formatos sin URL directa (storyboards, miniaturas, etc.)&lt;/li&gt;
&lt;li&gt;Preferir audio-only (ej. m4a/140) si está disponible&lt;/li&gt;
&lt;li&gt;Ignorar formatos pseudo-vacíos&lt;/li&gt;
&lt;li&gt;Tener un fallback progresivo (vídeo+audio) para casos raros&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La idea de la función de selección de formato es algo así (pseudocódigo simplificado):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pick_best_audio_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info_dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fmts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info_dict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;formats&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_valid_audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vcodec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acodec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;has_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;is_storyboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;audio_only&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fmts&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_valid_audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

    &lt;span class="c1"&gt;# Preferimos m4a / id 140 si existe (muy típico en YouTube)
&lt;/span&gt;    &lt;span class="n"&gt;m4a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;audio_only&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ext&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;m4a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;140&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m4a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;best_by_abr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m4a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Luego cualquier audio-only razonable
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;audio_only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;best_by_abr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audio_only&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Fallback: stream progresivo vídeo+audio (extraeremos audio con ffmpeg)
&lt;/span&gt;    &lt;span class="n"&gt;progressive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fmts&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_progressive_with_audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;progressive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;best_by_abr_or_tbr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;progressive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Último recurso
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bestaudio/best&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En la implementación real se añaden filtros extra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;has_url(f): revisa url, manifest_url o fragment_base_url.&lt;/li&gt;
&lt;li&gt;is_storyboard(f): descarta formatos con format_id tipo sb* o extensiones mhtml, imágenes, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Todo esto reduce bastante los casos de “no hay formatos válidos” y ahorra ancho de banda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jugar con player_client: Android, iOS y Web
&lt;/h2&gt;

&lt;p&gt;Un detalle interesante de yt-dlp es el argumento:&lt;br&gt;
&lt;code&gt;"extractor_args": {"youtube": {"player_client": [player_client]}}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;YouTube no se comporta igual si finges ser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un cliente Android&lt;/li&gt;
&lt;li&gt;un cliente iOS&lt;/li&gt;
&lt;li&gt;el reproductor web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En la práctica, para cierto contenido (y sobre todo Shorts) algunos clientes funcionan mejor que otros. El endpoint hace algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ios&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;downloaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ytdlp_opts_base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extract_info_only&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pick_best_audio_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;download_with_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;downloaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;log_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;

&lt;span class="c1"&gt;# Fallback sin player_client (a veces da formatos que los otros bloquean)
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;downloaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ytdlp_opts_base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extractor_args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# sin client
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En ytdlp_opts_base(...) también se ajustan cosas como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-Agent y Accept-Language&lt;/li&gt;
&lt;li&gt;force_ipv4&lt;/li&gt;
&lt;li&gt;retries, fragment_retries, socket_timeout&lt;/li&gt;
&lt;li&gt;cookiefile si existe (para sortear algunas restricciones)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conversión a MP3 con ffmpeg
&lt;/h2&gt;

&lt;p&gt;Una vez descargado el archivo (audio o vídeo+audio), lo pasamos por ffmpeg:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;FFMPEG_BIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;downloaded_path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-vn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                    &lt;span class="c1"&gt;# sin vídeo
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c:a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;libmp3lame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-b:a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calidad_mp3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# "192k", "128k" o "64k"
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-ar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;44100&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-ac&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final_mp3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;safe_run_ffmpeg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;Algunos&lt;/span&gt; &lt;span class="n"&gt;detalles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;safe_run_ffmpeg&lt;/span&gt; &lt;span class="n"&gt;es&lt;/span&gt; &lt;span class="n"&gt;un&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt; &lt;span class="n"&gt;que&lt;/span&gt; &lt;span class="n"&gt;limita&lt;/span&gt; &lt;span class="n"&gt;tiempo&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;ejecución&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;captura&lt;/span&gt; &lt;span class="n"&gt;errores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Usamos&lt;/span&gt; &lt;span class="n"&gt;siempre&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt; &lt;span class="n"&gt;Hz&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;canales&lt;/span&gt; &lt;span class="n"&gt;por&lt;/span&gt; &lt;span class="n"&gt;compatibilidad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aunque&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt; &lt;span class="n"&gt;voz&lt;/span&gt; &lt;span class="n"&gt;pura&lt;/span&gt; &lt;span class="n"&gt;se&lt;/span&gt; &lt;span class="n"&gt;podría&lt;/span&gt; &lt;span class="n"&gt;optimizar&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;mono&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Guardamos&lt;/span&gt; &lt;span class="n"&gt;tamaño&lt;/span&gt; &lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="n"&gt;en&lt;/span&gt; &lt;span class="n"&gt;MB&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="n"&gt;bitrate&lt;/span&gt; &lt;span class="n"&gt;para&lt;/span&gt; &lt;span class="n"&gt;poder&lt;/span&gt; &lt;span class="n"&gt;hacer&lt;/span&gt; &lt;span class="n"&gt;estadísticas&lt;/span&gt; &lt;span class="nf"&gt;agregadas &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt; &lt;span class="n"&gt;datos&lt;/span&gt; &lt;span class="n"&gt;personales&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;

&lt;span class="c1"&gt;## Descarga por streaming y limpieza
&lt;/span&gt;
&lt;span class="n"&gt;El&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;descarga&lt;/span&gt; &lt;span class="n"&gt;es&lt;/span&gt; &lt;span class="n"&gt;un&lt;/span&gt; &lt;span class="n"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;descargar&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;que&lt;/span&gt; &lt;span class="n"&gt;envía&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="n"&gt;archivo&lt;/span&gt; &lt;span class="n"&gt;como&lt;/span&gt; &lt;span class="n"&gt;StreamingResponse&lt;/span&gt; &lt;span class="n"&gt;en&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algo&lt;/span&gt; &lt;span class="n"&gt;como&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="sb"&gt;``&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;iterfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StreamingResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;iterfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;media_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio/mpeg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Disposition&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment; filename=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cache-Control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no-store, no-cache, must-revalidate, max-age=0, private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y, muy importante:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;El archivo descargado original se borra en un finally.&lt;/li&gt;
&lt;li&gt;Los MP3 generados viven en disco, pero hay un proceso de limpieza que va eliminando todo de forma automática cada 24 h.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ese mismo patrón lo usamos no solo para la parte de YouTube, sino para el resto de herramientas en la web, incluído el conversor de audio general:&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;https://convertiraudioamp3.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Seguridad, límites y aspectos legales
&lt;/h2&gt;

&lt;p&gt;Al ser un servicio público, hay varios puntos a cuidar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reCAPTCHA en la ruta de URL → MP3 para frenar abuso automatizado.&lt;/li&gt;
&lt;li&gt;Validación estricta de URLs (http(s):// y normalización) para no dejar que esto se convierta en un proxy abierto.&lt;/li&gt;
&lt;li&gt;Logs mínimos pero útiles: request_id, IP, user-agent, bitrate, tamaño; nada de contenido privado.&lt;/li&gt;
&lt;li&gt;Privacidad: archivos temporales y finales se eliminan de forma periódica.&lt;/li&gt;
&lt;li&gt;Derechos de autor: el formulario y los textos dejan claro que la opción de enlace (YouTube u otros) es solo para contenido propio o con permiso.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si solo quieres trastear con el flujo técnico, puedes ignorar la parte de YouTube y probar con vídeo local en la misma herramienta de&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/convertir_video_a_mp3" rel="noopener noreferrer"&gt;vídeo a MP3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;subiendo un MP4/MOV/MKV desde tu disco.&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>backend</category>
      <category>podcast</category>
    </item>
    <item>
      <title>Cómo hemos adaptado nuestro recortador de audio para funcionar bien en móvil</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Fri, 05 Dec 2025 17:29:14 +0000</pubDate>
      <link>https://forem.com/whario/como-hemos-adaptado-nuestro-recortador-de-audio-para-funcionar-bien-en-movil-33d4</link>
      <guid>https://forem.com/whario/como-hemos-adaptado-nuestro-recortador-de-audio-para-funcionar-bien-en-movil-33d4</guid>
      <description>&lt;p&gt;Durante meses, la herramienta principal de &lt;strong&gt;recortar audio a MP3&lt;/strong&gt; en convertiraudioamp3.com estaba pensada sobre todo para escritorio: vista de ondas, selección visual, teclado, ratón… En móvil funcionaba, pero la experiencia no era tan sencilla como quería, especialmente para algo tan cotidiano como &lt;strong&gt;recortar una nota de voz de WhatsApp&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;En este artículo cuento cómo he adaptado el recortador para &lt;strong&gt;funcionar mejor en móvil&lt;/strong&gt; creando un “modo simple”, apoyándonos en la arquitectura de jobs que ya usaba para el recortador avanzado y el &lt;strong&gt;&lt;a href="https://convertiraudioamp3.com/mezclar-audio" rel="noopener noreferrer"&gt;mezclador de audio online&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Arquitectura: jobs asíncronos con FastAPI + FFmpeg
&lt;/h3&gt;

&lt;p&gt;Tanto el recortador como el mezclador comparten la misma base:&lt;/p&gt;

&lt;p&gt;Backend en &lt;strong&gt;FastAPI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Procesado de audio con &lt;strong&gt;FFmpeg&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Un sistema de jobs que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;crea un “trabajo” con un job_id&lt;/li&gt;
&lt;li&gt;procesa el audio en segundo plano&lt;/li&gt;
&lt;li&gt;expone un endpoint de estado para ver el progreso&lt;/li&gt;
&lt;li&gt;expone un endpoint de descarga cuando está listo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La función central que usamos en ambos casos es algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def create_mix_job_from_files_and_spec(
    files: List[UploadFile],
    data: dict,
) -&amp;gt; tuple[str, Dict[str, Any]]:
    calidad = str(data.get("calidad_mp3", "128k"))
    items = data.get("items", [])
    if not isinstance(items, list) or not items:
        raise ValueError("items vacío")

# Guardar subidas en disco
    uploaded_paths: List[str] = []
    for f in files:
        unique = uuid.uuid4().hex
        safe_name = f.filename or "audio"
        dst = UPLOAD_DIR / f"{unique}_{safe_name}"
        with dst.open("wb") as buf:
            shutil.copyfileobj(f.file, buf)
        uploaded_paths.append(str(dst))

# Asegurar src_idx coherente
    for i, it in enumerate(items):
        if "src_idx" not in it:
            it["src_idx"] = i if i &amp;lt; len(uploaded_paths) else (len(uploaded_paths) - 1)

    job_id = uuid.uuid4().hex
    job = {
        "id": job_id,
        "status": "queued",
        "progress": 0.0,
        "step": "queued",
        "error": None,
        "bitrate": calidad,
        "items": items,
        "uploaded_paths": uploaded_paths,
        "result_path": None,
        "created_at": datetime.utcnow().isoformat(),
    }

    JOBS[job_id] = job
    _write_status_file(job)       # Persistimos estado en disco
    EXECUTOR.submit(_run_mix_job, job)  # Lanzamos FFmpeg en background

    return job_id, job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La clave es el campo items: una lista de segmentos a procesar (inicio, fin, crossfade, etc.). Para recortar un solo audio, simplemente mandamos un solo item con inicio y fin.&lt;/p&gt;

&lt;h4&gt;
  
  
  Modo simple de recorte: centrado en el móvil
&lt;/h4&gt;

&lt;p&gt;La página principal de recorte tiene vista de ondas, JS, interacción más avanzada… Para móvil queríamos algo &lt;strong&gt;ultra directo&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subes un archivo de audio (WhatsApp, M4A, OGG, WAV, MP3…)&lt;/li&gt;
&lt;li&gt;Escribes segundo de inicio y de fin&lt;/li&gt;
&lt;li&gt;El servidor recorta y te devuelve un MP3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En FastAPI definimos un modo simple separado:&lt;br&gt;
&lt;code&gt;@router.get(&lt;br&gt;
    "/recortar-audio-mp3/simple",&lt;br&gt;
    name="recortar_audio_mp3_simple",&lt;br&gt;
    tags=["seo"],&lt;br&gt;
)&lt;br&gt;
def recortar_audio_mp3_simple(request: Request):&lt;br&gt;
    """&lt;br&gt;
    Versión simple: 1 archivo + inicio/fin + calidad.&lt;br&gt;
    Canonical → /recortar-audio-mp3.&lt;br&gt;
    """&lt;br&gt;
    ctx = build_ctx_simple_es(request, with_recaptcha=True)&lt;br&gt;
    resp = render_es(request, "recortar-audio-mp3-simple.html", ctx)&lt;br&gt;
    return _no_cache(resp)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;El build_ctx_simple_es se encarga de montar el contexto para la plantilla (valores por defecto, hreflang, etc.) y _no_cache añade cabeceras para evitar caches “rebeldes” (ahora lo vemos).&lt;/p&gt;

&lt;p&gt;En el POST hacemos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validar que fin &amp;gt; inicio&lt;/li&gt;
&lt;li&gt;Verificar reCAPTCHA&lt;/li&gt;
&lt;li&gt;Construir el spec_data con un solo item&lt;/li&gt;
&lt;li&gt;Crear el job con create_mix_job_from_files_and_spec&lt;/li&gt;
&lt;li&gt;Redirigir a la página de estado
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@router.post(
    "/recortar-audio-mp3/simple",
    name="recortar_audio_mp3_simple_post",
)
async def recortar_audio_mp3_simple_post(
    request: Request,
    file: UploadFile = File(...),
    inicio: float = Form(...),
    fin: float = Form(...),
    calidad_mp3: str = Form("128k"),
    recaptcha_token: str = Form(""),
):
    if fin &amp;lt;= inicio:
        # devolver error de validación al formulario
        ...

    ok = await verificar_recaptcha(recaptcha_token, request.client.host)
    if not ok:
        # mostrar error de reCAPTCHA al usuario
        ...

    spec_data = {
        "calidad_mp3": calidad_mp3,
        "items": [
            {
                "nombre": file.filename or "audio",
                "inicio": float(inicio),
                "fin": float(fin),
                "crossfade_s": 0.0,   # sin crossfade; es un simple recorte
            }
        ],
    }

    job_id, _job = create_mix_job_from_files_and_spec([file], spec_data)
    url = request.url_for("recortar_audio_mp3_simple_status", job_id=job_id)
    return RedirectResponse(url=url, status_code=303)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Pantalla de estado: caches, móvil y progreso real
&lt;/h4&gt;

&lt;p&gt;En escritorio todo parecía ir bien: la página de estado iba actualizando progreso y al final mostraba el botón de descarga.&lt;/p&gt;

&lt;p&gt;En móvil (sobre todo en algunos navegadores), nos encontramos con dos problemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Caché agresiva&lt;/strong&gt;: al recargar, el navegador mostraba siempre el mismo HTML (por ejemplo, “20 % — Normalizando”), aunque el job ya hubiera terminado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Porcentaje calculado en cliente&lt;/strong&gt;: el progreso estimado se basaba en un step textual ("normalize 1/3", "trim 2/5", etc.), lo que hacía más fácil que se quedara “atascado” visualmente si intermedia algo se cacheaba.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solución 1: desactivar caché de forma explícita&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En la capa Python, envolvemos la respuesta con un helper _no_cache que añade cabeceras:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;def _no_cache(resp: Response) -&amp;gt; Response:&lt;br&gt;
    resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0, private"&lt;br&gt;
    resp.headers["Pragma"] = "no-cache"&lt;br&gt;
    resp.headers["Expires"] = "0"&lt;br&gt;
    return resp&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Y lo usamos siempre en las vistas de estado:&lt;br&gt;
&lt;code&gt;resp = render_es(request, "recortar-audio-mp3-simple-status.html", ctx)&lt;br&gt;
return _no_cache(resp)&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
En la plantilla también añadimos meta robots para no indexar las URLs de job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% block meta_extra %}
  &amp;lt;meta name="robots" content="noindex,nofollow"&amp;gt;
  {% if auto_refresh %}
    &amp;lt;meta http-equiv="refresh" content="15"&amp;gt;
  {% endif %}
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en que el refresh no lleva url=..., solo el número de segundos; así evitamos que el navegador “combine” cosas raras con la caché previa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solución 2: usar el progress del backend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El endpoint de estado (/mezclar-audio/status/{job_id}) ya devolvía un campo numérico progress (0–100). En lugar de recomputar el porcentaje en función de step, usamos directamente lo que diga el backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status = js.get("status", "unknown")
step = js.get("step") or ""
error_msg = js.get("error") or None

raw_progress = js.get("progress", 0.0)
try:
    pct = float(raw_progress or 0.0)
except (TypeError, ValueError):
    pct = 0.0
pct = max(0.0, min(100.0, pct))

if status == "done":
    pct = 100.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Para el texto “bonito” seguimos usando el step
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;_p, label = step_to_pct_label_es(step)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;En la plantilla, el progreso se pinta con algo muy simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="w-full bg-gray-200 rounded-full h-3 overflow-hidden"&amp;gt;
  &amp;lt;div class="bg-indigo-600 h-3"
       style="width: {{ (pct or 5.0)|round(0) }}%;"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto, el porcentaje ya no se queda “congelado” en móvil: cada petición GET devuelve un HTML fresco con el valor real de progress que va escribiendo el worker mientras FFmpeg avanza.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mezclar varios audios en el móvil (también sin apps)
&lt;/h3&gt;

&lt;p&gt;Además del recortador, he preparado un &lt;/p&gt;

&lt;h2&gt;
  
  
  mezclador simple de audios pensado para móvil
&lt;/h2&gt;

&lt;p&gt;, también 100 % online, sin registros, y sin instalar aplicaciones.&lt;/p&gt;

&lt;p&gt;En vez de trabajar con formas de onda avanzadas, este modo simple te deja:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elegir &lt;strong&gt;varios audios a la vez&lt;/strong&gt; (notas de voz de WhatsApp, MP3, M4A, OGG, WAV…).&lt;/li&gt;
&lt;li&gt;Definir un &lt;strong&gt;crossfade global&lt;/strong&gt; en segundos (por ejemplo, 2 s entre cada pista).&lt;/li&gt;
&lt;li&gt;Elegir la &lt;strong&gt;calidad MP3&lt;/strong&gt; de salida.&lt;/li&gt;
&lt;li&gt;Lanzar el proceso y recibir un &lt;strong&gt;solo MP3 mezclado&lt;/strong&gt;, listo para compartir.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A nivel técnico, por debajo uso exactamente el mismo sistema de &lt;strong&gt;jobs asíncronos con FFmpeg&lt;/strong&gt; que en el recortador:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cada archivo se normaliza y se convierte a un formato interno homogéneo (WAV 44.1 kHz, 16-bit).&lt;/li&gt;
&lt;li&gt;Generamos una lista de items con inicio, fin y crossfade_s para cada pista.&lt;/li&gt;
&lt;li&gt;El backend concatena los clips y aplica acrossfade entre uno y otro cuando procede, exportando el resultado en MP3 con el bitrate elegido.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eso nos permite mantener el mezclador:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ligero en el navegador móvil&lt;/strong&gt; (el trabajo pesado va al servidor).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robusto ante audios largos&lt;/strong&gt; (lo mismo: todo se hace por job, no en el hilo principal).&lt;/li&gt;
&lt;li&gt;Cohesivo con el resto de la plataforma (misma API de mezcla/recorte, mismo sistema de progreso).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Para el usuario final el flujo es:&lt;br&gt;
&lt;strong&gt;subir varios audios → elegir segs de crossfade y calidad → ver la página de progreso → descargar el MP3 final&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recortar audios a MP3 en el móvil — Preguntas frecuentes ❓
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;¿Puedo recortar notas de voz de WhatsApp sin instalar aplicaciones?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sí. Solo tienes que:&lt;/p&gt;

&lt;p&gt;Abrir el navegador de tu móvil (Chrome, Safari, etc.).&lt;/p&gt;

&lt;p&gt;Guardar la nota de voz de WhatsApp como archivo de audio.&lt;/p&gt;

&lt;p&gt;Subirla al &lt;a href="https://convertiraudioamp3.com/recortar-audio-mp3/simple" rel="noopener noreferrer"&gt;recortador simple&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Indicar segundos de inicio y fin, y descargar el MP3 recortado.&lt;/p&gt;

&lt;p&gt;Todo se hace online, sin instalar apps adicionales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Funciona tanto en Android como en iPhone?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sí. El &lt;a href="https://convertiraudioamp3.com/recortar-audio-mp3/simple" rel="noopener noreferrer"&gt;recortador&lt;/a&gt; y el &lt;a href="https://convertiraudioamp3.com/mezclar-audio/simple" rel="noopener noreferrer"&gt;mezclador&lt;/a&gt; funcionan en cualquier dispositivo con un navegador moderno:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android (Chrome, Firefox, etc.).&lt;/li&gt;
&lt;li&gt;iPhone (Safari, Chrome).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solo necesitas conexión a Internet y capacidad para seleccionar el archivo de audio desde el móvil.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Es gratis recortar audio a MP3?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sí, el uso de la herramienta es gratis. No pedimos registro y no hay planes de pago ocultos para &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;recortar o mezclar audios&lt;/a&gt;. Solo subes tu archivo, eliges el recorte y descargas tu MP3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Puedo usarlo también para mezclar varios audios en el móvil?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sí. Además del recortador, tienes un modo simple de &lt;a href="https://convertiraudioamp3.com/mezclar-audio/simple" rel="noopener noreferrer"&gt;mezcla de audios pensado para móvil&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Eliges varios archivos en el selector.&lt;/p&gt;

&lt;p&gt;Configuras un crossfade en segundos (por ejemplo, 2 s entre pistas).&lt;/p&gt;

&lt;p&gt;Descargas un único MP3 mezclado.&lt;/p&gt;

&lt;p&gt;Es ideal para juntar varias notas de voz o clips cortos sin tener que instalar un editor de audio complejo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué formatos de entrada soporta el recortador móvil?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Los formatos más habituales de audio en móvil, entre ellos:&lt;/p&gt;

&lt;p&gt;OPUS (muy común en WhatsApp),&lt;/p&gt;

&lt;p&gt;AMR, OGG, M4A,&lt;/p&gt;

&lt;p&gt;WAV y MP3.&lt;/p&gt;

&lt;p&gt;Independientemente del formato de entrada, la salida es siempre MP3, para máxima compatibilidad con reproductores, coches, apps de mensajería y redes sociales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué &lt;a href="https://convertiraudioamp3.com/calculadora-bitrate-mp3" rel="noopener noreferrer"&gt;calidad MP3&lt;/a&gt; debería elegir para el recorte?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Depende del uso:&lt;/p&gt;

&lt;p&gt;Para voz / notas de WhatsApp:&lt;br&gt;
64k o 96k suelen ser suficientes y generan archivos muy ligeros.&lt;/p&gt;

&lt;p&gt;Para música o mezclas:&lt;br&gt;
128k–192k ofrecen buena calidad; 256k–320k son opciones “máxima calidad”.&lt;/p&gt;

&lt;p&gt;En el modo simple dejamos 128k como valor por defecto porque equilibra calidad y peso del archivo.&lt;/p&gt;

</description>
      <category>mp3</category>
      <category>trimmer</category>
      <category>resources</category>
      <category>webdev</category>
    </item>
    <item>
      <title>🎧 Cómo hice un recortador de audio MP3 online con FastAPI + FFmpeg</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Sun, 05 Oct 2025 23:04:04 +0000</pubDate>
      <link>https://forem.com/whario/como-hice-un-recortador-de-audio-mp3-online-con-fastapi-ffmpeg-4m3o</link>
      <guid>https://forem.com/whario/como-hice-un-recortador-de-audio-mp3-online-con-fastapi-ffmpeg-4m3o</guid>
      <description>&lt;p&gt;Hace poco añadí a mi &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;convertidor de audio MP3 online — convertiraudioamp3.com&lt;/a&gt; una nueva herramienta:&lt;br&gt;
➡️ Recortar audios largos y descargarlos en MP3, directamente online.&lt;/p&gt;

&lt;p&gt;Detrás hay un trabajo interesante con FastAPI, FFmpeg, un poco de JavaScript moderno y un enfoque muy ligero pero escalable.&lt;/p&gt;

&lt;h2&gt;
  
  
  El objetivo
&lt;/h2&gt;

&lt;p&gt;Ofrecer algo más que convertir audios de WhatsApp:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permitir recortar audios largos (voz o música).&lt;/li&gt;
&lt;li&gt;Que funcione con formatos comunes: .opus, .ogg, .amr, .m4a, .mp3, .wav…&lt;/li&gt;
&lt;li&gt;Procesar el audio en el servidor (seguro, compatible, rápido).&lt;/li&gt;
&lt;li&gt;Mantener una experiencia fluida, sin recargar la página.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  La arquitectura
&lt;/h2&gt;

&lt;p&gt;🖥️ &lt;strong&gt;Backend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FastAPI gestiona tres endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;/recortar-audio/create → crea el trabajo y lanza FFmpeg.&lt;/li&gt;
&lt;li&gt;/recortar-audio/status/{id} → devuelve el progreso.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;/recortar-audio/download/{id} → permite descargar el MP3 final.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;El trabajo se ejecuta en un hilo separado usando subprocess.Popen con FFmpeg, leyendo su salida (-progress pipe:1) para actualizar el porcentaje en tiempo real.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Se utiliza reCAPTCHA v3 en cada solicitud para evitar abusos automáticos, y se registra cada conversión en un log CSV para analítica.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🚀 &lt;strong&gt;Lanzar FFmpeg y seguir el progreso&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Una de las claves fue capturar la salida de FFmpeg en tiempo real para mostrar progreso al usuario:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;def _run_ffmpeg_trim_with_progress(src, dst, start, end, bitrate, job):&lt;br&gt;
    dur = max(0.01, end - start)&lt;br&gt;
    cmd = [&lt;br&gt;
        "ffmpeg", "-hide_banner", "-y",&lt;br&gt;
        "-ss", str(start), "-i", src,&lt;br&gt;
        "-t", str(dur),&lt;br&gt;
        "-vn", "-acodec", "libmp3lame",&lt;br&gt;
        "-b:a", bitrate, "-ar", "44100", "-ac", "2",&lt;br&gt;
        "-progress", "pipe:1", dst&lt;br&gt;
    ]&lt;br&gt;
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)&lt;br&gt;
    job["status"] = "running"&lt;br&gt;
    for line in proc.stdout:&lt;br&gt;
        if line.startswith("out_time_ms="):&lt;br&gt;
            out_s = int(line.split("=")[1]) / 1_000_000&lt;br&gt;
            job["progress"] = round((out_s / dur) * 100, 1)&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cada línea con out_time_ms nos dice cuántos microsegundos lleva procesados FFmpeg.&lt;br&gt;
Con eso, actualizamos el progreso en memoria y lo consultamos por API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⚡ &lt;strong&gt;API asíncrona con FastAPI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@router.post("/recortar-audio/create")&lt;br&gt;
async def recortar_audio_create(request: Request, file: UploadFile = File(...),&lt;br&gt;
                                inicio: float = Form(...), fin: float = Form(...)):&lt;br&gt;
    src = UPLOAD_DIR / f"{uuid.uuid4().hex}.mp3"&lt;br&gt;
    with src.open("wb") as buf:&lt;br&gt;
        shutil.copyfileobj(file.file, buf)&lt;br&gt;
    job_id = uuid.uuid4().hex&lt;br&gt;
    job = {"status": "queued", "progress": 0}&lt;br&gt;
    JOBS[job_id] = job&lt;br&gt;
    threading.Thread(target=_run_ffmpeg_trim_with_progress,&lt;br&gt;
                     args=(str(src), f"{src.stem}_trim.mp3", inicio, fin, "128k", job),&lt;br&gt;
                     daemon=True).start()&lt;br&gt;
    return {"job_id": job_id, "poll_url": f"/recortar-audio/status/{job_id}"}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Con FastAPI y un hilo simple conseguimos asincronía real sin dependencias pesadas.&lt;br&gt;
Ideal para cargas ligeras y VPS pequeños.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 &lt;strong&gt;Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creamos un formulario accesible y responsive, con inputs para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subir el archivo de audio.&lt;/li&gt;
&lt;li&gt;Definir los segundos de inicio y fin.&lt;/li&gt;
&lt;li&gt;Seleccionar la calidad MP3 (64k–320k).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El formulario se comunica por &lt;strong&gt;AJAX&lt;/strong&gt;, usando un script JS que:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Envía los datos a /recortar-audio/create.&lt;/li&gt;
&lt;li&gt;Lanza un polling para seguir el progreso.&lt;/li&gt;
&lt;li&gt;Muestra una barra de carga hasta que el audio está listo.&lt;/li&gt;
&lt;li&gt;Finalmente, ofrece un enlace de descarga directa al MP3.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Todo se integra en el bundle generado con esbuild, para mantener el rendimiento óptimo y evitar problemas de caché.&lt;/p&gt;

&lt;h2&gt;
  
  
  Despliegue y rendimiento
&lt;/h2&gt;

&lt;p&gt;El backend corre tras un proxy Nginx, con:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;proxy_set_header X-Forwarded-Proto $scheme;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;para que FastAPI detecte correctamente el https:// en las URLs de descarga.&lt;/p&gt;

&lt;p&gt;El procesamiento con FFmpeg en servidor es sorprendentemente rápido incluso en VPS modestos. Para audios largos, se recomienda:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limitar tamaño/duración (por ejemplo, 200 MB o 60 min).&lt;/li&gt;
&lt;li&gt;Limpiar los temporales a diario.&lt;/li&gt;
&lt;li&gt;Registrar conversiones con fecha, IP y duración para análisis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resultado final: un recortador de audio MP3 online, ligero y privado
&lt;/h2&gt;

&lt;p&gt;Después de montar el endpoint y el formulario, obtuvimos una herramienta funcional que permite subir un audio, elegir los segundos de inicio y fin, y obtener el MP3 recortado en segundos.&lt;br&gt;
Todo ocurre en el servidor con FFmpeg y FastAPI, sin depender de servicios externos.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Puedes probar la versión online aquí:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://convertiraudioamp3.com/recortar-audio-mp3" rel="noopener noreferrer"&gt;Recortar audio MP3 online — convertiraudioamp3.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>webdev</category>
      <category>python</category>
      <category>mp3</category>
    </item>
    <item>
      <title>Convertir YouTube a MP3 online con FastAPI, yt-dlp y FFmpeg</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Tue, 09 Sep 2025 08:36:45 +0000</pubDate>
      <link>https://forem.com/whario/convertir-youtube-a-mp3-online-con-fastapi-yt-dlp-y-ffmpeg-1622</link>
      <guid>https://forem.com/whario/convertir-youtube-a-mp3-online-con-fastapi-yt-dlp-y-ffmpeg-1622</guid>
      <description>&lt;h1&gt;
  
  
  Convertir YouTube a MP3 online con FastAPI, yt-dlp y FFmpeg
&lt;/h1&gt;

&lt;p&gt;Muchos servicios ofrecen convertir YouTube a MP3 online, pero pocos muestran qué hay detrás técnicamente.&lt;br&gt;&lt;br&gt;
En este artículo quiero compartir cómo monté mi aplicación, que tiene una arquitectura un poco &lt;strong&gt;inusual&lt;/strong&gt; pero muy funcional:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend en &lt;strong&gt;Python con FastAPI&lt;/strong&gt; y servidor &lt;strong&gt;Uvicorn&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Todo desplegado en un &lt;strong&gt;hosting compartido con cPanel&lt;/strong&gt;, no en un VPS dedicado.
&lt;/li&gt;
&lt;li&gt;Expuesto a Internet mediante &lt;strong&gt;Apache actuando como proxy inverso&lt;/strong&gt; hacia el proceso Uvicorn.
&lt;/li&gt;
&lt;li&gt;Vistas renderizadas con &lt;strong&gt;Jinja2&lt;/strong&gt; para SEO (cada ruta devuelve HTML optimizado).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El resultado es una aplicación ligera, escalable y, lo más importante, con un flujo robusto para &lt;strong&gt;convertir videos de YouTube a MP3 online&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  🎬 Descargando el video de YouTube con &lt;code&gt;yt-dlp&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;El primer paso es obtener el contenido de YouTube. Para esto uso &lt;a href="https://github.com/yt-dlp/yt-dlp" rel="noopener noreferrer"&gt;&lt;code&gt;yt-dlp&lt;/code&gt;&lt;/a&gt;, un fork moderno de youtube-dl que soporta los cambios constantes en la plataforma.&lt;/p&gt;

&lt;p&gt;Ejemplo básico en Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;descargar_video&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destino&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yt-dlp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bestaudio/best&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# mejor calidad disponible
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;destino&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;# archivo de salida
&lt;/span&gt;        &lt;span class="n"&gt;url&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto genera un archivo de audio en formato original (puede ser webm o m4a, según lo que sirva YouTube).&lt;/p&gt;

&lt;h2&gt;
  
  
  Paso 2. Convertir el audio a MP3 con ffmpeg
&lt;/h2&gt;

&lt;p&gt;Luego paso ese archivo a MP3 con ffmpeg.&lt;br&gt;
Aquí está el comando en Python usando subprocess:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convertir_a_mp3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitrate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;output_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.mp3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ffmpeg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;# sobrescribir
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-vn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;# sin vídeo
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c:a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;libmp3lame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-b:a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitrate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# 64k, 128k, 192k
&lt;/span&gt;        &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔹 Endpoint en FastAPI
&lt;/h2&gt;

&lt;p&gt;Integrando todo en un endpoint de &lt;strong&gt;FastAPI&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/convertir-youtube&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convertir_youtube&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt; &lt;span class="n"&gt;calidad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;unique_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;
        &lt;span class="n"&gt;temp_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.webm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# 1. Descargar audio
&lt;/span&gt;        &lt;span class="nf"&gt;descargar_audio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_file&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c1"&gt;# 2. Convertir a MP3
&lt;/span&gt;        &lt;span class="n"&gt;mp3_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertir_a_mp3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitrate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;calidad&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;download_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/descargas/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mp3_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error en conversión: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔹 Script de arranque con Uvicorn en cPanel
&lt;/h2&gt;

&lt;p&gt;Sí, todo esto corre en cPanel (donde normalmente no esperas nada más que PHP).&lt;br&gt;
Para arrancar mi servidor tengo un pequeño start.sh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PROJECT_DIR="/home/usuario/public_html"
VENV="$PROJECT_DIR/venv/bin/activate"
PID_FILE="$PROJECT_DIR/uvicorn.pid"

cd "$PROJECT_DIR" || exit 1
source "$VENV"

nohup uvicorn app.main:app \
  --host 127.0.0.1 \
  --port 8000 \
  --proxy-headers \
  --forwarded-allow-ips="127.0.0.1" \
  &amp;gt; uvicorn.log 2&amp;gt;&amp;amp;1 &amp;amp;
echo $! &amp;gt; "$PID_FILE"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔹 Resultado final
&lt;/h2&gt;

&lt;p&gt;Con esta arquitectura logré montar un servicio que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Funciona en un hosting cPanel con Apache como proxy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Permite a cualquier usuario pegar un enlace de YouTube y obtener su MP3.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Usa herramientas open-source (yt-dlp + FFmpeg) para manejar todo el proceso.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Está pensado para ser ligero, rápido y seguro.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👉 Puedes probarlo en mi proyecto:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://convertiraudioamp3.com/convertir-youtube-a-mp3" rel="noopener noreferrer"&gt;Convertir YouTube a MP3 online&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ Conclusión:
&lt;/h2&gt;

&lt;p&gt;Aunque la infraestructura pueda parecer poco común (FastAPI en cPanel con Apache), es totalmente viable y demuestra que Python + FastAPI se puede adaptar incluso a entornos tradicionales.&lt;br&gt;
Y si necesitas un conversor de YouTube a MP3, no olvides que lo tienes gratis y online 😉.&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>webdev</category>
      <category>fastapi</category>
      <category>python</category>
    </item>
    <item>
      <title>Cómo convertir audios de WhatsApp, Opus y AAC a MP3 fácilmente (guía práctica)</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Thu, 04 Sep 2025 11:03:16 +0000</pubDate>
      <link>https://forem.com/whario/como-convertir-audios-de-whatsapp-opus-y-aac-a-mp3-facilmente-guia-practica-3dhi</link>
      <guid>https://forem.com/whario/como-convertir-audios-de-whatsapp-opus-y-aac-a-mp3-facilmente-guia-practica-3dhi</guid>
      <description>&lt;p&gt;Hoy en día recibimos muchísimos audios desde &lt;strong&gt;WhatsApp, Telegram o apps de grabadora de voz&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
El problema es que la mayoría vienen en formatos como &lt;strong&gt;.opus, .aac o .amr&lt;/strong&gt;, que no siempre se pueden reproducir en todos los dispositivos.&lt;/p&gt;

&lt;p&gt;En este post te mostraré:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👉 Por qué ocurre este problema.
&lt;/li&gt;
&lt;li&gt;👉 Cómo puedes convertirlos con &lt;code&gt;ffmpeg&lt;/code&gt; desde tu propio código.
&lt;/li&gt;
&lt;li&gt;👉 Y una solución mucho más rápida si no quieres complicarte: &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;convertiraudioamp3.com&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  📌 ¿Por qué los audios de WhatsApp no abren en PC?
&lt;/h2&gt;

&lt;p&gt;WhatsApp guarda sus notas de voz en &lt;strong&gt;.opus&lt;/strong&gt;, un formato optimizado para compresión.&lt;br&gt;&lt;br&gt;
El detalle es que muchos reproductores (por ejemplo Windows Media Player o algunos móviles) no lo soportan de forma nativa.&lt;/p&gt;

&lt;p&gt;Lo mismo ocurre con &lt;strong&gt;.aac&lt;/strong&gt;, &lt;strong&gt;.amr&lt;/strong&gt; o incluso grabaciones hechas con ciertas apps.&lt;/p&gt;


&lt;h2&gt;
  
  
  🔧 Opción 1: Convertir con FFmpeg
&lt;/h2&gt;

&lt;p&gt;Si te gusta la terminal o estás construyendo tu propia herramienta, puedes usar &lt;strong&gt;FFmpeg&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Aquí un snippet en &lt;strong&gt;Python&lt;/strong&gt; que convierte cualquier audio a MP3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convertir_a_mp3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;archivo_entrada&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;archivo_salida&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calidad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;128k&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Convierte un archivo de audio a MP3 usando ffmpeg.
    - archivo_entrada: ruta del archivo original (ej. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;audio.opus&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
    - archivo_salida: ruta donde se guardará el mp3 (ej. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;audio.mp3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
    - calidad: bitrate del mp3 (ej. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;64k&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;128k&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;192k&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;comando&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ffmpeg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;# sobrescribir si existe
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-i&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;archivo_entrada&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-vn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;# sin video
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c:a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;libmp3lame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-b:a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calidad&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-ar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;44100&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;# frecuencia estándar
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-ac&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# estéreo
&lt;/span&gt;        &lt;span class="n"&gt;archivo_salida&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comando&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Ejemplo de uso:
# convertir_a_mp3("nota_whatsapp.opus", "nota_whatsapp.mp3", "128k")
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con esto ya puedes convertir un .opus de WhatsApp o un .aac de tu grabadora en un MP3 estándar.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Opción 2: Convertir online (sin instalar nada)
&lt;/h2&gt;

&lt;p&gt;Si no quieres instalar librerías ni pelearte con la terminal, existe una alternativa mucho más sencilla:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/" rel="noopener noreferrer"&gt;convertiraudioamp3.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Solo necesitas:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Subir tu archivo de audio.
&lt;/li&gt;
&lt;li&gt;Elegir la calidad del MP3.
&lt;/li&gt;
&lt;li&gt;Descargarlo listo para usar en cualquier dispositivo.
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>mp3</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Cómo convertir YouTube a MP3 online (gratis y sin programas)</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Wed, 03 Sep 2025 09:37:57 +0000</pubDate>
      <link>https://forem.com/whario/como-convertir-youtube-a-mp3-online-guia-2025-gratis-y-sin-programas-38k3</link>
      <guid>https://forem.com/whario/como-convertir-youtube-a-mp3-online-guia-2025-gratis-y-sin-programas-38k3</guid>
      <description>&lt;h1&gt;
  
  
  🎧 Cómo convertir YouTube a MP3 online (guía 2025)
&lt;/h1&gt;

&lt;p&gt;A veces solo necesitas el &lt;strong&gt;audio de un video de YouTube&lt;/strong&gt;: una clase, un podcast, una canción.&lt;br&gt;&lt;br&gt;
La forma más rápida de hacerlo es con un &lt;strong&gt;conversor online a MP3&lt;/strong&gt;, sin instalar nada.&lt;/p&gt;

&lt;p&gt;👉 Aquí tienes la guía completa: &lt;a href="https://convertiraudioamp3.com/soporte/convertir-youtube-a-mp3" rel="noopener noreferrer"&gt;Convertir YouTube a MP3&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Características
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gratis y online&lt;/strong&gt;: no necesitas programas adicionales.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calidad a elegir&lt;/strong&gt;: 64k, 128k o 192k según tu uso.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Funciona en PC y móvil&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacidad&lt;/strong&gt;: los archivos se eliminan cada 24 horas.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📌 Paso a paso
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Abre el conversor ➡️ &lt;a href="https://convertiraudioamp3.com/convertir-youtube-a-mp3" rel="noopener noreferrer"&gt;Convertir YouTube a MP3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pega la URL del video de YouTube.
&lt;/li&gt;
&lt;li&gt;Selecciona la calidad:

&lt;ul&gt;
&lt;li&gt;🎶 192 kbps → música de buena calidad.
&lt;/li&gt;
&lt;li&gt;🎤 128 kbps → estándar (bueno para voz y música).
&lt;/li&gt;
&lt;li&gt;📱 64 kbps → muy ligero, ideal en móvil.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Descarga tu archivo MP3 listo para usar.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🎯 Casos de uso
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Guardar canciones de YouTube en tu móvil.
&lt;/li&gt;
&lt;li&gt;Descargar solo el audio de conferencias y charlas.
&lt;/li&gt;
&lt;li&gt;Escuchar playlists offline.
&lt;/li&gt;
&lt;li&gt;Compartir audios más ligeros por WhatsApp o email.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ❓ Preguntas frecuentes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;¿Necesito instalar un programa?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No, todo funciona en el navegador.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Puedo convertir playlists?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Sí, pegando cada enlace de la lista uno por uno.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Qué pasa con mis archivos?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Se eliminan automáticamente cada 24h.  &lt;/p&gt;




&lt;p&gt;🔗 &lt;strong&gt;Prueba ahora mismo:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://convertiraudioamp3.com/convertir-youtube-a-mp3" rel="noopener noreferrer"&gt;Convertir YouTube a MP3&lt;/a&gt;&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>mp3</category>
      <category>webdev</category>
      <category>resources</category>
    </item>
    <item>
      <title>Convert Audio to MP3 Online — A Simple Tool for Developers, Podcasters &amp; Content Creators</title>
      <dc:creator>Mario</dc:creator>
      <pubDate>Thu, 31 Jul 2025 08:33:01 +0000</pubDate>
      <link>https://forem.com/whario/convert-audio-to-mp3-online-a-simple-tool-for-developers-podcasters-content-creators-1fp9</link>
      <guid>https://forem.com/whario/convert-audio-to-mp3-online-a-simple-tool-for-developers-podcasters-content-creators-1fp9</guid>
      <description>&lt;p&gt;If you've ever needed to quickly convert audio files to MP3 — whether it's for a podcast, app integration, or social media content — you've likely found yourself jumping between unreliable tools, weird installers, or questionable ads.&lt;/p&gt;

&lt;p&gt;I just launched a lightweight online MP3 converter that you can use instantly:&lt;br&gt;
👉 [&lt;a href="https://convertiraudioamp3.com" rel="noopener noreferrer"&gt;https://convertiraudioamp3.com&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;What it does&lt;/strong&gt;&lt;br&gt;
You can upload any supported audio file (WAV, M4A, OGG, etc.), and the service will convert it to high-quality MP3 in seconds — directly in your browser, no sign-up needed.&lt;/p&gt;

&lt;p&gt;🔧 &lt;strong&gt;How it's built&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend: Python + FastAPI&lt;/li&gt;
&lt;li&gt;Audio processing: FFmpeg&lt;/li&gt;
&lt;li&gt;Hosting: Deployed on a Linux server with reverse proxy via Apache&lt;/li&gt;
&lt;li&gt;Frontend: Simple responsive layout, mobile-friendly&lt;/li&gt;
&lt;li&gt;Privacy: Files are automatically removed after conversion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔁 &lt;strong&gt;Use cases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re editing a podcast and need a quick way to compress a WAV file&lt;/li&gt;
&lt;li&gt;You’re coding a music app and need test MP3s&lt;/li&gt;
&lt;li&gt;You're sending a voice note to a client and need smaller size&lt;/li&gt;
&lt;li&gt;You just need a dead-simple conversion tool without installing anything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🧪 &lt;strong&gt;Future features&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch conversion&lt;/li&gt;
&lt;li&gt;Drag &amp;amp; drop UI&lt;/li&gt;
&lt;li&gt;MP3 metadata editing&lt;/li&gt;
&lt;li&gt;Mobile upload support from WhatsApp/Telegram voice notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🤝 &lt;strong&gt;Open for feedback&lt;/strong&gt;&lt;br&gt;
Let me know what would make this tool more helpful to you. I'm open to ideas, especially if you work in audio dev, content creation or music tech.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it here:&lt;/strong&gt;&lt;br&gt;
🔗 [&lt;a href="https://convertiraudioamp3.com" rel="noopener noreferrer"&gt;https://convertiraudioamp3.com&lt;/a&gt;]&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>resources</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
