Servidor RTSP + ffmpeg

Seguimos con lo del proyecto de fin de semana de montar un punto de acceso wifi con un servidor de streaming.
En el post mencionada mostraba como usar icecast2 como servidor de streaming, pero al empezar a conectar dispositivos me he encontrado con una cosa un tanto molesta, que la sincronización era malísima. Icecast2 funciona sobre el protocolo http y no era lo que quería hacer exactamente, sino hacerlo sobre RTSP. Así que en este post veremos como configurar un servidor RTSP y enviaremos el stream de MPD al servidor de streaming RTSP.

Lo primero será descargar el servidor y lo ejecutamos:

root@raspberrypi:~# mkdir mediamtx
root@raspberrypi:~# cd mediamtx
root@raspberrypi:~# wget https://github.com/bluenviron/mediamtx/releases/download/v1.9.0/mediamtx_v1.9.0_linux_armv7.tar.gz
root@raspberrypi:~# tar xvzf mediamtx_v1.9.0_linux_armv7.tar.gz
root@raspberrypi:~# ./mediamtx

Para hacer pruebas de como hacer el stream de un fichero mp3 he usado inicialmente gstreamer (que me ha llevado un buen rato para hacerlo funcionar)

root@raspberrypi:~# apt-get install gstreamer1.0-rtsp gstreamer1.0-tools

root@raspberrypi:~# gst-launch-1.0 filesrc location=/home/laura/Desktop/2024_09-07-Laura_Mora_Aubert-Silent-P9-200.mp3 ! decodebin ! audioresample ! audioconvert ! opusenc ! audio/x-opus, mapping=stream1 ! rtspclientsink location=rtsp://localhost:8554/test 

root@raspberrypi:~# gst-launch-1.0 filesrc location=/home/laura/Desktop/2024_09-07-Laura_Mora_Aubert-Silent-P9-200.mp3 ! decodebin ! audioresample ! audioconvert ! voaacenc ! audio/mpeg, mapping=/stream1 ! rtspclientsink location=rtsp://localhost:8554/test 

Me he encontrado algunos problemas con el códec que necesitaba, pero esta página me ha ayudado.

the stream doesn't contain any supported codec, which are currently H265, H264, Opus, MPEG-4 Audio

Finalmente en MPD modificamos el fichero /etc/mpd.conf y añadimos un nuevo audio_output

root@raspberrypi:~# vi /etc/mpd.conf
audio_output {
     name            "pipe to ffmpeg"
     type            "pipe"
     enabled         "yes"
     format          "48000:16:2"
     command         "ffmpeg -loglevel error -hide_banner 

Montar punto de acceso wifi y servidor de streaming con raspberry pi 5

Hace unos meses que descubrí las Silent Disco, varios auriculares Bluetooth conectados a un sólo emisor. Esta solución tiene un coste un poco elevado así que en otras he visto como se comparte un audio y luego los asistentes se sincronizan como pueden para bailar todos al unísono. En este post traigo una opción intermedia. Un dispositivo raspberry pi 5 reproduce una playlist que la manda a un icecast2 en local. La raspberry Pi 5 al tener wifi, crea un punto de acceso donde los asistentes se conectan al punto de acceso y configuran con VLC el stream de icecast2.

Script para generar el fichero sitemap.xml de una web estática

Hace unos años hice el crawling de la web antigua de kaosenlared. Ahora tiempo mas tarde el analytics de google les estaba dando muchos errores de páginas no encontradas, así que he tenido que generar los ficheros sitemap.xml para que google indexe el sitio.

He hecho un primer script para listar todas las páginas index.html del directorio y generar el fichero sitemap.xml que ocupaba la friolera de 150Mb pero al metérselo a google analytics daba errores de tamaño de archivo. Buscando los límites, nos encontramos que el sitemap.xml no puede ser más grande de 50Mb ni contener más de 50.000 entradas. Así que ha sido necesario crear un índice y separar los sitemap.xml en ficheros más pequeños.

A continuación el script de como lo he hecho

#!/bin/bash

# Directori on es troben les pàgines html
DIRECTORI=/home/virtualmin/archivo.kaosenlared.net/public_html

# URL base del lloc web
URL_BASE=https://archivo.kaosenlared.net

current_date=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Funció per generar el sitemap.xml
generar_sitemap() {
  # Initialize the URL count and the file number
  url_count=0
  file_number=1

  # Initialize the sitemap file name
  sitemap_file="sitemap_${file_number}.xml"
  sitemap_index="sitemap.xml"

  # Inicialitza el fitxer sitemap.xml
  echo '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' > $sitemap_index

  echo '<?xml version="1.0" encoding="UTF-8"?>' > $sitemap_file
  echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">' >> $sitemap_file


  echo "  <sitemap>" >> $sitemap_index
  echo "    <loc>$URL_BASE/$sitemap_file</loc>" >> $sitemap_index
  echo "    <lastmod>$current_date</lastmod>" >> $sitemap_index
  #echo "    <changefreq>monthly</changefreq>" >> $sitemap_index
  echo "  </sitemap>" >> $sitemap_index


  # Recorre els fitxers html del directori i subdirectoris
  find $DIRECTORI -type f -name "*.html" -not -path "*mailto*" -not -path "* *" -not -path "*&*" | while read 

add-apt-repository en debian 12

Me encuentro últimamente algunas aplicaciones que piden usar el comando add-apt-repository que es como se añaden los repositorios en ubuntu. En debian para poder hacer uso de este comando es necesario instalar software-properties-common

# apt-get install software-properties-common

Y listos ya podemos agregar el repositorio correspondiente. En el caso que me atiende es la instalación de los drivers de nvidia cuda, que están en los repositorios de debian, pero en su versión 11, cuando nvidia nos ofrece la versión 12.

# wget https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/cuda-keyring_1.1-1_all.deb
# dpkg -i cuda-keyring_1.1-1_all.deb
# add-apt-repository contrib
# apt-get update
# apt-get -y install cuda-toolkit-12-6

Y para instalar el driver cerrado de nvidia

# apt-get install -y cuda-drivers

Django: peticiones wsgi y asgi simultaneas (htmx y websockets)

Muy feliz de finalmente tenerlo funcionando! no sé si es la mejor forma, pero la cuestión es que funciona :D

Estos días me estoy peleando en hacer funcionar un chatbot con stream de openai sobre django. Me ha llevado un par de días de hacerlo funcionar pero aquí lo tenemos :D

Básicamente después de perderme entre decenas de páginas de documentación y abrir y cerrar otras decenas más de pestañas del navegador, lo que terminé haciendo fue localizar una aplicación que sí hacía lo que yo quería y luego la he implementado a mi aplicación. La aplicación que funcionaba like a charm en local es Django OpenAI-Powered Chatbot with HTMX Streaming. También recomiendo esta otra documentación (Learn to use Websockets with Django by building your own ChatGPT) que si eres relativamente nuevo con esto de htmx y websockets te va a aclarar mucho el tema :)

Inicié implementando el manual de saaspegasus pero no funcionaba. Así que he decidido directamente integrar el código de django_chatbot a mi aplicación y tampoco funcionaba… hasta que he visto lo de daphne! El python manage.py runserver de toda la vida no es síncrono pero daphne si, y lo que permite es activar las conexiones asíncronas. Al aplicar esto, todo ha empezado a funcionar :D

Después ha venido la parte de ponerlo en producción que de nuevo me he perdido entre múltiples páginas pero finalmente lo que sacado y aquí lo dejo para saber como implementarlo la próxima vez y también para otro …

Ficheros mp3

Este fin de semana en un curso el profesor comentaba que los ficheros mp3 se degradaban con el uso y terminaban dando problemas como cortes y que era mejor usar otros formatos como WAV o FLAC. Me sorprendió lo comentado, ya que según mi entendimiento, cuando abres un fichero mp3, este se carga en memoria (RAM) y desde ahí se realiza todo el proceso de descompresión.

Así que el objetivo de este post es el de comprobar y demostrar lo comentado. Esto también me sirve para corroborar a nivel de perito informático qué ocurre con la reproducción de ficheros de audio, vídeo y fotografías (el concepto es el mismo).

Para ello voy a usar un script que reproduzca 100 veces un clip de audio y compruebe en cada reproducción el hash del fichero.

Antes de empezar voy a instalar sox y libsox-fmt-mp3 para reproducir el mp3 en consola

# apt install sox libsox-fmt-mp3

Y voy a usar el siguiente script

$ vi play_mp3_and_check_hash.sh
#!/bin/bash

AUDIO_FILE=duck_quack.mp3

for ((i=1;i<=100;i++));
do
   SUM=`md5sum ${AUDIO_FILE}`
   echo "Loop number: ${i}, hash is: ${SUM}"
   play -q ${AUDIO_FILE} 2>/dev/null
done

Le doy permisos y lo ejecuto

$ chmod +x play_mp3_and_check_hash.sh
$ ./play_mp3_and_check_hash.sh
Loop number: 1, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
Loop number: 2, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
Loop number: 3, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
Loop number: 4, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
Loop number: 5, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
[...]
Loop number: 97, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
Loop number: 98, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
Loop number: 99, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3
Loop number: 100, hash is: 50c8fa8d674e6598951ab69bf2596c5d  duck_quack.mp3

Y la conclusión es que, …

Script para reiniciar nginx cuando han caducado los certificados de letsencrypt

Últimamente estoy de scripts para automatizar mi vida de sysadmin ;) aquí os dejo otro que comprueba si se han renovado los certificados y reinicia nginx en caso de que si haya ocurrido

# vi /root/scripts/check_certificates2.sh

#!/bin/bash

ADMIN_MAIL=""
DOMAINS=($(nginx -T |grep server_name |grep ";" |grep -v "#" |awk '{print $2}' |sed 's/;//' |grep -v "_" | sort -u))
CHECK_DIR="/root/scripts/check_domains"
REBOOT_NGINX=0

mkdir -p ${CHECK_DIR}

for i in "${DOMAINS[@]}"
do
   if [[ -f ${CHECK_DIR}/$i ]]; then
       OLD=`cat ${CHECK_DIR}/$i |awk '{print $1}'`
       CURRENT=`sha1sum /etc/letsencrypt/live/${i}/cert.pem |awk '{print $1}'`
       if [[ ${OLD} != ${CURRENT} ]]; then
           echo "hashes don't match for domain ${i}, please restart nginx"
           REBOOT_NGINX=1
           sha1sum /etc/letsencrypt/live/${i}/cert.pem ${CHECK_DIR}/$i
       fi
   else
       if [[ -f /etc/letsencrypt/live/${i}/cert.pem ]]; then
           sha1sum /etc/letsencrypt/live/${i}/cert.pem ${CHECK_DIR}/$i
       else
           echo "Domain ${i} has no certificate file"
       fi
   fi
done

if [[ ${REBOOT_NGINX} == "1" ]]; then
    echo "I reboot nginx due there are changes on certificates"
    service nginx restart
fi

Luego hago que se ejecute cada 10 minutos y listos

# vi /etc/crontab
*/10 *  * * *   root    /root/scripts/check_certificates2.sh

Script para realizar un sha1sum recursivo de los ficheros un directorio

Volvemos a la carga con mas scripts de bash. Esta vez necesito realizar un sha1sum para obtener el hash sha1 de unos ficheros que están dentro de muchos directorios. Como sha1sum no soporta la opción recursiva (en mi opinión un fallo), no queda otra que recurrir a nuestro amado bash.

Aquí el script para la ocasión

#!/bin/bash

DIRECTORY="material"

find "$DIRECTORY" -type d -print0 -maxdepth 4 | while read -r -d '' d; do
  echo "#### SHA1SUM Directorio: $d #####"
  sha1sum $d/*
done

script para dividir pdf en pdf mas pequeños con pdftk o pdfjam

Mi buen amigo Bruno, hace unos días me pasó una extracción de un teléfono móvil con la friolera de más de 51.000 páginas. Mi pobre y humilde portátil sacaba humo sólo al abrirlo así que he tenido que ingeniármelas para dividir el fichero en ficheros más pequeños y poder gestionar la información cómodamente. La solución, un script.

El script tiene dos opciones, usar pdftk o pdfjam. Deberemos comentar y descomentar la línea que nos interese.

Si queremos usar pdftk será necesario instalarlo

# apt install pdftk

Para usar pdfjam, que me ha procesado la información más rápido que con pdftk, será necesario instalar texlive-extra-utils

# apt install texlive-extra-utils

Otra herramienta que será necesario instalar es pdfinfo que se encuentra dentro del paquete poppler-utils

# apt install poppler-utils

A continuación el script

#!/bin/bash

INPUT_FILE=Redmi7-ASPERTIC.pdf
PAGES=`pdfinfo ${INPUT_FILE} |grep Pages |awk -F '           ' '{print $2}'`
FILENAME=`basename ${INPUT_FILE} |awk -F '.' '{print $1}'`
SPLIT=500
START=1
echo "Total Pages: ${PAGES}"
echo 

COUNTER=$START
ITER=1

while [ "$COUNTER" -le "$PAGES" ]
do
    START=$(($COUNTER))
    END=$(($START + $SPLIT - 1))
    echo 
    echo "#################"
    echo "start ${START} end ${END} iter ${ITER}"
    if [[ "$PAGES" -le "$END" ]]
    then
       END=$PAGES
       echo "last end is ${END} of total pages ${PAGES}"
    fi
    #pdftk ${INPUT_FILE} cat ${START}-${END} output ${FILENAME}_${ITER}.pdf
    pdfjam ${INPUT_FILE} ${START}-${END} -o ${FILENAME}_${ITER}.pdf
    COUNTER=$(($COUNTER + $SPLIT))
    ITER=$(($ITER + 1))
done

Y listos, lo mismo de siempre, crear el fichero, darle permisos de ejecución y ejecutarlo!

Aquí una captura del final de la ejecución del script sobre el susodicho fichero

Una utilidad …

Instalar dudle con debian 12

Por fin otra tarea menos! Esta ya daba vergüenzilla y todo al tener la instalación anterior de dudle aún con Debian 6!!! Así que directamente lo que he planteado es hacer una instalación limpia y los datos que puedan estar ahí, que pasen a mejor vida! Aunque creo que los datos aún serían compatibles, parece que tienen el mismo formato.

Vamos a ver como se hace ahora la instalación. Para ello he seguido el repositorio de github del proyecto que omite la información de la instalación y configuración de apache.

Primero vamos a instalar las dependencias:

# apt -y install apache2 git ruby-dev libxml2-dev zlib1g-dev
# gem install ratom

A continuación clonamos el repositorio en algún sitio del servidor, en mi caso lo he dejado en /var/www/dudle

# cd /var/www
# git clone https://github.com/kellerben/dudle.git

Ahora vamos a descargar los ficheros .mo correspondientes a los ficheros de idioma compilados. Vamos a crear un script, le damos permisos de ejecución, lo ejecutamos y finalmente borramos el fichero.

# cd dudle/locale

# vi download_mo.sh
for i in ??; do
	wget -O $i/dudle.mo https://dudle.inf.tu-dresden.de/locale/`basename $i`/dudle.mo
done

# chmod +x download_mo.sh

# ./download_mo.sh

# rm download_mo.sh

Ahora configuramos apache:

# cd /etc/apache2/sites-available

# vi dudle.conf
<VirtualHost *:80>
        ServerName      dudle.marsupi.org
        ServerAlias     *.dudle.marsupi.org dudle.lamardebits.org *.dudle.lamardebits.org dudle.lamardebits.cat *.dudle.lamardebits.cat
        ServerAdmin     xxx@lamardebits.org
        DocumentRoot    /var/www/dudle/

        ErrorLog        /var/log/apache2/dudle.error.log
        CustomLog       /var/log/apache2/dudle.access.log combined

	<Directory "/var/www/dudle">
		AllowOverride All

    		SetEnv RUBYLIB /var/www/dudle/
    		SetEnv RUBYOPT "-E UTF-8:UTF-8"
    		SetEnv GIT_AUTHOR_NAME="Blackhold"
    		SetEnv GIT_AUTHOR_EMAIL=xxx@domain.com
    		SetEnv GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
    		SetEnv GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"

		Order allow,deny
		Allow from all
	</Directory>
</VirtualHost>

# cd /etc/apache2/sites-enabled/; rm