
[edit] Le début de la conversation a commencé ici à peu près
Code : Tout sélectionner
<?xml version="1.0"?>
<data>
<image name="Image Satellite">
<url>http://oiswww.eumetsat.org/IPPS/html/latestImages/EUMETSAT_MSG_RGB-naturalcolor-westernEurope.jpg</url>
<path_sav>/tmp/image_sat.jpg</path_sav>
<update_time>3600</update_time>
<crop>0x110x1274x657</crop>
<resize>1274x547</resize>
</image>
<image name="Image Marine">
<url>http://marine.meteoconsult.fr/carte/meteo-marine/frontologie_0.php#etiquette</url>
<path_sav>/tmp/image_marine.png</path_sav>
<update_time>3600</update_time>
<search>http://images.meteoconsult.fr/image/mc-france/cartes/frontologie/produits/</search>
<crop>0x0x960x528</crop>
<resize>960x528</resize>
</image>
<image name="Temperature">
<url>http://meteociel.fr/cartes_obs/temp.png</url>
<path_sav>/tmp/temperature.png</path_sav>
<update_time>1800</update_time>
<crop>99x160x646x648</crop>
<resize>547x488</resize>
</image>
<image name="Vent">
<url>http://meteociel.fr/cartes_obs/vent.png</url>
<path_sav>/tmp/vent.png</path_sav>
<update_time>1800</update_time>
<crop>99x160x646x648</crop>
<resize>547x488</resize>
</image>
</data>
Code : Tout sélectionner
#!/bin/bash
. /etc/rc.conf
. /etc/rc.d/functions
. /etc/conf.d/imagesat
PID=$(ps -ef | grep -i imagesat | grep -v grep | awk '{ print $2 }')
case "$1" in
start)
stat_busy 'Starting ImageSAT'
[[ -z $PID ]] && su - $IMG_USER -c "nohup /usr/bin/imagesat $IMG_ARGS &"
if [[ $? -gt 0 ]]; then
stat_fail
else
add_daemon imagesat
stat_done
fi
;;
stop)
stat_busy 'Stopping ImageSAT'
[[ ! -z $PID ]] && kill $PID &> /dev/null
if [[ $? -gt 0 ]]; then
stat_fail
else
rm_daemon imagesat
stat_done
fi
;;
restart)
$0 stop
sleep 1
$0 start
;;
*)
echo "usage: $0 {start|stop|restart}"
esac
exit 0
Code : Tout sélectionner
IMG_USER=monuser
IMG_ARGS=""
Code : Tout sélectionner
mkdir /var/log/imagesat && chown mon_user:mon_user /var/log/imagesat
Code : Tout sélectionner
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, os, time, atexit, signal
import xml.etree.ElementTree as ET
import urllib.request
import Image
######
# Début de la classe Img
############
class Img:
def __init__(self):
self.url = str()
self.page = str()
self.picture = str()
self._path = str()
self.search = str()
self._time = int()
self.picSRC = None
self._crop = []
self._resize = []
def _get_crop(self):
return self._crop
def _set_crop(self, c):
for val in c.split("x"):
if _isNumber(val):
self._crop.append(int(val))
else:
with open(errorFile, 'a') as ef:
ef.write(time.strftime("%d/%m/%Y %H:%M:%S | ") + "Dans \"" + confFile + "\": <crop>" + c + "</crop> est incorrect ! Mettez des nombres compris entre 0 et inf séparés par x. Ex.: 0x110x1274x657\n")
sys.exit(2)
def _get_path(self):
return self._path
def _set_path(self, e):
# On découpe l'emplacement
(path, fl) = os.path.split(e)
# On vérifie qu'on a les droits de lecture sur l'emplacement
if not os.access(path, os.R_OK):
# Si ce n'est pas le cas, on écrit l'erreur et on arrête tout
with open(errorFile, "a") as ef:
ef.write(time.strftime("%d/%m/%Y %H:%M:%S | ") + " Vous n'avez pas les droits de lecture sur " + path + " !\n")
sys.exit(2)
# On vérifie qu'on a les droits d'écriture sur l'emplacement
elif not os.access(path, os.R_OK):
# Si ce n'est pas le cas, on écrit l'erreur et on arrête tout
with open(errorFile, "a") as ef:
ef.write(time.strftime("%d/%m/%Y %H:%M:%S | ") + " Vous n'avez pas les droits d'écriture sur " + path + " !\n")
sys.exit(2)
self._path = e
def _get_resize(self):
return self._resize
def _set_resize(self, r):
for val in r.split("x"):
if _isNumber(val):
self._resize.append(int(val))
else:
with open(errorFile, 'a') as ef:
ef.write(time.strftime("%d/%m/%Y %H:%M:%S | ") + "Dans \"" + confFile + "\": <resize>" + r + "</resize> est incorrect ! Mettez des nombres compris entre 0 et inf séparés par x. Ex.: 547x488\n")
sys.exit(2)
def _get_time(self):
return self._time
def _set_time(self, t):
# On vérifie que t est bien un nombre
if not _isNumber(t):
with open(errorFile, 'a') as ef:
ef.write(time.strftime("%d/%m/%Y %H:%M:%S | ") + "Dans \"" + confFile + "\": <update_time>" + t + "</update_time> est incorrect ! Mettez un nombre compris entre 0 et inf.\n")
sys.exit(2)
self._time = int(t)
# Définition des propriétés
path = property(_get_path, _set_path)
time = property(_get_time, _set_time)
crop = property(_get_crop, _set_crop)
resize = property(_get_resize, _set_resize)
######
# Début de la classe Daemon
############
class Daemon:
def __init__(self, pidfile):
e, f = os.path.split(pidfile)
if not (os.access(e, os.R_OK) and os.access(e, os.W_OK)):
stderr.write("Vous n'avez pas les droits sur : " + e + "\n")
exit(1)
self.pidfile = pidfile
self.imgList = []
def daemonize(self):
"""Méthode du double fork"""
try:
pid = os.fork()
# Si c'est le père, on quitte
if pid > 0:
sys.exit(0)
except OSError as err:
sys.stderr.write("Le fork #1 a échoué :{0}\n".format(err))
sys.exit(1)
# On se découpe de l'environnement du parent
os.chdir('/')
# setsid crée une nouvelle session pour rendre le processus fils indépendant
os.setsid()
os.umask(0)
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
except OSError as err:
sys.stderr.write("Le fork #2 a échoué :{0}\n".format(err))
sys.exit(1)
# On vide les tampons
sys.stdout.flush()
sys.stderr.flush()
si = open(os.devnull, 'r')
so = open(os.devnull, 'a+')
se = open(os.devnull, 'a+')
# Redirection des file descriptor standard
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# On enregistre la méthode delpid() qui sera exécutée à la fermeture du programme
atexit.register(self.delpid)
# On récupère le PID du processus
pid = str(os.getpid())
# On écrit le PID dans le fichier définit dans pidfile
with open(self.pidfile,'w+') as f:
f.write(pid + '\n')
# Méthode qui permet de supprimer le fichier dans lequel on écrit le PID
def delpid(self):
os.remove(self.pidfile)
# Méthode qui permet de démarrer le daemon
def start(self):
"""Démarrage du daemon."""
# On chercher si le fichier pidfile existe afin de savoir si un daemon s'exécute déjà
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
# Si un processus existe déjà
if pid:
message = "pidfile {0} existe déjà. Y a-t-il un daemon qui fonctionne ?\n"
sys.stderr.write(message.format(self.pidfile))
sys.exit(1)
# On charge la configuration
self.readConf()
# On lance le double fork
self.daemonize()
# On démarre la boucle principale
self.run()
def stop(self):
"""Arrêter le daemon."""
# On récupère le PID du daemon
try:
with open(self.pidfile,'r') as pf:
pid = int(pf.read().strip())
except IOError:
pid = None
# Si le PID n'est pas défini
if not pid:
message = "pidfile {0} n'existe pas. Le daemon ne fonctionne pas ?\n"
sys.stderr.write(message.format(self.pidfile))
return # not an error in a restart
# On essaie de d'arrêter le daemon
try:
# Tant que le processus n'est pas tuer, on boucle
while 1:
os.kill(pid, signal.SIGTERM)
# On attend pour éviter trop de consommation du CPU
time.sleep(0.1)
except OSError as err:
e = str(err.args)
if e.find("No such process") > 0:
if os.path.exists(self.pidfile):
os.remove(self.pidfile)
else:
print(str(err.args))
sys.exit(1)
def readConf(self):
(path, f) = os.path.split(errorFile)
# On teste les droits de lecture écriture sur errorFile
# Si on ne les a pas, on arrête tout
if not (os.access(path, os.R_OK) and os.access(path, os.W_OK)):
sys.exit(2)
# On teste les droits d'écriture sur le fichier confFile
# Si on ne les a pas, on arrête tout en l'écrivant dans errorFile
if not(os.access(confFile, os.R_OK) and os.path.isfile(confFile)):
with open(errorFile, 'a') as ef:
ef.write(time.strftime("%d/%m/%Y %H:%M:%S | ") + " Impossible de charger " + confFile + " !\n")
sys.exit(2)
# On récupère le fichier de configuration
tree = ET.parse(confFile)
root = tree.getroot()
# On récupère les données du fichier de configuration
for image in root:
# On crée un objet img
img = Img()
# On récupère les données pour 1 objet
img.url = getData(image.find('url'))
img.path = getData(image.find('path_sav'))
img.time = getData(image.find('update_time'))
img.crop = getData(image.find('crop'))
img.resize = getData(image.find('resize'))
img.search = getData(image.find('search'))
# si img.search est définie, il faut placer l'url de la page sur img.page
img.page = img.url
# On rajoute l'object à la liste
self.imgList.append(img)
def restart(self):
"""Redémarrage du daemon."""
self.stop()
self.start()
# Fonction principale qui aura une boucle infinie
def run(self):
# On cherche le temps le plus court pour le sleep
time2sleep = int()
for i, img in enumerate(self.imgList):
if i == 0:
time2sleep = img.time
elif img.time < time2sleep:
time2sleep = img.time
while 1:
# On fait une copie de time2sleep
cpts = time2sleep
# On parcourt toutes les images récupérées du fichier de configuration
for img in self.imgList:
# On regarde si la photo existe déjà
if os.path.exists(img.path) and os.path.isfile(img.path):
# On récupère le temps
now = time.time()
# On récupère quand a été créer l'image
timePic = os.stat(img.path)[8]
# Si l'image est trop vieille, on fait la mise à jour
if timePic + img.time <= now:
searchURL(img)
download(img)
modify(img)
else:
# On calcule combien de temps il reste jusqu'à la mise à jour
timeRemaining = timePic + img.time - now
# Si celui-ci est inférieur au plus petit temps, on le remplace
if timeRemaining < cpts:
cpts = timeRemaining
else:
# Si la recherche a été définie, on lance la récupération de la page
searchURL(img)
# On télécharge
download(img)
# Si des modifications ont été définies, on les applique
modify(img)
# On endort le daemon pendant le plus petit temps trouvé afin de ne pas consommer du CPU inutilement
time.sleep(cpts)
######
# Début des fonctions globales
############
def download(img):
# Téléchargement de l'image
imageWeb = urllib.request.urlopen(img.url)
# Sauvegarde de l'image
with open(img.path, "wb") as source:
source.write(imageWeb.read())
def modify(img):
# Si la découpe ou le redimensionnement est défini
if img.crop or img.resize:
# On charge l'image
img.picSRC = Image.open(img.path)
# Si on découpe l'image
if img.crop:
img.picSRC = img.picSRC.crop(img.crop)
# Si on redimensionne l'image
if img.resize:
img.picSRC = img.picSRC.resize((img.resize), Image.ANTIALIAS)
# On sauvegarde les modifications de l'image
img.picSRC.save(img.path)
def searchURL(img):
if img.search:
# On télécharge la page où se trouve l'image
dl = urllib.request.urlopen(img.page).read().decode('utf-8', 'ignore')
# On passe en revue toute la page web
for lecture in dl.split(" "):
if img.search in lecture:
img.url = lecture.split("=")[1][1:-1]
return
def getData(data):
if data != None:
return data.text
else:
return None
def _isNumber(number):
try:
s = int(number)
if s < 0:
return False
else:
return True
except ValueError:
return False
######
# Début du programme principal
############
if __name__ == "__main__":
# Définition du fichier de configuration et du fichier d'erreur
errorFile = "/var/log/imagesat/error.log"
confFile = "/etc/imagesat.cfg"
demon = Daemon("/var/run/imagesat/imagesat.pid")
if len(sys.argv) != 2:
print("Usage : {0} start|stop|restart".format(sys.argv[0]))
sys.exit(2)
arg = sys.argv[1]
if arg == "start":
demon.start()
elif arg == "stop":
demon.stop()
elif arg == "restart":
demon.restart()
else:
sys.stdout.write("{0} : Argument inconnu".format(arg))
sys.exit(2)
Code : Tout sélectionner
demon = Daemon("/var/run/imagesat/imagesat.pid")
Code : Tout sélectionner
mkdir/var/run/imagesat
chown mon_user:mon_user /var/run/imagesat
Code : Tout sélectionner
chmod +x /usr/bin/imagesat.py
Code : Tout sélectionner
rename /etc/imagesat /etc/imagesat.py
Code : Tout sélectionner
<?xml version="1.0"?>
<data>
<image name="Image Satellite">
<url>http://oiswww.eumetsat.org/IPPS/html/latestImages/EUMETSAT_MSG_RGB-naturalcolor-westernEurope.jpg</url>
<path_sav>/tmp/image_sat.jpg</path_sav>
<update_time>3600</update_time>
<crop>0x110x1274x657</crop>
<resize>1274x547</resize>
</image>
<image name="Image Marine">
<url>http://marine.meteoconsult.fr/carte/meteo-marine/frontologie_0.php#etiquette</url>
<path_sav>/tmp/image_marine.png</path_sav>
<update_time>3600</update_time>
<search>http://images.meteoconsult.fr/image/mc-france/cartes/frontologie/produits/</search>
<crop>0x0x960x528</crop>
<resize>960x528</resize>
</image>
<image name="Temperature">
<url>http://meteociel.fr/cartes_obs/temp.png</url>
<path_sav>/tmp/temperature.png</path_sav>
<update_time>1800</update_time>
<crop>99x160x646x648</crop>
<resize>547x488</resize>
</image>
<image name="Vent">
<url>http://meteociel.fr/cartes_obs/vent.png</url>
<path_sav>/tmp/vent.png</path_sav>
<update_time>1800</update_time>
<crop>99x160x646x648</crop>
<resize>547x488</resize>
</image>
</data>
Code : Tout sélectionner
#Fonctionnement de conky
total_run_times 0 #Temps en secondes ; 0 = toujours actif
background yes #Pour que conky tourne en arrière plan ; no = pour les tests
#Mémoire
double_buffer yes #Éviter le clignotement
no_buffers yes #Soustraire les mémoires tampons de la mémoire utilisée
text_buffer_size 1024 #Taille du cache pour le texte
#Affichage
out_to_console no #Affiche le texte sur la sortie standard
update_interval 10 #Taux de rafraîchissement de la fenêtre (s)
#Fenêtre conky
alignment bottom_left #Alignement
#---
gap_x 2 #Écart avec le bord gauche / droit
gap_y 224 #Écart avec le bord haut / bas
#---
draw_shades no #Afficher les ombres
draw_outline no #Afficher les contours de fenêtre
draw_borders yes #Afficher des contours autour des blocs de texte
border_width 2 #Largeur du contour
border_inner_margin 0 #Largeur des marges
#---
own_window yes #Utiliser sa propre fenêtre
# own_window_type override #Type de fenêtre ; normal / override / desktop
# own_window_type normal #Type de fenêtre ; normal / override / desktop
own_window_transparent yes #Pseudo transparence
own_window_hints undecorated,below,sticky,skip_taskbar,skip_pager # définition du type
own_window_class Conky
#Mise en forme
use_xft yes #Utiliser Xft (polices lissées etc)
xftalpha .1 #Utiliser Xft
override_utf8_locale yes #Force l'UTF8
uppercase no #Tout le texte en majuscule
use_spacer right #Ajoute des espaces après certains objets (qu'avec des polices fixes)
#---
xftfont Monospace:size=10 #Police par défaut
#---
default_bar_size 30 3 #Barre par défaut (longeur hauteur)
# stippled_borders 5 #Taille des pointillés
#Couleurs
default_color 000000 #Couleur par défaut
default_shade_color 333333 #Couleur des ombres
default_outline_color black #Couleur des contours
#---
#color1 000000 #Gris foncé
#color2 DDDDDD #Gris clair
#---
short_units yes #Unités courtes
pad_percents 0 #Unité à 2 décimales
color1 333333 # Gris
#color2 006FC0 # Bleu
#color3 8AD749 # Vert
color4 EECE01 # Jaune
color5 F8981F # Orange
#color6 F80E27 # Rouge
color2 3465A4 # Bleu
color3 6FEF22 # Vert
color5 EF7E22 # Orange
color6 BD1B5E # Rouge
minimum_size 385 190
maximum_width 400
TEXT
${image /tmp/image_marine.png -p -5,-5 -n -s 400x219}
Code : Tout sélectionner
${image /tmp/image_sat.jpg -p -5,-5 -n -s 400x220}
Code : Tout sélectionner
${image /tmp/vent.png -p -5, -5 -n -s 230x200}
Code : Tout sélectionner
${image /tmp/temperature.png -p -5, -5 -n -s 230x200}
Code : Tout sélectionner
#!/bin/bash
. /etc/rc.conf
. /etc/rc.d/functions
. /etc/conf.d/imagesat
PIDDIR=/var/run/imagesat
PIDFILE=$PIDDIR/imagesat.pid
[ -f $PIDFILE ] && PID=$(cat $PIDFILE)
PROG=/usr/bin/imagesat
case "$1" in
start)
stat_busy 'Starting ImageSAT'
[[ ! -d $PIDDIR ]] && (mkdir -p $PIDDIR ; chown $IMG_USER:$IMG_USER $PIDDIR)
[[ -z $PID ]] && su - $IMG_USER -c "$PROG start $IMG_ARGS"
if [[ $? -gt 0 ]]; then
stat_fail
else
add_daemon imagesat
stat_done
fi
;;
stop)
stat_busy 'Stopping ImageSAT'
[[ ! -z $PID ]] && su - $IMG_USER -c "$PROG stop $IMG_ARGS"
if [[ $? -gt 0 ]]; then
stat_fail
else
rm_daemon imagesat
stat_done
fi
;;
restart)
$0 stop
sleep 1
$0 start
;;
*)
echo "usage: $0 {start|stop|restart}"
esac
exit 0
Code : Tout sélectionner
yaourt -S imagesat