Session mod_python

Green Tree Python

Mod_python est une extension de apache pour écrire des applications web en python. Une des fonctionnalités est la gestion des sessions. En J2EE, l’api des servlet offre la même fonctionnalité…en plus intuitif.

En effet pour un programmeur Java demander la session associé à une requête est une action banale. Mais J2EE est un framework plus sécurisant et accessible. Ainsi lors du traitement d’une requête, le programmeur de la servlet peut demander et redemander à tout moment la session associée à la requête courante et cela même si entre temps il y a eu un forward.

Contrairement à J2EE, mod_python s’appuie sur les fondements de apache: une requête est exécutée par un worker i.e. un thread à part et cela s’applique aussi aux redirections internes (équivalent des forward des servlet java). Cela pose un problème au développeur habitué à Java. Car par défaut mod_python crée les objets session en les verrouillant. Cela part d’une bonne intention: une session c’est un client (navigateur) donc il est bon d’éviter la concurrence qui est un cas anormale. Si une session est verrouillée par une requête (worker), alors toute tentative de demander la session par une autre requête est bloquée jusqu’à le déverrouillage par la première requête.

Ainsi lorsque dans un publisher on demande une session puis que l’on redirige (forward) vers une psp qui dans son code demande la session, alors il y a blocage car le traitement de la psp est fait comme si c’était une autre requête. Une solution consiste à reproduire le comportement J2EE en demandant explicitement de ne pas verrouiller la session.

session = Session.Session(req, lock=0)

Apache, Mod_python et sqlite3

Green Tree Python
Image by Sebastian Niedlich (Grabthar) via Flickr

Web900 se devant de devenir multi-utilisateur, il fallut lui chercher un moyen de persistance plus robuste que le système de fichier et qui sache gérer la concurrence: une base de données. Sqlite3 est une base de données in-process qui simplifie le paramétrage puisqu’il n’y a pas de client-serveur.

L’utilisation conjointe de mod_python et de sqlite3 nécessite néanmoins quelques précautions. En effet, si sqlite est sans danger dans un programme mono-process, il n’en va pas de même avec mod_python où chaque worker de apache est un processus ayant chacun son espace mémoire pour python. Cela implique de ne pas utiliser le mode in-memory de sqlite (:memory:) et oblige à passer par une base physique sur le disque.

La gestion des redirections par mod_python doit également susciter une attention particulière. Dans le monde Java, une servlet peut transmettre une requèete à un autre composant web (servlet ou jsp) qui recoit alors l’intégralité de la requête initiale plus ce que y aurait mis la servlet initiale. Avec mod_python, cela ne marche pas comme ça. Si le mécanisme de redirection peut être interne, une nouvelle requête est créée donc on ne peut pas enrichir l’objet requête pour la passer au traitement suivant. Heureusement que mod_python fournit dans l’objet requête au pointeur sur la requête initiale. Cela fait tout de même un petit travail supplémentaire.

Interface web pour le N900

The Nokia N900 showing system information in x...
Image via Wikipedia

Il existait pour symbian un prototype pleinement fonctionnel d’interface web Mobile Web Server. Il permettait via un navigateur d’accéder au contenu et principales fonctions du terminal en temps réel: le terminal agissait comme un serveur !

Malheureusement le concept n’a pas eu de suite et le projet a été abandonné par Nokia. Concept trop geek sans doute pour les clients de Nokia. Il est pourtant bien pratique d’avoir accès sur grand écran à son répertoire, de composer et d’envoyer un sms avec un vrai clavier, d’accéder aux fichiers du téléphone sans avoir à brancher un câble ou ouvrir une connection dont le protocol porte un nom barbare comme ftp ou ssh.

Je me suis mis donc en tête de réaliser une solution équivalente. Le projet s’appelle web900. Il est constitué d’une partie qui s’installe sur un serveur http apache dont le role est de fournir l’ihm web et d’être le serveur auquel accède la partie cliente qui tourne sur le terminal. Le langage utilisé est python.

Web900 sur un .... N900

En l’état web900 donne quelques informations sur le statut du terminal (heure, charge, profil) et permet d’envoyer des sms, de visualiser le carnet d’adresse et d’initier des appels. Par ailleurs web900 permet d’ouvrir le navigateur du N900 sur une url donnée et d’envoyer des fichiers sur le terminal.

Techniquement web900 ne fait pas de “push” sur le N900 mais le N900 “pool” l’application web.

Interface web pour chaine de conversion freebox pour N900

The multimedia part of the two set-top boxes c...
Image via Wikipedia

Le billet Automatisation des enregistrements de la freebox pour n900 montrait comment on pouvait convertir automatiquement les enregistrements de la freebox et les transférer sur le terminal sans autre intervention humaine que celle de programmer les enregistrements.

La chaine qui va de l’enregistrement, au transfert sur le N900 en passant par le téléchargement sur le PC et la conversion peut durer plusieurs heures. Aussi le besoin de contrôler l’état du processus à distance amène à répéter les tâches de connexion en ssh, listing de répertoire, vérification de la présence de fichier .lock etc.

Ce billet se propose de mettre en place une interface web afin de controler le processus de conversion à distance. Pour cela nous allons écrire une application web en python. Se référer au billet précédent pour l’installation de mod_python, nous nous attacherons ici seulement à décrire l’interface web.

L’application se compose de page psp dont voici le point d’entrée. On affiche l’état de la Freebox, à savoir si un enregistrement est en cours ou non, le nombre d’enregistrements et l’espace occupé. La page montre également l’état du rapatriement, c’est-à-dire  les téléchargements des enregistrement. Enfin elle donne un état de l’étape de conversion.

capture d'écran de n900replay sur un ... n900

L’application donne aussi la liste des fichiers qui se trouvent sur la Freebox, la liste des fichiers rapatriés et enfin la liste des fichiers en file d’attente pour conversion ainsi que les fichiers convertis.

L’ensemble des sources se trouvent sur googlecode n900replay . On  y trouve les scripts de la chaîne de conversion ainsi que l’application web de gestion.

Développement web rapide en python

Python logo
Image via Wikipedia

Java souffre d’une lourdeur incompatible avec le prototypage d’application web. En effet nous entendons par prototypage le fait de réaliser rapidement une application qui sera dans ce cas précis en mode web. Java impose une architecture robuste mais incompatible avec un développement rapide. Il oblige ainsi à “packager” l’application dans un war déployé dans un conteneur (ex: Tomcat). Java manque tout simplement d’agilité comparé à php pour le développement web.

Pour autant php est trop orienté web et ne va pas bien s’adapter en cas de monté en puissance. En effet comment partager le code en ayant la richesse du paradigme objet ou comment faire s’exécuter du code en dehors d’un contexte web ? Ce que Java propose…

Python peut être une alternative qui présente le meilleur des 2 mondes: puissance de l’objet pour la maintenabilité, la réutilisation et la rapidité du prototypage avec un langage de script. C’est ce que mod_python propose en ajoutant au serveur apache la capacité à exécuter du code python.

Le paquet mod_python est disponible depuis le dépôt officiel de Ubuntu 10.10. Une fois installé il faut déclarer dans apache une url qui va devenir python-aware. Pour cela il suffit de se rendre dans le répertoire de configuration de apache pour y créer le fichier /etc/apache2/con.d/replay en supposant que l’on veuille faire une application web qui s’appelle replay.

Alias /replay /home/thierry/Vidéos/n900
<Location /replay>
        Order Allow,Deny
        Allow from 192.168.0.0/255.255.0.0
        AddHandler mod_python .psp .psp_ .py
        PythonHandler mod_python.psp | .psp .psp_
        PythonHandler mod_python.publisher | .py
        PythonDebug On
</Location>

Ce fichier nous permet de déclarer l’url /replay qui devient pythonique ! Les url de la forme /replay/*.psp seront servies par le moteur de page Python Server Page (équivalent des JSP) tandis que celles de la forme /replay/*.py seront exécutées comme des scriptes pythons (équivalent des servlet).
Les url de la forme /replay/*.psp_ permettent d’afficher le code source des pages .psp à côté du scripye python généré à des fins de débogage. Étant donné qu’on active le mode debug, le fichier restreint l’accès aux url au réseau local.

Python Server Page

Voici un exemple de page PSP:

<html>
<table width="100%">
<tr bgcolor="grey">
<th>Queue</th>
</tr>
<tr>
<td>
<%
# Etat du processus de conversion
%>

<table width="100%">
<tr>
<th>Fichier</th><th>Taille</th><th>Etat</th><th>Action</th>
</tr>
<%
import os
import os.path
list = os.listdir("/home/thierry/Vidéos/n900")
work_in_progress  = ""
if os.path.exists("/home/thierry/Vidéos/n900/.work_in_progress") :
  f = open("/home/thierry/Vidéos/n900/.work_in_progress", "r");
  work_in_progress = f.read()
  f.close()
for f in list:
  if f.endswith(".ts"):
    filename = unicode(f,'utf')
    is_work_in_progress = work_in_progress.rpartition('.mp4')[0].endswith(f)
    etat = "En attente"
    if is_work_in_progress :
      etat = "En cours"
%>
<%
    # ferme le bloc if
%>

<tr>
<td><%=filename.encode('ascii','xmlcharrefreplace')%></td><td><%=os.path.getsize("/home/thierry/Vidéos/n900/" + f)%></td><td><%=etat%></td>
<td>
<form action="action.py/remove_queue" method="POST">
<input type="hidden" name="file" value="<%=filename.encode('ascii','xmlcharrefreplace')%>">
<input type="submit" value="supprimer">
</form>
</td>
</tr>
<%
# ferme le bloc for
%>

</table>

</td>
</tr>
</table>
</html>

Retenons que le code python s’écrit dans la page PSP comme les scriptlet java dans les pages JSP. On est loin de l’industrialisation de J2EE, il n’y a pas de notion de taglib, c’est du rustique ! Attention à l’indentation pour que le générateur de code ferme un bloc il est nécessaire d’avoir une instruction qui de-indente i.e ferme le bloc précédent. Pour cela il faudra parfois ajouter une code vide avec la nouvelle indentation ou mieux avec un commentaire qui explique notre intention.

Scripte .py

Il s’agit des actions de l’application web pour reprendre la terminologie Struts.  Le gstionnaire mod_python.publisher va interpréter une url telle que /replay/action.py/remove_queue comme devant être traitée par la fonction remove_queue du module action.py. Les paramètres passées lors de l’appel à l’url seront passés à la fonction.

Voici un exemple de “servlet python” à placer à l’url /replay/action.py:

# -*- encoding: UTF-8 -*-
import os
import os.path
from mod_python import util

def remove_queue(req,file=None):
  list = os.listdir("/home/thierry/Vidéos/n900")
  for f in list:
    if f.endswith(".ts"):
      if file:
        if file==unicode(f,'utf').encode('ascii','xmlcharrefreplace'):
          os.remove("/home/thierry/Vidéos/n900/" + f)
      else:
        os.remove("/home/thierry/Videos/n900/" + f)

  util.redirect(req,"../queue.psp")

La fonction doit se terminer par une redirection vers une vue qui n’est autre qu’une page PSP !

Automatisation de la conversion des enregistrements de la Freebox pour N900

Ce billet montre la chaine complète de conversion des enregistrements des chaines de la TNT par une Freebox afin d’être lus par le N900. Il s’appuie sur les articles précédents en y ajoutant l’automatisation de la récupération des fichiers. La chaine de conversion se compose des étapes suivantes:

  1. rapatriement des enregistrements sur le PC
  2. conversion des enregistrement en H264
  3. rapatriement des fichiers convertis sur le N900

Chacune des étapes sera automatisée et s’effectuera par étage c’est-à-dire qu’une étape devra être complète avant de passer à la suivante. La procédure pourra être interrompue à tout moment pour se poursuivre plus tard. L’ensemble de la procédure doit minimiser l’espace disque utilisé et les étapes doivent s’exécuter de manière concurrente pour le mieux (1 en même temps que 3 par ex)

Rapatriement des enregistrements sur le PC

L’accès à la freebox se fait en FTP. La méthode utilisée est celle du miroitage différentiel à l’aide de l’outil lftp. Lftp permet de miroiter un répertoire entier ce qui correspond à notre problématique. Pour cela nous utilisons la commande mirror de lftp. L’aspect différentiel ou incrémental nous permet de reprendre un rapatriement interrompu. C’est l’option -c de la commande mirror. Mais ce n’est pas suffisant. Pour minimiser l’espace disque, une fois le rapatriement fait, les fichiers sont déplacés vers le répertoire de travail de l’étape 2. Il ne faut alors pas que la prochaine exécution de l’étape 1 ramène de nouveau les fichiers déjà traités ( la suppression des enregistrements est volontairement exclue de la procédure). En solution nous conservons la date du dernier mirroitage complet et utilisons l’option –newer-than=aaaammdd de la commande mirror qui parle d’elle-même. Pour finir le script de miroitage ne travaille que si aucun enregistrement n’est en cours. Pour cela nous comparons 2 fois de suite le contenu du répertoire des enregistrements de la Freebox.

En plus des spécificités précédente, ce script comme les autres, utilise la présence d’un fichier .lock pour s’assurer qu’aucune autre instance de lui-même est en train de travailler et rattrape les signaux SIGTERM SIGINT et SIGHUP pour faire le nettoyage qui se limite ici à supprimer les fichiers temporaires et le fichier de verrouillage.

Voici le script de téléchargement des enregistrements:

#!/bin/bash
echo "***********************************"
echo "*                                 *"
echo "*   Freebox record downloader     *"
echo "*                                 *"
echo "***********************************"

INPUT_DIR=.
FINAL_DIR=..
# Change 0000 by the password
CREDENTIAL=freebox,0000
LAST_MIRROR_FILE="$INPUT_DIR/.last_mirror"

# do not allow concurrency
if [ -e "$INPUT_DIR/.lock" ]; then
 exit 1
else
 touch "$INPUT_DIR/.lock"
fi

function cleanup_action {
 rm -f "$INPUT_DIR/.listing1.txt"
 rm -f "$INPUT_DIR/.listing2.txt"
 rm -f "$INPUT_DIR/.lock"
}

function clean_shutdown {
 echo "shutdown sequence..." 
 cleanup_action
 exit 1
}

trap 'clean_shutdown' SIGTERM SIGINT SIGHUP

# do not start download if the freebox is recording
lftp -u $CREDENTIAL -e "open; ls /Disque dur/Enregistrements/ ; quit" hd1.freebox.fr > "$INPUT_DIR/.listing1.txt"
sleep 5
lftp -u $CREDENTIAL -e "open; ls /Disque dur/Enregistrements/ ; quit" hd1.freebox.fr > "$INPUT_DIR/.listing2.txt"
cmp -s "$INPUT_DIR/.listing1.txt" "$INPUT_DIR/.listing2.txt" > /dev/null
if [ $? -eq 0 ]; then
 if [ -e "$LAST_MIRROR_FILE" ]; then 
 LAST_MIRROR=`cat "$LAST_MIRROR_FILE"`
 else
 LAST_MIRROR=0
 fi
 LAST_MIRROR_DATE=`date +%Y%m%d`
 lftp -u freebox,4085 -e "mirror -c --newer-than=$LAST_MIRROR /Disque dur/Enregistrements/ .; quit" hd1.freebox.fr
 # mirroring finished, save the current date because
 # we don't want to download these files again
 echo $LAST_MIRROR_DATE > "$LAST_MIRROR_FILE"
 # move files to processing dir if not buzy
 if [ ! -e "$FINAL_DIR/.lock" ]; then
 mv -u -f *.ts "$FINAL_DIR"
 fi
fi

cleanup_action

Voici le script de tâche cron à mettre dans /etc/cron.hourly

#!/bin/sh
N900_DIR=/home/thierry/Vidéos/n900
cd $N900_DIR/backup
./download.sh
#change thierry by your user
chown  thierry:thierry  $N900_DIR/*.ts

Conversion des enregistrements en H264

Une fois que les fichiers sont déplacés dans le répertoire /home/thierry/Vidéos/n900 un script se charge de les convertir en h264. Ce script commence par s’assurer qu’une autre instance de lui-même n’est pas déjà en train de travailler. La conversion elle même se base sur l’origine du fichier pour déterminer la résolution optimale (voir ce billet). Notons que la commande ffmpeg doit être lancé en arrière-plan car elle tue le shell quand elle se termine (étrange, peut-être spécifique à ma configuration?). Par ailleurs comme la conversion est un processus long, le script n’écrase pas le fichier de destination si il existe déjà. Pour finir le script rattrape les signaux d’interruptions pour faire le ménage. Dans son cas, le ménage consiste surtout à ne pas laisser un fichier converti incomplet. Donc on supprime le fichier en cours de création. Le prochain lancement le récréera. Enfin notons que le script supprime le fichier original une fois sa conversion terminée avec succès.

Voici le fichier de conversion:

#!/bin/bash

echo "***********************************"
echo "*                                 *"
echo "*    video converter for N900     *"
echo "*                                 *"
echo "***********************************"

INPUT_DIR=.
OUTPUT_DIR=$INPUT_DIR/converted

# do not allow concurrency
if [ -e "$INPUT_DIR/.lock" ]; then
 exit 1
else
 touch "$INPUT_DIR/.lock"
fi

function convert_file {
 original_def=$2
 original_file=$1
 converted_file="$OUTPUT_DIR/$original_file.mp4"

 if [ -e "$converted_file" ]; then
 return 1
 fi

 if [ $original_def == "mpeg" ]; then
 SIZE="400x480"
 elif [ $original_def == "dvd" ]; then
 SIZE="592x480"
 elif [ $original_def == "hd" ]; then
 SIZE="640x480"
 elif [ $original_def == "other" ]; then
 SIZE="320x240"
 else
 echo "format $original_def inconnu"
 return 1
 fi

 echo $converted_file > .work_in_progress
 (ffmpeg -i "$original_file" -acodec libfaac -vcodec libx264  -vpre hq -vpre baseline -crf 25 -maxrate 1000k -bufsize 2000k -threads 0 -flags2 -fastpskip  -s "$SIZE" "$converted_file" ) &
 echo $! > .work_pid
 wait       
 if [ $? -eq 0 ]; then
 rm "$original_file"
 fi
 rm .work_pid
 rm .work_in_progress

}

function clean_shutdown {
 echo "shutdown sequence..."
 if [ -e ".work_pid" ]; then
 kill `cat .work_pid`
 rm .work_pid
 fi
 if [ -e .work_in_progress ]; then
 rm "`cat .work_in_progress`"
 rm .work_in_progress
 fi
 rm "$INPUT_DIR/.lock"
 exit 1
}

trap 'clean_shutdown' SIGTERM SIGINT SIGHUP

find $INPUT_DIR -maxdepth 1 -type f ( -name "France 3*.ts" -o -name "France 4*.ts" -o -name "France 5*.ts" -o -name "La Chaîne*.ts" -o -name "NT1*.ts" -o -name "TMC*.ts" ) -print | ( while read i; do convert_file "$i" mpeg; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "DirectStar*.ts" -o -name "RTL9*.ts" -o -name "AB*.ts" ) -print | ( while read i; do convert_file "$i" dvd; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "Arte*.ts" -o -name "Direct 8*.ts" -o -name "France 2*.ts" -o -name "NRJ*.ts" ) -print | ( while read i; do convert_file "$i" hd; done )

# this just for any extra video i could put in the process directory
find $INPUT_DIR -maxdepth 1 -type f ( -name "*.mpeg" -o -name "*.vob" -o -name "*.mpg" -o -name "*.avi" ) | ( while read i; do convert_file "$i" other; done )

rm "$INPUT_DIR/.lock"

Voici le script cron à mettre dans /etc/cron.hourly

#!/bin/sh
N900_DIR=/home/thierry/Vidéos/n900
cd $N900_DIR
./convert_all.sh
chown -R thierry:thierry  ./converted

rapatriement des fichiers convertis sur le N900

Le rapatriement des fichiers est assuré par un script qui s’exécute sur le N900 et qui s’appuie sur la présence d’un deamon rsync sur le PC. Le script commence par s’assurer qu’il n’y a pas de conversion en cours sinon il rapatrierait un fichier incomplet. Puis il fait un rsync en spcifiant les options –remove-sent-files et –ignore-existing afin d’une part de libérer du disque sur le PC et d’autre part de limiter le temps de synchronisation en prevenant le rapatriement de fichiers qui seraient convertis en plusieurs fois (possible si on ne supprime pas les enregistrements sur la Freebox).
Voici le fichier de script à placer sur le N900:

#!/bin/sh
LOCAL_REPLAY_DIR=/home/user/MyDocs/.videos/replay
LOCK_FILE=$LOCAL_REPLAY_DIR/.lock
if [ -e $LOCK_FILE ]; then
	rm $LOCK_FILE
fi
rsync 192.168.0.12::"videos/n900/.lock"  $LOCAL_REPLAY_DIR/
if [ -e  $LOCK_FILE ]; then
  exit 1
fi
rsync -v --remove-sent-files --ignore-existing 192.168.0.12::"videos/n900/converted/*.mp4" $LOCAL_REPLAY_DIR

La planification de ce script est dévolue au l’outil Alarmed. On peut par exemple le programmer tous les jours à 7h00.

Cette chaine de conversion permet de libérer son utilisateur des contraintes de copie et de lancement de commande de conversion. Il n’a qu’à programmer les enregistrements pour que quelques heures plus tard ils soient disponibles pour un visionnage en mobilité !

Utilisation de lftp sans proxy avec la Freebox

Dans l’optique de faire une chaine de conversion automatisée des enregistrements fait sur la Freebox à destination du N900, nous avons réalisé dans les précédents billets la conversion et le transfert vers lze terminal. Le rapatriement des fichiers sur le PC devait se faire manuellement avec gFtp par exemple.

Dans l’utilisation de lftp comme outils de mirroitage ne fonctionnait pas. Pourquoi ? Le client gFtp arrive pourtant bien à effectuer les transferts. En examinant les réglages de gFtp on s’apperçoit que par défaut ce dernier n’utilise pas de proxy alors que le fichier /etc/lftp.conf nous indique que lftp utilise les réglages de proxy via les variables ftp_proxy, http_proxy et no_proxy.

Ne souhaitant pas entrer dans une configuration avancée de Squid et de lftp, nous choisissons de ne pas utiliser le proxy.

set net:no-proxy .freebox.fr

Cela est suffisant pour un serveur ftp qui se trouve sur le réseau local.

Nettoyage en cas de terminaison brusque d’un script Bash

Lors de la réalisation de l’automatisation de conversion vidéo dans le billet précédent, nous avons déja vu comment empêcher la réentrance avec la création d’un fichier “.lock”. Ce fichier est un artéfact de travail qui doit être supprimé quand le script se termine. Or le script peut être interrompu (tué) avant qu’il n’arrive à son terme.

Dans ce cas il faut quand même essayer de faire le ménage afin de permettre à nouveau l’execution du script. Bash contient une instruction trap qui permet de répondre aux signaux systèmes (SIGXXX) sauf SIGKILL (kill -9). Cela laisse en fait toute la lattidute de réagir au CTR-C ou à la commande kill de base (sans le -9).

Dans l’exemple du billet précédent l’interception des signaux peut être utilisée pour d’une part, supprimer le fichier qui était en train d’être encodé car il sera incomplet et d’autre part pour supprimer le fichier “.lock”. Le code suivant en illustre la mise en oeuvre:

#!/bin/bash

echo "***********************************"
echo "*                                 *"
echo "*    video converter for N900     *"
echo "*                                 *"
echo "***********************************"



INPUT_DIR=.
OUTPUT_DIR=$INPUT_DIR/converted



# do not allow concurrency 
if [ -e "$INPUT_DIR/.lock" ]; then
  exit 1
else
  touch "$INPUT_DIR/.lock"
fi

function convert_file {
	original_def=$2
	original_file=$1
	converted_file="$OUTPUT_DIR/$original_file.mp4"

	if [ -e "$converted_file" ]; then
		return 1
	fi
	
 	if [ $original_def == "mpeg" ]; then
		SIZE="400x480"
	elif [ $original_def == "dvd" ]; then
		SIZE="592x480"
	elif [ $original_def == "hd" ]; then
		SIZE="640x480"
	elif [ $original_def == "other" ]; then
		SIZE="320x240"
	else
		echo "format $original_def inconnu"
		return 1
	fi

	echo $converted_file > .work_in_progress
	(ffmpeg -i "$original_file" -acodec libfaac -vcodec libx264  -vpre hq -vpre baseline -crf 25 -maxrate 1000k -bufsize 2000k -threads 0 -flags2 -fastpskip  -s "$SIZE" "$converted_file" ) &
	echo $! > .work_pid
	wait        
	if [ $? -eq 0 ]; then
		rm "$original_file"
	fi
	rm .work_pid
	rm .work_in_progress


}

function clean_shutdown {
  echo "shutdown sequence..."
  if [ -e ".work_pid" ]; then
	kill `cat .work_pid`
	rm .work_pid
  fi
  if [ -e .work_in_progress ]; then
        rm "`cat .work_in_progress`"
	rm .work_in_progress
  fi
  rm .lock
  exit 1
}

trap 'clean_shutdown' SIGTERM SIGINT



find $INPUT_DIR -maxdepth 1 -type f ( -name "France 3*.ts" -o -name "France 4*.ts" -o -name "France 5*.ts" -o -name "La Chaîne*.ts" -o -name "NT1*.ts" -o -name "TMC*.ts" ) -print | ( while read i; do convert_file "$i" mpeg; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "DirectStar*.ts" -o -name "RTL9*.ts" -o -name "AB*.ts" ) -print | ( while read i; do convert_file "$i" dvd; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "Arte*.ts" -o -name "Direct 8*.ts" -o -name "France 2*.ts" -o -name "NRJ*.ts" ) -print | ( while read i; do convert_file "$i" hd; done )

find $INPUT_DIR -maxdepth 1 -type f ( -name "*.mpeg" -o -name "*.vob" -o -name "*.mpg" -o -name "*.avi" ) | ( while read i; do convert_file "$i" other; done )

rm "$INPUT_DIR/.lock"