Scripts

Convertir des marque-pages JSON en HTML Netscape

Lorsque je formate ou que je change d'ordinateur, je fais une sauvegarde de mes marque-pages. C'est normal. Depuis plus de cinq ans, j'ai donc des fichiers de sauvegarde qui s'entassent. Certains sont au format HTML Netscape, un standard. Mais d'autres sont au format JSON. Peu commodes à utiliser, je souhaitais les convertir en HTML. Malheur à moi !

Le nouvel Opera n'importe que des fichiers HTML. Firefox m'explique que le fichier n'est pas bon. Bien, tant pis, je me lance dans la conception d'un petit script pour convertir ces JSON en HTML. Et quoi de mieux que Javascript pour faire le job ?


<script type="text/javascript">
function parseObject(obj, result) {
	result += "\n";

	if(obj.constructor == Object) { 
		// Folders
		if(obj.hasOwnProperty("children")) {
			result += "<DT><H3 ";
			var tmp = new Object();

			for(var p in obj) {
				if(p === "name" || p === "title") {
					tmp["value"] = obj[p];
				} else if(p === "dateAdded" || p === "date_added") {
					tmp["date"] = obj[p];
				} else if(p === "lastModified" || p === "date_modified") {
					tmp["modified"] = obj[p];
				}
			}

			// WebKit doesn't know how to use POSIX time è.é
			// tmp["date"] = (tmp["date"]/1000000-11644473600);
			// tmp["modified"] = (tmp["modified"]/1000000-11644473600);

			result += 'ADD_DATE="' + String(tmp["date"]).substring(0,10) + '" ';
			result += 'LAST_MODIFIED="' + String(tmp["modified"]).substring(0,10) + '">';
			result += tmp["value"] + "</H3>\n";
			result += parseObject(obj["children"], "\n");

		}	
		// Links
		else if(obj.hasOwnProperty("url") || obj.hasOwnProperty("uri")){
			result += '<DT><A HREF="';
			var tmp = new Object();

			for(var p in obj) {
				if(p === "uri" || p === "url") {
					tmp["url"] = obj[p];
				} else if(p === "name" || p === "title") {
					tmp["value"] = obj[p];
				} else if(p === "dateAdded" || p === "date_added") {
					tmp["date"] = obj[p];
				} else if(p === "lastModified" || p === "date_modified") {
					tmp["modified"] = obj[p];
				} else if(p === "charset") {
					tmp["charset"] = obj[p];
				} else if(p === "annos") {
					tmp["description"] = obj[p][0].value;
				} 
			}

			// WebKit doesn't know how to use POSIX time è.é
			// tmp["date"] = (tmp["date"]/1000000-11644473600);
			// tmp["modified"] = (tmp["modified"]/1000000-11644473600);

			result += encodeURI(tmp["url"]) + '" ADD_DATE="' + String(tmp["date"]).substring(0,10) + '" LAST_MODIFIED="';
			result += String(tmp["modified"]).substring(0,10) + '" LAST_CHARSET="' + tmp["charset"] + '" >'; 
			result += tmp["value"]+ "</A>\n";
			if(tmp.hasOwnProperty("description"))
				result += "<DD>" + tmp["description"] + "\n";
		}
		// Others
		else {
			for(var p in obj) {
				result += parseObject(obj[p], "\n");
			}
		}
	} else if(obj.constructor == Array) {
		for(var p in obj) {
			result += parseObject(obj[p], "\n");
		}	
	} 

	return result;
}


function jsonToHTML(input){
	var output = "<!DOCTYPE NETSCAPE-Bookmark-file-1>\n";
	output += '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">\n';
	output += "<TITLE>Bookmarks</TITLE>\n"; 
	output += "<H1>Bookmarks</H1>\n";
	output += "<DL>\n";
	
	// We construct a valid JSON, thanks to Firefox's shitty code è.é
	input = input.replace(",]", "]");
	input = input.replace(",}", "}");
	input = input.replace("[,", "[");
	input = input.replace("{,", "{");
	
	var data = JSON.parse(input);
	console.log(data);
	output += parseObject(data, "\n");
	
	output += "</DL>\n";
	output += "</DL>";
	return output;
}
	
	// Usage : jsonToHTML(someInputFromATextarea);
</script>

Voir une démo - Voir le dépôt GitHub

Il s'avère que j'ai trouvé pourquoi Firefox me retourne une erreur avec ces fichiers. Au départ il les exportait avec un léger défaut : en fin de fichier, il inscrivait ,]}, ce qui, bien entendu, n'est pas valide. Je ne sais pas quand le tir a été corrigé, mais dorénavant les vieux fichiers de sauvegarde sont incompatibles…

Oh, et tant que j'y suis… Chrome et dérivés (Opera, Chromium…) tendent à ne pas savoir calculer un timestamp Unix correct. J'ai d'abord pensé qu'ils ajoutaient systématiquement un 0 après les deux premiers chiffres. Un simple ajout javascript aurait alors fait l'affaire :


tmp["date"] = String(tmp["date"]).slice(0,2).concat(String(tmp["date"]).slice(3));
tmp["modified"] = String(tmp["modified"]).slice(0,2).concat(String(tmp["modified"]).slice(3));

Mais en réalité ils vont plus loin dans leur incompétence : les trois premiers chiffres sont toujours 130 !

Intrigué (l'export en HTML donnant un timestamp correct), j'ai voulu creuser un peu plus. Il s'avère que Chrome n'utilise pas un timestamp Unix Epoch mais ce qui est communément appelé webkit timestamp, un format 64 bit qu'il partage avec Windows. Au lieu de compter les secondes depuis 1970, il compte les microsecondes depuis 1601. Pourquoi 1601 ? Oh, sans doute le fameux Ballmer Peak

Tout ce qu'il y a à faire, c'est de diviser par un million pour avoir les secondes, puis de soustraire le nombre de secondes entre 1970 et 1601 (soit 11644473600, ne me remerciez pas). Soit le code :


tmp["date"] = (tmp["date"]/1000000-11644473600);
tmp["modified"] = (tmp["modified"]/1000000-11644473600);

Configuration de Conky

Pour fonctionner, il nécessite que les fichier ip.sh et rhythmbox.sh soient présents dans le dossier ~/.conky et exécutables. Il ne faut pas oublier d'installer les polices Open Logos et StyleBats.

Copie de ip.sh :


	#!/bin/bash



# # Test de validité IPv4 de l'adresse entrée (expression régulière)

function isIPv4 {

 if [ $# = 1 ]

 then

  printf $1 | grep -Eq '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-4]|2[0-4][0-9]|[01]?[1-9][0-9]?)$'

  return $?

 else

  return 2

 fi

}



RETOUR=$(wget -qO- whatismyip.org)



isIPv4 $RETOUR && echo $RETOUR || echo "Pas de connexion"
	
Copie de rhythmbox.sh :

	#!/bin/bash





case "$1" in



progress)

    curr=`rhythmbox-client --no-start --print-playing-format "%te" | grep -v "Pas de lecture en cours" | grep -v Inconnu`

    tot=`rhythmbox-client --no-start --print-playing-format "%td" | grep -v "Pas de lecture en cours" | grep -v Inconnu`

    a=`date +'%S' | sed 's/^0\+//'`

if [ "$a" = "" ]; then

    a=0

fi

    b=$(( ($a*100) / 30 ))

    c=$(( 200-$b ))

if [ "$curr" = "" ]; then

    curr=0

fi

#si pas de durée totale, augmente pendant les 30 premieres secondes puis diminue pendant les 30 suivantes...

    if [ "$tot" = "" ]; then 

    if [ $a -le 30 ]; then

       expr $b

    else

       expr $c

    fi

    else

#Si durée totale déterminée, il faut traiter le retour de la commande donnant $curr (pour courant et $tot pour total, logique) qui renvoit une donnée sous la forme hh:mm:ss

    d="1"

    nbcurr=`echo $curr | wc -m`       #nbr de caractères permet de déterminer si il y a seulement m:ss ou si il y a h:mm:ss (au minimum, m:ss)

    posm=$(( $nbcurr-5 ))             #pour faire une commande cut générique, il faut définir l'endroit ou on coupe d'ou posx (position des minutes ici)

if [ $posm -lt $d ]; then

posm=``                               #Si position inférieur à 1, renvoit variable vide pour ne pas bloque cut

fi

    posm2=$(( $nbcurr-4 ))            #Forcément 1 indication minute (minimum)= pas besoin de vérifier si >1

    posh=$(( $nbcurr-8 ))             #Idem pour les heures

if [ $posh -lt $d ]; then

posh=``

fi

    posh2=$(( $nbcurr-7 ))

    currs=`echo $curr | tail -c3 | sed 's/^0\+//'` # Récupération des secondes forcément à la fin et supression du premier 0 pour ne pas avoir de problème de base (08 en hexa ou base 10)

if [ "$currs" = "" ]; then

    currs=0                          #si uniquement des zeros, ils sont tous supprimé donc redonner la valeur

fi

    currma=`echo $curr | cut -c$posm-$posm2 | sed 's/^0\+//'`

if [ "$currma" = "" ]; then

    currma=0                         #idem pour minutes

fi

    currm=$(( $currma*60 ))          #conversion en secondes

if [ $posh2 -lt $d ]; then

currh=0

else

    currha=`echo $curr | cut -c$posh-$posh2 | sed 's/^0\+//'`

if [ "$currha" = "" ]; then         #idem heures

    currha=0

fi

currh=$(( $currha*3600 ))          # conversion en secondes

fi



    currt=$(( ($currh+$currm) + $currs )) #calcule du nombre de secondes total



    nbtot=`echo $tot | wc -m`      #Même schema pour la suite mais avec la durée totale

    tposm=$(( $nbtot-5 ))

if [ $tposm -lt $d ]; then

tposm=``

fi

    tposm2=$(( $nbtot-4 ))

    tposh=$(( $nbtot-8 ))

if [ $tposh -lt $d ]; then

tposh=``

fi

    tposh2=$(( $nbtot-7 ))

    tots=`echo $tot | tail -c3 | sed 's/^0\+//'` ## OK

if [ "$tots" = "" ]; then

    tots=0

fi

    totma=`echo $tot | cut -c$tposm-$tposm2 | sed 's/^0\+//'` ## OK

if [ "$totma" = "" ]; then

    totma=0

fi

    totm=$(( $totma*60 )) ## OK

if [ $tposh2 -lt $d ]; then

toth=0

else

    totha=`echo $tot | cut -c$tposh-$tposh2 | sed 's/^0\+//'` ## OK

if [ "$totha" = "" ]; then

    totha=0

fi

toth=$(( $totha*3600 )) ## OK

fi



    tott=$(( ($toth+$totm) + $tots )) ## OK

        expr $currt \* 100  / $tott    #Et finalement expression du pourcentage accompli

    fi

    ;;



esac
	

Copie du .conkyrc :


	# set to yes if you want Conky to be forked in the background

background yes



cpu_avg_samples 2

net_avg_samples 2



out_to_console no



# X font when Xft is disabled, you can pick one with program xfontsel

#font 7x12

#font 6x10

#font 7x13

#font 8x13

#font 7x12

#font *mintsmild.se*

#font -*-*-*-*-*-*-34-*-*-*-*-*-*-*

#font -artwiz-snap-normal-r-normal-*-*-100-*-*-p-*-iso8859-1



# Use Xft?

use_xft yes



# Xft font when Xft is enabled

xftfont Bitstream Vera Sans Mono:size=9



own_window_transparent yes

own_window_type override

own_window_hints  undecorated,below,sticky,skip_taskbar,skip_pager

own_window_colour hotpink

# Text alpha when using Xft

xftalpha 0.8



on_bottom yes



# mail spool

mail_spool $MAIL



# Update interval in seconds

update_interval 1

# Create own window instead of using desktop (required in nautilus)

own_window yes



# Use double buffering (reduces flicker, may not work for everyone)

double_buffer yes



# Minimum size of text area

#minimum_size 280 5

#maximum_width 150



# Draw shades?

draw_shades yes



# Draw outlines?

draw_outline no



# Draw borders around text

draw_borders no



# Stippled borders?

stippled_borders 10



# border margins

border_margin 4



# border width

border_width 1



# Default colors and also border colors

default_color white

default_shade_color black

default_outline_color white



# Text alignment, other possible values are commented

#alignment top_left

#minimum_size 10 10

gap_x 13

gap_y 34

alignment top_right

#alignment bottom_left

#alignment bottom_right



# Gap between borders of screen and text



# Add spaces to keep things from moving about?  This only affects certain objects.

use_spacer no



# Subtract file system buffers from used memory?

no_buffers yes



# set to yes if you want all text to be in uppercase

uppercase no



# boinc (seti) dir

# seti_dir /opt/seti



# Possible variables to be used:

#

#      Variable         Arguments                  Description                

#  acpiacadapter                     ACPI ac adapter state.                   

#  acpifan                           ACPI fan state                           

#  acpitemp                          ACPI temperature.                        

#  adt746xcpu                        CPU temperature from therm_adt746x       

#  adt746xfan                        Fan speed from therm_adt746x             

#  battery           (num)           Remaining capasity in ACPI or APM        

#                                    battery. ACPI battery number can be      

#                                    given as argument (default is BAT0).     

#  buffers                           Amount of memory buffered                

#  cached                            Amount of memory cached                  

#  color             (color)         Change drawing color to color            

#  cpu                               CPU usage in percents                    

#  cpubar            (height)        Bar that shows CPU usage, height is      

#                                    bar's height in pixels                   

#  downspeed         net             Download speed in kilobytes              

#  downspeedf        net             Download speed in kilobytes with one     

#                                    decimal                                  

#  exec              shell command   Executes a shell command and displays    

#                                    the output in torsmo. warning: this      

#                                    takes a lot more resources than other    

#                                    variables. I'd recommend coding wanted   

#                                    behaviour in C and posting a patch :-).  

#  execi             interval, shell Same as exec but with specific interval. 

#                    command         Interval can't be less than              

#                                    update_interval in configuration.        

#  fs_bar            (height), (fs)  Bar that shows how much space is used on 

#                                    a file system. height is the height in   

#                                    pixels. fs is any file on that file      

#                                    system.                                  

#  fs_free           (fs)            Free space on a file system available    

#                                    for users.                               

#  fs_free_perc      (fs)            Free percentage of space on a file       

#                                    system available for users.              

#  fs_size           (fs)            File system size                         

#  fs_used           (fs)            File system used space                   

#  hr                (height)        Horizontal line, height is the height in 

#                                    pixels                                   

#  i2c               (dev), type, n  I2C sensor from sysfs (Linux 2.6). dev   

#                                    may be omitted if you have only one I2C  

#                                    device. type is either in (or vol)       

#                                    meaning voltage, fan meaning fan or temp 

#                                    meaning temperature. n is number of the  

#                                    sensor. See /sys/bus/i2c/devices/ on     

#                                    your local computer.                     

#  kernel                            Kernel version                           

#  loadavg           (1), (2), (3)   System load average, 1 is for past 1     

#                                    minute, 2 for past 5 minutes and 3 for   

#                                    past 15 minutes.                         

#  machine                           Machine, i686 for example                

#  mails                             Mail count in mail spool. You can use    

#                                    program like fetchmail to get mails from 

#                                    some server using your favourite         

#                                    protocol. See also new_mails.            

#  mem                               Amount of memory in use                  

#  membar            (height)        Bar that shows amount of memory in use   

#  memmax                            Total amount of memory                   

#  memperc                           Percentage of memory in use              

#  new_mails                         Unread mail count in mail spool.         

#  nodename                          Hostname                                 

#  outlinecolor      (color)         Change outline color                     

#  pre_exec          shell command   Executes a shell command one time before 

#                                    torsmo displays anything and puts output 

#                                    as text.                                 

#  processes                         Total processes (sleeping and running)   

#  running_processes                 Running processes (not sleeping),        

#                                    requires Linux 2.6                       

#  shadecolor        (color)         Change shading color                     

#  stippled_hr       (space),        Stippled (dashed) horizontal line        

#                    (height)        

#  swapbar           (height)        Bar that shows amount of swap in use     

#  swap                              Amount of swap in use                    

#  swapmax                           Total amount of swap                     

#  swapperc                          Percentage of swap in use                

#  sysname                           System name, Linux for example           

#  time              (format)        Local time, see man strftime to get more 

#                                    information about format                 

#  totaldown         net             Total download, overflows at 4 GB on     

#                                    Linux with 32-bit arch and there doesn't 

#                                    seem to be a way to know how many times  

#                                    it has already done that before torsmo   

#                                    has started.                             

#  totalup           net             Total upload, this one too, may overflow 

#  updates                           Number of updates (for debugging)        

#  upspeed           net             Upload speed in kilobytes                

#  upspeedf          net             Upload speed in kilobytes with one       

#                                    decimal                                  



#  uptime                            Uptime                                   

#  uptime_short                      Uptime in a shorter format               

#

#  seti_prog                         Seti@home current progress

#  seti_progbar      (height)        Seti@home current progress bar

#  seti_credit                       Seti@hoome total user credit





# variable is given either in format $variable or in ${variable}. Latter

# allows characters right after the variable and must be used in network

# stuff because of an argument

#${font Dungeon:style=Bold:pixelsize=10}I can change the font as well

#${font Verdana:size=10}as many times as I choose

#${font Perry:size=10}Including UTF-8,

#${font Luxi Mono:size=10}justo como este texto que o google traduz fêz o português

# stuff after 'TEXT' will be formatted on screen

#${font Grunge:size=12}${time %a  %b  %d}${alignr -25}${time %k:%M}



TEXT

${shadecolor black}${font openlogos:size=100}${color 73d216}${alignc}Ut${font}${color}${shadecolor}

${font Arial:size=20}${color 73d216}${alignc}Marvin${color}${font}



${color 73d216}Système ${color}$hr

${font StyleBats:size=10}P${font} Uptime: $uptime

${font StyleBats:size=10}A${font} Processeur : ${cpu}% ${color 73d216}${cpubar}$color

${font StyleBats:size=10}I${font} Mémoire : $memperc%  $mem	${color 73d216}${membar}$color

${font StyleBats:size=10}I${font} Swap :$swapperc%  ${color 73d216}${swapbar}$color

${font StyleBats:size=10}U${font} Batterie : $battery_percent%  $battery_time



${color 73d216}Disque dur ${color}$hr

${font StyleBats:size=10}C${font} Racine : ${fs_used_perc /}% ${fs_free /} ${color 73d216}${fs_bar 5,120 /}$color

${font StyleBats:size=10}C${font} ${if_mounted /home}Home : ${fs_used_perc /home}% ${fs_free /home} ${color 73d216}${fs_bar 5,120 /home}$color

${font StyleBats:size=10}C${font} ${if_mounted /media/donnees}Données : ${fs_used_perc /media/donnees}% ${fs_free /media/donnees} ${color 73d216}${fs_bar 5,120 /media/donnees}$color



${color 73d216}Réseau ${color}$hr

${font StyleBats:size=10}M${font} eth0 : ${addr eth0}

${font StyleBats:size=10}X${font} wlan0 : ${addr wlan0}

${font StyleBats:size=10}B${font} inet0 : ${texeci 120 $HOME/.conky/ip.sh}



#${if_running rhythmbox}${color 73d216}Multimédia ${color}$hr

#${font StyleBats:size=10}2${font} Artiste : ${exec rhythmbox-client --print-playing-format "%ta"}

#${font StyleBats:size=10}J${font} Album : ${exec rhythmbox-client --print-playing-format "%at"}

#${font StyleBats:size=10}4${font} Titre : ${exec rhythmbox-client --print-playing-format "%tt"}

#${font StyleBats:size=10}7${font} Durée : ${exec rhythmbox-client --print-playing-format "%te"} sur ${exec rhythmbox-client --print-playing-format "%td"}

#${color 73d216}${execibar 1 $HOME/.conky/rhythmbox.sh progress}${color}${else}

#$endif
	
Lancement au démarrage

Lancer la commande :


sh -c "sleep 20; conky;"
	

Désactiver le touchpad avec Bash

Un problème récurrent lors de l'installation de GNU/Linux sur un Asus, c'est la désactivation du touchpad. Il y a bien une combinaison de touches qui, sous Windows, le permet. Mais, étrangement, le kernel n'émet aucun keycode sous Linux. Il faut donc paramétrer une autre combinaison de touche, lançant la commande sh /home/USER/Scripts/toggle-touchpad.sh. On n'oubliera pas de rendre exécutable le script, et, encore une fois, de lire les notes :

#!/bin/bash
# Enable/Disable touchpad
# $device is found by using "xinput list"

device=13
property=132
mode="$(xinput --list-props $device | grep $property | cut -d':' -f2)"

if [ $mode -eq "1" ]
then
	xinput --set-prop $device $property 0
else
	xinput --set-prop $device $property 1
fi

Du screencast sous GNU/Linux partie 4

Le script screencaster.sh final qui semble fonctionner sur toute Buntu et dérivée, pour peu que l'on se conforme aux notes :

#!/bin/bash
#
# screencaster.sh - script to make screencasts on Linux 
#
# For information about using this script:
# http://okiebuntu.homelinux.com/blog/?p=175
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
# Nécessite libavcodec-extra-52
# Il faut peut-être ffmpeg depuis le dépôt Medibuntu. Il faudra aussi ajouter un peu de code pour faire en sorte que la taille et l'offset soit divisible par deux
# parec a besoin d'un monitor pour fonctionner : trouver un input avec pactl list | egrep -A2 '^(\*\*\* )?Source #'
# On peut arrêter l'enregistrement en configurant un raccourci clavier et en utilisant xdotool : xdotool search --onlyvisible --classname "gnome-terminal" key "Return"
#

# Config
monitor="alsa_output.pci-0000_00_1b.0.analog-stereo.monitor"
echo "set-source-mute ${monitor} false" | pacmd >/dev/null
threads=2
volume=0.25

# list of programs we depend on
progs="xdpyinfo grep head sed ffmpeg pacat parec sox libavcodec-extra-52"

# check for programs we depend on
result=0
for prog in $progs
do
  type -p $prog > /dev/null
  if (( $? != 0 )); then
    echo "Error: Cannot find required program '$prog'"
    result=1
  fi
done
if (( $result != 0 )); then
  exit 1
fi

screenSize="$(xwininfo -root | grep 'geometry' | awk '{print $2;}')" # default if we cant detect it
screenOffset="0,0" # default to top-left corner
frameRate="24" # default frame rate
baseName="`date +%Y%m%d`-`date +%H%M%S`-screencast" # default base filename for capture

# attempt to detect the dimension of the screen for the default
dimensions=`xdpyinfo | grep 'dimensions:' | head -1 | \
  sed -e 's/^.* \([0-9]\+x[0-9]\+\) pixels.*$/\1/'`
if [[ "$dimensions" =~ [0-9]+x[0-9]+ ]]; then
  screenSize=$dimensions
fi

# collect command line settings
while getopts 'hs:o:t:r:p:w' param ; do
  case $param in
    s)
      screenSize="$OPTARG"
      ;;
    o)
      screenOffset="$OPTARG"
      ;;
    t)
      timeToRecord="$OPTARG"
      ;;
    w)
      INFO=$(xwininfo)
      WIN_GEO=$(echo $INFO | grep -oEe 'geometry [0-9]+x[0-9]+' | grep -oEe '[0-9]+x[0-9]+')
      WIN_XY=$(echo $INFO | grep -oEe 'Corners:\s+\+[0-9]+\+[0-9]+' | grep -oEe '[0-9]+\+[0-9]+' | sed -e 's/\+/,/' )
      screenOffset="$WIN_XY"
      screenSize="$WIN_GEO"
      ;;
    r)
      frameRate="$OPTARG"
      ;;
    *)
      echo ""
      echo "$0 - records screencast"
      echo ""
      echo "$0 [options] [base-filename]"
      echo ""
      echo "options:"
      echo "	-h            show brief help"
      echo "	-s <size>     screensize to record as <width>x<height>"
      echo "	-o <offset>   offset off recording area as <xoffset>,<yoffset>"
      echo "	-t <time>     time to record (in seconds)"
      echo "	-w	      window to record"
      echo "	-r	      framerate (FPS)"
      echo ""
      exit 0
      ;;
  esac
done

shift $(( $OPTIND - 1 ))

# determine basename of files
if [ -n "$1" ] ; then
  baseName="$1"
fi

echo ""
echo "Size = $screenSize"
echo "Offset = $screenOffset"
echo "Rate = $frameRate"
echo "Filename = $baseName"

# get ready to start recording
echo ""
if [ -n "$timeToRecord" ]; then
  echo "Preparing to capture for $timeToRecord seconds."
else
  echo "Preparing to capture."
  echo "Press ENTER when finished capturing."
fi
sleep 3
echo ""

# start playing silence to make sure there is always audio flowing
pacat /dev/zero &
pidSilence=$!

# starts recording video using x11grab to make mpeg2video
ffmpeg -f alsa -ac 2 -i pulse \
       -f x11grab -r "$frameRate" -s "$screenSize" -i :0.0+"$screenOffset" \
       -acodec pcm_s16le -vcodec libx264 -vpre lossless_ultrafast \
       -threads "$threads" -y "$baseName.avi" &
pidVideo=$!

#ffmpeg -y -an \
#  -s "$screenSize" -r "$frameRate" -f x11grab -i :0.0+"$screenOffset" \
#  -s "$screenSize" -r "$frameRate" -aspect 4:3 -vcodec mpeg2video -sameq \
#  -f mpeg2video "$baseName.mpeg" &
#pidVideo=$!

# starts recording raw audio from output
parec -d "$monitor" --format=s16le --rate=44100 --channels=2 "$baseName-audio.raw" &
pidAudio=$!

# starts recording raw audio from input
rec -b 32 -r 44100 "$baseName-voice.wav" &
pidMicro=$!

echo ""
echo "Video recording started with process ID $pidVideo"
echo "Audio recording started with process ID $pidAudio"
echo "Voice recording started with process ID $pidMicro"
echo ""

# wait for recording to be done, either by timer or user hitting enter
if [ -n "$timeToRecord" ]; then
  sleep "$timeToRecord"
else
  read nothing
fi

# stop recordings
echo ""
echo "Terminating recordings ..."
kill -15 $pidVideo $pidAudio 
kill -15 $pidSilence
kill -15 $pidMicro
wait

# filter and normalize the audio
echo "" 
echo "Filtering and normalizing sound ..." 
sox --norm -s -b 16 -L -r 44100 -c 2 -v "$volume" "$baseName-audio.raw" "$baseName-audio.wav"  highpass 65 lowpass 12k
sox -v "$volume" "$baseName-audio.wav" "$baseName-audio2.wav"
sox --norm -s -b 32 -L -r 44100 -c 2 -v 2.0 "$baseName-voice.wav" "$baseName-voice2.wav"  highpass 65 lowpass 12k

# Récupère le son du microphone
#echo ""
#echo "Extract audio from avi"
#ffmpeg -i "$baseName.avi" -ar 44100 -ac 2 "$baseName-2.wav"

# encode video and audio into mp4 file
echo "" 
echo "Encoding to final Youtube format..." 
sox "$baseName-audio2.wav" "$baseName-voice2.wav" -m "$baseName-final.wav"

ffmpeg -i "$baseName-final.wav" -acodec libfaac -ab 320k -y "$baseName-final.aac"

ffmpeg -i "$baseName.avi" -an -s 1280x720 -aspect 16:9 -acodec libfaac -ab 320k -vcodec libx264 -vpre fast -crf 18 -threads "$threads" -y "$baseName.mp4"

ffmpeg -isync -i "$baseName-final.aac" -i "$baseName.mp4" -s 1280x720 -aspect 16:9 -acodec libfaac -ab 320k -vcodec libx264 -vpre fast -crf 18 -threads "$threads" -y  "$baseName-final.mp4" 

#ffmpeg -isync -i "$baseName.wav" -i "$baseName.mpeg" -acodec mp2 -ab 192k -vcodec copy "$baseName.avi"

# convert to ogg - to turn on uncomment next three lines
#echo""
#echo "convert to theora"
#ffmpeg2theora "$baseName.avi" -o "$baseName.ogv"

# convert avi to flv - to turn on uncomment next three lines
#echo""
#echo "convert to theora"
# ffmpeg -i "$baseNamee.avi" -ab 56 -ar 44100 -b 200 -r 15 -s 320x240 -f flv  "$baseNamee.flv"

echo ""
echo "DONE! Final media written in file $baseName.mp4"

echo ""
exit 0

Voilà, prochaine étape : un peu de python pour donner une interface simple. À noter que sous Gnome 3, un outil de screencast est incorporé d'office, via un raccourci-clavier dont personne ne se souvient, et qui n'est pas paramétrable ; il prend tout l'écran. Ma solution permet au moins de lancer un screencast à distance, selon des paramètres (30fps pour une vidéo en HD, c'est un must), et surtout de choisir une fenêtre d'un simple clic.

Du screencast sous GNU/Linux partie 3

Une solution m'est apparue : ffmpeg, que j'avais dénigré jusqu'ici, est en réalité la meilleure façon de faire un screencast sous GNU/Linux. Qualité vidéo irréprochable, post-traitement (dont une conversion en mp4 HD pour Youtube…), enregistrement des sons et du micro en parallèle, et la cerise sur le gâteau : tout est en ligne de commande.

Je me suis servi du script screencaster disponible ici sous licence GPL en le mixant un peu à ma sauce. Il reste donc sous licence GPL, mais n'a été testé que sous Ubuntu et dérivées jusqu'à présent. À ce titre, il nécessite libavcodec-extra-52, mais aussi ffmpeg du dépôt Medibuntu. Le reste des dépendances s'installe d'un simple sudo apt-get install xdpyinfo grep head sed pacat parec sox.

Quant à l'utilisation, il suffit de le lancer avec l'option -h pour comprendre assez rapidement. L'option -w fait apparaitre un curseur, il n'y a plus qu'à cliquer sur la fenêtre de votre choix pour lancer l'enregistrement. Terminez alors l'enregistrement en tapant Entrée. Il existe une petite astuce pour éviter d'avoir à revenir sur la console pour arrêter l'enregistrement, et donc devoir couper la vidéo en post-prod : on peut arrêter l'enregistrement en configurant un raccourci clavier et en utilisant xdotool : xdotool search --onlyvisible --classname "gnome-terminal" key "Return".


#!/bin/bash
#
# screencaster.sh - script to make screencasts on Linux 
#
# For information about using this script:
# http://www.guillaume.litaudon.fr/blog/
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.

# list of programs we depend on
progs="xdpyinfo grep head sed ffmpeg pacat parec sox"

# check for programs we depend on
result=0
for prog in $progs
do
  type -p $prog > /dev/null
  if (( $? != 0 )); then
    echo "Error: Cannot find required program '$prog'"
    result=1
  fi
done
if (( $result != 0 )); then
  exit 1
fi

screenSize="$(xwininfo -root | grep 'geometry' | awk '{print $2;}')" # default if we cant detect it
screenOffset="0,0" # default to top-left corner
frameRate="24" # default frame rate
baseName="`date +%Y%m%d`-`date +%H%M%S`-screencast" # default base filename for capture

# attempt to detect the dimension of the screen for the default
dimensions=`xdpyinfo | grep 'dimensions:' | head -1 | \
  sed -e 's/^.* \([0-9]\+x[0-9]\+\) pixels.*$/\1/'`
if [[ "$dimensions" =~ [0-9]+x[0-9]+ ]]; then
  screenSize=$dimensions
fi

# collect command line settings
while getopts 'hs:o:t:r:p:w' param ; do
  case $param in
    s)
      screenSize="$OPTARG"
      ;;
    o)
      screenOffset="$OPTARG"
      ;;
    t)
      timeToRecord="$OPTARG"
      ;;
    w)
      INFO=$(xwininfo)
      WIN_GEO=$(echo $INFO | grep -oEe 'geometry [0-9]+x[0-9]+' | grep -oEe '[0-9]+x[0-9]+')
      WIN_XY=$(echo $INFO | grep -oEe 'Corners:\s+\+[0-9]+\+[0-9]+' | grep -oEe '[0-9]+\+[0-9]+' | sed -e 's/\+/,/' )
      screenOffset="$WIN_XY"
      screenSize="$WIN_GEO"
      ;;
    r)
      frameRate="$OPTARG"
      ;;
    *)
      echo ""
      echo "$0 - records screencast"
      echo ""
      echo "$0 [options] [base-filename]"
      echo ""
      echo "options:"
      echo "	-h            show brief help"
      echo "	-s <size>     screensize to record as <width>x<height>"
      echo "	-o <offset>   offset off recording area as <xoffset>,<yoffset>"
      echo "	-t <time>     time to record (in seconds)"
      echo "	-w	      window to record"
      echo "	-r	      framerate (FPS)"
      echo ""
      exit 0
      ;;
  esac
done

shift $(( $OPTIND - 1 ))

# determine basename of files
if [ -n "$1" ] ; then
  baseName="$1"
fi

echo ""
echo "Size = $screenSize"
echo "Offset = $screenOffset"
echo "Rate = $frameRate"
echo "Filename = $baseName"

# get ready to start recording
echo ""
if [ -n "$timeToRecord" ]; then
  echo "Preparing to capture for $timeToRecord seconds."
else
  echo "Preparing to capture."
  echo "Press ENTER when finished capturing."
fi
sleep 3
echo ""

# start playing silence to make sure there is always audio flowing
pacat /dev/zero &
pidSilence=$!

# starts recording video using x11grab to make mpeg2video
ffmpeg -f alsa -ac 2 -i pulse \
       -f x11grab -r "$frameRate" -s "$screenSize" -i :0.0+"$screenOffset" \
       -acodec pcm_s16le -vcodec libx264  -vpre lossless_ultrafast \
       -threads 0 -y "$baseName.mkv" &
pidVideo=$!

#ffmpeg -y -an \
#  -s "$screenSize" -r "$frameRate" -f x11grab -i :0.0+"$screenOffset" \
#  -s "$screenSize" -r "$frameRate" -aspect 4:3 -vcodec mpeg2video -sameq \
#  -f mpeg2video "$baseName.mpeg" &
#pidVideo=$!

# starts recording raw audio
parec -d alsa_output.pci-0000_00_1b.0.analog-stereo.monitor --format=s16le --rate=44100 --channels=2 "$baseName.raw" &
pidAudio=$!

echo ""
echo "Video recording started with process ID $pidVideo"
echo "Audio recording started with process ID $pidAudio"
echo ""

# wait for recording to be done, either by timer or user hitting enter
if [ -n "$timeToRecord" ]; then
  sleep "$timeToRecord"
else
  read nothing
fi

# stop recordings
echo ""
echo "Terminating recordings ..."
kill -15 $pidVideo $pidAudio 
kill -15 $pidSilence
wait

# filter and normalize the audio
echo "" 
echo "Filtering and normalizing sound ..." 
sox --norm -s -b 16 -L -r 44100 -c 2 -v 0.33 "$baseName.raw" "$baseName.wav"  highpass 65 lowpass 12k

# Récupère le son du microphone
echo ""
echo "Extract audio from mkv"
ffmpeg -i "$baseName.mkv" -ar 44100 -ac 2 "$baseName-2.wav"

# encode video and audio into mp4 file
echo "" 
echo "Encoding to final Youtube format..." 
sox "$baseName.wav" "$baseName-2.wav" -m -p gain | ffmpeg -f sox -i - -i "$baseName.mkv" -s 1280x720 -aspect 16:9 -vcodec libx264 -vpre fast -crf 18 -threads 0 -acodec libfaac -ab 320k -y "$baseName.mp4"

#ffmpeg -isync -i "$baseName.wav" -i "$baseName.mpeg" -acodec mp2 -ab 192k -vcodec copy "$baseName.avi"

# convert to ogg - to turn on uncomment next three lines
#echo""
#echo "convert to theora"
#ffmpeg2theora "$baseName.avi" -o "$baseName.ogv"

# convert avi to flv - to turn on uncomment next three lines
#echo""
#echo "convert to theora"
# ffmpeg -i "$baseNamee.avi" -ab 56 -ar 44100 -b 200 -r 15 -s 320x240 -f flv  "$baseNamee.flv"

echo ""
echo "DONE! Final media written in file $baseName.mp4"

echo ""
exit 0
Haut de page