Écriture d’un Plugin

Vous avez une excellente idée pour étendre Picard avec un plugin mais vous ne savez pas par où commencer. Malheureusement, c’est un problème courant qui empêche beaucoup trop de ces grandes idées de voir le jour. Peut-être que ce tutoriel vous aidera à commencer à transformer votre grande idée en réalité.

Les plugins Picard sont écrits en Python, c’est donc le langage de programmation que vous utiliserez. Veuillez vérifier le fichier INSTALL.md dans le dépôt Picard sur GitHub pour voir la version minimale requise. Il s’agit de Python 3.6 au moment où ce tutoriel a été écrit. Consultez également la Plugins API pour obtenir des informations supplémentaires, notamment les paramètres passés à chacun des types de fonctions.

Pour les besoins de ce tutoriel, nous allons développer un plugin simple pour sauvegarder les informations sur les arguments fournis par Picard aux plugins de traitement track et release. Cela démontrera comment on accède à ces informations, et fournira un utilitaire que vous pourrez trouver utile lorsque vous développerez vos propres plugins.

La première chose que nous devons inclure est l’information d’en-tête qui décrit le plugin.

PLUGIN_NAME = "Example plugin"
PLUGIN_AUTHOR = "This authors name"
PLUGIN_DESCRIPTION = "This plugin is an example"
PLUGIN_VERSION = '0.1'
PLUGIN_API_VERSIONS = ['2.2']
PLUGIN_LICENSE = "GPL-2.0-or-later"
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"

Ensuite, nous listons les modules qui seront référencés dans notre code. Dans ce cas, nous utiliserons le module os pour construire le chemin du fichier de sortie, et le module json pour formater le texte du dictionnaire d’arguments pour une meilleure lisibilité. Nous enregistrerons notre fichier de sortie dans le répertoire de base utilisé pour le nommage des fichiers. Nous importons donc le module config de Picard, ainsi que le module log pour pouvoir écrire des messages de débogage ou d’erreur dans le journal de Picard. Enfin, nous importons les hooks de traitement appropriés et les paramètres de priorité des plugins.

import json
import os

from picard import config, log
from picard.metadata import (register_album_metadata_processor,
                           register_track_metadata_processor)
from picard.plugin import PluginPriority

Avertissement

Pour assurer une compatibilité maximale, vous ne devez utiliser que des modules Python standard, ou des modules tiers qui sont déjà inclus dans Picard. Si vous utilisez d’autres modules, alors le plugin ne fonctionnera pas correctement s’il est utilisé sur un système qui n’a pas la bonne version du module installé ou si quelqu’un utilise une version exécutable de Picard.

Maintenant nous pouvons commencer à ajouter le code que nous voulons que Picard exécute. Tout d’abord, nous allons identifier le fichier de sortie pour stocker les informations sur les paramètres fournis par Picard. Il s’agit d’un fichier nommé data_dump.txt qui sera stocké dans le répertoire de sortie du nommage des fichiers. Nous trouvons le nom du paramètre de configuration dont nous avons besoin, move_files_to, en examinant le code source de Picard pour l’écran de paramétrage des options correspondant. Dans ce cas, il s’agit d’une TextOption dans la classe RenamingOptionsPage trouvée dans le fichier picard/ui/options/renaming.py.

file_to_write = os.path.join(config.setting["move_files_to"], "data_dump.txt")

La partie suivante est une fonction permettant d’écrire un objet Python dans notre fichier de sortie. Pour permettre à la même fonction d’être utilisée dans différentes situations, nous incluons des paramètres pour identifier le type de ligne (type d’entrée), l’objet à écrire, et des options pour écrire au format JSON et ajouter ou écraser un fichier de sortie existant. Dans notre cas, nous voulons écraser le fichier à chaque fois qu’une nouvelle version est traitée, mais toujours ajouter les informations sur les pistes au fichier.

Nous avons également inclus un contrôle d’erreur pour écrire une entrée dans le journal Picard en cas d’exception.

def write_line(line_type, object_to_write, dump_json=False, append=True):
    file_mode = 'a' if append else 'w'
    try:
        with open(file_to_write, file_mode, encoding="UTF-8") as f:
            if dump_json:
                f.write('{0} JSON dump follows:\n'.format(line_type,))
                f.write('{0}\n\n'.format(json.dumps(object_to_write, indent=4)))
            else:
                f.write("{0:s}: {1:s}\n".format(line_type, str(object_to_write),))
    except Exception as ex:
        log.error("{0}: Error: {1}".format(PLUGIN_NAME, ex,))

Maintenant, nous incluons les fonctions qui seront appelées lorsque les releases et les tracks seront récupérés par Picard. Le hook de la fonction release fournit trois arguments, et le hook de la fonction track fournit quatre arguments. Les types d’arguments sont décrits dans la section Plugins API. Le premier argument, album, est un objet qui contient des informations sur l’album sélectionné. Voir la classe Album dans le fichier picard/album.py du code source de Picard pour plus d’informations.

Le second argument, metadata, est un objet qui contient les tags et les variables que Picard a assignés pour la version et la piste en cours. C’est ici que vous pouvez ajouter ou modifier les tags et les variables que Picard met à disposition de l’utilisateur pour les scripts. Voir la classe Metadata dans le fichier picard/metadata.py du code source de Picard pour plus d’informations.

Les arguments track et release sont des dictionnaires Python contenant les informations fournies en réponse aux appels de Picard à l’API MusicBrainz. Les informations peuvent différer selon les paramètres Options de métadonnées de l’utilisateur pour des choses comme « Utiliser les relations de validation » ou « Utiliser les relations de suivi ».

def dump_release_info(album, metadata, release):
    write_line('Release Argument 1 (album)', album, append=False)
    write_line('Release Argument 3 (release)', release, dump_json=True)

def dump_track_info(album, metadata, track, release):
    write_line('Track Argument 1 (album)', album)
    write_line('Track Argument 3 (track)', track, dump_json=True)
    # write_line('Track Argument 4 (release)', release, dump_json=True)

Enfin, nous devons enregistrer nos fonctions afin qu’elles soient traitées avec les événements appropriés. Dans notre cas, nous mettons la priorité à « HIGH » pour que nous puissions sortir les informations des paramètres dès qu’elles sont reçues par Picard avant que d’autres plugins aient l’opportunité de les modifier.

# Register the plugin to run at a HIGH priority so that other plugins will
# not have an opportunity to modify the contents of the metadata provided.
register_album_metadata_processor(dump_release_info, priority=PluginPriority.HIGH)
register_track_metadata_processor(dump_track_info, priority=PluginPriority.HIGH)

Le fichier de code complet du plugin ressemble à quelque chose comme:

PLUGIN_NAME = "Example plugin"
PLUGIN_AUTHOR = "This authors name"
PLUGIN_DESCRIPTION = "This plugin is an example"
PLUGIN_VERSION = '0.1'
PLUGIN_API_VERSIONS = ['2.2']
PLUGIN_LICENSE = "GPL-2.0-or-later"
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"

import json
import os

from picard import config, log
from picard.metadata import (register_album_metadata_processor,
                           register_track_metadata_processor)
from picard.plugin import PluginPriority

file_to_write = os.path.join(config.setting["move_files_to"], "data_dump.txt")

def write_line(line_type, object_to_write, dump_json=False, append=True):
    file_mode = 'a' if append else 'w'
    try:
        with open(file_to_write, file_mode, encoding="UTF-8") as f:
            if dump_json:
                f.write('{0} JSON dump follows:\n'.format(line_type,))
                f.write('{0}\n\n'.format(json.dumps(object_to_write, indent=4)))
            else:
                f.write("{0:s}: {1:s}\n".format(line_type, str(object_to_write),))
    except Exception as ex:
        log.error("{0}: Error: {1}".format(PLUGIN_NAME, ex,))

def dump_release_info(album, metadata, release):
    write_line('Release Argument 1 (album)', album, append=False)
    write_line('Release Argument 3 (release)', release, dump_json=True)

def dump_track_info(album, metadata, track, release):
   write_line('Track Argument 1 (album)', album)
   write_line('Track Argument 3 (track)', track, dump_json=True)
   # write_line('Track Argument 4 (release)', release, dump_json=True)

# Register the plugin to run at a HIGH priority so that other plugins will
# not have an opportunity to modify the contents of the metadata provided.
register_album_metadata_processor(dump_release_info, priority=PluginPriority.HIGH)
register_track_metadata_processor(dump_track_info, priority=PluginPriority.HIGH)

C’est tout pour le code de notre plugin. Maintenant nous devons l’empaqueter afin de pouvoir l’installer dans Picard. Si nous voulons juste l’utiliser localement pour nous-mêmes, le plus simple est de nommer le fichier comme my_plugin.py. S’il y a plusieurs fichiers, comme des plugins qui incluent des écrans de réglages supplémentaires, alors les fichiers doivent être enregistrés dans un répertoire tel que my_plugin avec le fichier principal nommé __init__.py. Le répertoire est ensuite archivé dans un fichier my_plugin.zip, dont le nom de fichier est identique au nom du répertoire inclus. Le contenu de l’archive s’afficherait comme suit:

my_plugin/__init__.py
my_plugin/another_file.py
my_plugin/etc

Si vous êtes arrivé jusqu’ici, félicitations ! Vous venez de créer votre premier plugin Picard. Vous avez maintenant un point de départ pour transformer cette grande idée en réalité.

Voir aussi

Des portions pertinentes du code source de Picard, y compris:

  • Modules de paramétrage des options dans picard/ui/options/ pour les noms utilisés pour accéder aux paramètres.

  • La classe Album dans le fichier picard/album.py.

  • La classe Metadata et les fonctions d’enregistrement du plugin de traitement des métadonnées dans le fichier picard/metadata.py.

  • La classe PluginPriority dans le fichier picard/plugin.py.