Each plugin must provide some metadata as variables. Those variables should be placed at the top of the file.
PLUGIN_NAME = "Example plugin" PLUGIN_AUTHOR = "This authors name" PLUGIN_DESCRIPTION = "This plugin is an example" PLUGIN_VERSION = '0.1' PLUGIN_API_VERSIONS = ['2.1', '2.2'] PLUGIN_LICENSE = "GPL-2.0-or-later" PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"
PLUGIN_DESCRIPTIONshould be as simple as possible, while still describing the main function.
PLUGIN_VERSIONshould be filled with the version of Plugin.
PLUGIN_API_VERSIONSshould be set to the versions of Picard this plugin to run with. New Picard versions will usually support older plugin API versions, but on breaking changes support for older plugin versions can be dropped.
PLUGIN_LICENSEshould be set with the license name of the plugin. If possible use one of the license names from the SPDX License List. But you are welcomed to use another licnese if the one you chose is not available in the list.
PLUGIN_LICENSE_URLshould be set to a URL pointing to the full license text.
PLUGIN_API_VERSIONS available for Picard 2:
MusicBrainz metadata can be post-processed at two levels, album and track. The types of the arguments passed to the processor functions in the following examples are as follows:
dictwith release data from MusicBrainz JSON web service
dictwith track data from MusicBrainz JSON web service
PLUGIN_NAME = "Disc Numbers" PLUGIN_AUTHOR = "Lukas Lalinsky" PLUGIN_DESCRIPTION = "Moves disc numbers from album titles to tags." from picard.metadata import register_album_metadata_processor import re def remove_discnumbers(tagger, metadata, release): matches = re.search(r"\(disc (\d+)\)", metadata["album"]) if matches: metadata["discnumber"] = matches.group(1) metadata["album"] = re.sub(r"\(disc \d+\)", "", metadata["album"]) register_album_metadata_processor(remove_discnumbers)
PLUGIN_NAME = "Feat. Artists" PLUGIN_AUTHOR = "Lukas Lalinsky" PLUGIN_DESCRIPTION = "Removes feat. artists from track titles." from picard.metadata import register_track_metadata_processor import re def remove_featartists(tagger, metadata, track, release): metadata["title"] = re.sub(r"\(feat. [^)]*\)", "", metadata["title"]) register_track_metadata_processor(remove_featartists)
Plugins can register themselves to listen for different events. Currently the following event hooks are available:
Note: Event hooks are available since API version 2.2
This hook is called after a file has been loaded into Picard. This could for example be used to load additional data for a file. Usage:
from picard.file import register_file_post_load_processor def file_post_load_processor(file): pass register_file_post_load_processor(file_post_load_processor)
This hook is called after a file has been saved. This can for example be used to run additional post-processing on the file or write extra data. Note that the file's metadata is already the newly saved metadata. Usage:
from picard.file import register_file_post_save_processor def file_post_save_processor(file): pass register_file_post_save_processor(file_post_save_processor)
This hook is called after a file has been added to a track (on the right pane of Picard).
from picard.file import register_file_post_addition_to_track_processor def file_post_addition_to_track_processor(track, file): pass register_file_post_addition_to_track_processor(file_post_addition_to_track_processor)
This hook is called after a file has been removed from a track (on the right pane of Picard).
from picard.file import register_file_post_removal_from_track_processor def file_post_removal_from_track_processor(track, file): pass register_file_post_removal_from_track_processor(file_post_removal_from_track_processor)
This hook is called after an album has been removed from Picard.
from picard.album import register_album_post_removal_processor def album_post_removal_processor(album): pass register_album_post_removal_processor(album_post_removal_processor)
Plugins can extend Picard with support for additional file formats. See the existing
file format implementations
for details on how to implement the
PLUGIN_NAME = "..." PLUGIN_AUTHOR = "..." PLUGIN_DESCRIPTION = "..." PLUGIN_VERSION = '...' PLUGIN_API_VERSIONS = ['...'] PLUGIN_LICENSE = "..." PLUGIN_LICENSE_URL = "..." from picard.file import File from picard.formats import register_format from picard.metadata import Metadata class MyFile(File): EXTENSIONS = [".foo"] NAME = "Foo Audio" def _load(self, filename): metadata = Metadata() # Implement loading and parsing the file here. # This method is supposed to return a Metadata instance filled # with all the metadata read from the file. metadata['~format'] = self.NAME return metadata def _save(self, filename, metadata): # Implement saving the metadata to the file here. pass register_format(MyFile)
To define new
tagger script functions use
register_script_function(function, name=None) from the
parser is an instance of
picard.script.ScriptParser, the rest of the
arguments passed to it are the arguments from the function call in the tagger script.
PLUGIN_NAME = "Initials" PLUGIN_AUTHOR = "Lukas Lalinsky" PLUGIN_DESCRIPTION = "Provides tagger script function $initials(text)." PLUGIN_VERSION = '0.1' PLUGIN_API_VERSIONS = ['2.0'] PLUGIN_LICENSE = "GPL-2.0" PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.txt" from picard.script import register_script_function def initials(parser, text): return "".join(a[:1] for a in text.split(" ") if a[:1].isalpha()) register_script_function(initials)
register_script_function supports two optional arguments:
The default value for both of them is True.
Right-click context menu actions can be added to albums, tracks, files in Unmatched Files, Clusters and the ClusterList (parent folder of Clusters).
PLUGIN_NAME = u'Remove Perfect Albums' PLUGIN_AUTHOR = u'ichneumon, hrglgrmpf' PLUGIN_DESCRIPTION = u'''Remove all perfectly matched albums from the selection.''' PLUGIN_VERSION = '0.2' PLUGIN_API_VERSIONS = ['0.15.1'] PLUGIN_LICENSE = "GPL-2.0" PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.txt" from picard.album import Album from picard.ui.itemviews import BaseAction, register_album_action class RemovePerfectAlbums(BaseAction): NAME = 'Remove perfect albums' def callback(self, objs): for album in objs: if isinstance(album, Album) and album.is_complete() and album.get_num_unmatched_files() == 0\ and album.get_num_matched_tracks() == len(list(album.iterfiles()))\ and album.get_num_unsaved_files() == 0 and album.loaded == True: self.tagger.remove_album(album) register_album_action(RemovePerfectAlbums())
register_x_action where x is album, track, file, cluster or clusterlist.