# Plugins: How to access original Metadata?

**URL:** <https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410>\
**Category:** MusicBrainz Picard\
**Tags:** picard-plugin-development, metadata, plugin\
**Created:** [September 21, 2019, 6:13pm UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410 "2019-09-21T18:13:19Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Deleted\_Editor\_2057975](https://community.metabrainz.org/user_avatar/community.metabrainz.org/deleted_editor_2057975/32/20836_2.png) [@Deleted\_Editor\_2057975](https://community.metabrainz.org/u/Deleted_Editor_2057975)\
**Post date:** [September 21, 2019, 6:13pm UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/1 "2019-09-21T18:13:19Z")

</div>

I want to write a plugin where I need the original metadata values from the local files.

Since there is not much documentation for the API (or I don’t find it), I haven’t found a way to access the original metadata.  
Based on [https://picard.musicbrainz.org/docs/plugin-api/#metadata\_processors](https://picard.musicbrainz.org/docs/plugin-api/#metadata_processors) I wrote already a plugin (without the need to access the original metadata) and it works fine.

For my new plugin I need to read the original metadata.  
With the passed arguments ([https://picard.musicbrainz.org/docs/plugin-api/#metadata\_processors](https://picard.musicbrainz.org/docs/plugin-api/#metadata_processors)) I only have the new metadata, from the MusicBrainz server (as far as I know and based on my poor Python knowledge).

Pls., give me a hint.

---

<div class="post-metadata">

**Author:** ![Jim\_DeLaHunt](https://community.metabrainz.org/letter_avatar_proxy/v4/letter/j/53a042/32.png) [@Jim\_DeLaHunt](https://community.metabrainz.org/u/Jim_DeLaHunt)\
**Post date:** [September 22, 2019, 4:07pm UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/2 "2019-09-22T16:07:40Z")

</div>

Hello, @NeroA, welcome to MusicBrainz! Thank you for your interest. I hope you will get helpful answers here.

> [@NeroA](#):
>
> I want to write a plugin…

I take it you mean, you want to write a plugin for the Picard music file tagging application. There are many things in the MusicBrainz world, and so I want to be sure which of those things you are interested in.

> [@NeroA](#):
>
> I need the original metadata values from the local files.

Here’s a rather basic question: is Picard the right foundation for your software? Picard is a tool for tagging music files with MusicBrainz-derived metadata. It sounds like you want to read metadata out of music files. That is different than Picard’s purpose, so Picard might not be convenient for you.

Maybe consider writing a Python program which uses metadata-reading modules to get at the existing metadata. **[Mutagen](https://mutagen.readthedocs.io/en/latest/)** is a pretty well-regarded Python module for exactly that purpose. If I’m not mistaken, Picard uses mutagen itself.

Good luck with your development!

---

<div class="post-metadata">

**Author:** ![outsidecontext](https://community.metabrainz.org/user_avatar/community.metabrainz.org/outsidecontext/32/90_2.png) [@outsidecontext](https://community.metabrainz.org/u/outsidecontext)\
**Post date:** [September 22, 2019, 5:33pm UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/3 "2019-09-22T17:33:22Z")

</div>

If you want to access the original metadata first you need access to the files, than you can access `File.orig_metadata`. Since Picard 2.2 you can use the file\_post\_addition\_to\_track\_processor event hook, which gets triggered when a file gets attached to a track.

Have a look at this. Right now I can only give you a quick pointer, but if you have further questions or need more details I can answer you in more depth tomorrow.

Before Picard 2.2 accessing the original file metadata in a plugin is difficult and not really supported. I have seen plugins accessing the file objects in the album metadata processor, but those are only available under specific circumstances and might also change later.

---

<div class="post-metadata">

**Author:** ![Deleted\_Editor\_2057975](https://community.metabrainz.org/user_avatar/community.metabrainz.org/deleted_editor_2057975/32/20836_2.png) [@Deleted\_Editor\_2057975](https://community.metabrainz.org/u/Deleted_Editor_2057975)\
**Post date:** [September 22, 2019, 7:59pm UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/4 "2019-09-22T19:59:15Z")

</div>

Hello @Jim_DeLaHunt!  
Thanks for your replay.

> [@Jim\_DeLaHunt](#):
>
> I take it you mean, you want to write a plugin for the Picard music file tagging application. There are many things in the MusicBrainz world …

Yes, of course!  
Since thit topic is in category _MusicBrainz Picard_ and the links are to Picards Plugins API, it shoul be clear.

> [@Jim\_DeLaHunt](#):
>
> is Picard the right foundation for your software? Picard is a tool for tagging music files with MusicBrainz-derived metadata.

I know. I use [musicbrainz.org](http://musicbrainz.org) and Picard for a while and I know how it works.  
However, for special cases I don’t want to use just the data from [musicbrainz.org](http://musicbrainz.org) but I want to modify them a little bit for my needs. (It’s about dealing with missing files where I use dummy-files .. )

---

<div class="post-metadata">

**Author:** ![Deleted\_Editor\_2057975](https://community.metabrainz.org/user_avatar/community.metabrainz.org/deleted_editor_2057975/32/20836_2.png) [@Deleted\_Editor\_2057975](https://community.metabrainz.org/u/Deleted_Editor_2057975)\
**Post date:** [September 22, 2019, 8:22pm UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/5 "2019-09-22T20:22:06Z")

</div>

> [@outsidecontext](#):
>
> If you want to access the original metadata first you need access to the files, than you can access `File.orig_metadata` . Since Picard 2.2 you can use the file\_post\_addition\_to\_track\_processor event hook, which gets triggered when a file gets attached to a track.

Thx!  
This seems to be a good starting point. I saw it already.  
Currently I don’t know the event hierarchy in Picard.  
Since I plan to use the `track_metadata_processor` (as I saw it in the example), I’m not sure if and how I can pass original metadata from the `file_post_addition_to_track_processor` to `track_metadata_processor`.

Maybe I should explain what I want to do: In my music library I have several albums with one or more missing files. I want to have the full number of files/tracks for my albums and so I use dummy files (silent files with 1 sec.).  
For these dummy files I want to add “[MISSING]” to the track-title (after I get the data from the MusicBrainz server) fields. Therefore I need to know, if my local file is just a dummy file.

---

<div class="post-metadata">

**Author:** ![outsidecontext](https://community.metabrainz.org/user_avatar/community.metabrainz.org/outsidecontext/32/90_2.png) [@outsidecontext](https://community.metabrainz.org/u/outsidecontext)\
**Post date:** [September 23, 2019, 8:58am UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/6 "2019-09-23T08:58:03Z")

</div>

I don’t think you need the `track_metadata_processort` for this. What you want to do is perfectly suited for the `file_post_addition_to_track_processor`. Something like this should do the trick:

```python
from picard.file import (
    register_file_post_addition_to_track_processor,
    register_file_post_removal_from_track_processor,
)

def is_dummy_file(file):
    # Do whatever you need to do to detect the dummy file and return True if it is a dummy file.
    # Some things you might need:
    # file.metadata => The file metadata as it will be saved
    # file.orig_metadata => The original file metadata as it was loaded
    # file.filename => The full path to the file
    return False

def file_post_addition_to_track_processor(track, file):
    if is_dummy_file(file):
        title = "[MISSING] %s" % track.metadata['title']
        # Set the title for both the track and file. This ensures
        # it will both be displayed and saved correctly.
        track.metadata['title'] = title
        file.metadata['title'] = title

def file_post_removal_from_track_processor(track, file):
    # You probably also want to remove the [MISSING] part again if a file gets removed from a track
    if track.metadata['title'].startswith('[MISSING] '):
        track.metadata['title'] = track.metadata['title'][10:]
        # We don't need to update the file because it gets reset to its original metadata anyway.

register_file_post_addition_to_track_processor(file_post_addition_to_track_processor)
register_file_post_removal_from_track_processor(file_post_removal_from_track_processor)

```

The above code is totally untested, I just wrote this down here. But it should give you an idea. What the code does not consider is that a track might have multiple files assigned, this would complicate things a bit. But if you need this you can access all linked files for a track with `track.linked_files`.

---

<div class="post-metadata">

**Author:** ![Deleted\_Editor\_2057975](https://community.metabrainz.org/user_avatar/community.metabrainz.org/deleted_editor_2057975/32/20836_2.png) [@Deleted\_Editor\_2057975](https://community.metabrainz.org/u/Deleted_Editor_2057975)\
**Post date:** [September 23, 2019, 10:15am UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/7 "2019-09-23T10:15:59Z")

</div>

@outsidecontext, THX very much!

If I can do anything in `file_post_addition_to_track_processor` it’s just fine!  
I thought, I don’t have the new metadata (from [musicbrainz.org](http://musicbrainz.org)) already here but if they are already available, it’s quite easy.

I’ll try it later.

---

<div class="post-metadata">

**Author:** ![Deleted\_Editor\_2057975](https://community.metabrainz.org/user_avatar/community.metabrainz.org/deleted_editor_2057975/32/20836_2.png) [@Deleted\_Editor\_2057975](https://community.metabrainz.org/u/Deleted_Editor_2057975)\
**Post date:** [September 23, 2019, 5:58pm UTC](https://community.metabrainz.org/t/plugins-how-to-access-original-metadata/444410/8 "2019-09-23T17:58:29Z")

</div>

It works (more or less) well for me!

I could do it all in `file_post_addition_to_track_processor` but then I get unmatched entries (without green check-mark).  
Therefore I do some tricky rename of the title (remove existing [MISSING] in `file_post_load_processor` and add it again in `file_post_addition_to_track_processor`). With this trick the internal comparison works well also for files already tagged with [MISSING].  
Without changing `track.metadata` it might be possible to do all in one hook, but I don’t like this.  
It seems to fit my need for my basic workflow, however I will get just “great matches” after refresh data from the server.  
I need to do more tests and there are still ways to improve the plugin. Anyway, it looks good and helps me a lot.

```
PLUGIN_NAME = "[MISSING]"
PLUGIN_AUTHOR = "Nero A."
PLUGIN_DESCRIPTION = "Add [MISSING] to dummy tracks"
PLUGIN_VERSION = '0.3'
PLUGIN_API_VERSIONS = ['2.2']
PLUGIN_LICENSE = "GPL-3.0-or-later"
PLUGIN_LICENSE_URL = "https://gnu.org/licenses/gpl.html"

from picard.file import register_file_post_load_processor
from picard.file import register_file_post_addition_to_track_processor
from picard.file import register_file_post_removal_from_track_processor

MISSING = " [MISSING]"
DUMMY_TAG_ORIG_TITLE = "__PLUGIN_[MISSING]_ORIGINAL_TITLE"

def is_dummy_file(file):
    if file.orig_metadata["~length"] == "0:01":
        return True

def is_modified_dummy_file(file):
    if file.orig_metadata[DUMMY_TAG_ORIG_TITLE] != "":
        return True

def remove_missing(title):
    if title.endswith(MISSING):
        return title[:-len(MISSING)]
    return title

def add_missing(title):
    if title.endswith(MISSING):
        return title
    return (title + MISSING)

def file_post_load_processor(file):
    if is_dummy_file(file):
        if file.orig_metadata["title"].endswith(MISSING):
            file.orig_metadata[DUMMY_TAG_ORIG_TITLE] = file.orig_metadata["title"]
            file.orig_metadata["title"] = remove_missing(file.orig_metadata["title"])

def file_post_addition_to_track_processor(track, file):
    if is_dummy_file(file):
        track.metadata["title"] = add_missing(track.metadata["title"])
        
        if is_modified_dummy_file(file):
            file.orig_metadata["title"] = file.orig_metadata[DUMMY_TAG_ORIG_TITLE]
            file.orig_metadata[DUMMY_TAG_ORIG_TITLE] = ""
            
        file.metadata["title"] = add_missing(file.metadata["title"])

def file_post_removal_from_track_processor(track, file):
    if is_dummy_file(file):
        track.metadata["title"] = remove_missing(track.metadata["title"])

register_file_post_load_processor(file_post_load_processor)
register_file_post_addition_to_track_processor(file_post_addition_to_track_processor)
register_file_post_removal_from_track_processor(file_post_removal_from_track_processor)
```
