documentation and small bug fixes
This commit is contained in:
@@ -1,134 +1,216 @@
|
||||
# audio-library-manager
|
||||
|
||||
Tools for maintaining audio library trees.
|
||||
`audio-library-manager` maintains a music library in two forms:
|
||||
|
||||
The package currently contains two managers:
|
||||
1. a source FLAC tree, optionally normalised so FLAC files do not exceed a
|
||||
configured sample rate; and
|
||||
2. a mirrored Opus tree, where FLAC files are converted to `.opus` and
|
||||
non-FLAC sidecar files are copied unchanged.
|
||||
|
||||
- `flac-48khz-manager.rkt`: keeps a FLAC tree at a maximum sample rate of 48 kHz.
|
||||
- `flac2opus-manager.rkt`: mirrors a FLAC source tree to an Opus target tree.
|
||||
The current entry point is `audio-manager.rkt`, exported as the function
|
||||
`audio-manager` from `main.rkt`.
|
||||
|
||||
Both managers use the same administration files in the source tree:
|
||||
The implementation is intentionally simple. It is a command-line batch process:
|
||||
scan files, compare with the saved state, run a small ordered set of processing
|
||||
steps, write the state, send a report. It does not use places, channels,
|
||||
threads or a worker protocol.
|
||||
|
||||
- `.music-info.db`: keystore state database
|
||||
- `.flac-48khz-manager.ini`: configuration
|
||||
- `.flac-48khz-manager.log`: log file
|
||||
## Requirements
|
||||
|
||||
The `flac2opus` state uses its own key prefix in `.music-info.db`, so it can share
|
||||
the same database with the 48 kHz manager without mixing state entries. State
|
||||
keys use only normalized paths relative to the configured source/base directory:
|
||||
absolute mount points are not stored, and path separators are always `/`.
|
||||
Racket packages:
|
||||
|
||||
## FLAC 48 kHz manager
|
||||
- `racket-audio`
|
||||
- `simple-ini`
|
||||
- `simple-log`
|
||||
- `net-lib`
|
||||
|
||||
The command keeps a FLAC directory tree at a maximum sample rate of 48 kHz.
|
||||
Files above the configured threshold are converted in place by a direct call to
|
||||
`racket-audio/audio-encoder`. The conversion code is deliberately synchronous:
|
||||
no places, channels or worker protocol are used.
|
||||
Native libraries are provided or loaded by `racket-audio`. For the current audio
|
||||
path this normally means FLAC, Opus/Opusenc, TagLib and SoXR. FFmpeg is not part
|
||||
of the intended FLAC-to-Opus path; it is only relevant for `racket-audio` features
|
||||
that need FFmpeg-based decoding.
|
||||
|
||||
Run:
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
racket flac-48khz-manager.rkt /path/to/flac-tree
|
||||
```
|
||||
|
||||
When a FLAC file has no embedded picture and the same directory contains one of
|
||||
`cover.jpg`, `folder.jpg`, `cover.png` or `folder.png`, the manager embeds that
|
||||
image as front-cover picture before fingerprinting/conversion. This keeps FLAC
|
||||
artwork consistent before a later Opus mirror is made.
|
||||
|
||||
The `flac-48khz-manager` scanner only processes `.flac` files. Other files such
|
||||
as `.mp3`, booklets and cover images are ignored by this manager.
|
||||
|
||||
## FLAC to Opus mirror manager
|
||||
|
||||
The command mirrors a source directory to a target directory. FLAC files are
|
||||
converted to Ogg Opus files with extension `.opus`; all other regular files are
|
||||
copied unchanged, preserving their relative path and modification time. Examples
|
||||
include `booklet.pdf`, `cover.jpg`, `folder.png`, cue sheets, text files and
|
||||
other sidecar files.
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
racket flac2opus-manager.rkt /path/to/flac-tree /path/to/opus-tree
|
||||
racket flac2opus-manager.rkt --kbps 192 /path/to/flac-tree /path/to/opus-tree
|
||||
```
|
||||
|
||||
The default Opus bitrate is 224 kbps. The library API exposes the same setting
|
||||
as keyword argument:
|
||||
From a Racket REPL or a small wrapper script:
|
||||
|
||||
```racket
|
||||
(manage-flac2opus-tree source-directory target-directory #:kbps 224)
|
||||
#lang racket/base
|
||||
|
||||
(require audio-library-manager)
|
||||
|
||||
(audio-manager "/mnt/music" "/mnt/music-opus")
|
||||
```
|
||||
|
||||
Metadata is copied through `racket-audio/taglib` and `racket-audio/audio-encoder`:
|
||||
ordinary TagLib properties are transferred, embedded pictures are transferred,
|
||||
and an additional `FLAC2OPUS` comment is written to mark the conversion. The
|
||||
batch converter itself remains a simple synchronous function call.
|
||||
The first argument is the source music tree. The second argument is the target
|
||||
Opus mirror tree.
|
||||
|
||||
The manager removes target files that belonged to source files which disappeared
|
||||
since the previous run. It deliberately does not mirror its own root-level
|
||||
administration files: `.music-info.db`, `.flac-48khz-manager.ini` and
|
||||
`.flac-48khz-manager.log`.
|
||||
The optional log level defaults to `debug`:
|
||||
|
||||
Paths are handled as Racket paths rather than by splitting on `/`, so Windows
|
||||
absolute paths and UNC paths such as `\\panderleou\music` are preserved by the
|
||||
platform path implementation.
|
||||
```racket
|
||||
(audio-manager "/mnt/music" "/mnt/music-opus" #:log-level 'info)
|
||||
```
|
||||
|
||||
## Files created in the source tree
|
||||
|
||||
The manager stores its administration files in the source directory:
|
||||
|
||||
- `.audio-manager.ini` — configuration, read with `simple-ini`.
|
||||
- `.audio-manager.db` — serialized state hash, keyed by normalized relative
|
||||
paths.
|
||||
- `.audio-manager.log` — log file.
|
||||
|
||||
State keys are normalized relative paths from the source tree. Absolute mount
|
||||
points are not stored, and path separators are normalized to `/`. This makes the
|
||||
state less dependent on the local mount point used on a particular machine.
|
||||
|
||||
## Configuration
|
||||
|
||||
Important configuration keys are created automatically in `.flac-48khz-manager.ini`.
|
||||
The same file is used by both managers. The implementation now reads this file as a simple-ini value directly; there is no separate configuration struct layered on top of the ini model.
|
||||
The manager expects a configuration file at:
|
||||
|
||||
```ini
|
||||
[manager]
|
||||
max-sample-rate=48000
|
||||
hash-algorithm="sha256"
|
||||
change-detection="flac-taglib"
|
||||
dry-run=#f
|
||||
display-log=#t
|
||||
log-file=".flac-48khz-manager.log"
|
||||
compression-level=5
|
||||
|
||||
[opus-manager]
|
||||
log-file=".flac2opus-manager.log"
|
||||
|
||||
[mail]
|
||||
enabled=#f
|
||||
send-on-success=#f
|
||||
send-on-error=#t
|
||||
host=""
|
||||
port=25
|
||||
tls=#f
|
||||
username=""
|
||||
password=""
|
||||
from=""
|
||||
to=""
|
||||
cc=""
|
||||
bcc=""
|
||||
subject-prefix="[flac-48khz-manager]"
|
||||
```text
|
||||
<source-tree>/.audio-manager.ini
|
||||
```
|
||||
|
||||
SMTP reports are HTML and summarize counters and errors.
|
||||
Example:
|
||||
|
||||
## Change detection
|
||||
```ini
|
||||
[flac]
|
||||
max-khz=48000
|
||||
|
||||
The default change detection is `flac-taglib`:
|
||||
[opus]
|
||||
kbps=224
|
||||
|
||||
1. unchanged files are skipped with a cheap `size` + `mtime` comparison;
|
||||
2. new or visibly changed files get a semantic fingerprint;
|
||||
3. for FLAC files, the semantic fingerprint consists of FLAC STREAMINFO data,
|
||||
including the FLAC audio MD5 signature, plus TagLib properties and embedded
|
||||
picture metadata/content hash;
|
||||
4. for non-FLAC files in the Opus mirror, the default is `mtime` + `size`;
|
||||
5. full-file hashing is available by setting `change-detection="hash"`.
|
||||
[mail]
|
||||
from=Audio Manager <audio@example.org>
|
||||
to=Hans <hans@example.org>
|
||||
server=smtp.example.org
|
||||
port=25
|
||||
user=
|
||||
passwd=
|
||||
```
|
||||
|
||||
This avoids reading every complete audio file during a normal run, which matters
|
||||
on Windows and network shares.
|
||||
The mail section is currently required. If the mail configuration is missing or
|
||||
incomplete, the manager fails at startup with a configuration error. This is
|
||||
intentional: a batch run should not silently skip reporting.
|
||||
|
||||
## FLAC files with ID3v2 prefixes
|
||||
## Processing model
|
||||
|
||||
Version 0.1.4 accepts native FLAC files that start directly with `fLaC`, and FLAC
|
||||
files with an ID3v2 tag before the `fLaC` marker. The latter occurs in some
|
||||
libraries and is accepted by players such as foobar2000/libFLAC. The fast
|
||||
STREAMINFO reader skips the ID3v2 prefix before reading the FLAC metadata.
|
||||
The manager uses a file walker with an ordered processing pipeline. Each step
|
||||
receives:
|
||||
|
||||
```racket
|
||||
base-path path info
|
||||
```
|
||||
|
||||
and returns the same three values, with `info` possibly enriched. The `info` hash
|
||||
contains the relative path, type, extension, state and any metadata discovered by
|
||||
earlier steps.
|
||||
|
||||
The important file states are:
|
||||
|
||||
- `new` — the relative path was not present in the saved state.
|
||||
- `changed` — the path existed before, but type, size or modification time
|
||||
changed.
|
||||
- `unchanged` — the path exists and its basic filesystem state is unchanged.
|
||||
- `deleted` — the path existed in the saved state but no longer exists in the
|
||||
source tree.
|
||||
|
||||
For unchanged entries, the previous enriched `info` hash is reused. For changed
|
||||
entries, the previous `info` hash is reused but marked as `changed` and updated
|
||||
with the current type, size and modification time. Stages must therefore treat
|
||||
`changed` as the signal to re-inspect content-dependent data.
|
||||
|
||||
The pipeline is grouped conceptually as:
|
||||
|
||||
1. enrichment: normalize/identify known extensions, sniff audio files, detect
|
||||
FLAC-with-ID3, and read FLAC sample rate/bit depth when needed;
|
||||
2. conversion/actions: downsample FLAC files above the configured rate, embed a
|
||||
sidecar cover image when a FLAC has no embedded picture, convert FLAC to
|
||||
Opus, and copy non-FLAC sidecar files;
|
||||
3. cleanup/logging: remove target files for deleted source entries and log
|
||||
progress.
|
||||
|
||||
## FLAC handling
|
||||
|
||||
FLAC files are detected by extension and confirmed by `racket-audio` sniffing.
|
||||
A file with extension `.flac` but an ID3 prefix is reported as a FLAC-with-ID3
|
||||
case. Such files are not treated as ordinary sidecar files.
|
||||
|
||||
For valid FLAC files:
|
||||
|
||||
- sample rate and bit depth are read through the FLAC decoder;
|
||||
- files with a sample rate greater than `[flac] max-khz` are converted in place
|
||||
through `racket-audio/audio-encoder`;
|
||||
- conversion is synchronous and direct;
|
||||
- if the file has no embedded picture, `cover.jpg`, `cover.jpeg`, `folder.jpg`,
|
||||
`folder.jpeg`, `cover.png` or `folder.png` in the same directory is embedded
|
||||
as a front-cover picture.
|
||||
|
||||
Temporary FLAC files produced by interrupted conversions should be ignored by
|
||||
the scanner. The implementation uses manager-specific temporary names for this
|
||||
purpose.
|
||||
|
||||
## Opus mirror handling
|
||||
|
||||
For every processable FLAC source file, the manager writes a corresponding Opus
|
||||
file under the target tree:
|
||||
|
||||
```text
|
||||
source: album/track.flac
|
||||
target: album/track.opus
|
||||
```
|
||||
|
||||
The default bitrate is 224 kbps and can be changed with `[opus] kbps`.
|
||||
|
||||
Non-FLAC files are copied unchanged to the target tree when they are new,
|
||||
changed, or missing from the target. Hidden files are ignored. Typical copied
|
||||
sidecar files include cover images, PDFs, cue sheets and logs.
|
||||
|
||||
When a source entry is deleted, the corresponding target entry is removed. For a
|
||||
deleted FLAC source, the target extension is changed from `.flac` to `.opus`.
|
||||
Renames are deliberately represented as `deleted` plus `new`; no rename detection
|
||||
is attempted.
|
||||
|
||||
## Logging and report
|
||||
|
||||
The manager logs to:
|
||||
|
||||
```text
|
||||
<source-tree>/.audio-manager.log
|
||||
```
|
||||
|
||||
The report contains sections for:
|
||||
|
||||
- FLAC files with ID3 tags;
|
||||
- FLAC conversions and cover updates;
|
||||
- Opus conversions;
|
||||
- file counters for processed, new, changed, unchanged and deleted files;
|
||||
- failed conversions and failed copies.
|
||||
|
||||
At the end of a successful scan the serialized state is written to
|
||||
`.audio-manager.db` and the report is sent by mail.
|
||||
|
||||
## API
|
||||
|
||||
```racket
|
||||
(audio-manager music-path opus-path #:log-level [log-level 'debug])
|
||||
```
|
||||
|
||||
Runs the complete batch process. `music-path` is the source tree and `opus-path`
|
||||
is the target Opus mirror tree. The function returns `'done` after a completed
|
||||
run.
|
||||
|
||||
## Design notes
|
||||
|
||||
The package intentionally keeps responsibilities separated:
|
||||
|
||||
- `audio-manager.rkt` orchestrates the batch run and owns the pipeline;
|
||||
- `private/file-walker.rkt` scans the filesystem and maintains the state model;
|
||||
- `private/flac-handling.rkt` contains FLAC inspection and FLAC downsampling;
|
||||
- `private/opus-handling.rkt` contains FLAC-to-Opus conversion;
|
||||
- `private/mail.rkt` sends reports;
|
||||
- `private/log.rkt` defines the logger;
|
||||
- `private/util.rkt` contains small path/date/cover helpers.
|
||||
|
||||
The manager does not implement an audio pipeline itself. Audio decoding,
|
||||
encoding, metadata and resampling are delegated to `racket-audio`.
|
||||
|
||||
Reference in New Issue
Block a user