Skip to main content

beets-pysacd

A beets plugin that imports SACD ISO images, using pysacd.

beets can't tag an .iso. For each disc image this plugin reads the TOC, finds the MusicBrainz release and the medium (the SACD stereo or multichannel layer) that the disc is, converts each selected area to DSF or FLAC in a staging directory, and imports that through beets' normal importer. Each area becomes an album of its own, on its own medium of the release.

Install

pip install beets-pysacd

Then enable the plugin, with beets' MusicBrainz plugin, in your beets config:

plugins: musicbrainz sacd

Requirements: Python 3.11 or later, beets 2.12 or later (2.x), pysacd 0.2 or later (its engine, and libFLAC for format: flac).

Use

beet sacd-import Album.iso              # one image
beet sacd-import ~/rips/                # every .iso under a directory
beet sacd-import -a both -f flac Album.iso
beet sacd-import --release 1d1f0a6e-... Album.iso   # use this MusicBrainz release
beet sacd-info Album.iso                # print the TOC; changes nothing
beet sacd-info ~/rips/                  # ...of every image under a directory

Plain beet import also converts the ISO images it finds (an .iso given on the command line, or inside a directory being imported) before importing them. Your other files in the same run are imported exactly as your settings say. Turn this off with auto: no.

An area that is already in the library is skipped. Imported albums carry two flexible attributes, sacd_iso_sha (a fingerprint of the disc's TOCs) and sacd_area (stereo or multichannel), so beet ls sacd_area:multichannel lists the multichannel albums.

DVD-Audio, DVD-Video and Blu-ray images

Disc-image folders often hold DVD and Blu-ray images next to SACDs. The plugin identifies them and lists them, but doesn't convert them: beet sacd-info prints one line for each, and imports log why each was left alone.

== .../Bluray/LIVE_CONCERT.iso
  Blu-ray image, volume 'LIVE_CONCERT', 4 playlists: catalogued, not converted
== .../DVDA/SURROUND_MIX.iso
  DVD-Audio + DVD-Video image, volume 'SURROUND_MIX', 5 title sets: catalogued, not converted

It reads the image's file system (UDF, including the UDF 2.50 metadata partition that Blu-ray uses, or ISO 9660) and looks for the standard folders: AUDIO_TS with AUDIO_TS.IFO for DVD-Audio, VIDEO_TS for DVD-Video and BDMV for Blu-ray. The count is of DVD title sets or Blu-ray playlists. This lives in the plugin (beetsplug/_sacd_discimage.py), not in pysacd, and reads only directory structures, never the audio.

Configuration

sacd:
    areas: stereo        # stereo | multichannel | both
    format: dsf          # dsf | flac (24-bit PCM)
    pcm_rate: 88200      # flac: 88200 | 176400
    auto: yes            # convert ISOs found by plain `beet import`
    remove_iso: no       # delete an ISO once its import is verified
    staging: ''          # where to convert; default: a hidden directory in the library directory
    musicbrainz: yes     # let pysacd find the release (barcode, catalogue number, ISRCs)
    contact: ''          # contact (e.g. an e-mail address) for pysacd's MusicBrainz User-Agent
    threads: 0           # conversion threads; 0 = one per CPU

sacd-import options override areas, format, pcm_rate and remove_iso for one run.

Keeping or removing ISO images

ISO images are kept by default, and never written to. With remove_iso: yes (or --remove-iso) an image is deleted only after every selected area has been imported and verified: an album with that disc's fingerprint and area exists in the library, with all of the area's tracks, and every file exists. An import you skip, or one that fails, keeps the image.

Files and staging

Converted files always move from the staging directory into your library, whatever copy, move or in-place settings you use for other files: the staging directory is temporary and is deleted afterwards, also after an error. By default it is created inside the library directory, so the move is a rename on the same filesystem; set staging to put it elsewhere.

Hybrid SACDs

MusicBrainz lists a hybrid SACD as one release with several media: the CD layer, the SACD stereo layer and sometimes the SACD multichannel layer, all with the same recordings. beets puts the tracks of every medium into one candidate and then matches files to tracks. With the same recordings on every layer, it maps the SACD files to the CD layer (medium 1), overwrites their release track IDs, and writes the album-level media as "Media". See the beets discussion.

This plugin keeps each area on its own medium:

  • When pysacd matched the release, it knows the medium: that release's candidates are cut down to that medium before beets compares them, and the album's media is that medium's format, e.g. "Hybrid SACD (SACD layer, 2 channels)".
  • Without pysacd's match, the medium is the one SACD-layer medium of the candidate with the area's track count and channel type. If that is ambiguous (a set of two SACDs, say), the candidate is left as beets made it.
  • A final check after you choose a match maps the files to the medium if they still point elsewhere, and logs it.

How it works

For each ISO image, and each selected area of it:

  1. pysacd reads the TOC (read-only) and fingerprints the disc from its TOCs. An area whose fingerprint and area are already in the library is skipped.
  2. pysacd matches the MusicBrainz release and medium, and converts the area into the staging directory, writing the release's IDs into the files. A match on a medium that isn't an SACD layer (a vinyl, CD or digital edition sharing the catalogue number or barcode) isn't used: the area is converted without IDs and beets matches it the normal way. A match on the CD layer of a hybrid release keeps the release; the area goes to that release's SACD layer with its track count and channels.
  3. beets imports the staging directory. Because the files agree on the release ID, beets looks that release up directly. For this one import task only, the plugin then:
    • hides the physical-disc tags from matching. pysacd tags the physical disc ("disc 1 of 1", media "Hybrid SACD"), but MusicBrainz counts each layer as a medium ("disc 2 of 3", "Hybrid SACD (SACD layer, 2 channels)"). beets would count those tags against the right candidate, enough to keep a correct match from being applied automatically. beets writes the medium's own values when it applies the match;
    • cuts the release's candidates down to the area's medium (see Hybrid SACDs);
    • counts only an album on the same medium as a duplicate. beets' default duplicate keys are album artist and album, which both areas of a disc (and a CD rip of the same release) share: without this, importing the multichannel area after the stereo one would be skipped as a duplicate. Another copy of the same layer is still a duplicate;
    • moves the files into the library (see Files and staging).
  4. The album gets sacd_iso_sha and sacd_area; with remove_iso, the ISO is deleted once every area is verified. The staging directory is removed.

Nothing here changes how beets treats your other files in the same import.

Development

pip install -e '.[dev]'
ruff check . && mypy beetsplug
PYSACD_TESTS=/path/to/pysacd/tests pytest -q

The tests build synthetic SACD images with pysacd's test generator (tests/sacdgen.py in the pysacd source tree, found through PYSACD_TESTS, default ../pysacd/tests) and import them into a throwaway beets library. MusicBrainz is mocked with a hybrid release whose media share recordings, so the tests need no network. DVD and Blu-ray images for the cataloguing tests come from tests/discgen.py, which writes minimal UDF and ISO 9660 file systems.

Licence

GPL-2.0-or-later (see LICENSE). The plugin imports pysacd, which is GPL-2.0-or-later.

Metadata

Release files for beets-pysacd 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for beets-pysacd 0.1.0
File Size Uploaded Provenance Actions
beets_pysacd-0.1.0.tar.gz 29.1 kB With provenance Details for beets_pysacd-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for beets-pysacd 0.1.0
File Interpreter ABI Platform Actions
beets_pysacd-0.1.0-py3-none-any.whl Python 3 none any Details for beets_pysacd-0.1.0-py3-none-any.whl

Total release size: 52.8 kB

Release files / beets_pysacd-0.1.0.tar.gz

Download URL beets_pysacd-0.1.0.tar.gz
Size 29.1 kB
Tags Source
SHA-256 checksum
How to use checksums
d542e4820e87ce0135eabd65747a04b66706f227c98b7f3dd598271123fb5c9e
BLAKE2b-256 checksum
How to use checksums
ace612e4a93cf5e88fc7eda856dfa28011b26adb22fd345f0bbe3a57099ee2ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Release files / beets_pysacd-0.1.0-py3-none-any.whl

Download URL beets_pysacd-0.1.0-py3-none-any.whl
Size 23.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
20dead9d497584e139687ae146bb8d906594b1eafd8b883bf9870556ec0a6624
BLAKE2b-256 checksum
How to use checksums
0397f5a39381297327b8881c5ceca492691378be061d43e94d730607c7b9d06c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Security for beets-pysacd 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for beets_pysacd-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page