Commit f608aba5 authored by Glen Mével's avatar Glen Mével

`tvrec`: program network television recordings

parent a28de4fa
#!/bin/env -iS TERM=${TERM} /bin/bash
##
## Chaînes connues
##
# Ce sont les chaînes diffusées par le CRANS sur le campus de l’ENS Cachan.
declare -a channels=(
# nº | nom complet | abrév | adresse IP | commentaire
# chaines nationales
' 1 | TF1 | tf1 | udp://@239.100.9.0:1234'
' 2 | France 2 | fr2 | udp://@239.100.0.0:1234'
' 3 | France 3 | fr3 | udp://@239.100.0.4:1234'
' 4 | Canal+ | c+ | udp://@239.100.2.0:1234 | autre adresse : udp://@239.100.2.4:1234 (quelle différence ?)'
' 5 | France 5 | fr5 | udp://@239.100.3.3:1234'
' 6 | M6 | m6 | udp://@239.100.3.0:1234'
' 7 | Arte | a | udp://@239.100.3.2:1234'
' 8 | C8 | c8 | udp://@239.100.1.0:1234'
' 9 | W9 | w9 | udp://@239.100.3.1:1234'
'10 | TMC | tmc | udp://@239.100.9.2:1234'
'11 | TFX | tfx | udp://@239.100.9.3:1234'
'12 | NRJ12 | nrj | udp://@239.100.9.1:1234'
'13 | LCP Public Sénat | lcp | udp://@239.100.9.4:1234'
'14 | France 4 | fr4 | udp://@239.100.0.1:1234'
'15 | BFM TV | bfm | udp://@239.100.1.1:1234'
'16 | CNEWS | cn | udp://@239.100.1.2:1234'
'17 | CSTAR | cs | udp://@239.100.1.3:1234'
'18 | Gulli | gu | udp://@239.100.1.4:1234'
'19 | France Ô | fro | udp://@239.100.0.2:1234'
'20 | TF1 Séries Films | tf1sf | | dans le multiplex R7, qui est brouillé sur le campus de Cachan'
'21 | L’Équipe | eq | | dans le multiplex R7, qui est brouillé sur le campus de Cachan'
'22 | 6ter | 6t | udp://@239.100.3.4:1234'
'23 | RMC Story | rmcs | | dans le multiplex R7, qui est brouillé sur le campus de Cachan |'
'24 | RMC Découverte | rmcd | | dans le multiplex R7, qui est brouillé sur le campus de Cachan | ptdr'
'25 | Chérie 25 | ch25 | | dans le multiplex R7, qui est brouillé sur le campus de Cachan | xd | lol alpha |'
'26 | LCI | lci | udp://@239.100.2.2:1234'
'27 | FranceInfo | fri | udp://@239.100.0.3:1234'
# chaînes locales
'30 | BFM Paris | bfmp | udp://@239.100.0.5:1234'
'31 | Canal 31 | c31 | udp://@239.100.8.0:1234'
'32 | IDF1 | idf | udp://@239.100.8.1:1234'
'33 | France 24 | fr24 | udp://@239.100.8.2:1234'
'34 | viàGrandParis | via | udp://@239.100.8.3:1234'
# chaînes payantes
'41 | Paris Première | ppr | udp://@239.100.2.3:1234'
)
##
## Le Code, Le Vrai
##
# Appelle le programme «tput» s’il est disponible, ne fait rien sinon.
function tput() {
command -v tput >&- && command tput "$@"
}
# Termine avec un message d’erreur.
function error() {
echo>&2 -n "$(tput bold ; tput setaf 1)"
echo>&2 'erreur:' "$@"
echo>&2 -n "$(tput sgr0)"
exit 1
}
# Affiche un message d’information.
function message() {
echo -n "$(tput bold ; tput setaf 2)"
echo "$@"
echo -n "$(tput sgr0)"
}
# Vérifie que le programme donné est disponible.
function check_program() {
command -v "$1" >&- || error "la commande «$1» n’est pas disponible"
}
##
## (1) Parser les arguments.
##
# Affiche l’aide.
function usage() {
declare cmd_name="$(printf %q "$0")"
function ul() {
echo -n "$(tput smul)$1$(tput sgr0)"
}
declare chann="$(ul chaîne)"
declare titl="$(ul titre)"
declare time_start="$(ul heure_début)"
declare time_sto="$(ul heure_fin)"
declare date_forma="$(ul format_date)"
echo>&2 "
Enregistre une chaîne de télévision.
L’enregistrement est placé dans le dossier courant, avec un nom de
fichier qui contient la date, la chaîne et le titre donné.
Usage:
$cmd_name [$(ul options)] $chann $titl
Arguments obligatoires:
$chann chaîne à enregistrer (le numéro, le nom complet, ou une
abréviation; pour les noms reconnus, voir code source)
$titl titre à donner au fichier (peut contenir tout caractère
valide dans un nom de fichier)
Options:
--start $time_start (-t)
heure de début (suit le format de «at»)
--stop $time_sto (-T)
heure de fin (suit le format de «at»; un temps relatif
est interprété par rapport au début de l’enregistrement)
--date-format $date_forma (-d)
format de date utilisé dans le nom du fichier produit
(suit le format de «date»)
--abbr (-s)
utiliser l’abréviation de la chaîne plutôt que son nom
complet dans le nom du fichier produit
--no (-n)
utiliser le numéro de la chaîne plutôt que son nom
complet dans le nom du fichier produit
"
exit 1
}
# Options par défaut.
declare start_date='now'
declare stop_date='now + 24 hours'
declare file_name_date_format='%F-%H%M'
declare file_name_use_channel_name='fullname' # 'fullname' | 'abbr' | 'no'
declare short_options='t:T:d:an'
declare long_options='start:,stop:,date-format:,abbr,no'
check_program getopt
unset GETOPT_COMPATIBLE
getopt -T >/dev/null && error "ce script requiert la version GNU de «getopt»"
declare args="$(getopt -n "$0" -o "$short_options" -l "$long_options" -- "$@")" || usage
eval "set -- $args"
while [ $# -gt 0 ] ; do
case "$1" in
-t | --start )
start_date="$2"
shift
;;
-T | --stop )
stop_date="$2"
shift
;;
-d | --date-format )
file_name_date_format="$2"
shift
;;
-a | --abbr )
file_name_use_channel_name='abbr'
;;
-n | --no )
file_name_use_channel_name='no'
;;
--)
shift
break
;;
esac
shift
done
[ $# -eq 2 ] || usage
declare chan_hint="$1"
declare title="$2"
##
## (2) Parser la liste des chaînes et trouver celle demandée.
##
# Rogne (ôte les espaces initiaux et finaux) la variable de nom donné.
function trim_var() {
declare -n _trim_var_ref="$1"
# remove leading whitespace characters
_trim_var_ref="${_trim_var_ref#"${_trim_var_ref%%[![:space:]]*}"}"
# remove trailing whitespace characters
_trim_var_ref="${_trim_var_ref%"${_trim_var_ref##*[![:space:]]}"}"
}
# Utilisation:
# parse_array 'ligne' var1 var2 … varN
# Découpe 'ligne' en cellules séparées par des barres verticales '|', stocke la
# 1re cellule dans la variable var1, la 2nde dans var2, etc. Stocke les cellules
# restantes dans un tableau dans varN. Les cellules sont rognées.
function parse_array() {
declare _parse_array_input="$1"
shift
# split the first cells and assign them to the names given, one cell per name:
IFS='|' read -r "$@" <<< "$_parse_array_input "
# split remaining cells and assign them to the last name given, as an array:
declare -n _parse_array_ref="${@: -1}"
IFS='|' read -r -a "${!_parse_array_ref}" <<< "$_parse_array_ref"
# trim individual cells:
for _parse_array_ref ; do
for _parse_array_index in "${!_parse_array_ref[@]}" ; do
trim_var "_parse_array_ref[$_parse_array_index]"
done
done
}
declare found='false'
for line in "${channels[@]}" ; do
parse_array "$line" chan_no chan_name chan_abbr chan_addr chan_com remaining
unset remaining
found='true'
shopt -s nocasematch
[[ "$chan_hint" -eq "$chan_no" ]] 2>&- && break
[[ "$chan_hint" = "$chan_name" ]] && break
[[ "$chan_hint" = "$chan_abbr" ]] && break
shopt -u nocasematch
found='false'
done
[ "$found" = 'true' ] || error "la chaîne «$chan_hint» est inconnue"
[ -n "$chan_addr" ] || error "la chaîne «$chan_name» est inaccessible: $chan_com"
check_program date
check_program at
check_program sleep
check_program systemd-inhibit
check_program ffmpeg
##
## (3) Attendre l’heure de début.
##
# Bloque la mise en veille en attendant l’heure de début.
systemd-inhibit --who="$0" --why='enregistrement TV programmé' \
sleep infinity &>/dev/null &
declare pid_inhibit="$!"
# Met fin au blocage lorsque l’enregistrement doit commencer.
<<<"$(at -M "$start_date" <<< "kill -TERM $pid_inhibit" |& tail -1)" \
read -r _ start_job_id _ start_at
# Nettoyage en cas d’interruption.
function mytrap() {
at -d "$start_job_id" &>/dev/null || true
kill -TERM "$pid_inhibit" &>/dev/null || true
message 'enregistrement annulé'
exit 0
}
trap 'mytrap' INT QUIT TERM
# Attend l’heure de début.
message "enregistrement programmé de $chan_name, début prévu: $start_at"
wait -f "$pid_inhibit"
##
## (4) Calculer le nom du fichier produit.
##
declare date="$(date "+$file_name_date_format")"
declare chan
case "$file_name_use_channel_name" in
'fullname' ) chan="$chan_name" ;;
'abbr' ) chan="$chan_abbr" ;;
'no' ) chan="$chan_no" ;;
* ) error 'ERREUR INTERNE!' ;;
esac
declare output_file_name="${date}_${chan}_${title}.ts"
##
## (5) Enregistrer.
##
# Lance l’enregistrement.
systemd-inhibit --who="$0" --why='enregistrement TV en cours' \
ffmpeg -y -i "$chan_addr" -map 0 -c copy -copy_unknown "$output_file_name" \
</dev/null &>/dev/null &
declare pid_ffmpeg="$!"
# Met fin à l’enregistrement au temps spécifié.
<<<"$(at -M "$stop_date" <<< "kill -TERM $pid_ffmpeg" |& tail -1)" \
read -r _ stop_job_id _ stop_at
# Nettoyage en cas d’interruption.
function mytrap() {
at -d "$stop_job_id" &>/dev/null || true
kill -TERM "$pid_ffmpeg" &>/dev/null || true
message 'enregistrement interrompu'
exit 0
}
trap 'mytrap' INT QUIT TERM
# Attend l’heure de fin.
message "enregistrement commencé, fin prévue: $stop_at"
wait -f "$pid_ffmpeg"
message "enregistrement terminé"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment