MusicBrainz Picard

Overview

MusicBrainz Picard

Github Actions Status Codacy Grade

MusicBrainz Picard is a cross-platform (Linux/Mac OS X/Windows) application written in Python and is the official MusicBrainz tagger.

Picard supports the majority of audio file formats, is capable of using audio fingerprints (AcoustIDs), performing CD lookups and disc ID submissions, and it has excellent Unicode support. Additionally, there are several plugins available that extend Picard's features.

When tagging files, Picard uses an album-oriented approach. This approach allows it to utilize the MusicBrainz data as effectively as possible and correctly tag your music. For more information, see the illustrated quick start guide to tagging.

Picard is named after Captain Jean-Luc Picard from the TV series Star Trek: The Next Generation.

Binary downloads are available here.

Support and issue reporting

Please report all bugs and feature requests in the MusicBrainz issue tracker. If you need support in using Picard please read the documentation first and have a look at the MusicBrainz community forums.

Installing

INSTALL.md has instructions on building this codebase.

Comments
  • Mitigate thread competition by scanning directories before loading files

    Mitigate thread competition by scanning directories before loading files

    Summary

    • This is a…
      • [ ] Bug fix
      • [ ] Feature addition
      • [x] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences: Change directory scanning (add_directory) logic to scan everything before adding files (add_files).

    Problem

    The thread pool threads have to deal with a whole bunch of different tasks and compete for both IO and CPU. The current code launches concurrent tasks for directory scanning and file processing, which leads to harmful competition and slows down the file processing.

    • JIRA ticket (optional): PICARD-840

    Solution

    Instead of running the directory scanning interleaved with the file processing, a single Python thread is used to scan first and then forwards the files list to the Tagger.add_files at once. The thread runs the unified recursive/non-recursive file scanning code.

    Tested on a 17k file library cleaning file cache before and between tests (SysInternals RamMap->Empty Standby list). Completely loading the library took:

    • Latest master clone: 32 minutes, with HDD reading speeds fluctuating between 1-5 MB/s for for most of the time (was running reasonably fine until the 7-8k song mark, then slowed down. Seemingly due to the lack of files to process as the directory/file scanning is frequently interrupted);
    • Latest branch clone: 13 minutes, with HDD reading speeds fluctuating between 8-10MB/s for most of the time.

    Action

    opened by Gabrielcarvfer 93
  • PICARD-2471: Restrict Picard to a single instance

    PICARD-2471: Restrict Picard to a single instance

    Summary

    • This is a…
      • [ ] Bug fix
      • [x] Feature addition
      • [ ] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences:

    This is a draft where we can discuss my GSoC project progress. Picard is going to work in single-instance mode thanks to named pipes

    Problem

    Ticket description is clear enough so I'll just paste it here:

    When passing folders to Picard via command line (such as in Foobar), Picard will launch a new instance for each folder rather than loading them all in one instance. The ability to force it all into one instance would be helpful.

    • JIRA ticket (optional): PICARD-2471

    Solution

    I've created a cross-platform communication protocol for Picard instances, based on named pipes. Now, in this draft, you can see my progress in integrating it into the Picard app.

    TODO

    • [x] Communication protocol works on all supported operating systems
    • [x] Pipe can distinguish between app versions
    • [x] If pipe is listened to, Picard sends all the arguments there and leaves without spawning an instance
    • [x] Picard accepts -s/--stand-alone-instance argument to create a forced, standalone instance, that doesn't listen to any pipe
    • [x] Picard creates a "pipe server" thread that listens to any possible messages in the background and passes messages to file-adding methods
    • [x] in tagger.py/main: picard_args.FILE args are converted to absolute paths before passing them to the existing instanec
    • [x] Changes are tested
    • [x] Changes are documented
    • [x] Commits are properly organised
    • [ ] Merge :)

    Notes

    • Type annotations are my helpers, since I always use mypy while writing Python. When we will be merging into master, I can remove them if they are problematic for you
    • Commit structure is also just a draft to be organised properly
    • Because it's just a draft, I'll let you know when workflows will make sense.
    opened by skelly37 61
  • PICARD-2526: Allow starting processing actions from the command line

    PICARD-2526: Allow starting processing actions from the command line

    Summary

    • This is a…
      • [ ] Bug fix
      • [x] Feature addition
      • [ ] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences:

    Users will be able to pass some commands to the existing instance with -e/--exec flag.

    Problem

    It would be useful to tell the existing Picard instance what to do, e.g. QUIT, useful in combination with the other single-instance mode improvements

    • JIRA ticket (optional): PICARD-2526

    Solution

    • add command:// prefix to the commands
    • send them as any other arg
    • parse with urlparse, split by ";"
    • execute

    Action

    opened by skelly37 54
  • PICARD-936: Improve UI responsiveness whilst busy loading, saving etc.

    PICARD-936: Improve UI responsiveness whilst busy loading, saving etc.

    Resolves PICARD-936.

    Improves the responsiveness during worker thread operations by triggering main thread to process events after events are sent to it.

    Improves file loading performance by moving operations into separate threadpools.

    This PR was brought to you as a joint effort between Sophist-UK and @samj1912.

    Needs more work 
    opened by Sophist-UK 54
  • PICARD-1734: Add AcousticBrainz analyzer features

    PICARD-1734: Add AcousticBrainz analyzer features

    Summary

    • This is a…
      • [ ] Bug fix
      • [x] Feature addition
      • [ ] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences: Call the AcousticBrainz/Essentia feature extractor from Picard and submit the extracted features.

    Problem

    • JIRA ticket (optional): PICARD-1734

    Solution

    A single button triggers the check for duplicate AcousticBrainz features duplicates (same recordingID + extractor version), extract features of non-duplicates and tries to submit them.

    Action

    Part of the AcousticBrainz code comes from the acoustic-brainz client, that has a different license (GPL-v3). Not sure what to do with it.

    opened by Gabrielcarvfer 53
  • PICARD-1043: Add support for iTunes Movement tags - 1.4.x branch

    PICARD-1043: Add support for iTunes Movement tags - 1.4.x branch

    Resolves https://tickets.metabrainz.org/browse/PICARD-1043

    Note 1: This is targetted at the 1.4.x branch in order to avoid issues of installing Qt5 with Py2. Note 2: This PR currently includes the commits from #667 pending their inclusion in the 1.4.x branch. Once Py3 support is in master or #667 cherry picked into 1.4.x I will rebase without these. Note 3: See #695 for equivalent PR for master branch.

    opened by Sophist-UK 48
  • PICARD-204: Support for recording-level original release date

    PICARD-204: Support for recording-level original release date

    Summary

    • This is a…
      • [ ] Bug fix
      • [x] Feature addition
      • [ ] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences:

    Use first-release-date of recordings if available.

    Problem

    • JIRA ticket (optional): PICARD-204

    Solution

    Use the first-release-date field on recordings if available and fill originaldate and originalyear with this information. If the data is not set on a recording (should not happen actually once this is fully rolled out) it would still use the data from the release group.

    For more flexibility this makes available two additional variables ~recordingoriginaldate and ~releaseoriginaldate for scripting. This e.g. allows to always use the original date for the entire release instead of using the per recording date.

    Action

    At this moment this is partially rolled out on musicbrainz.org. This patch already works for getting originaldate when querying standalone-recordings. For full support also for releases with different per-recording dates https://github.com/metabrainz/musicbrainz-server/pull/1847 needs to be merged and rolled out.

    opened by phw 39
  • Make Picard tags more comprehensive.

    Make Picard tags more comprehensive.

    This PR will eventually provide a more comprehensive mapping of Picard tags onto tags supported by various file formats. My intention is to develop this in steps and share code at each step for review comments.

    Step 1: Improve compatibility with Jaikoz based on Philip Wolfer's spreadsheet (see first commit) and add support for as many ID3v2.4 tags that can be easily included. Data will be populated from the standard MB responses where data is included, otherwise they will be left to plugins to populate from e.g. AcousticBrainz, other MB calls for artist details etc.

    Step 1 is now complete

    Step 2: Add test support for Step 1.

    Step 3: Bug fixing.

    Needs more work 
    opened by Sophist-UK 38
  • PICARD-2178: Select random sample of loaded files to use as renaming examples

    PICARD-2178: Select random sample of loaded files to use as renaming examples

    Summary

    • This is a…
      • [ ] Bug fix
      • [x] Feature addition
      • [ ] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences: Instead of using the two provided file examples for renaming, pick up to 10 random entries from the loaded files and use them to show before/after the renaming script. Also includes a button to refresh the up to 10 entries.

    Problem

    • JIRA ticket (optional): PICARD-2178

    Writing a naming script is hard by itself.

    Solution

    Make it a bit easier to check the script by selecting loaded files as examples and showing the before and after.

    Action

    opened by Gabrielcarvfer 31
  • PICARD-1273: Add an option to exclude new cover art type

    PICARD-1273: Add an option to exclude new cover art type "Raw / Unedited"

    Summary

    • This is a…
      • [ ] Bug fix
      • [x] Feature addition
      • [ ] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences: This change provides the user with the option of excluding selected cover art types from downloading from CAA.

    Problem

    STYLE-980 has added a new cover art style "Raw / Unedited". Although beneficial for identifying and allowing better images for archival purposes, the images within this type are generally not suitable for tagging purposes. Picard currently allows the user to select the types of images to download from CAA, but does not provide the ability to exclude selected image types from being downloaded.

    • JIRA ticket: PICARD-1273: Add an option to exclude new cover art type "Raw / Unedited"

    Solution

    This change adds new user settings to indicate whether selected image types should be excluded from downloads from CAA and the list of image types to be excluded, similar to the option to only include selected image types. Additional logic is applied to the list of images selected for download, and any images with a type intersecting the user's "exclusion" type list are removed from the download list.

    Generally speaking, the current logic for determining whether to download an image is (where the image is downloaded when "types" evaluates to True):

    if self.restrict_types:
        # only keep enabled caa types
        types = set(image["types"]).intersection(
            set(self.caa_types))
    else:
        types = True
    

    This change adds a further check for exclusion, as:

    if self.restrict_types:
        # only keep enabled caa types
        types = set(image["types"]).intersection(
            set(self.caa_types))
    else:
        types = True
    if types and self.omit_types:
        # omit selected caa types
        types = not set(image["types"]).intersection(
            set(self.caa_types_to_omit))
    

    Action

    Currently, it appears that CAA does not return the new "Raw/Unedited" type in the list of image types associated with an image. This is described (with an example) in CAA-113: CAA does not return "Raw/Unedited" in image type array. This will need to be addressed before the proposed changes will work.

    opened by rdswift 30
  • [PICARD-695] Tags are not properly cleared for some files even with the clear metadata option.

    [PICARD-695] Tags are not properly cleared for some files even with the clear metadata option.

    Hello, I notice that in some case, the metadata were not competetly cleared even with the option enabled. By saving the file without any metadata before saving with the metadata, it seems to work. I would like to provide the mp3 to illustrate the problem but the file is too big...

    Thank you for your work, it is great ;)

    Decision required 
    opened by NCenerar 29
  • PICARD-2546: Sequential processing of executable commands

    PICARD-2546: Sequential processing of executable commands

    Summary

    • This is a…
      • [x] Bug fix
      • [ ] Feature addition
      • [x] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences: Process commands through a queue object. Track threads created during command processing. Add a blocking to_main() function.

    Problem

    As noted in PICARD-2546, commands are processed in separate threads so they cannot be sequenced.

    • JIRA ticket (optional): PICARD-2546

    Solution

    Process command line executable commands through a queue object:

    • Implement a queue object to hold the executable command queue
    • Implement a command processor thread to parse the command queue
    • Implement command file tracking to avoid circular references
    • Do not queue additional commands after a QUIT has been queued

    Track threads created during executable command processing for consideration before marking the command as having been completed:

    • Add a new blocking version of the to_main() function
    • Track the status of threads created while an executable command is being processed
    • Block new command execution until current command is complete
    • Test for completion includes all threads created during the command processing

    Update and add new tests.

    Action

    I realize that this method is not perfect because it blocks on threads started during command execution including those that have nothing to do with the command, and currently only blocks threads started with the Runnable class. I welcome any suggestions for improving this.

    opened by rdswift 4
  • Limit worker threads based on CPUs

    Limit worker threads based on CPUs

    Summary

    • This is a…
      • [ ] Bug fix
      • [ ] Feature addition
      • [x] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences:

    Problem

    The default thread pool by default has a max. thread count based on QThread.idealThreadCount(), which basically sets the thread count based on the system's available CPU cores.

    Especially during file loading this number of threads gets exhausted. But this means there are 8 loading threads running, but there is always at least the main UI thread. Also there are the save thread pool (which might be used when the user is saving files in parallel) and the priority thread pool (which currently is used in metadatabox updates only and is meant for everything that should not be blocked by normal background tasks).

    Solution

    Limit number of threads in the main pol to QThread.idealThreadCount() minus 3 (main thread, priority pool + save pool). Keep at least 2, as one thread gets used up by the new pipe config.

    In my tests with 8 cores this gave 5. It improved responsiveness during loading of many files significantly. But we might consider going a bit higher. We have one thread reserved for the pipe handling, which usually isn't doing much. Then save thread pool is usually idle. Priority gets used if the user clicks some item with metadata.

    opened by phw 21
  • PICARD-2331: Port to PyQt6

    PICARD-2331: Port to PyQt6

    Summary

    WIP of porting Picard to PyQt6.

    Problem

    • JIRA ticket (optional): PICARD-2331

    Solution

    • [x] Upgrade all references to PyQt6 and apply general API changes
    • [x] Replacement for QAction.menu (for CD drive selection button) (done in #2169)
    • [x] Remove custom network timeout implementation (now supported by Qt6 itself)
    • [x] Bump API version to 3.0
    • [x] Remove macOS 10.12 / 10.13 builds (no longer supported by Qt6)
    • [x] Update resource loading for PyQt6 (requires PyQt >= 6.3.1)
    • [ ] Handle no longer available QtWinExtras.QWinTaskbarButton (see https://www.qt.io/blog/qt-extras-modules-in-qt-6, QTBUG-94008, QTBUG-89564)
    • [ ] Migrate media player (WIP)
    • [ ] Investigate failings tests on Linux CI
    • [x] Test on Linux
    • [ ] Test on macOS
    • [ ] Test on Windows
    Feature Needs more work Do not merge yet 
    opened by phw 1
  • PICARD-2354: DeprecationWarning: the imp module is deprecated in favour of importlib

    PICARD-2354: DeprecationWarning: the imp module is deprecated in favour of importlib

    Summary

    The imp module will be removed in Python 3.12 so we have to switch to importlib

    • This is a…
      • [ ] Bug fix
      • [ ] Feature addition
      • [x] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences:

    imp is used only in pluginmanager.py in 2 places, so it doesn't change much of the codebase.

    Problem

    • JIRA ticket (optional): PICARD-2354

    Solution

    I've found some way to replace it in importlib examples but I'm not sure if they're the best way to achieve this.

    Action

    opened by skelly37 2
  • PICARD-1001 Rework local cover art handling

    PICARD-1001 Rework local cover art handling

    Summary

    • This is a…
      • [x] Bug fix
      • [ ] Feature addition
      • [x] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences: Rework local cover art handling from a cover art provider to a property of files.

    Problem

    Local cover art as a provider doesn't make sense - local cover art is a property of a file, same as tag cover art. Problems caused by local cover art being treated as a provider include incorrectly indicating that files have been modified, or if an album was loaded without any files associated (for example via a tagger link on musicbrainz.org), no cover art at all.

    • JIRA ticket (optional): PICARD-1001

    Solution

    Move the handling of local cover art to the File class and delete the Local File cover art provider

    Action

    I don't know how to write Qt UI, so someone familiar with that will have to update the UI for this change.

    In the current state, the local_cover_regex setting is re-used, and a new load_local_cover_art boolean is used to enable the feature. The new setting should default to the same as the enabled status of the Local FIle cover art provider used to be.

    Some decision has to be made on how to result ordering of loading cover art from tags vs. local files. Currently local file art loading is skipped if cover art was loaded from tags.

    Needs more work 
    opened by kepstin 9
  • Improve matching for VA releases

    Improve matching for VA releases

    • if an album was incorrectly tagged with a label name or whatever, instead of Various Artists Picard tries to match Various Artists (from database) to the name, usually leading to a very low similarity, reducing a lot the chance to find the correct release

    • a contrario, if an album was tagged with Various Artists, it is very likely it is a VA compilation, so increase the weight

    Summary

    • This is a…
      • [x] Bug fix
      • [ ] Feature addition
      • [ ] Refactoring
      • [ ] Minor / simple change (like a typo)
      • [ ] Other
    • Describe this change in 1-2 sentences:

    Problem

    • JIRA ticket (optional): PICARD-XXX

    Solution

    Action

    opened by zas 0
Releases(release-2.8.4)
Owner
MetaBrainz Foundation
A California 501.c.3 non-profit organization running MusicBrainz and other open data/open source projects.
MetaBrainz Foundation
music library manager and MusicBrainz tagger

beets Beets is the media library management system for obsessive music geeks. The purpose of beets is to get your music collection right once and for

beetbox 11.3k Nov 20, 2022
PICARD - Parsing Incrementally for Constrained Auto-Regressive Decoding from Language Models

This is the official implementation of the following paper: Torsten Scholak, Nathan Schucher, Dzmitry Bahdanau. PICARD - Parsing Incrementally for Con

ElementAI 202 Nov 16, 2022