music-disc-maker 1.0.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.
- music_disc_maker-1.0.0/LICENSE +21 -0
- music_disc_maker-1.0.0/PKG-INFO +178 -0
- music_disc_maker-1.0.0/README.md +159 -0
- music_disc_maker-1.0.0/music_disc_maker/__init__.py +12 -0
- music_disc_maker-1.0.0/music_disc_maker/__main__.py +18 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer1_outer.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer2_surface.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer2_surface_alt.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer3_accent.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer3_accent_alt.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer4_edge.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer4_edge_alt.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer5_inner_a.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer5_inner_a_alt.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer6_inner_b.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer6_inner_b_alt.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/assets/disc_layers/layer7_center.png +0 -0
- music_disc_maker-1.0.0/music_disc_maker/audio.py +43 -0
- music_disc_maker-1.0.0/music_disc_maker/classes.py +10 -0
- music_disc_maker-1.0.0/music_disc_maker/config.py +3 -0
- music_disc_maker-1.0.0/music_disc_maker/config_generator.py +537 -0
- music_disc_maker-1.0.0/music_disc_maker/defaults.py +24 -0
- music_disc_maker-1.0.0/music_disc_maker/disc_icons.py +289 -0
- music_disc_maker-1.0.0/music_disc_maker/generate_js.py +680 -0
- music_disc_maker-1.0.0/music_disc_maker/images.py +60 -0
- music_disc_maker-1.0.0/music_disc_maker/io_utils.py +65 -0
- music_disc_maker-1.0.0/music_disc_maker/loader.py +237 -0
- music_disc_maker-1.0.0/music_disc_maker/loot.py +222 -0
- music_disc_maker-1.0.0/music_disc_maker/manifests.py +93 -0
- music_disc_maker-1.0.0/music_disc_maker/models.py +70 -0
- music_disc_maker-1.0.0/music_disc_maker/pack_builder.py +256 -0
- music_disc_maker-1.0.0/music_disc_maker/parser.py +48 -0
- music_disc_maker-1.0.0/music_disc_maker/validation.py +78 -0
- music_disc_maker-1.0.0/pyproject.toml +34 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DJ Woodward-Magar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: music-disc-maker
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Build scripted Minecraft Bedrock custom music disc add-ons.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: DJ Stomp
|
|
7
|
+
Author-email: 85457381+DJStompZone@users.noreply.github.com
|
|
8
|
+
Requires-Python: >=3.12,<4.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pillow (>=12.2.0,<13.0.0)
|
|
15
|
+
Requires-Dist: pytest (>=8.0.0) ; extra == "dev"
|
|
16
|
+
Requires-Dist: tqdm (>=4.66.0,<5.0.0)
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# music-disc-maker
|
|
20
|
+
|
|
21
|
+
Build scripted Minecraft Bedrock custom music disc add-ons without globally replacing vanilla sound effects.
|
|
22
|
+
|
|
23
|
+
## Single-disc mode
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
music-disc-maker song.mp3 --id whiplash --title "Whiplash!"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The CLI also works as a module:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python -m music_disc_maker song.mp3 --id whiplash --title "Whiplash!"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Config mode
|
|
36
|
+
|
|
37
|
+
`music_disc_maker.toml`:
|
|
38
|
+
|
|
39
|
+
```toml
|
|
40
|
+
pack_id = "custom_discs"
|
|
41
|
+
pack_title = "Custom Music Discs"
|
|
42
|
+
namespace = "custom"
|
|
43
|
+
output_root = "dist"
|
|
44
|
+
dummy_sound_event = "pre_ram.screamer"
|
|
45
|
+
server_module_version = "2.8.0-beta"
|
|
46
|
+
item_format_version = "1.21.70"
|
|
47
|
+
sound_definitions_format_version = "1.20.20"
|
|
48
|
+
min_engine_version = [1, 21, 70]
|
|
49
|
+
default_comparator_signal = 13
|
|
50
|
+
comparator_signal_min = 1
|
|
51
|
+
comparator_signal_max = 13
|
|
52
|
+
pack_icon_size = 256
|
|
53
|
+
loot_enabled = true
|
|
54
|
+
# Optional: point at a custom layer directory or zip matching the bundled layer names.
|
|
55
|
+
# icon_layer_source = "disc_layers"
|
|
56
|
+
|
|
57
|
+
[[discs]]
|
|
58
|
+
input = "audio/whiplash.mp3"
|
|
59
|
+
id = "whiplash"
|
|
60
|
+
title = "Whiplash!"
|
|
61
|
+
comparator_signal = 13
|
|
62
|
+
|
|
63
|
+
[[discs]]
|
|
64
|
+
input = "audio/doom_banjo.mp3"
|
|
65
|
+
id = "doom_banjo"
|
|
66
|
+
title = "Doom Banjo"
|
|
67
|
+
sound_id = "record.doom_banjo"
|
|
68
|
+
comparator_signal = 11
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Run it:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
music-disc-maker --config music_disc_maker.toml
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
If `--config` is omitted, the tool checks the current directory for common config names such as `music_disc_maker.toml`, `music-disc-maker.toml`, `.music-disc-maker.toml`, JSON equivalents, `discs.json`, and `[tool.music-disc-maker]` / `[tool.music_disc_maker]` in `pyproject.toml`.
|
|
78
|
+
|
|
79
|
+
CLI arguments override config-file values where applicable. Config-file paths are resolved relative to the config file, not wherever your shell happens to be yelling from.
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Procedural icons
|
|
83
|
+
|
|
84
|
+
Generated item icons now use bundled layered 16x16 disc assets instead of the old one-color fallback puck. The generator derives a deterministic icon from the disc namespace, ID, and title, so rebuilding the same pack keeps the same icon.
|
|
85
|
+
|
|
86
|
+
To use your own layer set, provide either a directory or zip containing files named like:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
layer1_outer.png
|
|
90
|
+
layer2_surface.png
|
|
91
|
+
layer2_surface_alt.png
|
|
92
|
+
layer3_accent.png
|
|
93
|
+
layer3_accent_alt.png
|
|
94
|
+
layer4_edge.png
|
|
95
|
+
layer4_edge_alt.png
|
|
96
|
+
layer5_inner_a.png
|
|
97
|
+
layer5_inner_a_alt.png
|
|
98
|
+
layer6_inner_b.png
|
|
99
|
+
layer6_inner_b_alt.png
|
|
100
|
+
layer7_center.png
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then set it in config:
|
|
104
|
+
|
|
105
|
+
```toml
|
|
106
|
+
icon_layer_source = "assets/disc_layers.zip"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Or pass it on the CLI:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
music-disc-maker --config music_disc_maker.toml --icon-layer-source assets/disc_layers.zip
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Loot tables
|
|
116
|
+
|
|
117
|
+
By default, the behavior pack writes chest loot tables for:
|
|
118
|
+
|
|
119
|
+
- `chests/simple_dungeon`
|
|
120
|
+
- `chests/abandoned_mineshaft`
|
|
121
|
+
- `chests/stronghold_corridor`
|
|
122
|
+
- `chests/ancient_city`
|
|
123
|
+
|
|
124
|
+
The generated tables preserve the bundled vanilla entries for those four targets and append a small extra custom-disc pool. Disable this with:
|
|
125
|
+
|
|
126
|
+
```toml
|
|
127
|
+
loot_enabled = false
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Or from the CLI:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
music-disc-maker --config music_disc_maker.toml --disable-loot
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
## Generate a config from audio files
|
|
138
|
+
|
|
139
|
+
Scan the current directory and create `music_disc_maker.toml`:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
music-disc-maker-config
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The config generator uses `ffprobe` metadata when available. The generated disc `id` comes from the normalized song title, while the display `title` becomes `Artist - Title` when artist metadata is present. If metadata is missing, filenames like `01 - DJ Stomp - Whiplash! (Official Audio).mp3` are parsed as a fallback, because manually typing that crap is how souls leave bodies.
|
|
146
|
+
|
|
147
|
+
The metadata scan shows a `tqdm` progress bar by default. Disable it with `--no-progress` when piping logs or being aggressively boring.
|
|
148
|
+
|
|
149
|
+
Useful options:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
music-disc-maker-config --recursive --overwrite --pack-title "DJ Stomp Discs" --namespace dj
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Preview without writing:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
music-disc-maker-config --stdout
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
If your files use `Title - Artist.ext` instead of `Artist - Title.ext`:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
music-disc-maker-config --filename-order title-artist --overwrite
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For a big messy library, use MusicBrainz Picard or beets to fix the tags first, then regenerate the TOML. This tool reads metadata; it does not rewrite your files.
|
|
168
|
+
|
|
169
|
+
## Testing
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
python -m pytest
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT License. See the [LICENSE](LICENSE) file for details.
|
|
178
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# music-disc-maker
|
|
2
|
+
|
|
3
|
+
Build scripted Minecraft Bedrock custom music disc add-ons without globally replacing vanilla sound effects.
|
|
4
|
+
|
|
5
|
+
## Single-disc mode
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
music-disc-maker song.mp3 --id whiplash --title "Whiplash!"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The CLI also works as a module:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python -m music_disc_maker song.mp3 --id whiplash --title "Whiplash!"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Config mode
|
|
18
|
+
|
|
19
|
+
`music_disc_maker.toml`:
|
|
20
|
+
|
|
21
|
+
```toml
|
|
22
|
+
pack_id = "custom_discs"
|
|
23
|
+
pack_title = "Custom Music Discs"
|
|
24
|
+
namespace = "custom"
|
|
25
|
+
output_root = "dist"
|
|
26
|
+
dummy_sound_event = "pre_ram.screamer"
|
|
27
|
+
server_module_version = "2.8.0-beta"
|
|
28
|
+
item_format_version = "1.21.70"
|
|
29
|
+
sound_definitions_format_version = "1.20.20"
|
|
30
|
+
min_engine_version = [1, 21, 70]
|
|
31
|
+
default_comparator_signal = 13
|
|
32
|
+
comparator_signal_min = 1
|
|
33
|
+
comparator_signal_max = 13
|
|
34
|
+
pack_icon_size = 256
|
|
35
|
+
loot_enabled = true
|
|
36
|
+
# Optional: point at a custom layer directory or zip matching the bundled layer names.
|
|
37
|
+
# icon_layer_source = "disc_layers"
|
|
38
|
+
|
|
39
|
+
[[discs]]
|
|
40
|
+
input = "audio/whiplash.mp3"
|
|
41
|
+
id = "whiplash"
|
|
42
|
+
title = "Whiplash!"
|
|
43
|
+
comparator_signal = 13
|
|
44
|
+
|
|
45
|
+
[[discs]]
|
|
46
|
+
input = "audio/doom_banjo.mp3"
|
|
47
|
+
id = "doom_banjo"
|
|
48
|
+
title = "Doom Banjo"
|
|
49
|
+
sound_id = "record.doom_banjo"
|
|
50
|
+
comparator_signal = 11
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Run it:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
music-disc-maker --config music_disc_maker.toml
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If `--config` is omitted, the tool checks the current directory for common config names such as `music_disc_maker.toml`, `music-disc-maker.toml`, `.music-disc-maker.toml`, JSON equivalents, `discs.json`, and `[tool.music-disc-maker]` / `[tool.music_disc_maker]` in `pyproject.toml`.
|
|
60
|
+
|
|
61
|
+
CLI arguments override config-file values where applicable. Config-file paths are resolved relative to the config file, not wherever your shell happens to be yelling from.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## Procedural icons
|
|
65
|
+
|
|
66
|
+
Generated item icons now use bundled layered 16x16 disc assets instead of the old one-color fallback puck. The generator derives a deterministic icon from the disc namespace, ID, and title, so rebuilding the same pack keeps the same icon.
|
|
67
|
+
|
|
68
|
+
To use your own layer set, provide either a directory or zip containing files named like:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
layer1_outer.png
|
|
72
|
+
layer2_surface.png
|
|
73
|
+
layer2_surface_alt.png
|
|
74
|
+
layer3_accent.png
|
|
75
|
+
layer3_accent_alt.png
|
|
76
|
+
layer4_edge.png
|
|
77
|
+
layer4_edge_alt.png
|
|
78
|
+
layer5_inner_a.png
|
|
79
|
+
layer5_inner_a_alt.png
|
|
80
|
+
layer6_inner_b.png
|
|
81
|
+
layer6_inner_b_alt.png
|
|
82
|
+
layer7_center.png
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Then set it in config:
|
|
86
|
+
|
|
87
|
+
```toml
|
|
88
|
+
icon_layer_source = "assets/disc_layers.zip"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Or pass it on the CLI:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
music-disc-maker --config music_disc_maker.toml --icon-layer-source assets/disc_layers.zip
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Loot tables
|
|
98
|
+
|
|
99
|
+
By default, the behavior pack writes chest loot tables for:
|
|
100
|
+
|
|
101
|
+
- `chests/simple_dungeon`
|
|
102
|
+
- `chests/abandoned_mineshaft`
|
|
103
|
+
- `chests/stronghold_corridor`
|
|
104
|
+
- `chests/ancient_city`
|
|
105
|
+
|
|
106
|
+
The generated tables preserve the bundled vanilla entries for those four targets and append a small extra custom-disc pool. Disable this with:
|
|
107
|
+
|
|
108
|
+
```toml
|
|
109
|
+
loot_enabled = false
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Or from the CLI:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
music-disc-maker --config music_disc_maker.toml --disable-loot
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## Generate a config from audio files
|
|
120
|
+
|
|
121
|
+
Scan the current directory and create `music_disc_maker.toml`:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
music-disc-maker-config
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The config generator uses `ffprobe` metadata when available. The generated disc `id` comes from the normalized song title, while the display `title` becomes `Artist - Title` when artist metadata is present. If metadata is missing, filenames like `01 - DJ Stomp - Whiplash! (Official Audio).mp3` are parsed as a fallback, because manually typing that crap is how souls leave bodies.
|
|
128
|
+
|
|
129
|
+
The metadata scan shows a `tqdm` progress bar by default. Disable it with `--no-progress` when piping logs or being aggressively boring.
|
|
130
|
+
|
|
131
|
+
Useful options:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
music-disc-maker-config --recursive --overwrite --pack-title "DJ Stomp Discs" --namespace dj
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Preview without writing:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
music-disc-maker-config --stdout
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
If your files use `Title - Artist.ext` instead of `Artist - Title.ext`:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
music-disc-maker-config --filename-order title-artist --overwrite
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
For a big messy library, use MusicBrainz Picard or beets to fix the tags first, then regenerate the TOML. This tool reads metadata; it does not rewrite your files.
|
|
150
|
+
|
|
151
|
+
## Testing
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
python -m pytest
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT License. See the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from music_disc_maker.models import BuildConfig, BuiltDisc, DiscInput, PackPaths
|
|
4
|
+
from music_disc_maker.pack_builder import ScriptedDiscPackBuilder
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"BuildConfig",
|
|
8
|
+
"BuiltDisc",
|
|
9
|
+
"DiscInput",
|
|
10
|
+
"PackPaths",
|
|
11
|
+
"ScriptedDiscPackBuilder",
|
|
12
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from music_disc_maker.loader import load_build_config
|
|
4
|
+
from music_disc_maker.pack_builder import ScriptedDiscPackBuilder
|
|
5
|
+
from music_disc_maker.parser import parse_args
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> int:
|
|
9
|
+
"""Run the pack generator."""
|
|
10
|
+
args = parse_args()
|
|
11
|
+
config = load_build_config(args)
|
|
12
|
+
builder = ScriptedDiscPackBuilder(config)
|
|
13
|
+
builder.build()
|
|
14
|
+
return 0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from music_disc_maker.io_utils import run_command
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def convert_audio(input_file: Path, output_file: Path) -> float:
|
|
9
|
+
"""Convert an input audio file to a mono streaming OGG and return duration in seconds."""
|
|
10
|
+
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
11
|
+
|
|
12
|
+
run_command([
|
|
13
|
+
"ffmpeg",
|
|
14
|
+
"-y",
|
|
15
|
+
"-i",
|
|
16
|
+
str(input_file),
|
|
17
|
+
"-vn",
|
|
18
|
+
"-ac",
|
|
19
|
+
"1",
|
|
20
|
+
"-ar",
|
|
21
|
+
"44100",
|
|
22
|
+
"-c:a",
|
|
23
|
+
"libvorbis",
|
|
24
|
+
"-q:a",
|
|
25
|
+
"4",
|
|
26
|
+
str(output_file),
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
probe = run_command([
|
|
30
|
+
"ffprobe",
|
|
31
|
+
"-v",
|
|
32
|
+
"error",
|
|
33
|
+
"-show_entries",
|
|
34
|
+
"format=duration",
|
|
35
|
+
"-of",
|
|
36
|
+
"default=noprint_wrappers=1:nokey=1",
|
|
37
|
+
str(output_file),
|
|
38
|
+
])
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
return round(float(probe.stdout.strip()), 3)
|
|
42
|
+
except ValueError as exc:
|
|
43
|
+
raise RuntimeError(f"Unable to read duration from ffprobe output: {probe.stdout!r}") from exc
|