Some useful Bash commands


# Batch download
youtube-dl -i -F -a ~/Desktop/list.txt # Get the format
youtube-dl -i -f <format> -a ~/Desktop/list.txt # Use the format

# Automagically download the desired resolution
youtube-dl -i -f "bestvideo[height<=720]+bestaudio/best[height<=720]" -a ~/Desktop/list.txt

## Avoid throttling
youtube-dl -i --external-downloader aria2c --external-downloader aria2c --external-downloader-args "-j 8 -s 8 -x 8 -k 5M" -f mp4-240p -a ~/Desktop/list.txt
youtube-dl -f 18 -4 -i # Use IPv4

## Rate limit
youtube-dl -i -r 120k -a ~/Desktop/list.txt


# Batch download
wget -r -H -nc -np -nH --cut-dirs=1 -e robots=off -l1 -i ~/Desktop/list.txt

# Rate limit
wget --no-check-certificate --limit-rate 60k
wget --no-check-certificate --limit-rate 20k -i ~/Desktop/list.txt # Batch download

# Recursively fetch a directory
wget -r --no-parent --reject "index.html*" -e robots=off

## To avoid downloading the directory structure as well
wget -r -nH -nd -np -R index.html* -e robots=off

# Avoid the robots.txt issue
 -e robots=off  (See

# Download one type only
 -A jpeg,jpg,bmp,gif

# Use a user-agent
 -U -U "Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv: Gecko/20121223 Ubuntu/9.25 (jaunty) Firefox/3.8"

# So, get only fonts in an entire site
wget -r --no-parent --reject "index.html*" -e robots=off --limit-rate 60k -A otf,ttf,ttc,dfont 

Batch stuff

# Convert multiple video to audio
for i in *.mp4; do ffmpeg -i "$i" -vn "${i%mp4}mp3"; done
# Convert avi to mp4
for i in *.avi; do ffmpeg -i "$i" -c:a aac -b:a 128k -c:v libx264 -crf 23 -preset fast -profile:v baseline -level 3.0 -threads 2 "${i%avi}mp4"; done
# Convert mkv to mp4
for i in *.mkv; do ffmpeg -i "$i" -c:a aac -b:a 128k -c:v libx264 -crf 23 -preset fast -s 720x480 -threads 2 "${i%mkv}mp4"; done
# Convert .png to .jpg
for i in *.png; do mogrify -format jpg "$i" && rm "$i"; echo "$i converted to ${i%.*}.jpg"; done
# Same, but recursive
find . -iname '*.png' | while read i; do mogrify -format jpg "$i" && rm "$i"; echo "Converted $i to ${i%.*}.jpg"; done

# Randomize name
for i in *.mp4; do mv -i "$i" "${RANDOM}.mp4"; done

# Recursive copy
find . -name "*.zip" -exec cp -t ./direction_dir {} +
find . -type f -name "*.mp3" -exec cp {} /tmp/MusicFiles \ # Alternative
# Recursive deletion
find . -name "*.*" -print0 | xargs -0 rm -rf
# Recursive md5sum
find -type f -exec md5sum "{}" + > checklist.chk

# Recursive rename with modified date
j=1; for i in `find . -type f`; do mv -- "$i" "$(date +%Y-%m-%d_%H\'%M\'%S -r $i)_$j.${i##*.}"; j=$((j + 1)); done
## Will output: 2021-02-01_15'16'06_foo.webm
## Note: use IFS=$'\n' before the loop, for find to understand space in filename (mandatory on Windows)

# Recursive move in folders
for j in {01..31}; do for i in `find . -name "*\-$j\_*"`; do mkdir -p "$j" && cp "$i" "$j/"; done; done
## Use case: we have a bunch of files named 2021-02-01_15'16'06_bar.webm, we want them to be moved into the folder corresponding to their month (01 to 31)

Video to gif

# Simple
for i in *.mp4; do ffmpeg -i "$i" -an -s 320x240 -pix_fmt rgb24 -r 10 "${i%mp4}gif"; done
# Optimal with compression
for i in *.webm; do ffmpeg -i "$i" -vf fps=10,scale=320:-1:flags=lanczos,palettegen "$i.png"; ffmpeg -i "$i" -i "$i.png" -filter_complex 'fps=10,scale=320:-1:flags=lancz os[x];[x][1:v]paletteuse' "${i%webm}gif"; rm "$i.png"; done
# Recursive on the first 15 seconds with deletion of step's products
for i in `find . -name "*.webm"`;do ffmpeg -ss 0 -t 15 -i "$i" -vf fps=10,scale=320:-1:flags=lanczos,palettegen "$i.png"; ffmpeg -ss 0 -t 15 -i "$i" -i "$i.png" -filter_complex 'fps=10, scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse' "${i%webm}gif"; rm "$i.png"; rm "$i";done


# Get filename and extension
filename=$(basename -- "$fullfile")

Universal code syntax hightlighter

Un syntax highlighter minimal (encore plus léger et themable que microlight).

const hightlight = (code) => code
  // operators
  '<span class="c-keyword">$1</span>$2')
  // types
  '<span class="c-type">$1</span>$2')
  // comments
  .replaceAll(/(\/\*[^]*?\*\/|(\/\/)[^\n\r]+)/gim,'<span class="c-comment">$1</span>')
  // strings
  .replaceAll(/('.*?')/g,'<span class="c-string">$1</span>')
  // function & variables names (with link)
  .replaceAll(/([a-z\_\$][a-z0-9_]*)(\s?([\(\)\[\];]|[=+\-\*,<]\s)|\s>)/gi,'<a id="var-$1" href="#var-$1" class="c-variable">$1</a>$2')
  // braces
  .replaceAll(/(\{|\}|\]|\[|\|)/gi,'<span class="c-punctuation">$1</span>')
  // numbers
  .replaceAll(/(0x[0-9a-f]*|\b(\d*\.)?([\d]+(e-?[0-9]*)?)\b)/gi,'<span class="c-atom">$1</span>')//|(0x[0-9abcdefx]*)
  // tabulations (2 spaces)
  //.replace(/\t/g,'  ')

    .forEach((code) => {
code .c-type {font-weight:700}
code .c-variable, .c-type {color: #228}
code .c-keyword {color: #708}
code .c-string {color:#a22}
code .c-punctuation {color:#666}
code .c-atom {color:#281}
code .c-comment, .c-comment * {color: #A70!important}
code *:target{background-color:#ff6}

Mais en quelques minutes d'utilisation, on voit qu'il souffre quelques problèmes. On peut donc l'améliorer rapidement en :

const hightlight = (code) => code
    .replaceAll(/(<)/g,'<span>$1</span>') // Mandatory, else innerHTML comments them with <!-- -->  
    // Strings
    .replaceAll(/('.*?'|".*?")/g,'<span class="c-string">$1</span>')
    // Operators
        '<span class="c-operator">$1</span>$2')
    // Types
        '<span class="c-type">$1</span>$2')
    // Comments
    .replaceAll(/(\/\*[^]*?\*\/|(?<!\:)(\/\/)[^\n\r]+|<span><<\/span>\!\-\-[^]*?\-\->)/gim,'<span class="c-comment">$1</span>')
    // Variables & Function names
    .replaceAll(/([a-z\_\$][a-z0-9_]*)(\s?([\(\)\[\];]|[=+\-\*,<]\s)|\s>)/gi,'<a id="var-$1" href="#var-$1" class="c-variable">$1</a>$2')
    // Braces
    .replaceAll(/(\{|\}|\]|\[|\|)/gi,'<span class="c-punctuation">$1</span>')
    // Numbers
    .replaceAll(/(0x[0-9a-f]*|\b(\d*\.)?([\d]+(e-?[0-9]*)?)\b)/gi,'<span class="c-atom">$1</span>')//|(0x[0-9abcdefx]*)
    // Tabs (2 spaces)
    //.replace(/\t/g,'  ')

document.querySelectorAll('pre > code')
                .forEach((code) => {
code .c-type {font-weight:700}
code .c-variable, .c-type {color: #228}
code .c-operator {color: #708}
code .c-string {color:#a22}
code .c-punctuation {color:#666}
code .c-atom {color:#281}
code .c-comment, .c-comment * {color: #A70!important}
code *:target{background-color:#ff6}
/* Tomorrow Night theme */
@media screen and (prefers-color-scheme: dark) {
    code .c-type {color:#DB9455;font-style:700}
    code .c-operator {color: #B194B4}
    code .c-variable {color: #83A1C1}
    code .c-string {color:#D7C467}
    code .c-atom {color: #B1BE59}
    code .c-punctuation {color:inherit}
    code .c-comment, .c-comment * {color: #999!important;opacity:.5}

Modifications :

  • thème sombre automatique
  • support des langages à markup et PHP
  • ajout de const et let pour Javascript, foreach pour PHP
  • les commentaires ne se font plus dans les strings
  • les commentaires ne se font plus sur les URI (//)
  • appel via textContent et non le plus lent et moins fiable innerText (voir mais pour résumer : innerText n'était pas standard, il ne s'applique qu'à des HTMLElement et non des Node, il ne retourne que le contenu visible, change les espaces et demande des infos de layout qui font qu'il est de 12 à 300 fois moins rapide)

Rendre un paragraphe éditable depuis l'Inspecteur de documents

<p contenteditable="true" spellcheck="true">…</p>

Use $ & $$ Instead of document.querySelector/All in JavaScript without jQuery

From comment below:

const $ = (q, d = document) => 
  /#\S+$/.test(q)                       // check if query asks for ID
  ? d.querySelector.bind(d)(q)          // if so, return one element
  : [...d.querySelectorAll.bind(d)(q)]  // else, return all elements in an array.

Usage : $('#main').innerHTML

Enlever le flag "default" sur des sous-titres de .mkv

En considérant que le stream des sous-titres est 0:3 :

ffmpeg -i input.mkv -map 0:0 -map 0:1 -map 0:2 -map 0:3 -disposition:s -default -default_mode infer_no_subs -c copy output.mkv

Traitement de masse :

for i in *.mkv; do ffmpeg -i "$i" -map 0:0 -map 0:1 -map 0:2 -map 0:3 -disposition:s -default -default_mode infer_no_subs -c copy "./DONE/${i%.mkv}-test.mkv"; done

Combine one image and one audio file to make one video

ffmpeg -loop 1 -i img.jpg -i music.mp3 -shortest -acodec aac -vcodec libx264 result.mp4

Some poor workaround for Aura Creator saying there is no device

TL,DR: use Aura Creator with USB only, switch to 2.4GHz later.

So I bought an AZERTY ROG Falchion some days ago, working fine, was detected by Aura Creator… until the compatibility broke. Aura Creator was saying there is no device, but Aura Sync was working, and I could change my settings in Armoury Crate.

So, I reinstalled Aura Creator, losing my profiles. It was working until it broke again. I checked the log (DebugLog.log at \AppData\Local\Packages\XXXXX.AURACreator_xxxxx\LocalState\) and it was saying [Rescan] Get unsupported device ROG FALCHION, type: Keyboard which was weird. So, I messed around until I could get a grab on how this software was poorly designed.

The trick is: ROG Live Service detects connected devices and saves them to C:\ProgramData\ASUS\ROG Live Service\devices.ini. When the ROG Falchion is connected through USB only, there is one unique device in this file, with an US Layout. But when it is connected through 2.4GHz, there are two devices with two different PID. Somehow, the one with EU Layout isn't recognized by Aura Creator (but works fine with Aura Sync and Armoury Crate), despite the facts that Aura Creator has the correct .csv.

My workaround for users of EU layouts is quite simple: use Aura Creator with USB only, the 2.4GHz button off. Then you may be able to edit profiles to use when you'll switch to 2.4GHz later on. I hope this helps you.

Oh, and btw, before reinstalling Aura Creator you could save your profiles located in the \AppData\Local\Packages\XXXXX.AURACreator_xxxxx\ folder.

How do I stop my ROG Falchion wireless keyboard from rebooting Ubuntu?

For a more permanent solution, append the following config to /usr/share/X11/xorg.conf.d/40-libinput.conf:

Section "InputClass"
Identifier         "disable asus falchioh sys control"
MatchIsKeyboard    "on"
MatchProduct       "ASUSTeK ROG FALCHION System Control"
Option             "Ignore" "on"

Install python 3.6 on Ubuntu 20+ with distutils and pip support

sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update

Search and install something like python36-full. Then you can use pip with it:

python3.6 -m pip install [Package_to_install]

Python One-Liner Webserver HTTP

Python 2

python -m SimpleHTTPServer 8000

Python 3

python -m http.server 8000

Ripper un DVD multilangue avec mplayer/mencoder

La syntaxe de base :

mplayer -dumpstream dvdnav://1 -nocache -dumpfile my-duper-dvd.mpg

Il faut trouver le chapitre avec mplayer dvd://1. Une fois trouvé, on identifie l'AID de la langue souhaitée avec mplayer dvd://2 -identify :

subtitle ( sid ): 0 language: de
subtitle ( sid ): 1 language: de

Du coup notre commande devient :

mplayer -dumpstream dvdnav://2 -aid 129 -nocache -dumpfile my-duper-dvd.mpg

Si jamais des sous-titres sont lancés automatiquement, il suffit d'ajouter -nosub.

Remplacements multiples de termes dans plusieurs fichiers

La ligne de commande qui peut vous sauver plusieurs heures de boulot :

while read -r pattern replacement; do sed -i -- "s/$pattern/$replacement/" *.tex ; done < patterns.txt

Ça remplace toutes les occurrences de plusieurs termes dans tous les fichiers d'un type donné.

Ça prend en entrée un fichier patterns.txt constitué d'une liste comme suit :

foo bar
truc muche

Où foo et truc seront remplacés respectivement par bar et muche.


Les DNS de FDN : bookmarklet

Redirects a youtube page to an one. Copy and paste this into the url field of a browser bookmark.

javascript:(window.location.href.includes(""))?location.href=window.location.href.replace("",""):console.log('not a youtube page')

Chargement dynamique d'un Javascript en fonction de l'existence d'une classe dans le DOM

function isAnyClassOutThere(class,tag){
   // On transforme le string passé en paramètre en une expression régulière
   //(sans cela, test() ne retournera rien)
   var regExpClass = new RegExp(class);

   // S'il n'y avait pas de tag passé en paramètre, on considère que l'on regarde
   // tous les tags
   var tag = tag || "all";
   var elm = (tag == "all") ? document.all : document.getElementsByTagName(tag);

   var result = false;

   // Pour chaque élément que l'on a récupéré dans elm
   for(var i=0; i < elm.length; i++){

      // On test la correspondance
         result = true;

         // On sort de la boucle (pas très joli, mais évite de faire un return 
         // dans une boucle)

return result;
function loadJS(url){
   // On crée un élément script
   var elm=document.createElement("script");

   // On lui attribue une url
   elm.setAttribute("src", url);

   // On renseigne le type
   elm.setAttribute("type", "text/javascript");

   // Enfin, on l'ajoute en temps qu'enfant de l'élément head
   if(isAnyClassOutThere("galerie", "ul")){

Remplacement propre des caractères spéciaux en PHP

function filtre($string) {
    $search = array ('@(é|è|ê|ë|Ê|Ë)@','@(á|ã|à|â|ä|Â|Ä)@i','@(ì|í|i|i|î|ï|Î|Ï)@i','@(ú|û|ù|ü|Û|Ü)@i','@(ò|ó|õ|ô|ö|Ô|Ö)@i','@(ñ|Ñ)@i','@(ý|ÿ|Ý)@i','@(ç)@i','@( )@i','@(^a-zA-Z0-9_)@', '@(\'|\.)@');
    $replace = array ('e','a','i','u','o', 'n', 'y', 'c','-','', '');
    $in = preg_replace($search, $replace, $string);
    $in = preg_replace('/([^.a-z0-9]+)/i', '-', $string);
    return strtolower($string);

Désactiver le touchpad avec Bash

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

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

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

Oubliez votre lien d'administration avec Javascript !

Appuyer 3 fois sur Entrée mènera au lien d'administration :

function keyboardAuth(redirection) {
    var enterCount = 0;

    document.addEventListener("keypress", function(event) {
        var keycode = (event.keyCode ? event.keyCode : event.which);
        if(keycode == '13'){
            if(enterCount >= 3){
                enterCount = 0;
                window.location = redirection;
        setTimeout(function(){ enterCount = 0; },2000);


Scrolling doux vers une ancre

$('a[href*=#]').on('click', function(event){
$('html,body').animate({scrollTop:$(this.hash).offset().top}, 700); });

Un lien « Retour en haut » dynamique

<div class="relative">
    <div class="toTop">
        <a href="#top" title="Retour en haut">
            Retour en haut
<footer id="footer">
.relative {

.toTop {
function toTop(element,footer){
    var scroll = $(window).scrollTop();
    var maxScroll = $(window).height() * 0.4;

    if(scroll > maxScroll)

    if($(element).offset().top + $(element).height() >= $(footer).offset().top - 10)
        $(element).css('position', 'absolute');
    if($(document).scrollTop() + window.innerHeight < $(footer).offset().top)
        $(element).css('position', 'fixed');

$(window).scroll(function() {

$(window).resize(function() {