Crear Podcast a partir de un canal de Twitch
No tengo tiempo para ver los canales de Twitch que me gustaría seguir en el momento de la emisión y tras unos días probando una aplicación Android para descargar los vídeos me he decidido a crear un podcast privado que se genere de forma automática a partir del contenido de un canal en Twitch.
Buscar nuevos vídeos publicados
Una vez más, gracias a Angel he descubierto twitch-dl, una aplicación en python con la que listar y descargar los vídeos de cualquier canal de Twitch. Para listarlos en formato “json” es tan sencillo como ejecutar el siguiente comando desde el directorio donde tengamos descargado “twitch-dl”.
python3 twitch-dl.pyz videos jordillatzer -j
Descargar los vídeos nuevos
Ya tenemos un precioso “json” del que extraer las “id” de los últimos vídeos publicados. En mi script he utilizado jq para extraer la id de los últimos vídeos y compruebo si ya lo he descargado anteriormente con las “id’s” que tengo guardadas en el archivo “descargados.txt”. Para descargar el audio del vídeo es tan sencillo como lo siguiente.
twitch-dl.pyz download -q audio_only 221837124
Convertir los vídeos descargados a mp3
Pero el archivo que se descarga es muy grande para ser un audio y está en formato “mkv” por lo que lo proceso con FFMPEG y elimino los silencios pasándolo al mismo tiempo de mkv a mp3.
ffmpeg -loglevel 24 -i "$file" -af silenceremove=1:0:-50dB "${file%.mkv}.mp3"
Subir el audio generado a un servidor WebDav
Ya he escrito en un post como tengo implementado un servidor webdav gracias a Rclone usando como almacenamiento mis nubes públicas. Uso el mismo Rclone en este script para subir el contenido descargado y recodificado en local.
rclone copy $canal remoto:twitch/$canal/ --create-empty-src-dirs
Generar el feed con todos los archivos
Como plantilla para la generación del feed he utilizado esta de Matthew Dickens eliminando de la misma todo lo relativo a itunes ya que va a ser de uso privado. Guardo por un lado el encabezado del xml y por otro los archivos ya incluidos junto con el pie, así me resulta fácil poner como primer “item” del feed un nuevo audio que descargue. Hago uso de ffprobe para extraer los metadatos del mp3 que incrustar en el feed y el comando awk
para extraer la información del nombre del archivo.
Tras poner el feed en el servidor webdav donde he colocado los archivos de audio ya sólo resta añadirlo y actualizarlo en la aplicación de Podcast.
Script completo
A continuación dejo el código completo de script, si quieres descargarlo o copiarlo te recomiendo que pases por mi Repositorio de scripts y lo descargues ya que Hugo no lleva bien la importación de texto externo de momento.
#!/bin/bash
###################################################################
#Script Name: twitch2podcast.sh
#Description: Generación de Podcast a partir de canal de Twitch
#Args: N/A
#Creation/Update: 20220317/20240429
#Author: www.sherblog.pro
#Email: sherlockes@gmail.com
###################################################################
CANAL="jordillatzer"
TITULO="Jordi Llatzer en Twitch"
SERVIDOR="homezgz.ddns.net:5005"
FECHA=$(date)
twitch_dir=~/twitch
twdl=$twitch_dir/twitch-dl.pyz
# Incluye el archivo de mensajes de Telegram
source ~/SherloScripts/bash/telegram_V2.sh
###############
## Funciones ##
###############
#----------------------------------------------------------#
# Comprobar la salida #
#----------------------------------------------------------#
comprobar(){
if [ $1 -eq 0 ]; then
mensaje+=$'OK'
else
mensaje+=$'ERROR'
$notificacion "$mensaje"
#exit 0
fi
mensaje+=$'\n'
}
#----------------------------------------------------------#
# Buscar los últimos vídeos de un canal no descargados #
#----------------------------------------------------------#
buscar_ultimos () {
# Valores de argumentos
local canal=${1:?Falta el nombre del canal}
local titulo=${2:?Falta el título del canal}
# Obtiene el json de los ultimos vídeos.
tele_msg_instr "Search last videos"
echo "- Obteniendo últimos vídeos de $titulo"
json=$(python3 $twdl videos $canal --json)
tele_check $?
# Limitar a 15 videos la lista de descargadoscase
tele_msg_instr "Trim downloaded list"
echo "- Recortando listas de descargados"
head -n15 $twitch_dir/$canal/descargados.txt > tmp
mv tmp $twitch_dir/$canal/descargados.txt
# Limitar a 15 videos la lista de items
head -n150 $twitch_dir/$canal/items.xml > tmp
mv tmp $twitch_dir/$canal/items.xml
tele_check $?
# Busca sobre los ultimos diez videos
for i in {0..9}
do
# Si el vídeo a comenzado hace menos de 3 horas pasa al siguiente
publicado=$(echo "$json" | jq ".videos[$i].publishedAt" | cut -c2- | rev | cut -c2- | rev)
publicado=$(date -d "$publicado+3 hours" +%s)
# obtiene la identificación y minutos de duración del último vídeo
id=$(echo "$json" | jq ".videos[$i].id" | cut -c2- | rev | cut -c2- | rev)
mins=$(expr $(echo "$json" | jq ".videos[$i].lengthSeconds") / 60)
if [[ $(date +%s) < $publicado ]]
then
tele_msg_instr "El vídeo $id está en emisión"
tele_msg_resul "..."
continue
fi
# Comprobar si el archivo ya ha sido descargado
if grep -q $id $twitch_dir/$canal/descargados.txt
then
if [ $i -eq 0 ]
then
echo "- No hay nuevos vídeos.";
tele_msg_instr "No hay nuevos vídeos"
tele_msg_resul "..."
# No sigue comprobando si ya ha visto uno descargado
break
fi
echo "- El vídeo $id ya se ha descargado.";
tele_msg_instr "Video $id already downloaded"
tele_msg_resul "..."
# No sigue comprobando si ya se ha visto uno descargado
continue
else
echo "- Descargando el audio del vídeo $id.";
tele_msg_instr "Download $id audio"
if (( $mins > 10 ))
then
# Descarga el audio en formato mkv
#$twdl download -q audio_only --auth-token huimxzjg56hs2xxlhx8zsj3wggej6n $id;
$twdl download -q audio_only $id;
tele_check $?
#resultado=$?
#comprobar $resultado
if [ $resultado -ne 0 ]; then
# No se ha descargado correctamente, pasa al siguiente
echo "El audio no se ha descargado correctamente"
continue
fi
else
echo "- El archivo sólo tiene $mins minutos, no se descarga."
tele_msg_instr "Archivo de sólo $mins minutos."
tele_msg_resul "..."
fi
# Añade el archivo al principio de la lista de descargados
#echo $id >> $twitch_dir/$canal/descargados.txt;
echo $id | cat - $twitch_dir/$canal/descargados.txt > temp && mv temp $twitch_dir/$canal/descargados.txt
fi
done
}
#----------------------------------------------------------#
# Pasa a mp3 los vídeos descargados en la carpeta #
#----------------------------------------------------------#
convertir_mp3 () {
local canal=${1:?Falta el nombre del canal}
echo "- Buscando archivos para convertir en $canal"
tele_msg_instr "Search $canal files"
tele_msg_resul "..."
for file in ./*.mkv; do
local nombre=$(basename $file .mkv)
local id_ep=$(echo $nombre | awk -F'_' '{print $2}')
echo "- Episodio $id_ep, codificando audio y eliminando silencios"
tele_msg_instr "Recode $id_ep audio"
ffmpeg -loglevel 24 -i "$file" -af silenceremove=1:0:-50dB "${file%.mkv}.mp3"
tele_check $?
echo "- Episodio $id_ep, moviendo mp3"
tele_msg_instr "Move $id_ep mp3"
mv $nombre.mp3 $canal/mp3/$id_ep.mp3
tele_check $?
echo "- Episodio $id_ep, eliminando el video"
tele_msg_instr "Delete $id_ep video"
rm $file
tele_check $?
done
}
#----------------------------------------------------------#
# Actualizar el feed #
#----------------------------------------------------------#
# (Coge la info de
actualizar_feed () {
# Valores de argumentos
local servidor=${1:?Falta el servidor del feed}
local canal=${2:?Falta el nombre del canal}
local titulo=${3:?Falta el título del canal}
# Encabezado del feed
echo "- Insertando el encabezado del feed"
mensaje+=$"Actualizando el Feed"
mensaje+=$'\n'
cat > $canal/feed.xml <<END_HEADER
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
<atom:link href="$servidor/twitch/$canal/feed.xml" rel="self" type="application/rss+xml"/>
<title>$TITULO</title>
<description>Un podcast creado por Sherlockes a partir del canal de $TITULO</description>
<copyright>Copyright 2022 Sherlockes</copyright>
<language>es</language>
<pubDate>$FECHA</pubDate>
<lastBuildDate>$FECHA</lastBuildDate>
<image>
<link>https://sherblog.pro</link>
<title>$TITULO</title>
<url>$servidor/twitch/$canal/artwork.jpg</url>
</image>
<link>https://www.sherblog.pro</link>
END_HEADER
# Buscando mp3's locales para extraer info
echo "- Buscando nuevos episodios descargados"
find $canal -type f -name "*.mp3" | while read -r file; do
NOM_EP=$(basename $file)
ART_EP=$(ffprobe -loglevel error -show_entries format_tags=artist -of default=noprint_wrappers=1:nokey=1 $file)
TIT_EP=$(ffprobe -loglevel error -show_entries format_tags=title -of default=noprint_wrappers=1:nokey=1 $file)
ID_EP=$(echo $NOM_EP | awk -F'_' '{print $2}')
ID_EP=$(basename $file .mp3)
# Obteniendo info a partir de la ID
json=$(python3 $twdl info $ID_EP -json)
FEC_EP=$(echo "$json" | jq ".publishedAt" | cut -c2- | rev | cut -c2- | rev)
FEC_EP=$(date -d $FEC_EP +"%Y-%m-%dT%H:%M:%S%:z")
FEC_EP=$(date --date "$FEC_EP-2 hours" "+%a, %d %b %Y %T %Z")
lengthSeconds=$(echo "$json" | jq ".lengthSeconds")
echo "- Añadiendo episodio $ID_EP a la lista de episodios"
mensaje+=$"Añadiendo episodio $ID_EP a la lista"
mensaje+=$'\n'
# crea el "item.xml" con info del episodio
cat >> $canal/item.xml <<END_ITEM
<item>
<guid isPermaLink="true">$servidor/$canal/$ID_EP</guid>
<title>$TIT_EP</title>
<link>https://www.twitch.tv/videos/$ID_EP</link>
<description>$TIT_EP</description>
<pubDate>$FEC_EP</pubDate>
<author>$ART_EP</author>
<content:encoded><![CDATA[<p>Episodio descargado de Twitch.</p>]]></content:encoded>
<enclosure length="$lengthSeconds" type="audio/mpeg" url="$servidor/twitch/$canal/mp3/$NOM_EP"/>
</item>
END_ITEM
done
# Añade los "items.xml" ya descargado a continuación del "item.xml"
cat $canal/items.xml >> $canal/item.xml
mv $canal/item.xml $canal/items.xml
# Añadir lista de episodios al feed
echo "- Añadiendo lista de episodios al feed"
cat $canal/items.xml >> $canal/feed.xml
# Añadiendo el pie del feed
echo "- Añadiendo el pie del feed"
cat >> $canal/feed.xml <<END
</channel>
</rss>
END
}
#----------------------------------------------------------#
# Subir contenido actualizado #
#----------------------------------------------------------#
subir_contenido () {
# Valores de argumentos
local canal=${1:?Falta el nombre del canal}
# Subiendo archivos a la nube via rclone
echo "- Subiendo los mp3's al sevidor remoto"
tele_msg_instr "Upload mp3 to server"
rclone copy $canal Sherlockes78_GD:twitch/$canal/ --create-empty-src-dirs
tele_check $?
# Eliminando audio y video local
echo "- Eliminando audios locales"
find . -type f -name "*.mp3" -delete
# Borrando los archivos de la nube anteriores a 30 días
tele_msg_instr "Delete older mp3s"
rclone delete Sherlockes78_GD:twitch/$canal/mp3 --min-age 30d
tele_check $?
}
########################
## Programa principal ##
########################
echo "#####################################"
echo "## Twitch to Podcast by Sherlockes ##"
echo "#####################################"
tele_msg_title "From twitch to podcast"
cd $twitch_dir
echo "- Corriendo en $twitch_dir"
# Comprobar la instalación de twitch-dl en el directorio
#. ~/SherloScripts/bash/twitch-dl.sh && check
# Buscar nuevos videos y convertirlos a mp3
buscar_ultimos "$CANAL" "$TITULO"
# Convertir a mp3 los vídeos descargados (Si los hay)
if ls ./*.mkv 1> /dev/null 2>&1; then
convertir_mp3 "$CANAL"
fi
# Actualizar el feed y sube el nuevo contenido (Si lo hay)
if ls ./$CANAL/mp3/*.mp3 1> /dev/null 2>&1; then
actualizar_feed "$SERVIDOR" "$CANAL" "$TITULO"
subir_contenido "$CANAL"
else
echo "- No hay nuevo contenido."
tele_msg_instr "No new content"
tele_msg_resul "..."
fi
# Envia el mensaje de telegram con el resultado
tele_end