Een Plug-in schrijven

Stel dat je een geweldig idee voor het uitbreiden van Picard hebt, maar dat je niet weet waar je moet beginnen. Helaas gebeurt dat wel vaker, waardoor veel goede ideeën nooit het daglicht zien. Misschien helpt deze korte handleiding je om je goede idee te verwezenlijken.

Picard plugins are written in Python, so that’s the programming language you’ll be using. Please check the INSTALL.md file in the Picard repository on GitHub to see the minimum version requirements. This is Python 3.6 as of the time this tutorial was written. Also refer to the Plugins API for additional information, including the parameters passed to each of the function types.

Voor deze handleiding gaan we een eenvoudige plug-in maken voor het opslaan van informatie over argumenten die Picard verstrekt aan plug-ins die track en release verwerken. Zo zie je hoe de informatie wordt benaderd en heb je een basis die je kan gebruiken om je eigen plug-ins te ontwikkelen.

Allereerst moeten we een header toevoegen om de plug-in te omschrijven.

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"

Vervolgens vermelden we de modules waarnaar we in onze code verwijzen. In dit geval gebruiken we de module os om het pad voor het uitvoerbestand te maken en de module json om de tekst van de argumentbibliotheek leesbaar op te maken. We slaan ons uitvoerbestand op in de basismap die we voor het hernoemen van bestanden gebruiken, dus importeren we de module config uit Picard. Bovendien importeren we de module log om debug- of foutmeldingen naar het logboek van Picard te schrijven. Tot slot importeren we relevante verwerkingshooks en instellingen voor de prioriteiten van plug-ins.

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

Waarschuwing

Voor maximale compatibiliteit gebruik je alleen standaard Python-modules of modules van derde partijen die al met Picard worden meegeleverd. Als je andere modules gebruikt, zal de plug-in niet goed werken op een systeem dat niet de juiste versie van de module heeft of als iemand een draagbare versie van Picard gebruikt.

Nu kunnen we code toevoegen die we door Picard willen laten uitvoeren. Eerst bepalen we het uitvoerbestand waarin we de parameterinformatie van Picard opslaan. Dit is het bestand data_dump.txt, dat wordt opgeslagen in de map waarin bestanden worden opgeslagen. We kunnen de naam van de instelling die daarvoor nodig is (move_files_to) vinden in de broncode van de relevante instellingenpagina van Picard. In dit geval gaat het om een TextOption in de klasse RenamingOptionsPage, te vinden in het bestand picard/ui/options/renaming.py.

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

Nu moeten we een functie maken om een Python-object naar ons uitvoerbestand te schrijven. Om te zorgen dat dezelfde functie in verschillende situaties kan worden gebruikt, voegen we parameters toe voor het type regel (invoertype), het object dat moet worden geschreven en opties om de JSON-indeling te gebruiken en om een bestaand uitvoerbestand aan te vullen of te overschrijven. In ons geval willen we het bestand elke keer als er een nieuwe uitgave wordt verwerkt overschrijven, maar bij elk nummer informatie aan het bestand toevoegen.

We voegen ook foutcontroles toe, zodat er bij een afwijking informatie naar het logboek van Picard wordt geschreven.

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(f"{line_type} JSON dump follows:\n")
                f.write(f"{json.dumps(object_to_write, indent=4)}\n\n")
            else:
                f.write(f"{line_type}: {str(object_to_write)}\n")
    except Exception as ex:
        log.error("%s: Error: %s", PLUGIN_NAME, ex,)

Now we include the functions to be called when releases and tracks are retrieved by Picard. The release function hook provides three arguments, and the track function hook provides four arguments. The argument types are described in the Plugins API section. The first argument, album, is an object that holds information about the selected album. See the Album class in the picard/album.py file in Picard’s source code for more information.

Het tweede argument, metadata is een object met de tags en variabelen die Picard aan de huidige uitgave en het huidige nummer heeft toegewezen. Hier kan je tags en variabelen die Picard beschikbaar stelt voor scripts toevoegen en bewerken. Zie de klasse Metadata in het bestand picard/metadata.py in de broncode van Picard voor meer informatie.

The track and release arguments are Python dictionaries containing the information provided in response to Picard’s calls to the MusicBrainz API. The information may differ, depending on the user’s Opties voor metadata settings for things like “Use release relationships” or “Use track relationships”.

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)

Ten slotte moeten we onze functies registreren, zodat ze met de relevante gebeurtenissen worden verwerkt. In ons geval stellen we de prioriteit in op HIGH, zodat we de parameterinformatie wegschrijven zodra ze door Picard is ontvangen, en voordat andere plug-ins de informatie kunnen wijzigen.

# 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)

De volledige code van de plug-in ziet er ongeveer zo uit:

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(f"{line_type} JSON dump follows:\n")
                f.write(f"{json.dumps(object_to_write, indent=4)}\n\n")
            else:
                f.write(f"{line_type}: {str(object_to_write)}\n")
    except Exception as ex:
        log.error("%s: Error: %s", 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)

Dat is alles wat onze code betreft. Nu moeten we de plug-in verpakken, zodat we hem in Picard kunnen installeren. Als we de plug-in alleen lokaal en voor onszelf gaan gebruiken, kunnen we hem gewoon mijn_plug-in.py noemen. Als de plug-in uit meerdere bestanden bestaat, zoals wanneer een plug-in meerdere optiepagina’s heeft, moeten de bestanden worden opgeslagen in een map als mijn_plug-in, met het hoofdbestand __init__.py. Vervolgens pak je die map in als het bestand mijn_plug-in.zip, waarbij je zorgt dat de naam van het archief gelijk is aan de naam van de map. De inhoud van het archief zou er ongeveer zo uit moeten zien:

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

Als je zover bent gekomen: gefeliciteerd! Je hebt net je eerste plug-in voor Picard gemaakt. Nu heb je een beginpunt om je goede idee in de praktijk te brengen.

Zie ook

Relevante onderdelen van Picards broncode zijn onder andere: