#1 Le 30/07/2012, à 13:06
- toulipe
[Script] Mini gestionnaire de téléchargement basé sur plowshare
J'avais du temps à perdre pendant cet été, et m'étais mis en tête, plus ou moins pour m'amuser, d'essayer d'apprendre à scripter en bash - ce que je n'avais jamais fait auparavant, et ne ferai probablement plus jamais, cela parce que l'informatique n'est pas ma branche, et que les années qui suivent seront pour moi trop chargées pour que je n'ait le temps de m'adonner à ce type de loisir.
De fil en aiguille, j'ai décidé d'essayer d'écrire un vrai programme (mon premier et mon dernier !), en l'occurrence, un mini gestionnaire de téléchargement. Il s'appuie sur plowshare, un outil en ligne de commande qui permet de télécharger à la chaîne sur les sites d'hébergement de fichier (rapidshare, 4shared, etc.), et sur yad, un autre outil en ligne de commande qui permet de créer des interfaces graphiques simple (un peu comme zenity, mais en plus riche) à partir d'un script bash.
Le script est malheureusement encore fortement buggué et probablement inutilisable en l'état, et la rédaction de certaines sections (en particulier celle qui concerne les téléchargements proprement dits, et la gestion de la barre de progression) n'est pas terminée. Comme je n'aurai probablement pas le temps de retravailler le code avant un bon moment (si je le retravaille un jour), je le laisse tout de même ici, au cas où il pourrait donner des idées à certains, ou leur servir pour quoi que ce soit.
Présentation rapide :
Le lancement du script provoque la création d'un répertoire de cache (~/.cache/gplowdown) où sont stockées les informations relatives aux téléchargements. Il provoque aussi la création d'une icône dans la zone de notification (qui comprend un menu simple permettant d'accéder aux fonctions du script).
L'interface graphique comprend principalement trois fenêtres :
- une fenêtre d'ajout de liens, dans laquelle on doit coller les URL des fichiers que l'on veut télécharger. Lors d'un ajout de liens, les URL sont testées unes par unes pour vérifier qu'elles sont valides. S'il y a des liens morts, ou invalides, etc., une fenêtre s'ouvre pour afficher les erreurs rencontrées
- une fenêtre comprenant la liste de tous les liens qui ont été ajoutés, classés par état (en attente de téléchargement, en cours de téléchargement, téléchargé, ou invalide). Cette fenêtre est mise à jour automatiquement pour suivre l'état d'avancement des téléchargements.
- une fenêtre comprenant une barre de progression, qui affiche l'état d'avancement du téléchargement en cours.
Images :
http://i.imgur.com/rHpzZ.png
http://pix.toile-libre.org/upload/origi … 050341.png
http://i.imgur.com/rHpzZ.png
http://pix.toile-libre.org/upload/origi … 050341.png
Liens annexes :
Plowshare : http://code.google.com/p/plowshare/
Yad : http://code.google.com/p/yad/ (version compilée du logiciel ici)
Script :
#!/bin/bash
# Dépendances : plowshare, curl, yad, wmctrl, xwininfo
set -o pipefail
export repertoire_telechargements="$HOME/Téléchargements"
export cache="$HOME/.cache/gplowdown"
export liste_liens_en_attente="$cache/liste-liens-en-attente"
export liste_liens_en_cours="$cache/liste-liens-en-cours"
export liste_liens_telecharge="$cache/liste-liens-telecharge"
export liste_liens_erreur="$cache/liste-liens-erreur"
export liste_liens_liste="$cache/liste-liens-liste"
export liste_liens_infos="$cache/liste-liens-infos"
export tube_notification="/tmp/gplowdown-fifo-notification"
export tube_progression="/tmp/gplowdown-fifo-progression"
export tube_tableau="/tmp/gplowdown-fifo-tableau"
export infos_fichier_progression=/tmp/gplowdown-infos-fichier-progression
export pid_filtre=/tmp/gplowdown-pid-filtre
#########################################################################################################################
#------------------------------------------- NOTIFICATION -------------------------------------------------------------#
notification ()
{
mkfifo $tube_notification
exec 3<> $tube_notification
if ! [ -s $liste_liens_en_attente ] ; then
clic_zone_notification () {
! wmctrl -c "Ajouter des liens - gplowdown" && bash -c fenetre_progression
}
else
clic_zone_notification () {
! wmctrl -c "Téléchargement en cours - gplowdown" && bash -c fenetre_progression
}
fi
export -f clic_zone_notification
yad --notification --item-separator=+ \
--text=gplowdown --image=gtk-goto-bottom \
--listen \
--command="bash -c clic_zone_notification" <&3 &
echo "menu:Ajouter des liens+bash -c fenetre_ajout_liens+gtk-add|Liste des liens+bash -c fenetre_tableau+gtk-sort-ascending|Quitter+bash -c quitter+gtk-quit" > $tube_notification
}
#########################################################################################################################
#---------------------------------------------- TEST DES LIENS ---------------------------------------------------------#
extraction_nouveaux_liens ()
{
declare -r stderr_plowdown=$cache/stderr-plowdown
declare -i nombre_liens=$(grep "." $liste_liens_en_attente | wc -l)
> $stderr_plowdown
# Il faut d'abord nettoyer le tableau :
traitement_tableau supprimer en_attente
while read lien ; do
let numero_lien++
nom_fichier="$(grep -A1 $lien $liste_liens_infos | tail -1)"
if [ -z $nom_fichier ] ; then
test_nouveaux_liens $lien
else
lien=$(echo $lien | sed 's/\//\\\//g')
traitement_tableau modifier en_attente $lien "$nom_fichier"
fi
done < $liste_liens_en_attente | yad --title="Analyse des liens - gplowdown" \
--window-icon=gtk-goto-bottom \
--width=400 \
--dialog-sep \
--button=gtk-close:1 \
--progress --pulsate \
--auto-close --auto-kill
[ "$?" -ne 0 ] && exit
grep '^http' $stderr_plowdown && fenetre_affichage_erreurs
! xwininfo -name 'Liste des liens - gplowdown' && fenetre_tableau
}
export -f extraction_nouveaux_liens
test_nouveaux_liens ()
{
lien=$1
declare -i code_sortie_plowdown
echo $lien >> $stderr_plowdown
while : ; do
echo "#Examen des liens [$numero_lien/$nombre_liens] ..."
# Il s'agit d'abord de vérifier si le lien
# est valide, mais aussi de récupérer quelques
# informations intéressantes, à savoir
# le nom du fichier et sa taille.
plowdown -t 20 --printf %u%n%f \
--exec "curl -I %u | grep 'Content-Length' | cut -f 2 -d ' '" $lien \
>> $liste_liens_infos 2>> $stderr_plowdown
retour=$?
traitement_erreurs_test_liens
done
return $retour # voir plus bas
}
export -f test_nouveaux_liens
traitement_erreurs_test_liens ()
{
case $retour in
0)
lien=$(echo $lien | sed 's/\//\\\//g')
sed -i "/$lien/q" $stderr-plowdown
sed -i "s/$lien//" $stderr_plowdown
traitement_tableau modifier en_attente $lien $(sed -n "/$lien/{n;p;}" $liste_liens_infos)
break ;;
3|5|6)
# Le retour d'un code d'erreur 3, 5, ou 6, est,
# la plupart du temps, causé par un problème
# de connexion.
let essai++
if [ $essai -eq $nombre_maxi_essai_connexion ] ; then
echo $lien >> $liste_liens_erreur
echo -e "$lien\nNom inconnu\nInconnue" >> $liste_liens_infos
lien=$(echo $lien | sed 's/\//\\\//g')
traitement_tableau modifier erreur $lien 'Nom inconnu'
sed -i "s/$lien//" $liste_liens_en_attente
break
else
temps_latence=$temps_latence_reconnexion
while [ $temps_latence -gt 0 ]; do
echo "#Problème de connexion. Nouvelle tentative dans $temps_latence s..."
let temps_latence--
sleep 1
done
fi ;;
*)
echo $lien >> $liste_liens_erreur
echo -e "$lien\nNom inconnu\nInconnue" >> $liste_liens_infos
lien=$(echo $lien | sed 's/\//\\\//g')
traitement_tableau modifier erreur $lien 'Nom inconnu'
sed -i "s/$lien//" $liste_liens_en_attente
break ;;
esac
}
export -f traitement_erreurs_test_liens
fenetre_affichage_erreurs ()
{
# Aérer le stderr de plowdown
sed -i -e 's/Starting download.*//' \
-e 's/File URL.*//' \
-e 's/Filename.*//' \
-e '/^$/d' -e 's/^http.*/\n&/' $stderr_plowdown
yad --width=600 --height=400 \
--window-icon=gtk-goto-bottom \
--title="Résultats du test des liens - gplowdown" \
--image=gtk-dialog-error \
--text="Il y a eu un problème pendant le traitement des liens :" \
--text-info --wrap --show-uri \
--filename=$stderr_plowdown \
--dialog-sep \
--button=gtk-close
}
export -f fenetre_affichage_erreurs
#########################################################################################################################
#------------------------------------------- LISTE DES LIENS -----------------------------------------------------------#
traitement_tableau ()
{
# $1 = action (modifier, supprimer)
# $2 = statut du lien (en_attente, en_cours, telecharge, erreur)
# $3 = lien
# $4 = nom du fichier associé au lien
# Pas de paramètre : mise à jour du tableau pour la fenêtres de liste des liens
# Noter que yad ne lit pas correctement le fichier $liste-liens-liste s'il n'y a pas une ligne vide en fin
# de fichier.
local icone_en_attente=gtk-add
local icone_en_cours=gtk-execute
local icone_telecharge=gtk-apply
local icone_erreur=gtk-dialog-error
# Si le fichier est vide, sed ne peut peut rien faire
! [ -s $liste_liens_liste ] && echo > $liste_liens_liste
if [ "$1" = modifier ] ; then
if [ "$2" = en_attente ] ; then
if grep -q $icone_en_cours $liste_liens_liste ; then
sed -i "0,/$icone_en_cours/ s//$icone_en_attente\n$3\n$4\n&/" $liste_liens_liste
elif grep -q $icone_telecharge $liste_liens_liste ; then
sed -i "0,/$icone_telecharge/ s//$icone_en_attente\n$3\n$4\n&/" $liste_liens_liste
elif grep -q $icone_erreur $liste_liens_liste ; then
sed -i "0,/$icone_erreur/ s//$icone_en_attente\n$3\n$4\n&/" $liste_liens_liste
else
sed -i "\$a$icone_en_attente\n$3\n$4" $liste_liens_liste
fi
elif [ "$2" = en_cours ] ; then
# Uniquement besoin de changer le statut, et aussi la forme du lien
perl -i -0pe "s/$icone_en_attente(.*\n.*$3)/$icone_en_cours\1/g;" $liste_liens_liste
elif [ "$2" = telecharge ] ; then
# On insère juste avant la première occurrence de $icone_erreur :
sed -i "/\n.*\n/!N;/$3/{$d;N;d};P;D" $liste_liens_liste
sed -i "0,/$icone_erreur/ i\$icone_telecharge\n$3\n$4" $liste_liens_liste
elif [ "$2" = erreur ] ; then
# Pour un lien erroné, c'est tout à la fin du tableau
sed -i "/\n.*\n/!N;/$3/{$d;N;d};P;D" $liste_liens_liste
sed -i "\$a$icone_erreur\n$3\n$4" $liste_liens_liste
fi
elif [ "$1" = supprimer ] ; then
if [ "$2" = en_attente ] ; then
sed -i "/$icone_en_attente/,+2d" $liste_liens_liste
elif [ "$2" = telecharge ] ; then
sed -i "/$icone_telecharge/,+2d" $liste_liens_liste
elif [ "$2" = erreur ] ; then
sed -i "/$icone_erreur/,+2d" $liste_liens_liste
fi
fi
sed -i '/^$/d' $liste_liens_liste
# Vérifie si le tableau est ouvert, et si c'est le cas, le met à jour
if xwininfo -name 'Liste des liens - gplowdown' 2>/dev/null || [ -z "$1" ] ; then
exec 3<> $tube_tableau
if xwininfo -name 'Liste des liens - gplowdown' 2>/dev/null ; then
echo -e '\f' > $tube_tableau
# Bug de yad : ne prend pas en compte la toute première entrée du tableau
# si le tableau est renouvelé alors que la fenêtre est déjà ouverte. Il faut donc
# écrire la première entrée en double
sed 3q $liste_liens_liste > $tube_tableau
fi
cat $liste_liens_liste > $tube_tableau
fi
}
export -f traitement_tableau
nettoyage_tableau ()
{
selection=$(yad --width=300 \
--window-icon='gtk-goto-bottom' \
--title='Nettoyage - gplowdown' \
--image=gtk-clear \
--text='Choisissez les éléments à supprimer :' \
--form \
--field='Liens en attente':CHK \
--field='Liens téléchargés':CHK \
--field='Liens invalides':CHK \
--dialog-sep)
if [ "$?" -eq 0 ] ; then
if [ "$(echo $selection | cut -f 1 -d '|')" = TRUE ] ; then
traitement_tableau supprimer en_attente
> $liste_liens_en_attente
fi
if [ "$(echo $selection | cut -f 2 -d '|')" = TRUE ] ; then
traitement_tableau supprimer telecharge
> $liste_liens_telecharge
fi
if [ "$(echo $selection | cut -f 3 -d '|')" = TRUE ] ; then
traitement_tableau supprimer erreur
> $liste_liens_erreur
fi
fi
}
export -f nettoyage_tableau
fenetre_tableau ()
{
! [ -p $tube_tableau ] && mkfifo $tube_tableau
traitement_tableau
yad --width=800 --height=400 \
--window-icon=gtk-goto-bottom \
--title='Liste des liens - gplowdown' \
--list --ellipsize=end \
--column='':IMG --column=Lien --column=Fichier \
--button=gtk-add:'bash -c fenetre_ajout_liens' \
--button=Télécharger:'bash -c fenetre_progression' \
--button=Nettoyer:'bash -c nettoyage_tableau' \
--button=gtk-close < $tube_tableau
}
export -f fenetre_tableau
#########################################################################################################################
#--------------------------------------------- AJOUT DE LIENS ----------------------------------------------------------#
fenetre_ajout_liens ()
{
sed -i '/^$/d' $liste_liens_en_attente
liste_liens_en_attente_temp=$liste_liens_en_attente-`date +%s`
yad --width=750 --height=400 \
--title='Ajouter des liens - gplowdown' \
--window-icon=gtk-goto-bottom \
--text="Les fichiers associés à chaque lien sont téléchargés dans l'ordre, en partant du haut pour aller vers le bas. Chaque lien peut être déplacé ou supprimé, et de nouveaux liens peuvent être ajoutés.\n" \
--text-info --editable --wrap --margins=5 \
--filename=$liste_liens_en_attente \
--dialog-sep \
--button=gtk-save:0 --button=gtk-cancel:1 \
> $liste_liens_en_attente_temp
if [ "$?" -eq 0 ] ; then
# Formatage basique : pas d'espaces ou tabulations
# en début ou fin de ligne, pas de lignes doubles, pas de lignes vides.
sed -i -e 's/^[ \t]*//' -e 's/[ \t]*$//' $liste_liens_en_attente_temp
sed -i '/^$/d' $liste_liens_en_attente_temp
awk '!x[$0]++' $liste_liens_en_attente_temp > $liste_liens_en_attente
rm -f $liste_liens_en_attente_temp
extraction_nouveaux_liens
else
# Yad n'imprime rien sur stdout si aucune modification
# n'a été apportée au texte de la boîte de dialogue.
# On supprime la liste de lien temporaire si elle est vide.
if ! [ -s $liste_liens_en_attente_temp ] ; then
rm -f $liste_liens_en_attente_temp
# Sinon, on fait en sorte de ne garder
# que les listes de liens temporaires les plus récentes.
elif [ `ls $cache | grep -c 'liste-liens-en-attente[0-9]*'` -ge 15 ] ; then
liste_liens_en_attente_ancienne=`ls -t $cache | grep 'liste-liens-en-attente-[0-9]*' | tail -1`
rm -f $cache/$liste_liens_en_attente_ancienne
fi
fi
}
export -f fenetre_ajout_liens
#########################################################################################################################
#--------------------------------------------- BARRES DE PROGRESSION --------------------------------------------------#
# 3 éléments principaux :
# 1°) Une fenêtre de progression, qui ne fait que lire un fifo
# 2°) Un processus non visible (plowdown, curl) qui télécharge les fichiers. Le stderr de plowdown est redirigé
# vers un fichier temporaire, de sorte que l'état d'avancement des téléchargements puisse être connu
# par la fenêtre de progression. Désavantage notable : si le téléchargement est trop lent, (de l'ordre de 50 k/s)
# le fichier temporaire va acquérir une taille assez importante. Avantage : passer par un fichier temp permet
# de se passer de faire des calculs (taille du fichier, etc.), et reflète d'une manière plus fiable
# l'état du téléchargement.
# 3°) Un filtre (awk), qui extrait du fichier temporaire les informations importantes (pourcentage du téléchargement
# et temps restant), pour les envoyer vers le fifo, qui, lui, est directement connecté à la fenêtre
# de progression
download_start ()
{
while : ; do
lien=$(grep -m1 '^http' $liste_liens_en_attente)
if [ -z $lien ] ; then
echo '#Rien à télécharger' > $tube_progression
exit
fi
nom_fichier=$(grep -A1 $lien $liste_liens_infos | tail -1)
echo '#Préparation...' > $tube_progression
echo $lien > $liste_liens_en_cours
lien_sed=$(echo $lien | sed 's/\//\\\//g')
sed -i "s/$lien_sed//" $liste_liens_en_attente
traitement_tableau modifier en_cours $lien_sed $nom_fichier
# On lance le filtre si besoin est :
! [ -s $pid_filtre ] && filtre &
echo $! > $pid_filtre
plowdown $lien -o $repertoire_telechargements 2> $infos_fichier_progression
retour=$?
case $retour in
0)
echo $lien >> $liste_liens_telecharges
> $liste_liens_en_cours
traitement_tableau modifier telecharge $lien_sed $nom_fichier
break
;;
3|5|6)
let essai ++
if [ $essai -eq $nombre_maxi_essai_connexion ] ; then
echo $lien >> $liste_liens_erreur
> $liste_liens_en_cours
traitement_tableau modifier erreur $lien_sed $nom_fichier
break
else
echo '#Pas de connexion...' > $tube_progression
sleep 20
fi
;;
*)
echo $lien >> $liste_liens_erreur
> $liste_liens_en_cours
traitement_tableau modifier erreur $lien_sed $nom_fichier
break
;;
esac
> $infos_fichier_progression
done
}
export -f download_start
filtre ()
{
exec 5<> $tube_progression
lien=$(grep -m1 '^http' $liste_liens_en_cours)
nom_fichier=$(grep -A1 $lien $liste_liens_infos | tail -1)
while : ; do
awk 'END{ print $(NF-11) "\n#'$nom_fichier' (" $(NF-1) " restant)" }' \
$infos_fichier_progression > $tube_progression
sleep 1
done
}
export -f filtre
fenetre_progression ()
{
! [ -p $tube_progression ] && mkfifo $tube_progression
exec 5<> $tube_progression
if [ -s $liste_liens_en_cours ] ; then
filtre &
echo $! > $pid_filtre
else
echo '#Aucun téléchargement en cours.' > $tube_progression
fi
yad --title='Téléchargement en cours - gplowdown' \
--window-icon=gtk-goto-bottom \
--dialog-sep \
--button=gtk-media-record:"bash -c download_start" \
--button=gtk-media-stop:"bash -c download_stop" \
--button=gtk-close \
--progress < $tube_progression
kill $(cat $pid_filtre) && rm -f $pid_filtre
}
export -f fenetre_progression
download_stop ()
{
declare -a pids_plowdown='`pidof -x plowdown`'
for pid_plowdown in ${pids_plowdown[*]} ; do
pid_curl=$(ps --ppid $pid_plowdown | grep -o '[0-9]*')
[ -n $pid_curl ] && kill $pid_curl
kill $pid_plowdown
done 2> /dev/null
rm -f $infos_fichier_progression
echo '#Stoppé' > $tube_progression
}
export -f download_stop
quitter ()
{
# Un message d'avertissement s'il y a
# des téléchargements en cours.
if pidof plowdown ; then
yad --width=100 \
--window-icon="gtk-goto-bottom" \
--title=gplowdown \
--image=gtk-dialog-warning \
--text='Il y a des téléchargements en cours. Dois-je vraiment les stopper ?' \
--button=gtk-yes:0 --button=gtk-no:1
if [ $? -ne 0 ] ; then
exit
fi
fi
wmctrl -c 'Liste des liens - gplowdown'
wmctrl -c 'Téléchargements en cours - gplowdown'
wmctrl -c 'Ajouter des liens - gplowdown'
echo quit > $tube_notification
download_stop
rm -f $infos_fichier_progression
rm -f $tube_notification
rm -f $tube_progression
rm -f $tube_tableau
# plowdown crée lui aussi des fichiers temporaires
rm -f /tmp/plowdown.*
}
export -f quitter
#########################################################################################################################
#-------------------------------------------------- EXECUTION ----------------------------------------------------------#
! [ -d $cache ] && mkdir $cache
! [ -f $liste_liens_infos ] && touch $liste_liens_infos
! [ -f $liste_liens_en_attente ] && touch $liste_liens_en_attente
! [ -f $liste_liens_en_cours ] && touch $liste_liens_en_cours
! [ -f $liste_liens_telecharge ] && touch $liste_liens_telecharge
! [ -f $liste_liens_erreur ] && touch $liste_liens_erreur
! [ -f $liste_liens_liste ] && touch $liste_liens_liste
! [ -p $tube_notification ] && notification
fenetre_tableau
Ce que je voulais faire mais n'ai pas eu le temps de terminer :
- offrir la possibilité de définir des "groupes" de fichiers à télécharger, et indiquer l'état global d'avancement du téléchargement de tel groupe plutôt que l'état d'avancement de chaque fichier de ce groupe
- utiliser l'infobulle de la zone de notification pour afficher des informations sur l'état des téléchargements
- permettre plusieurs téléchargements simultanés
- utiliser plusieurs barres de progression d'une manière intelligente.
Si un jour j'ai le temps, j'essayerai de finir le script.
Sur ce, je vous souhaite à tous une bonne fin d'été, et surtout, beaucoup de soleil !
Edit modo : taille des images. Merci de relire les règles, et de mettre une miniature ou un lien.
Dernière modification par na kraïou (Le 15/08/2012, à 20:06)
Hors ligne
#2 Le 03/08/2012, à 15:52
- kikislater
Re : [Script] Mini gestionnaire de téléchargement basé sur plowshare
Super ça !
Bonne idée
Hors ligne
#3 Le 05/08/2012, à 09:58
- Xun
Re : [Script] Mini gestionnaire de téléchargement basé sur plowshare
Sympa
Merci !
Hors ligne
#4 Le 15/08/2012, à 18:23
- toulipe
Re : [Script] Mini gestionnaire de téléchargement basé sur plowshare
Amendement important du premier post.
Hors ligne