{"id":19662371,"url":"https://github.com/calne-ca/beets-plugin-extended-metadata","last_synced_at":"2026-06-11T12:31:50.859Z","repository":{"id":57414524,"uuid":"356922965","full_name":"calne-ca/beets-plugin-extended-metadata","owner":"calne-ca","description":"A beets plugin that extends the query sytax to query for Extended Metadata","archived":false,"fork":false,"pushed_at":"2021-12-28T01:21:03.000Z","size":115,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-05-05T04:44:37.362Z","etag":null,"topics":["beets","beets-plugin","music","plugin","python","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/calne-ca.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-11T16:42:41.000Z","updated_at":"2025-06-27T19:20:02.000Z","dependencies_parsed_at":"2022-09-10T03:13:18.081Z","dependency_job_id":null,"html_url":"https://github.com/calne-ca/beets-plugin-extended-metadata","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/calne-ca/beets-plugin-extended-metadata","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calne-ca%2Fbeets-plugin-extended-metadata","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calne-ca%2Fbeets-plugin-extended-metadata/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calne-ca%2Fbeets-plugin-extended-metadata/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calne-ca%2Fbeets-plugin-extended-metadata/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/calne-ca","download_url":"https://codeload.github.com/calne-ca/beets-plugin-extended-metadata/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calne-ca%2Fbeets-plugin-extended-metadata/sbom","scorecard":{"id":263166,"data":{"date":"2025-08-11","repo":{"name":"github.com/calne-ca/beets-plugin-extended-metadata","commit":"305791cb62c4ab9df3763115279f1335d7acc813"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/21 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/calne-ca/beets-plugin-extended-metadata/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/calne-ca/beets-plugin-extended-metadata/build.yml/master?enable=pin","Warn: pipCommand not pinned by hash: publish.sh:3","Warn: pipCommand not pinned by hash: publish.sh:4","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:20","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:21","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-17T11:18:21.836Z","repository_id":57414524,"created_at":"2025-08-17T11:18:21.836Z","updated_at":"2025-08-17T11:18:21.836Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34199516,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["beets","beets-plugin","music","plugin","python","python3"],"created_at":"2024-11-11T16:10:53.712Z","updated_at":"2026-06-11T12:31:50.841Z","avatar_url":"https://github.com/calne-ca.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![PyPI version](https://badge.fury.io/py/beets-extended-metadata.svg)](https://badge.fury.io/py/beets-extended-metadata) [![Build](https://github.com/calne-ca/beets-plugin-extended-metadata/workflows/Build/badge.svg)](https://github.com/calne-ca/beets-plugin-extended-metadata/actions?query=workflow%3ABuild)\n\n# Beets Extended Metadata Plugin\nThis is a plugin for the music management tool [beets](https://beets.io).\u003cbr\u003e\nThis plugin adds [Extended Metadata](https://github.com/calne-ca/beets-plugin-extended-metadata/blob/master/EMD.md) capabilities to beets.\nIt extends the beets query syntax, allowing you to query songs based on Extended Metadata and also allows you to write,\nupdate and view Extended Metadata based on queries.\n\n## Setup\n\n### Install the plugin\n\n````bash\npip beets-extended-metadata\n````\n\n### Configure the plugin\nEdit your [beets configuration file](https://beets.readthedocs.io/en/stable/reference/config.html) and add the following section:\n\n````yaml\nextendedmetadata:\n    query_prefix: 'x'\n    input_field: 'comments'\n````\n\nAlso add *extendedmetadata* to the *plugins* section.\n\nThe *query_prefix* defines a prefix that you need to add to the parts of your queries which should look into Extended Metadata instead of the normal metadata.\n\nThe *input_field* is the name of the audio tag, according to [this audio file fields list](https://github.com/calne-ca/beets-plugin-extended-metadata/blob/master/beetsplug/emd/audiofilefields.py), that contains your Extended Metadata string.\nAs default, the *comments* field will be used. Depending on what field you choose some software, including beets, will not be able to handle or persist it.\nI recommend using the *comments* field, since most software out there will be able to work with this field and having any other information in this field is usually unnecessary.\n\n## Limitations\n\nThe query syntax as well as the *emd* sub-command syntax have some limitations regarding the characters you can use for tag names / tag values.\n\n**Tag Name Limitations:**\u003cbr\u003e\nA tag name may only contain letters and numbers (including unicode letters) as well as hyphens, underscores and spaces.\n\n**Tag Value Limitations:**\u003cbr\u003e\nA tag value may contain any character except for commas, also it cannot start with a colon.\nThis limitation is necessary because colons and commas are special characters that cannot be escaped in any way without breaking the query/command syntax.\n\n## Writing Queries\n\n### Assumptions:\n- You configured *x* as the query prefix and *comments* as the input field.\n- Your library contains songs with Extended Metadata strings in the *comments* field.\n- You imported the songs into your beets library after writing the metadata to the files.\n\n### Examples\n\n**Note**: All queries are case-insensitive.\nIf you have a value *Abc* it will match the query value *abc*.\nIf you want to query case-sensitive, use [regex queries](#searching-for-all-rock-variant-songs).\n\n#### Searching for all russian songs\n\nThis assumes you have a custom tag *language* containing the language of the song.\n\n\n````bash\nbeet list x:language:russian\n````\n\nHere you can see how you can reference a custom tag from your Extended Metadata.\nYou start a query part with your query prefix *x*, followed by a colon.\nAfter that the syntax is the same as with normal beets queries, but it will reference tag names and values from the Extended Metadata instead.\n\n#### Searching for all songs that use synthesizer v\n\nThis assumes you have a custom tag *vocal_synth* containing the vocal synthesizer used in the song.\n\n````bash\nbeet list x:vocal_synth:\"syntheszer v\"\n````\n\nYou can query for values containing spaces by enclosing them in parentheses or quotes.\nThis is just the way a shell works and is not done by this plugin.\n\n#### Searching for all songs that are either japanese or chinese\n\nThis assumes you have a custom tag *language* containing the language of the song.\n\n````bash\nbeet list x:language:japanese,chinese\n````\n\nBy passing multiple values, separated by commas, you can query files that match one of the provided values\n\n#### Searching for all rock variant songs\n\nThis assumes you have a custom tag *genre* containing the genre of the song.\n\n````bash\nbeet list x:genre::.+rock\n````\n\nHere you can see how you can use [regex](https://en.wikipedia.org/wiki/Regular_expression) to make your queries more flexible.\nJust like with beets you can specify that your query value is regex by using the double colon *::* instead of a single colon.\n\n#### Searching for all songs in japanese that to not come from japan from the last 3 years\n\nThis assumes you have a custom tag *language* containing the language of the song and a custom tag *origin* containing the origin country.\n\n````bash\nbeet list x:.language:japanese x:.origin:'!japan' year:2010..2020\n````\n\nIn this example you can see how to easily combine Extended Metadata queries with normal audio field queries.\nIt also shows how to negate query values. If you prefix the query tag value with ! it will mean *not equals* / *not contains*.\n\n## Managing Extended Metadata\n\nThe query capabilities work as long as the Extended Metadata has been written to the files according to the [Extended Metadata documentation](EMD.md).\nThis means it is not required to use this plugin to write the Extended Metadata to your files.\n\nThe plugin provides an *emd* subcommand to write, update and show Extended Metadata based on beets queries.\nThe sub command requires a beets query that matches the items you want to apply options to, and a list of options that define what you want to do.\nTo get an overview of all options you can use the *--help* option:\n\n```shell\n$ beet emd --help\nUsage: beet emd \u003cquery\u003e [options]\n\nOptions:\n  -h, --help            show this help message and exit\n  -y, --yes             automatically confirms yes/no prompts that require\n                        user input\n  -u UPDATE_EXPRESSION, --update=UPDATE_EXPRESSION\n                        update or move a tag value. Example: \"tag1:v1/tag1:v2\"\n                        or \"tag1:v1/tag2:v1\" or \"tag1:v1/tag2:v2\".\n  -r RENAME_EXPRESSION, --rename=RENAME_EXPRESSION\n                        rename a tag. Example: \"tag1/tag2\".\n  -a ADD_EXPRESSION, --add=ADD_EXPRESSION\n                        add a tag value. Example: \"tag1:v1\" or\n                        \"tag1:v1,v2,v3\".\n  -c COPY_EXPRESSION, --copy=COPY_EXPRESSION\n                        copy a tag value from/to extended metadata or normal\n                        metadata. Tags prefixed with . refer to tags of normal\n                        metadata. Examples: \"tag1/tag2\", \".artist/tag2\",\n                        \"tag1/.artist\", \".artist/.album_artist\"\n  -d DELETE_EXPRESSION, --delete=DELETE_EXPRESSION\n                        delete a tag value or tag. Example: \"tag1\" or\n                        \"tag1:v1\".\n  -s, --show            show the extended meta data of the items\n```\n\nThe query argument can be passed to the *emd* command in the same way you pass it to the *list* command.\nThe options are optional, but you have to set at least one option.\nAll non-flag options are repeatable. By repeating an option you can apply multiple actions of the same type.\n\n### Examples\n\n#### Show Extended Metadata of matching files\n\n```shell\nbeet emd artist:ギガP album:'No title' -s\n```\n\nWith the *show* option the Extended Metadata of each matching file will be printed to the screen.\nThe Extended Metadata will be shown in its json format.\nYou can also combine this option with any other options,\nin which case the shown Extended Metadata represents the resulting Extended Metadata after all other option have been applied.\n\n#### Add tags for a specific artist\n\n```shell\nbeet emd artist:REOL -a language:japanese -a origin:japan\n```\n\nHere you can see how to add new tag values to the Extended Metadata of the file.\nYou can add multiple tags by repeating the *add* option.\n\n#### Remove a tag from all files\n\n```shell\nbeet emd '' -d genre\n```\n\nYou can match all files by simply passing an empty string to the *query* option.\nThis deletes the *genre* tag from all files, regardless of its value.\n\n#### Remove a tag value for certain audio formats\n\n```shell\nbeet emd path::*\\.flac -d tag:uncompressed\n```\n\nHere we do not delete the entire tag but only a certain value of the tag.\nYou can use a path query to apply the changes to certain directories or, in this case, certain audio formats by using regex.\n\n#### Rename a tag in all files\n\n```shell\nbeet emd '' -r singer/vocals\n```\n\nWith the *rename* option you can rename a tag by passing the old- and the new tag name separated by */*.\n\n#### Update a tag value\n\n```shell\nbeet emd x:origin:germany -u category:good/category:favorite\n```\n\nWith the *update* option you can change a tag value.\nThe syntax is the same as with the *rename* option but it also includes a tag value.\n\n#### Move a tag value in all files\n\n```shell\nbeet emd '' -u tag:metal/genre:metal\n```\n\nBy defining different tag names ins the old- and new value expression you can move a value from one tag to another.\n\n#### Move and update a tag value in all files\n\n```shell\nbeet emd '' -u tag:'thrash metal'/genre:metal\n```\n\nBy defining different tags and values in both the old- and new tag value expression you can move and change a tag value at the same time.\n\n#### Copy a value from normal metadata to extended metadata in all files\n```shell\nbeet emd '' -c .comments/lyrics\n```\n\nIn a copy expression your can use normal metadata tags as source or destination for a copy operation by prefixing the tag name with a dot.\nIn this example the value from the normal metadata tag *comments* will be copied to an EMD tag *lyrics*.\nWhen the destination tag does not exist it will be created, if it does exist the value will be appended to existing values.\n\n**Hint:** When adding EMD to a file for the first time you can use this to back up existing tag values from the tag that will be used to store the EMD.\n\n#### Everything combined\n\n```shell\nbeet emd x:origin:germany,austria x:language:'!german' -a tag:western,lederhosen -a category:good -d circle -u circle:'hyper hyper'/tag:hyper -r category/rating -c .artist:artist -s\n```\n\nThis adds the values *western* and *lederhosen* to the tag *tag*, \nadds *good* to the tag *category*, \ndeletes the tag *circle*, \nmoves the value *hyper hyper* from tag *circle* to the tag *tag* and changes it to *hyper*,\nrenames the tag *origin* to country, copies the value from the normal tag *artist* to the emd tag *artist*\nand prints the resulting Extended Metadata to the screen\nfor all songs from *germany* or *austria* that are *not* *german*.\n\nHere we delete and rename tags that are also referenced in add and update operations.\nIn general this works, but it is important to be aware of the order in which the options are applied:\n1. Update\n2. Rename\n3. Add\n4. Copy\n5. Delete\n6. Show\n\nSo in this example we rename *category* to *rating* before we add the value *good* to the tag *category*.\nSo we basically add a tag with the old name after renaming it.\nThis is probably not what we want. In this example we could easily fix this by simply using the new tag name for adding the value.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalne-ca%2Fbeets-plugin-extended-metadata","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcalne-ca%2Fbeets-plugin-extended-metadata","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalne-ca%2Fbeets-plugin-extended-metadata/lists"}