Script Powershell pour trier ses vidéos

Je possède près d’un 1 To de vidéos sur un disque externe… lequel est régulièrement plein avec les nouveautés.

Je cherchais donc un moyen de pouvoir trier les trier facilement en les ouvrant une à une dans VLC histoire de voir quelques extraits et me rappeler l’histoire puis m’afficher un popup pour la conserver (choix par défaut) ou l’effacer.

Voici un petit script Powershell que j’ai fait pour l’occasion :

[void] [System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

# Demande de sélectionner un répertoire
$OpenFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
$OpenFolderDialog.SelectedPath = [System.Environment+SpecialFolder]::UserProfile
$OpenFolderDialog.ShowDialog()
$SearchPath = $OpenFolderDialog.SelectedPath

# Parcours récursif du répertoire
$SearchDirInfo = New-Object System.IO.DirectoryInfo $SearchPath
$SearchEnumerator = $SearchDirInfo.EnumerateFiles("*", [System.IO.SearchOption]::AllDirectories).GetEnumerator()

foreach ($Result in $SearchEnumerator)
{
    # Affiche le chemin dans la console
    Write-Host "Found" $Result.FullName

    # Ouvre VLC
    & 'C:\Program Files\VideoLAN\VLC\vlc.exe' $Result.FullName | Out-Null

    # Pose la question de le conserver
    $Answer = [System.Windows.Forms.MessageBox]::Show( `
        [string]::Concat("Conserver ", $Result.Name, " ?"), `
        "Conserver le fichier ?", `
        [System.Windows.MessageBoxButton]::YesNo,
        [System.Windows.Forms.MessageBoxIcon]::Question,
        [System.Windows.Forms.MessageBoxDefaultButton]::Button1
    )

    if ($Answer -eq [System.Windows.MessageBoxResult]::Yes)
    {
        Write-Host "A garder !"
    }
    elseif ($Answer -eq [System.Windows.MessageBoxResult]::No)
    {
        Write-Host "A supprimer !"
        Remove-Item -Verbose $Result.FullName
    }
    else
    {
        Write-Error "Oops, réponse invalide"
        break
    }
}

Résultat, je le lance depuis l’explorateur (clic-droit > Exécuter avec Powershell), il me demande de sélectionner le répertoire de départ :

Capture-1

Puis, pour chaque vidéo, m’ouvre VLC pour que je visionne quelques extraits et, à la fermeture, pose la question de la conservation :

Capture-2

Si le script est ouvert depuis une invite Powershell, on peut voir clairement le chemin du fichier et s’il a était conservé ou supprimé :

Capture-2

Bonus : J’avais fait il y a quelques temps un script équivalent en shell, moins évolué et non documenté (pas drôle sinon). Il utilise zenity pour afficher le dialogue et indique également la date de création du fichier. Le voici :

#!/bin/sh
find "${PWD}" -type f | sort -R | while read -r f
do
    vlc --no-qt-error-dialogs  "${f}" >/dev/null
    dc="$(stat "${f}" | sed 's/\\/\\\\/')"
    if ! zenity --question --text="Conserver '${f}' ?\nDate de création : ${dc}" --no-wrap
    then
        rm -fv "${f}"
    fi
done

Utilisation :

cd /mnt/usb && sh script.sh

Puppet : contain vs include

Si votre code Puppet s’organise avec des rôles et des profiles (ce qui est très fortement recommandé), il y a de fortes chances que vous ayez du code ressemblant à ceci :

class roles::webserver {
   include profiles::base
   include profiles::apache
}

A l’exécution, surprise, l’ordre d’exécution n’est pas forcément celui attendu. Dans l’exemple ci-dessus, profiles::apache pourrait très bien passer avant.

Pour forcer l’ordre, on va donc utiliser des chaining arrows :

class roles::webserver {
   include profiles::base
   include profiles::apache

   Class['profiles::base'] ->
   Class['profiles::apache']
}

Cette approche se voit régulièrement et pose pourtant un problème de taille : l’ordre ne pas récursif, autrement dit il ne s’applique pas aux include que contiennent profiles::base et profiles::apache.

Ainsi, si profiles::apache inclut lui-même apache, il est tout fait possible que apache vienne s’exécuter avant ce qu’il y a dans profiles::base.

La bonne approche est d’utiliser le mot-clé contain qui, couplé avec les chaining arrows, garanti l’ordre d’exécution comme attendu (d’abord profiles::base puis profiles::apache) :

class roles::webserver {
   contain profiles::base
   contain profiles::apache

   Class['profiles::base'] ->
   Class['profiles::apache']
}

Note : si votre code est bien fait, la question de l’ordre d’exécution ne devrait pas se poser. Lorsque vous écrivez votre code, pensez à bien mettre un require/notify sur toutes les ressources qui dépendent de quelque chose.

Petite astuce pour vérifier que les dépendances (require et notify) sont correctement définies, exécuter Puppet avec l’option ordering à random. Avec cette option, Puppet « secoue tout » et, à chaque exécution, change l’ordre (en tenant compte des dépendances, heureusement :)). rspec-puppet a aussi un réglage pour activer ce comportement.

Ruby / IRB : Supprimer dynamiquement une classe ou un module

Contrairement à Python, Ruby ne permet pas de supprimer / undefine une classe ou un module en lui attribuant nil :

[23] pry(main)> module Foo; end  
=> nil
[24] pry(main)> Foo.class
=> Module
[25] pry(main)> Foo = nil
(pry):69: warning: already initialized constant Foo
(pry):66: warning: previous definition of Foo was here
=> nil
[27] pry(main)> Foo.class
=> NilClass
[28] pry(main)> module Foo; end
TypeError: Foo is not a module

Ce comportement s’explique par le fait qu’une classe ou un module est avant tout une constante, d’où le already initialized constant Foo.

Heureusement, il existe une méthode privée remove_const pour supprimer du contexte une constante. Visibilité privée oblige, impossible de faire directement Foo.remove_const :

[30] pry(main)> Foo.remove_const
NoMethodError: private method `remove_const' called for Foo:Module

La solution est d’appeler remove_const par l’intermédiaire de Object (Object is the default root of all Ruby objects.) :

[6] pry(main)> Object.send(:remove_const, :Foo)
=> Foo
[7] pry(main)> Foo
NameError: uninitialized constant Foo

Puppet : Convertir une chaîne en nombre

Puppet permet de convertir une chaîne en nombre de plusieurs façons.

Implicitement :

# cat test.pp
$nombre = '8080'
$type = type($nombre)
notify { "Type: ${type}": }
# puppet apply --verbose test.pp
Notice: Type: Integer[8080, 8080]

Chose qui ne fonctionne pas toujours, notamment avec Hiera :

# cat /etc/puppetlabs/code/environments/production/hieradata/common.yaml 
foo::port: '8080'
# cat test.pp
class foo(Any $port) {
    $type = type($port)
    notify { "Type: ${type}": }
}
include foo
# puppet apply --verbose test.pp
Notice: Type: String

On peut cependant lui forcer la main en ajoutant 0 – méthode que l’on peut retrouver dans la documentation officielle :

# cat test.pp
class foo(Any $port) {
    $port_int = 0 + $port
    $type = type($port_int)
    notify { "Type: ${type}": }
}
include foo
# puppet apply --verbose test.pp
Notice: Type: Integer[8080, 8080]

Procéder de la sorte génère le warning suivant depuis Puppet 4.10.2 :

Warning: The string '8080' was automatically coerced to the numerical value 8080

En mode strict, Puppet va même transformer cela en erreur.

La seule et unique méthode désormais est d’utiliser Numeric (apparu avec Puppet 4.1), Integer ou Float :

# cat test.pp 
class foo(Any $port) {
    $port_int = Numeric($port)
    $type = type($port_int)
    notify { "Type: ${type}": }
}
include foo
# puppet apply --verbose test.pp
Notice: Type: Integer[8080, 8080]

Puppet : The argument signature (String format, [String timezone]) is deprecated for #strfime

Pour obtenir un timestamp avec Puppet, je faisais jusqu’à présent :

$timestamp = strftime('%s')

Cela fonctionnait bien mais générait le warning suivant :

Puppet : The argument signature (String format, [String timezone]) is deprecated for #strfime

La bonne pratique est désormais :

$timestamp = Timestamp().strftime('%s')

Pour rappel, strftime est fourni nativement depuis Puppet 4.8. Ce n’est plus puppetlabs-stdlib qui est utilisé. Il y a d’ailleurs un changement dans la syntaxe entre les deux, cf PUP-6724.

Source : https://docs.puppet.com/puppet/latest/function.html#strftime

Kaspersky : Désactiver l’alerte pour certificat SSL auto-signé

Kaspersky Internet Security avertit par défaut lorsque l’on consulte un site avec un certificat SSL auto-signé :

Alerte Kaspersky SSL

Pratique au début, cela en devient vite pénible tellement le nombre de sites concernés est important. J’arrive rapidement à une bonne dizaine d’alertes en regardant mes flux RSS…

Pour le désactiver, deux possibilités : désactiver complètement l’inspection SSL ou uniquement le bloqueur de de pub + la navigation privée.

Désactiver l’inspection SSL

Dans les réglages de Kaspersky > Avancé > Réseaux, sélectionner « Ne pas analyser les connexions sécurisées ». Une demande de confirmation s’affichera :

Désactiver le bloqueur de pub et la navigation privée :

Dans les réglages de Kaspersky > Protection :

Kaspersky Désactive Analyse SSL

Dans les deux cas, à vos risques et périls car cela réduit forcément la sécurité 🙂