Some useful Bash commands

youtube-dl

# 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 https://www.youtube.com/watch?v=uhikhkih # Use IPv4

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

wget

# 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 https://trucmuche.com/truc.mp3
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  http://example.com/configs/.vim/

## To avoid downloading the directory structure as well
wget -r -nH -nd -np -R index.html* -e robots=off http://example.com/configs/.vim/

# Avoid the robots.txt issue
 -e robots=off  (See http://www.krazyworks.com/wget-and-user-agent-header/)

# 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:1.9.0.2) 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 from multiple folder, renaming the files to add the folder they was in
ls . > list; while read LINE <&3; do for i in `find $LINE -name "*.svg"`;do filename=$(basename -- "$i"); cp $i "$LINE-$filename"; done; done 3< list; rm list;

# 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

Other

# Get filename and extension
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"

FFmpeg: 10 bits HEVC vers 8 bits en préservant les couleurs

ffmpeg -i input.mkv -map 0 -c:v libx264 -crf 18 -vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p -c:a copy output.mkv

Virer VLC de l'applet de son Cinnamon

VLC fait régulièrement planter l'applet de son de Cinnamon : une fois fermé, il reste indéfiniment, comme si un processus fantôme nommé VLC.instance# s'accaparait l'applet même après un killall.

Ouvrir /usr/share/cinnamon/applets/sound@cinnamon.org/applet.js et modifier :

let players_with_seek_support = [
    'clementine', 'banshee', 'rhythmbox', 'rhythmbox3', 'pragha', 'quodlibet',
    'amarok', 'xnoise', 'gmusicbrowser', 'vlc', 'qmmp', 'deadbeef', 'audacious'];

Mastodon share & DM with inline javascript

<!-- Share -->
<a href="javascript:var server=prompt('Instance ?').replace(/(http(s)?:\/\/)/g,'').replace(/\/$/,'');window.location.href=`https://${server}/share?visibility=public&text=%20via%20@yomli@mastodon.xyz%20%3A%20&url={racine_du_site}{lien_permalink}`;void 0;">Share</a>
<!-- Direct message -->
<a href="javascript:var server=prompt('Instance ?').replace(/(http(s)?:\/\/)/g,'').replace(/\/$/,'');window.location.href=`https://${server}/share?visibility=direct&text=@yomli@mastodon.xyz%20%3A%20&url={racine_du_site}{lien_permalink}`;void 0;">DM</a>

Pure CSS modal dialog

<a href="#popup">Open</a>
<dialog id="popup">
    <a href="#!">Close</a>
</dialog>
dialog { display: block; }
dialog:not(:target):not([open]) { display: none; }

Récupérer un fichier supprimé avec grep

grep -a -C 2000 "global_objects\.add" /dev/sda2 | tee recovered_data

-a est une option qui signifie qu'on veut traiter les fichiers binaires « comme si » c'était des fichiers textes : normalement la commande ne marche que sur du texte, mais si vous voulez chercher une chaîne de caractère dans les octets qui composent une image JPG, libre à vous ;
-C 2000, « C » pour contexte : si la chaîne de caractères est trouvée, je veux que grep me renvoie les 2000 lignes qui « entourent » la ligne où la chaîne apparaît. Comme mon script faisait dans les 1000 lignes, j'ai tapé large. Vous pouvez aussi jouer avec -B (pour before) et -A (pour after) pour avoir un contexte non-symmétrique ;
"global_objects\.add" : une chaîne de caractères dont je me rappelais qu'elle était présente dans le script en question. Si vous avez perdu un texte, essayez de vous rappeler d'un titre ou d'une séquence de mots spécifique. Pour ma part, je me souvenais qu'à un moment, je remplissais un set nommé global_objects ;
/dev/sda2 : le truc magique, c'est que grep peut tourner sur une partition complète ! Ici, la partition qui correspond à un mon dossier perso ;
tee recovered_data : ça, c'est optionnel, mais ça permet, lorsque la chaîne est trouvée, à la fois de l'afficher et de la stocker dans le fichier recovered_data.

Pour faire simple : on demande à grep de scanner l'intégralité du disque dur (ou de la partition en question) comme si c'était un gros texte, et si jamais il trouve la chaîne global_objects.add, de nous afficher les 2000 lignes de texte qui l'encadrent.

System font stack in 2023

I used to write:

system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"

But in 2023, system-ui is much more supported, so this covers almost all fonts:

system-ui, -apple-system, BlinkMacSystemFont, sans-serif

Send a direct message on Mastodon with a one-click link

<a href="javascript:var server=prompt('Instance ?').replace(/(http(s)?:\/\/)/g,'').replace(/\/$/,'');window.location.href=`https://${server}/?visibility=direct&text=@yomli@mastodon.xyz`;void 0;">Direct</a>

Javascript - Efficiently replace all accented characters in a string

const strip_accents = (str) => str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');

Two things are happening here:

  1. normalize()ing to NFD Unicode normal form decomposes combined graphemes into the combination of simple ones. The è of Crème ends up expressed as e + ̀.
  2. Using a regex character class to match the U+0300 → U+036F range, it is now trivial to globally get rid of the diacritics, which the Unicode standard conveniently groups as the Combining Diacritical Marks Unicode block.

Same with another syntax:

str.normalize('NFKD').replace(/\p{Diacritic}/gu, '');

Extraire les caractères d'une police TTF en SVG

fontforge -lang=ff -c 'Open($1); SelectWorthOutputting(); foreach Export("svg"); endloop;' foo.ttf

Universal code syntax hightlighter

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

const hightlight = (code) => code
  // operators
.replaceAll(/\b(var|function|typeof|new|return|if|for|in|while|break|do|continue|switch|case|try|catch)([^a-z0-9\$_])/g,
  '<span class="c-keyword">$1</span>$2')
  // types
.replaceAll(/\b(RegExp|Boolean|Number|String|Array|Object|Function|this|true|false|NaN|undefined|null|Infinity)([^a-z0-9\$_])/g,
  '<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,'  ')

document.querySelectorAll('code')
    .forEach((code) => {
        code.innerHTML=hightlight(code.innerText)
    });
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
    .replaceAll(/\b(var|const|let|function|typeof|new|return|if|for|foreach|in|while|break|do|continue|switch|case|try|catch)([^a-z0-9\$_])/g,
        '<span class="c-operator">$1</span>$2')
    // Types
    .replaceAll(/\b(RegExp|Boolean|Number|String|Array|Object|Function|this|true|false|NaN|undefined|null|Infinity)([^a-z0-9\$_])/g,
        '<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.innerHTML=hightlight(code.textContent)
                });
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 http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ 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"
EndSection

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 :

ID_AUDIO_ID=128
ID_AID_128_LANG=de
...
ID_AUDIO_ID=129
ID_AID_129_LANG=ja
...
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.