redeflacue 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redeflacue-0.1.0/.editorconfig +19 -0
- redeflacue-0.1.0/.github/workflows/ci.yml +53 -0
- redeflacue-0.1.0/.github/workflows/release.yml +31 -0
- redeflacue-0.1.0/.gitignore +27 -0
- redeflacue-0.1.0/.pre-commit-config.yaml +16 -0
- redeflacue-0.1.0/.python-version +1 -0
- redeflacue-0.1.0/CHANGELOG.md +20 -0
- redeflacue-0.1.0/LICENSE +29 -0
- redeflacue-0.1.0/PKG-INFO +135 -0
- redeflacue-0.1.0/README.md +106 -0
- redeflacue-0.1.0/pyproject.toml +88 -0
- redeflacue-0.1.0/src/redeflacue/__init__.py +14 -0
- redeflacue-0.1.0/src/redeflacue/__main__.py +3 -0
- redeflacue-0.1.0/src/redeflacue/cli.py +64 -0
- redeflacue-0.1.0/src/redeflacue/processor.py +91 -0
- redeflacue-0.1.0/src/redeflacue/py.typed +0 -0
- redeflacue-0.1.0/src/redeflacue/splitter.py +53 -0
- redeflacue-0.1.0/tests/conftest.py +22 -0
- redeflacue-0.1.0/tests/fixtures/sample_192k.cue +21 -0
- redeflacue-0.1.0/tests/fixtures/sample_multi_file.cue +17 -0
- redeflacue-0.1.0/tests/fixtures/sample_windows1251.cue +10 -0
- redeflacue-0.1.0/tests/test_cli.py +219 -0
- redeflacue-0.1.0/tests/test_processor.py +208 -0
- redeflacue-0.1.0/tests/test_splitter.py +140 -0
- redeflacue-0.1.0/uv.lock +891 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
indent_size = 4
|
|
10
|
+
|
|
11
|
+
[*.{yml,yaml,toml,json}]
|
|
12
|
+
indent_size = 2
|
|
13
|
+
|
|
14
|
+
[*.md]
|
|
15
|
+
trim_trailing_whitespace = false
|
|
16
|
+
|
|
17
|
+
# Encoding fixtures must keep their legacy byte encoding.
|
|
18
|
+
[tests/fixtures/*windows1251*]
|
|
19
|
+
charset = unset
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
name: py${{ matrix.python-version }} / ${{ matrix.os }}
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
os: [ubuntu-latest, macos-latest]
|
|
20
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v7
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0 # hatch-vcs derives the version from git tags
|
|
26
|
+
|
|
27
|
+
- uses: astral-sh/setup-uv@v10.3.0
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
python-version: ${{ matrix.python-version }} # exported as UV_PYTHON
|
|
31
|
+
|
|
32
|
+
- name: Install sox (Linux)
|
|
33
|
+
if: runner.os == 'Linux'
|
|
34
|
+
run: sudo apt-get update && sudo apt-get install -y sox libsox-fmt-all
|
|
35
|
+
|
|
36
|
+
- name: Install sox (macOS)
|
|
37
|
+
if: runner.os == 'macOS'
|
|
38
|
+
run: brew install sox
|
|
39
|
+
|
|
40
|
+
- run: uv sync --frozen
|
|
41
|
+
- run: uv run ruff check
|
|
42
|
+
- run: uv run ruff format --check
|
|
43
|
+
- run: uv run mypy src
|
|
44
|
+
- run: uv run pytest --cov --cov-report=xml
|
|
45
|
+
|
|
46
|
+
- name: Upload coverage
|
|
47
|
+
uses: codecov/codecov-action@v7
|
|
48
|
+
with:
|
|
49
|
+
files: coverage.xml
|
|
50
|
+
flags: py${{ matrix.python-version }}-${{ matrix.os }}
|
|
51
|
+
fail_ci_if_error: false
|
|
52
|
+
# Required for private repositories.
|
|
53
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
pypi:
|
|
9
|
+
name: Build and publish to PyPI
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
# PyPI trusted publisher: r8/redeflacue, release.yml, environment pypi.
|
|
12
|
+
environment:
|
|
13
|
+
name: pypi
|
|
14
|
+
url: https://pypi.org/p/redeflacue
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v7
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0 # hatch-vcs derives the version from the pushed tag
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@v10.3.0
|
|
25
|
+
|
|
26
|
+
- run: uv build
|
|
27
|
+
|
|
28
|
+
- name: Smoke-test the wheel
|
|
29
|
+
run: uv run --isolated --no-project --with dist/*.whl redeflacue --version
|
|
30
|
+
|
|
31
|
+
- run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Byte-compiled / cache
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.mypy_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
|
|
8
|
+
# Environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
.env
|
|
12
|
+
|
|
13
|
+
# Build / dist
|
|
14
|
+
build/
|
|
15
|
+
dist/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
|
|
18
|
+
# Coverage
|
|
19
|
+
.coverage
|
|
20
|
+
.coverage.*
|
|
21
|
+
coverage.xml
|
|
22
|
+
htmlcov/
|
|
23
|
+
|
|
24
|
+
# Editors / OS
|
|
25
|
+
.idea/
|
|
26
|
+
.vscode/
|
|
27
|
+
.DS_Store
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.17.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: local
|
|
9
|
+
hooks:
|
|
10
|
+
# Use the project environment because deflacue has no type stubs.
|
|
11
|
+
- id: mypy
|
|
12
|
+
name: mypy
|
|
13
|
+
entry: uv run mypy src
|
|
14
|
+
language: system
|
|
15
|
+
types: [python]
|
|
16
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-10-10
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- deflacue-compatible CLI (`redeflacue`, `python -m redeflacue`) with these flags:
|
|
10
|
+
`-d/--dest`, `-e/--encoding`, `-r/--recursive`, `--dry-run`, `-v/--verbose`; plus
|
|
11
|
+
`--version`.
|
|
12
|
+
- Library API: `plan_extractions()`, `build_sox_command()`, `Extraction`.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Track boundaries on non-44.1 kHz FLAC images. Cut points are passed to SoX as times instead of
|
|
17
|
+
44.1 kHz sample counts.
|
|
18
|
+
|
|
19
|
+
[Unreleased]: https://github.com/r8/redeflacue/compare/v0.1.0...HEAD
|
|
20
|
+
[0.1.0]: https://github.com/r8/redeflacue/releases/tag/v0.1.0
|
redeflacue-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Sergey Storchay
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: redeflacue
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CUE + FLAC splitter that handles non-44.1 kHz audio
|
|
5
|
+
Project-URL: Homepage, https://github.com/r8/redeflacue
|
|
6
|
+
Project-URL: Issues, https://github.com/r8/redeflacue/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/r8/redeflacue/blob/main/CHANGELOG.md
|
|
8
|
+
Author-email: Sergey Storchay <r8@r8.com.ua>
|
|
9
|
+
License-Expression: BSD-3-Clause
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: audio,cue,deflacue,flac,hi-res,sox,split
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: deflacue>=2.0.1
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# redeflacue
|
|
31
|
+
|
|
32
|
+
`redeflacue` splits CUE + FLAC album images without assuming a 44.1 kHz sample rate.
|
|
33
|
+
|
|
34
|
+
It uses [deflacue](https://github.com/idlesign/deflacue) to parse cue sheets and keeps its
|
|
35
|
+
command-line interface, output layout, and tag mapping. The difference is in the arguments sent
|
|
36
|
+
to SoX: cue positions are passed as times instead of sample counts. This avoids misplaced track
|
|
37
|
+
boundaries on 96 kHz, 192 kHz, and other non-44.1 kHz files.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
Install the package with `uv` or `pipx`:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
uv tool install redeflacue
|
|
45
|
+
# or
|
|
46
|
+
pipx install redeflacue
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
SoX is also required:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
sudo apt install sox libsox-fmt-all # Debian / Ubuntu
|
|
53
|
+
brew install sox # macOS
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
# Split .cue files in a directory.
|
|
60
|
+
redeflacue /home/me/cues/
|
|
61
|
+
|
|
62
|
+
# Read Windows-1251 cue sheets recursively and choose an output directory.
|
|
63
|
+
redeflacue -e windows-1251 -r -d /mnt/library/ /home/me/cues/
|
|
64
|
+
|
|
65
|
+
# Print the SoX commands without running them.
|
|
66
|
+
redeflacue --dry-run -v /home/me/cues/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Flag | Description |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `-d, --dest DIR` | Output root; defaults to `deflacue/` beside each cue sheet |
|
|
72
|
+
| `-e, --encoding ENC` | Cue-sheet encoding, such as `windows-1251` |
|
|
73
|
+
| `-r, --recursive` | Search subdirectories |
|
|
74
|
+
| `--dry-run` | Print planned work without running SoX |
|
|
75
|
+
| `-v, --verbose` | Print each SoX command |
|
|
76
|
+
| `--version` | Print the version |
|
|
77
|
+
|
|
78
|
+
Tracks are written to:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
<destination>/<performer>/<date - album>/<track number - title>.flac
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The cue sheet supplies the track number, title, artist, album, genre, date, ISRC, and
|
|
85
|
+
description tags.
|
|
86
|
+
|
|
87
|
+
## Sample-rate fix
|
|
88
|
+
|
|
89
|
+
deflacue stores cue positions as 44.1 kHz sample offsets and passes them to `sox trim` as sample
|
|
90
|
+
counts. SoX interprets those counts using the input file's actual sample rate, so boundaries are
|
|
91
|
+
wrong when that rate is not 44.1 kHz.
|
|
92
|
+
|
|
93
|
+
A cue position is based on 75 frames per second. Because deflacue uses 588 samples per cue frame
|
|
94
|
+
(`44100 / 75`), dividing its stored offset by 44100 recovers the cue time. redeflacue sends that
|
|
95
|
+
time to SoX as `HH:MM:SS.ssssss`, which works independently of the input rate.
|
|
96
|
+
|
|
97
|
+
## Python API
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from pathlib import Path
|
|
101
|
+
|
|
102
|
+
from deflacue.parser import CueParser
|
|
103
|
+
from redeflacue import build_sox_command, plan_extractions
|
|
104
|
+
|
|
105
|
+
cue = CueParser.from_file(fpath=Path("album.cue")).run()
|
|
106
|
+
for extraction in plan_extractions(cue, Path("."), Path("out")):
|
|
107
|
+
print(
|
|
108
|
+
build_sox_command(
|
|
109
|
+
extraction.src,
|
|
110
|
+
extraction.dst,
|
|
111
|
+
extraction.start_s,
|
|
112
|
+
extraction.end_s,
|
|
113
|
+
extraction.tags,
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
git clone https://github.com/r8/redeflacue.git
|
|
122
|
+
cd redeflacue
|
|
123
|
+
uv sync
|
|
124
|
+
uv run pytest
|
|
125
|
+
uv run ruff check
|
|
126
|
+
uv run ruff format --check
|
|
127
|
+
uv run mypy src
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Integration tests are skipped when SoX is not installed.
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
BSD-3-Clause. Cue-sheet parsing is provided by Igor Starikov's BSD-licensed
|
|
135
|
+
[deflacue](https://github.com/idlesign/deflacue).
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# redeflacue
|
|
2
|
+
|
|
3
|
+
`redeflacue` splits CUE + FLAC album images without assuming a 44.1 kHz sample rate.
|
|
4
|
+
|
|
5
|
+
It uses [deflacue](https://github.com/idlesign/deflacue) to parse cue sheets and keeps its
|
|
6
|
+
command-line interface, output layout, and tag mapping. The difference is in the arguments sent
|
|
7
|
+
to SoX: cue positions are passed as times instead of sample counts. This avoids misplaced track
|
|
8
|
+
boundaries on 96 kHz, 192 kHz, and other non-44.1 kHz files.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
Install the package with `uv` or `pipx`:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
uv tool install redeflacue
|
|
16
|
+
# or
|
|
17
|
+
pipx install redeflacue
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
SoX is also required:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
sudo apt install sox libsox-fmt-all # Debian / Ubuntu
|
|
24
|
+
brew install sox # macOS
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
# Split .cue files in a directory.
|
|
31
|
+
redeflacue /home/me/cues/
|
|
32
|
+
|
|
33
|
+
# Read Windows-1251 cue sheets recursively and choose an output directory.
|
|
34
|
+
redeflacue -e windows-1251 -r -d /mnt/library/ /home/me/cues/
|
|
35
|
+
|
|
36
|
+
# Print the SoX commands without running them.
|
|
37
|
+
redeflacue --dry-run -v /home/me/cues/
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Flag | Description |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `-d, --dest DIR` | Output root; defaults to `deflacue/` beside each cue sheet |
|
|
43
|
+
| `-e, --encoding ENC` | Cue-sheet encoding, such as `windows-1251` |
|
|
44
|
+
| `-r, --recursive` | Search subdirectories |
|
|
45
|
+
| `--dry-run` | Print planned work without running SoX |
|
|
46
|
+
| `-v, --verbose` | Print each SoX command |
|
|
47
|
+
| `--version` | Print the version |
|
|
48
|
+
|
|
49
|
+
Tracks are written to:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
<destination>/<performer>/<date - album>/<track number - title>.flac
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The cue sheet supplies the track number, title, artist, album, genre, date, ISRC, and
|
|
56
|
+
description tags.
|
|
57
|
+
|
|
58
|
+
## Sample-rate fix
|
|
59
|
+
|
|
60
|
+
deflacue stores cue positions as 44.1 kHz sample offsets and passes them to `sox trim` as sample
|
|
61
|
+
counts. SoX interprets those counts using the input file's actual sample rate, so boundaries are
|
|
62
|
+
wrong when that rate is not 44.1 kHz.
|
|
63
|
+
|
|
64
|
+
A cue position is based on 75 frames per second. Because deflacue uses 588 samples per cue frame
|
|
65
|
+
(`44100 / 75`), dividing its stored offset by 44100 recovers the cue time. redeflacue sends that
|
|
66
|
+
time to SoX as `HH:MM:SS.ssssss`, which works independently of the input rate.
|
|
67
|
+
|
|
68
|
+
## Python API
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from pathlib import Path
|
|
72
|
+
|
|
73
|
+
from deflacue.parser import CueParser
|
|
74
|
+
from redeflacue import build_sox_command, plan_extractions
|
|
75
|
+
|
|
76
|
+
cue = CueParser.from_file(fpath=Path("album.cue")).run()
|
|
77
|
+
for extraction in plan_extractions(cue, Path("."), Path("out")):
|
|
78
|
+
print(
|
|
79
|
+
build_sox_command(
|
|
80
|
+
extraction.src,
|
|
81
|
+
extraction.dst,
|
|
82
|
+
extraction.start_s,
|
|
83
|
+
extraction.end_s,
|
|
84
|
+
extraction.tags,
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
git clone https://github.com/r8/redeflacue.git
|
|
93
|
+
cd redeflacue
|
|
94
|
+
uv sync
|
|
95
|
+
uv run pytest
|
|
96
|
+
uv run ruff check
|
|
97
|
+
uv run ruff format --check
|
|
98
|
+
uv run mypy src
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Integration tests are skipped when SoX is not installed.
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
BSD-3-Clause. Cue-sheet parsing is provided by Igor Starikov's BSD-licensed
|
|
106
|
+
[deflacue](https://github.com/idlesign/deflacue).
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "redeflacue"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "CUE + FLAC splitter that handles non-44.1 kHz audio"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "BSD-3-Clause"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Sergey Storchay", email = "r8@r8.com.ua" }]
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
dependencies = ["deflacue>=2.0.1"]
|
|
15
|
+
keywords = ["cue", "flac", "sox", "split", "hi-res", "audio", "deflacue"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"Operating System :: MacOS",
|
|
21
|
+
"Operating System :: POSIX :: Linux",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: Multimedia :: Sound/Audio :: Conversion",
|
|
30
|
+
"Typing :: Typed",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/r8/redeflacue"
|
|
35
|
+
Issues = "https://github.com/r8/redeflacue/issues"
|
|
36
|
+
Changelog = "https://github.com/r8/redeflacue/blob/main/CHANGELOG.md"
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
redeflacue = "redeflacue.cli:main"
|
|
40
|
+
|
|
41
|
+
[dependency-groups]
|
|
42
|
+
dev = [
|
|
43
|
+
"pytest>=8",
|
|
44
|
+
"pytest-cov>=5",
|
|
45
|
+
"ruff>=0.6",
|
|
46
|
+
"mypy>=1.10",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[tool.hatch.version]
|
|
50
|
+
source = "vcs"
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["src/redeflacue"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 100
|
|
57
|
+
src = ["src", "tests"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
|
|
61
|
+
|
|
62
|
+
[tool.mypy]
|
|
63
|
+
files = ["src"]
|
|
64
|
+
disallow_untyped_defs = true
|
|
65
|
+
disallow_incomplete_defs = true
|
|
66
|
+
check_untyped_defs = true
|
|
67
|
+
no_implicit_optional = true
|
|
68
|
+
warn_unused_ignores = true
|
|
69
|
+
warn_redundant_casts = true
|
|
70
|
+
warn_return_any = true
|
|
71
|
+
strict_equality = true
|
|
72
|
+
|
|
73
|
+
[[tool.mypy.overrides]]
|
|
74
|
+
module = ["deflacue", "deflacue.*"]
|
|
75
|
+
ignore_missing_imports = true
|
|
76
|
+
|
|
77
|
+
[tool.pytest.ini_options]
|
|
78
|
+
testpaths = ["tests"]
|
|
79
|
+
addopts = ["-ra", "--strict-markers"]
|
|
80
|
+
markers = ["integration: requires sox binary"]
|
|
81
|
+
|
|
82
|
+
[tool.coverage.run]
|
|
83
|
+
source = ["redeflacue"]
|
|
84
|
+
branch = true
|
|
85
|
+
|
|
86
|
+
[tool.coverage.report]
|
|
87
|
+
show_missing = true
|
|
88
|
+
skip_covered = true
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("redeflacue")
|
|
7
|
+
except PackageNotFoundError: # pragma: no cover
|
|
8
|
+
__version__ = "0.0.0+unknown"
|
|
9
|
+
|
|
10
|
+
from redeflacue.cli import main
|
|
11
|
+
from redeflacue.processor import Extraction, plan_extractions
|
|
12
|
+
from redeflacue.splitter import build_sox_command
|
|
13
|
+
|
|
14
|
+
__all__ = ["Extraction", "__version__", "build_sox_command", "main", "plan_extractions"]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import shutil
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from redeflacue import __version__
|
|
10
|
+
from redeflacue.processor import iter_cue_files, process_cue
|
|
11
|
+
|
|
12
|
+
LOGGER = logging.getLogger("redeflacue")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
16
|
+
ap = argparse.ArgumentParser(
|
|
17
|
+
prog="redeflacue",
|
|
18
|
+
description="Split CUE + FLAC images without assuming a 44.1 kHz sample rate.",
|
|
19
|
+
)
|
|
20
|
+
ap.add_argument("source", type=Path, help="Directory containing .cue files")
|
|
21
|
+
ap.add_argument(
|
|
22
|
+
"-d",
|
|
23
|
+
"--dest",
|
|
24
|
+
type=Path,
|
|
25
|
+
default=None,
|
|
26
|
+
help="Output root (default: ./deflacue next to each cue)",
|
|
27
|
+
)
|
|
28
|
+
ap.add_argument(
|
|
29
|
+
"-e", "--encoding", default=None, help="Encoding of the .cue file (e.g. windows-1251)"
|
|
30
|
+
)
|
|
31
|
+
ap.add_argument("-r", "--recursive", action="store_true", help="Recurse into subdirectories")
|
|
32
|
+
ap.add_argument("--dry-run", action="store_true", help="Plan tracks without running SoX")
|
|
33
|
+
ap.add_argument("-v", "--verbose", action="store_true", help="Show each SoX command")
|
|
34
|
+
ap.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
35
|
+
return ap
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
39
|
+
args = build_parser().parse_args(argv)
|
|
40
|
+
|
|
41
|
+
logging.basicConfig(
|
|
42
|
+
level=logging.DEBUG if args.verbose else logging.INFO,
|
|
43
|
+
format="%(levelname)s: %(message)s",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if shutil.which("sox") is None:
|
|
47
|
+
LOGGER.error("sox not found. Install: sudo apt install sox libsox-fmt-all")
|
|
48
|
+
return 1
|
|
49
|
+
|
|
50
|
+
src_root = args.source.resolve()
|
|
51
|
+
if not src_root.exists():
|
|
52
|
+
LOGGER.error("Path %s does not exist", src_root)
|
|
53
|
+
return 1
|
|
54
|
+
|
|
55
|
+
for cue in iter_cue_files(src_root, args.recursive):
|
|
56
|
+
target_root = (
|
|
57
|
+
args.dest.resolve() / cue.parent.name if args.dest else cue.parent / "deflacue"
|
|
58
|
+
)
|
|
59
|
+
if not args.dry_run:
|
|
60
|
+
target_root.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
process_cue(cue, target_root, args.encoding, args.dry_run)
|
|
62
|
+
|
|
63
|
+
LOGGER.info("Done.")
|
|
64
|
+
return 0
|