tagutil: gérer vos tags audio en CLI

Posted by kAworu Tue, 27 May 2008 23:53:00 GMT

tagutil koi-est-ce ?

En cherchant un outil puissant pour gérer les tags et les noms de mes fichiers de musique. j’ai trouvé … Rien.

Amarok ou quelques outils graphiques font ça pas trop mal, mais jamais tout ce que j’avais besoin. De plus je voulais un truc en ligne de commande, histoire de pouvoir le planter dans des scripts, éviter les click, abuser de la completion ZSH et toussa.

Comme j’ai rien trouvé je l’ai codé (CLAP CLAP monself). C’est codé en C (un vrai langage d’homme), ça utilise taglib pour modifier et lire les tags des fichiers audios.

Ze FM

Comme je suis chic (et que comme je pond un article tout les six mois, faut bien que je les remplisse un peu) voici le FM aka le __Fuc^WFriendly Manual__. lorsque l’on lance tagutil sans arguments ça donne ça:

TagUtil v1.1 by kAworu.

usage: ./tagutil <opt> [optarg] <files>
Modify or output music file's tag.

Options:
    -- <files>             : only show files tag. -- is not needed but useful if the first file
                             argument may be a file that could match an option.
    -e <files>             : show tag and prompt for editing (need $EDITOR environment variable)
    -r <PATTERN> <files>   : rename files with the given PATTERN. you can use keywords in PATTERN:
                             title(%t), album(%a), artist(%A), year(%y), track(%T), comment(%c),
                             and genre(%g). example: "%A - %a - (%T) - %t"
    -t <TITLE> <files>     : change title tag to TITLE for all given files
    -a <ALBUM> <files>     : change album tag to ALBUM for all given files
    -A <ARTIST> <files>    : change artist tag to ARTIST for all given files
    -y <YEAR> <files>      : change year tag to YEAR for all given files
    -T <TRACK> <files>     : change track tag to TRACK for all given files
    -c <COMMENT> <files>   : change comment tag to COMMENT for all given files
    -g <GENRE> <files>     : change genre tag to GENRE for all given files

afficher

lorsque l’on apelle tagutil avec seulement des fichiers en arguments (pas d’option) il va afficher les tags du/des fichiers (dans l’ordre donné). Attention, si un fichier en argument n’existe pas ou n’est pas un fichier tagutil affichera une erreur et se terminera.

% ./tagutil music/JOHNNY_CASH_-_at_san_quentin_-_13_-_folsom_prison_blues.ogg 
FILE: "music/JOHNNY_CASH_-_at_san_quentin_-_13_-_folsom_prison_blues.ogg"
title   - "Folsom Prison blues"
album   - "At San Quentin"
artist  - "Johnny Cash"
year    - "1969"
track   - "13"
comment - ""
genre   - "Country"

editer

Pour éditer en masse ou par script, les options -t|-a|-A|-y|-T|-c|-g servent à éditer le champ correspondant de tous les fichiers donnés en argument (c’est rarement utile pour -t et -T d’avoir plus d’un fichier, mais bon). par exemple:

% tagutil -A "Johnny Cash" music/JOHNNY_CASH*

pour éditer en mode interactif, on peut utiliser l’option -e. Pour chaque fichier donné en argument tagutil va afficher les tags, puis demander à l’utilisateur s’il veut éditer les tags. Si l’utilisateur répond y|yes $EDITOR est lancé pour éditer les tags (très convi d’éditer avec son éditeur favori). Exemple:

% tagutil -e music/JOHNNY_CASH*

renommer

tagutil sait renommer un fichier en fonction de ses tags (c’est en faite la fonctionalité principale dont j’avais besoin qui m’a poussé à écrire tagutil). pour chaque fichier donné en argument, tagutil va le renommer suivant le “template” donné à l’option -r. Si l’ancien nom et le nouveau sont pareil, il ne fait rien, sinon il affiche l’ancien nom et le nouveau puis demande une confirmation de l’utilisateur. Si un fichier existe déjà avec le nom de destination, tagutil va abandonner et ne pas renommer le fichier pour éviter d’effacer un fichier existant. Un petit exemple si on veut renommer notre fichier avec le template “<artist> - <album> - <title>”:

% ./tagutil -r '%A - %a - %t' JOHNNY_CASH_-_at_san_quentin_-_13_-_folsom_prison_blues.ogg 
rename "JOHNNY_CASH_-_at_san_quentin_-_13_-_folsom_prison_blues.ogg" to "Johnny Cash - At San Quentin - Folsom Prison blues.ogg" ? [y/n] y
% ls "Johnny Cash - At San Quentin - Folsom Prison blues.ogg" 
Johnny Cash - At San Quentin - Folsom Prison blues.ogg

Installation

tagutil est géré avec un repository mercurial. l’adresse est https://hg.kaworu.ch/tagutil/. C’est donc possible soit de faire un clone du repository:

% hg clone https://hg.kaworu.ch/tagutil/

ou de se rendre sur l’url et de télécharger une archive (gz, zip, ou bz2).
Faite attention de prendre une version “tag” (genre v1.1) si vous voulez avoir une chance de pouvoir compiler car il est fort possible tip (le dernier commit dans le jargon mercurial) ne compile pas sur certains OS, voir pas tout court.

Pour la compilation voici ce qui est conseillé par la team de développement officielle (aka moi). N’oubliez pas d’installer la taglib avant de compiler tagutil :

# pour GNU/Linux
% cc -o tagutil -Wall -std=c99 -O2 -pipe -D_XOPEN_SOURCE=500 -ltag_c tagutil.c
# pour *BSD
% cc -o tagutil -Wall -std=c99 -O2 -pipe -I/usr/local/include -L/usr/local/lib/ -ltag_c tagutil.c

EDIT: il y’a maintenant deux Makefile (merci bapt), choisissez le bon suivant votre OS et lancez make tagutil.

ToDo

Il y’aurait deux trois truc à faire (mais j’ai la flemme) comme gérer les option avec getop() pour être plus “UNIX friendly”, utiliser les autohell pour le build. Il y certainement aussi d’autres fonctionalité ou option qui pourraient être utile mais que j’utilise pas donc n’hésitez pas à m’envoyer un mail si vous voulez ajouter une fonctionalité etc.
Si y’en a qui ont le courage (des couilles quoi) n’hésitez pas à modifier le code et mettre pleins de bugs dedans (j’ai l’habitude) ;)
tagutil est sous licence BSD, donc n’hésitez pas non plus à faire pleins d’argent avec.

Posted in | 5 comments |

Un Camembert

Posted by kAworu Fri, 22 Feb 2008 09:18:00 GMT

et des graphiques !

dans l’esprit de la convitude des représentations de données, on trouve des jolis camembert.

camembert

je suis tombé sur un pdf de Stephen Few (”save the Pies for Dessert”) qui nous explique pourquoi les représentation de camembert saylemal et ifopa. Il est pas très long et assez interactif.

Bonne lecture ;)

Posted in | 1 comment |

what-i-dont-like-about-ruby/

Posted by kAworu Tue, 15 Jan 2008 12:00:00 GMT

Hein?

/me aime beaucoup Ruby (entre autre). Mais voilà, aucun langage de programmation n’est parfait, comme aucun outil n’est universel. Pour une tâche donnée, certains outils sont (parfait|bien|difficile|impossible).

Ce qui me chicane, c’est les [mauvaises argumentations][blog].

Malheureusement, on ne peut poster de commentaires sur ce blog, donc je fais un petit billet :)

FUNCTION CALLS WITHOUT PARENTHESES

foo = bar

Is bar a variable? Is bar a method? There’s no way to know for sure.

ah ? Ben si. s’il existe une variable bar, alors c’est la variable qui est utilisée, sinon c’est la méthode (et si aucun des deux n’existe, ça raise NameError).

Le fait de pouvoir appeler des méthodes sans parenthèse est très pratiques. Souvent ça améliore la lisibilité. Par exemple, grâce à ça, il est possible d’implémenter en Ruby des DSL (Domain Specific Langage) d’une lisibilité et facilité déconcertante. Voici un exemple piqué dans le projet libastag, où l’on décrit une chorégraphique à exécuter (on peut bouger les oreilles et changer la couleurs de leds) :

Choregraphy.new do
    at time 0
        set all leds to green
        move left ear forward from degrees 10
end

si l’on était obligé de mettre des parenthèses, ça donnerait tout de suite moins cosmétique :

Choregraphy.new do
    at(time(0))
        set(all(leds(to(green))))
        move(left(ear(forward(from(degrees(10))))))
end

(ici le programmeur LISP sourit)

On a après un exemple en C et en PHP :

// C:
foo = bar(); // function
foo = bar;   // variable

// PHP:
$foo = bar(); // function
$foo = $bar;  // variable

Dommage, il faudrait aller un poil plus loin. continuons :

// Java
String  str = "Hello world";
int[]   ary = {12};

str.length()    // function
ary.length      // variable

Oui, cher lecteur, en Java il faut appeler length() pour avoir la taille d’un objet String, length pour avoir la taille un tableau, et pour les autres objets, ça dépend de l’implémentation :)

Personnelement, je pense que tous les dev Java se posent cette question avant d’écrire length : “String, array, ou autre chose ?”.

Ruby interdit l’accès aux variable d’instances. On utilise des méthodes public pour “simuler” l’accès et la modification de variables, mais cela reste des méthodes. On le fait parfois en Java également à grand coups de pioche setMyvar(arg) et getMyvar(). alors que Ruby donne du sucre syntaxique (omettre les parenthèse, permettre les méthodes avec =).

NO EXPLICIT RETURN

retourner implicitement la dernière évaluation d’une méthode est un concept magnifique de la programmation fonctionelle. Rien n’empêche en ruby d’écrire return, cela laisse justement la liberté au programmeur de choisir son paradigme de programmation !

First of all, in some cases you have to explicitly return anyway (e.g. at the beginning of a function)

En programmation fonctionel, si il faut écrire return, c’est qu’il y a un défaut de conception.

def f ary
    return "pas glop" if ary.empty?
    ...
end

# devrais être écris
def f ary
    if ary.empty?
        "pas glop"
    else
        ...
    end
end

dans le 2ème cas, la méthode f va retourner la dernière expression évaluée dans son bloc; le _if_ qui lui-même va retourner la dernière expressions évalue dans son bloc etc.

FUNCTIONS AND LAMBDAS AND PROCS AND BLOCKS

Confusingly, Procs created using proc or lambda behave differently from those created by Proc.new, but nobody really seems to understand those subtle differences.

(Ruby 1.9) Un objet Proc déclaré avec lambda, puis appelé avec un nombre d’argument plus petit/grand qu’attendu lève une exception ArgumentError (comme une méthode déclarée avec def), alors qu’avec Proc.new ou proc ou encore en bloc anonyme cette exception n’est pas levée (si vous apellez avec trop peu d’argument, il est fort probable qu’un autre exception soit levée, car les arguments manquant seront remplaçé par nil).

Proc#lambda? permet de savoir si le bloc se comporte comme s’il avait été déclaré avec lambda ou proc.

Proc.new { |i| i }.call # => nil
Proc.new {}.call(2) # => nil

lambda { |i| i }.call
ArgumentError: wrong number of arguments (0 for 1)
lambda {}.call(2)
ArgumentError: wrong number of arguments (1 for 0)

Cela permet de choisir entre un objet Proc “strict” (qui réagit comme une Method) ou permissive. Tout ça est très bien documenté :

  • “Changes in Ruby 1.9”
  • rdoc de Kernel.lambda
  • “Programming Ruby” (pp 343-345)
  • “Ruby cookbook” (Recipe 7.1. Creating and Invoking a Block)

Instead of passing a block to a function, you pass it a lambda. This also makes it possible to pass multiple blocks to a single function, something that is not currently possible with Ruby.

Il est possible de donner un bloc de code, un objet Proc (qu’il soit construit avec proc, lambda ou Proc.new) ou une méthode définie avec def en paramètre d’une méthode. Il est également possible de donner plusieurs objets Proc à un méthode (ce qui est équivalent à donner deux bloc de code).

def justcall
    yield
end

def hellodef
    puts "Hello world"
end

helloproc = Proc.new { puts "Hello world" }
hellolambda = lambda { puts "Hello world" }

justcall &helloproc # "Hello world"
justcall &hellolambda # "Hello world"
justcall &(method :hellodef) # Hello world


def doboth first, second
    first.call
    second.call
end

doboth  lambda { puts "FIRST" },
        lambda { puts "SECOND" }

TOO MANY SYNONYMS

  • Enumerable#collect / Enumerable#map
  • Enumerable#find / Enumerable#detect
  • proc / lambda
  • Array#length / Array#size

All these pairs do exactly (or almost exactly) the same. Why does Ruby have all these synonyms?

Parfois, le almost fait toute la différence. Retourner un Array vide ou nil est différent, et il arrive qu’on aie besoin/envie d’un comportement.

La nature dynamique de Ruby nous permet de changer les class et les méthode déjà définie. Si je redéfini Enumerable#find (comme le fait Rails), le comportement “standard” est toujours disponible avec Enumerable#detect.

Les autres langage choisissent une syntax pour une méthode (length(), size(), len(arg)) et t’obligent à retenir leur choix arbitraire. Ruby te laisse le choix d’utiliser celui que tu préfère.

INJECT? EH?

Ruby’s inject method is basically a reduce or fold function with a different name. Why pick a new name for a function that usually goes a different name?

Enumerable#reduce est un alias de Enumerable#inject (depuis Ruby 1.9) voir ici

So what should we do now?

Rien n’est pas parfait, mais avant de critiquer il faut être sûr de quoi on parle.

“A bon entendeur”

Posted in | 2 comments |