Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 30/07/2012, à 14: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, à 21:06)

Hors ligne

#2 Le 03/08/2012, à 16: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, à 10:58

Xun

Re : [Script] Mini gestionnaire de téléchargement basé sur plowshare

Sympa big_smile
Merci !

Hors ligne

#4 Le 15/08/2012, à 19:23

toulipe

Re : [Script] Mini gestionnaire de téléchargement basé sur plowshare

Amendement important du premier post.

Hors ligne