veloura-audio 0.3.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.
- veloura_audio-0.3.0/MANIFEST.in +4 -0
- veloura_audio-0.3.0/PKG-INFO +188 -0
- veloura_audio-0.3.0/README.md +159 -0
- veloura_audio-0.3.0/docs/release_checklist.md +53 -0
- veloura_audio-0.3.0/docs/veloura_scope.md +86 -0
- veloura_audio-0.3.0/examples/streamer_player.py +145 -0
- veloura_audio-0.3.0/pyproject.toml +57 -0
- veloura_audio-0.3.0/setup.cfg +4 -0
- veloura_audio-0.3.0/tests/test_beat_aware_experiment.py +93 -0
- veloura_audio-0.3.0/tests/test_veloura_audio.py +266 -0
- veloura_audio-0.3.0/tests/test_veloura_cache.py +119 -0
- veloura_audio-0.3.0/tests/test_veloura_pcm.py +48 -0
- veloura_audio-0.3.0/tests/test_veloura_public_api.py +87 -0
- veloura_audio-0.3.0/veloura/__init__.py +5 -0
- veloura_audio-0.3.0/veloura/__main__.py +6 -0
- veloura_audio-0.3.0/veloura/audio/__init__.py +99 -0
- veloura_audio-0.3.0/veloura/audio/beat.py +485 -0
- veloura_audio-0.3.0/veloura/audio/cache.py +130 -0
- veloura_audio-0.3.0/veloura/audio/constants.py +23 -0
- veloura_audio-0.3.0/veloura/audio/crossfade.py +287 -0
- veloura_audio-0.3.0/veloura/audio/ffmpeg_stream.py +123 -0
- veloura_audio-0.3.0/veloura/audio/models.py +83 -0
- veloura_audio-0.3.0/veloura/audio/pcm.py +83 -0
- veloura_audio-0.3.0/veloura/audio/presets.py +75 -0
- veloura_audio-0.3.0/veloura/audio/resolver.py +70 -0
- veloura_audio-0.3.0/veloura/audio/session.py +139 -0
- veloura_audio-0.3.0/veloura/audio/transition.py +376 -0
- veloura_audio-0.3.0/veloura/cli.py +192 -0
- veloura_audio-0.3.0/veloura/py.typed +1 -0
- veloura_audio-0.3.0/veloura_audio.egg-info/PKG-INFO +188 -0
- veloura_audio-0.3.0/veloura_audio.egg-info/SOURCES.txt +33 -0
- veloura_audio-0.3.0/veloura_audio.egg-info/dependency_links.txt +1 -0
- veloura_audio-0.3.0/veloura_audio.egg-info/entry_points.txt +2 -0
- veloura_audio-0.3.0/veloura_audio.egg-info/requires.txt +12 -0
- veloura_audio-0.3.0/veloura_audio.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: veloura-audio
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Reusable Python audio engine with crossfade and beat-aware transition planning.
|
|
5
|
+
Project-URL: Homepage, https://github.com/VelouraAudio/veloura-audio
|
|
6
|
+
Project-URL: Repository, https://github.com/VelouraAudio/veloura-audio
|
|
7
|
+
Project-URL: Issues, https://github.com/VelouraAudio/veloura-audio/issues
|
|
8
|
+
Keywords: audio,crossfade,ffmpeg,music,streaming
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
16
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Mixers
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Provides-Extra: stream
|
|
21
|
+
Requires-Dist: yt-dlp>=2026.3.17; extra == "stream"
|
|
22
|
+
Provides-Extra: discord
|
|
23
|
+
Requires-Dist: discord.py>=2.3.0; extra == "discord"
|
|
24
|
+
Requires-Dist: PyNaCl>=1.5.0; extra == "discord"
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: discord.py>=2.3.0; extra == "all"
|
|
27
|
+
Requires-Dist: PyNaCl>=1.5.0; extra == "all"
|
|
28
|
+
Requires-Dist: yt-dlp>=2026.3.17; extra == "all"
|
|
29
|
+
|
|
30
|
+
# Veloura
|
|
31
|
+
|
|
32
|
+
Veloura is a reusable Python audio transition engine for smooth queue playback.
|
|
33
|
+
It provides FFmpeg-backed PCM decoding, equal-power crossfades, transition
|
|
34
|
+
analysis, beat-aware planning, and a small CLI for local inspection.
|
|
35
|
+
|
|
36
|
+
Veloura is framework-agnostic. Use it in streamer tools, radio pipelines,
|
|
37
|
+
desktop music apps, Discord/Twitch bots, or backend automation without tying
|
|
38
|
+
your project to one bot implementation.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- Equal-power crossfade mixing for signed 16-bit PCM audio
|
|
43
|
+
- Queue/session helpers for buffering, skip, and current-track snapshots
|
|
44
|
+
- Smart transition planning based on track duration, silence trim, and loudness
|
|
45
|
+
- Beat/BPM analysis with beat-aware transition plans
|
|
46
|
+
- Project-local or user-cache transition analysis storage
|
|
47
|
+
- Optional `yt-dlp` stream resolution for URLs and search queries
|
|
48
|
+
- Optional Discord audio source compatibility
|
|
49
|
+
- Pure-Python PCM fallback for Python builds without `audioop`
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- Python 3.12 or newer
|
|
54
|
+
- FFmpeg for decoding, playback, silence analysis, loudness analysis, and beat
|
|
55
|
+
analysis
|
|
56
|
+
- `yt-dlp` only when resolving online stream/search inputs
|
|
57
|
+
- `discord.py` and `PyNaCl` only when using the Discord audio source directly
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
Install the core package:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install veloura-audio
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Install stream resolution support:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install "veloura-audio[stream]"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Install Discord voice support:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install "veloura-audio[discord]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Install every optional integration:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install "veloura-audio[all]"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Quick Start
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from veloura.audio import AudioTrack, CrossfadeSession, transition_preset
|
|
89
|
+
|
|
90
|
+
config = transition_preset("streamer")
|
|
91
|
+
|
|
92
|
+
track = AudioTrack.from_source(
|
|
93
|
+
"/music/current-song.flac",
|
|
94
|
+
title="Artist - Current Song",
|
|
95
|
+
duration=184,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
session = CrossfadeSession(volume=0.65, crossfade_seconds=config.base_crossfade_seconds)
|
|
99
|
+
session.source.enqueue(track)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
For online sources, install `veloura-audio[stream]` and resolve a playable stream:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
import asyncio
|
|
106
|
+
|
|
107
|
+
from veloura.audio import resolve_stream_track, transition_preset
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def main():
|
|
111
|
+
track = await resolve_stream_track(
|
|
112
|
+
"artist song official audio",
|
|
113
|
+
transition_config=transition_preset("streamer"),
|
|
114
|
+
)
|
|
115
|
+
print(track.title, track.stream_url)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
asyncio.run(main())
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## CLI
|
|
122
|
+
|
|
123
|
+
Veloura exposes the same core tools through `python -m veloura` or the
|
|
124
|
+
`veloura` console script:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
python -m veloura presets
|
|
128
|
+
python -m veloura prepare ./song.mp3 --preset streamer
|
|
129
|
+
python -m veloura analyze ./song.mp3
|
|
130
|
+
python -m veloura plan ./current.mp3 ./next.mp3 --preset broadcast
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
For YouTube/search inputs, install `veloura-audio[stream]` and add `--resolve`:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
python -m veloura plan "current song" "next song" --preset streamer --resolve
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Transition analysis is cached under `~/.cache/veloura` by default, or under the
|
|
140
|
+
directory set in `VELOURA_CACHE_DIR`. Use `--cache-dir` for a project-local
|
|
141
|
+
cache, or `--no-cache` when comparing fresh analysis.
|
|
142
|
+
|
|
143
|
+
## Presets
|
|
144
|
+
|
|
145
|
+
- `streamer`: balanced transitions for livestream/background music
|
|
146
|
+
- `broadcast`: longer, smoother radio-style blends
|
|
147
|
+
- `low-latency`: shorter analysis windows for weaker machines or fast queues
|
|
148
|
+
|
|
149
|
+
Aliases such as `streamer-safe`, `broadcast-smooth`, and `fast` are also
|
|
150
|
+
available.
|
|
151
|
+
|
|
152
|
+
## Standalone Example
|
|
153
|
+
|
|
154
|
+
The example player resolves local files or stream queries, prepares transition
|
|
155
|
+
analysis, mixes the queue, and pipes PCM into `ffplay`:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
python examples/streamer_player.py ./song-a.mp3 ./song-b.mp3 --preset streamer
|
|
159
|
+
python examples/streamer_player.py ./song-a.mp3 ./song-b.mp3 --cache-dir ./veloura-cache
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Public API
|
|
163
|
+
|
|
164
|
+
Prefer these names for new projects:
|
|
165
|
+
|
|
166
|
+
- `AudioTrack`
|
|
167
|
+
- `CrossfadeSession`
|
|
168
|
+
- `FileAnalysisCache`
|
|
169
|
+
- `transition_preset`
|
|
170
|
+
- `prepare_smart_transition`
|
|
171
|
+
- `plan_beat_transition`
|
|
172
|
+
- `resolve_stream_track`
|
|
173
|
+
|
|
174
|
+
Compatibility names:
|
|
175
|
+
|
|
176
|
+
- `MixerTrack`
|
|
177
|
+
- `CrossfadeAudioSource`
|
|
178
|
+
|
|
179
|
+
## Development Checks
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
python3 -m unittest discover -s tests
|
|
183
|
+
python3 -m py_compile veloura/*.py veloura/audio/*.py examples/*.py
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Before publishing publicly, confirm the package name on PyPI, add project URLs,
|
|
187
|
+
choose the license/copyright owner, build the distribution, and publish to
|
|
188
|
+
TestPyPI first.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Veloura
|
|
2
|
+
|
|
3
|
+
Veloura is a reusable Python audio transition engine for smooth queue playback.
|
|
4
|
+
It provides FFmpeg-backed PCM decoding, equal-power crossfades, transition
|
|
5
|
+
analysis, beat-aware planning, and a small CLI for local inspection.
|
|
6
|
+
|
|
7
|
+
Veloura is framework-agnostic. Use it in streamer tools, radio pipelines,
|
|
8
|
+
desktop music apps, Discord/Twitch bots, or backend automation without tying
|
|
9
|
+
your project to one bot implementation.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Equal-power crossfade mixing for signed 16-bit PCM audio
|
|
14
|
+
- Queue/session helpers for buffering, skip, and current-track snapshots
|
|
15
|
+
- Smart transition planning based on track duration, silence trim, and loudness
|
|
16
|
+
- Beat/BPM analysis with beat-aware transition plans
|
|
17
|
+
- Project-local or user-cache transition analysis storage
|
|
18
|
+
- Optional `yt-dlp` stream resolution for URLs and search queries
|
|
19
|
+
- Optional Discord audio source compatibility
|
|
20
|
+
- Pure-Python PCM fallback for Python builds without `audioop`
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Python 3.12 or newer
|
|
25
|
+
- FFmpeg for decoding, playback, silence analysis, loudness analysis, and beat
|
|
26
|
+
analysis
|
|
27
|
+
- `yt-dlp` only when resolving online stream/search inputs
|
|
28
|
+
- `discord.py` and `PyNaCl` only when using the Discord audio source directly
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
Install the core package:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install veloura-audio
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install stream resolution support:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install "veloura-audio[stream]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Install Discord voice support:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install "veloura-audio[discord]"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Install every optional integration:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install "veloura-audio[all]"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from veloura.audio import AudioTrack, CrossfadeSession, transition_preset
|
|
60
|
+
|
|
61
|
+
config = transition_preset("streamer")
|
|
62
|
+
|
|
63
|
+
track = AudioTrack.from_source(
|
|
64
|
+
"/music/current-song.flac",
|
|
65
|
+
title="Artist - Current Song",
|
|
66
|
+
duration=184,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
session = CrossfadeSession(volume=0.65, crossfade_seconds=config.base_crossfade_seconds)
|
|
70
|
+
session.source.enqueue(track)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For online sources, install `veloura-audio[stream]` and resolve a playable stream:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import asyncio
|
|
77
|
+
|
|
78
|
+
from veloura.audio import resolve_stream_track, transition_preset
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def main():
|
|
82
|
+
track = await resolve_stream_track(
|
|
83
|
+
"artist song official audio",
|
|
84
|
+
transition_config=transition_preset("streamer"),
|
|
85
|
+
)
|
|
86
|
+
print(track.title, track.stream_url)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
asyncio.run(main())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## CLI
|
|
93
|
+
|
|
94
|
+
Veloura exposes the same core tools through `python -m veloura` or the
|
|
95
|
+
`veloura` console script:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
python -m veloura presets
|
|
99
|
+
python -m veloura prepare ./song.mp3 --preset streamer
|
|
100
|
+
python -m veloura analyze ./song.mp3
|
|
101
|
+
python -m veloura plan ./current.mp3 ./next.mp3 --preset broadcast
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For YouTube/search inputs, install `veloura-audio[stream]` and add `--resolve`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python -m veloura plan "current song" "next song" --preset streamer --resolve
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Transition analysis is cached under `~/.cache/veloura` by default, or under the
|
|
111
|
+
directory set in `VELOURA_CACHE_DIR`. Use `--cache-dir` for a project-local
|
|
112
|
+
cache, or `--no-cache` when comparing fresh analysis.
|
|
113
|
+
|
|
114
|
+
## Presets
|
|
115
|
+
|
|
116
|
+
- `streamer`: balanced transitions for livestream/background music
|
|
117
|
+
- `broadcast`: longer, smoother radio-style blends
|
|
118
|
+
- `low-latency`: shorter analysis windows for weaker machines or fast queues
|
|
119
|
+
|
|
120
|
+
Aliases such as `streamer-safe`, `broadcast-smooth`, and `fast` are also
|
|
121
|
+
available.
|
|
122
|
+
|
|
123
|
+
## Standalone Example
|
|
124
|
+
|
|
125
|
+
The example player resolves local files or stream queries, prepares transition
|
|
126
|
+
analysis, mixes the queue, and pipes PCM into `ffplay`:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
python examples/streamer_player.py ./song-a.mp3 ./song-b.mp3 --preset streamer
|
|
130
|
+
python examples/streamer_player.py ./song-a.mp3 ./song-b.mp3 --cache-dir ./veloura-cache
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Public API
|
|
134
|
+
|
|
135
|
+
Prefer these names for new projects:
|
|
136
|
+
|
|
137
|
+
- `AudioTrack`
|
|
138
|
+
- `CrossfadeSession`
|
|
139
|
+
- `FileAnalysisCache`
|
|
140
|
+
- `transition_preset`
|
|
141
|
+
- `prepare_smart_transition`
|
|
142
|
+
- `plan_beat_transition`
|
|
143
|
+
- `resolve_stream_track`
|
|
144
|
+
|
|
145
|
+
Compatibility names:
|
|
146
|
+
|
|
147
|
+
- `MixerTrack`
|
|
148
|
+
- `CrossfadeAudioSource`
|
|
149
|
+
|
|
150
|
+
## Development Checks
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
python3 -m unittest discover -s tests
|
|
154
|
+
python3 -m py_compile veloura/*.py veloura/audio/*.py examples/*.py
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Before publishing publicly, confirm the package name on PyPI, add project URLs,
|
|
158
|
+
choose the license/copyright owner, build the distribution, and publish to
|
|
159
|
+
TestPyPI first.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Veloura Public Release Checklist
|
|
2
|
+
|
|
3
|
+
Use this checklist before publishing Veloura as a public/global Python package.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
|
|
7
|
+
- Confirm the public package name on PyPI: `veloura-audio`
|
|
8
|
+
- Choose the legal license and add a `LICENSE` file
|
|
9
|
+
- Decide the copyright owner or contributor line
|
|
10
|
+
- Add official project URLs to `pyproject.toml`:
|
|
11
|
+
- Homepage
|
|
12
|
+
- Source repository
|
|
13
|
+
- Issue tracker
|
|
14
|
+
- Documentation
|
|
15
|
+
|
|
16
|
+
## Package Metadata
|
|
17
|
+
|
|
18
|
+
- Keep `veloura.__version__` and `pyproject.toml` version in sync
|
|
19
|
+
- Confirm the Python version floor: currently `>=3.12`
|
|
20
|
+
- Confirm optional extras:
|
|
21
|
+
- `stream` for `yt-dlp`
|
|
22
|
+
- `discord` for Discord voice support
|
|
23
|
+
- `all` for every optional integration
|
|
24
|
+
- Recheck classifiers after choosing a license
|
|
25
|
+
|
|
26
|
+
## Local Verification
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python3 -m unittest discover -s tests
|
|
30
|
+
python3 -m py_compile veloura/*.py veloura/audio/*.py examples/*.py
|
|
31
|
+
python3 -m build
|
|
32
|
+
python3 -m twine check dist/*
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Publishing Flow
|
|
36
|
+
|
|
37
|
+
1. Build from a clean source tree.
|
|
38
|
+
2. Upload to TestPyPI first.
|
|
39
|
+
3. Install from TestPyPI in a fresh virtual environment.
|
|
40
|
+
4. Run `python -m veloura presets`.
|
|
41
|
+
5. Test `prepare`, `analyze`, and the streamer example with local files.
|
|
42
|
+
6. Test stream resolution with `pip install "veloura-audio[stream]"`.
|
|
43
|
+
7. Publish the same checked artifacts to PyPI.
|
|
44
|
+
|
|
45
|
+
## Release Notes
|
|
46
|
+
|
|
47
|
+
For each public release, include:
|
|
48
|
+
|
|
49
|
+
- New features
|
|
50
|
+
- Compatibility notes
|
|
51
|
+
- Dependency or Python-version changes
|
|
52
|
+
- Known limitations
|
|
53
|
+
- Migration notes for renamed or deprecated public APIs
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Veloura Scope
|
|
2
|
+
|
|
3
|
+
Veloura is a reusable Python audio transition engine. The Discord bot is one
|
|
4
|
+
consumer of the package, not the package's identity.
|
|
5
|
+
|
|
6
|
+
## Target Use Cases
|
|
7
|
+
|
|
8
|
+
- Streamer background music engines
|
|
9
|
+
- 24/7 radio or livestream music pipelines
|
|
10
|
+
- Discord, Twitch, or web music bots
|
|
11
|
+
- Desktop music tools that need smooth queue playback
|
|
12
|
+
- Audio automation scripts that need predictable fades and gain control
|
|
13
|
+
|
|
14
|
+
## Core Package Owns
|
|
15
|
+
|
|
16
|
+
- Audio track models
|
|
17
|
+
- PCM helpers
|
|
18
|
+
- FFmpeg-backed PCM decoding
|
|
19
|
+
- Equal-power crossfade mixing
|
|
20
|
+
- Queue/session buffering
|
|
21
|
+
- Silence trim analysis
|
|
22
|
+
- Loudness normalization
|
|
23
|
+
- Beat/BPM analysis
|
|
24
|
+
- Beat-aware transition planning
|
|
25
|
+
- Stream URL resolution helpers
|
|
26
|
+
- Presets for streamer, broadcast, and low-latency modes
|
|
27
|
+
- File-based transition analysis cache
|
|
28
|
+
- CLI tools for local analysis and transition planning
|
|
29
|
+
|
|
30
|
+
## Core Package Does Not Own
|
|
31
|
+
|
|
32
|
+
- Discord buttons, views, embeds, slash commands, or permissions
|
|
33
|
+
- Lavalink nodes
|
|
34
|
+
- Guild/server state
|
|
35
|
+
- User playlists and saved songs
|
|
36
|
+
- Spotify, Apple Music, Deezer, or Tidal scraping
|
|
37
|
+
- Bot banners and presence
|
|
38
|
+
- Database schemas for a specific app
|
|
39
|
+
|
|
40
|
+
## Public API Direction
|
|
41
|
+
|
|
42
|
+
Prefer these public names for new projects:
|
|
43
|
+
|
|
44
|
+
- `AudioTrack`
|
|
45
|
+
- `CrossfadeSession`
|
|
46
|
+
- `FileAnalysisCache`
|
|
47
|
+
- `transition_preset`
|
|
48
|
+
- `prepare_smart_transition`
|
|
49
|
+
- `plan_beat_transition`
|
|
50
|
+
- `resolve_stream_track`
|
|
51
|
+
|
|
52
|
+
Keep these names for compatibility:
|
|
53
|
+
|
|
54
|
+
- `MixerTrack`
|
|
55
|
+
- `CrossfadeAudioSource`
|
|
56
|
+
|
|
57
|
+
## Release Milestones
|
|
58
|
+
|
|
59
|
+
### 0.3
|
|
60
|
+
|
|
61
|
+
- Generic `AudioTrack` public model
|
|
62
|
+
- Streamer/broadcast/low-latency presets
|
|
63
|
+
- `python -m veloura` CLI
|
|
64
|
+
- File-based transition analysis cache
|
|
65
|
+
- Generated audio tests for trim/loudness behavior
|
|
66
|
+
- Docs for non-Discord use
|
|
67
|
+
|
|
68
|
+
### 0.4
|
|
69
|
+
|
|
70
|
+
- Example streamer player script
|
|
71
|
+
- Cleaner status/snapshot model for external UIs
|
|
72
|
+
- Optional playback adapters
|
|
73
|
+
|
|
74
|
+
### 0.5
|
|
75
|
+
|
|
76
|
+
- Stronger beat-aware planning
|
|
77
|
+
- Better confidence scoring
|
|
78
|
+
- Safer automatic fallback decisions
|
|
79
|
+
- Transition preview/export helpers
|
|
80
|
+
|
|
81
|
+
### 1.0
|
|
82
|
+
|
|
83
|
+
- Stable typed API
|
|
84
|
+
- Backward-compatible migration notes
|
|
85
|
+
- Separate docs site/package README
|
|
86
|
+
- Published package ready for public use
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Minimal standalone streamer player powered by Veloura.
|
|
3
|
+
|
|
4
|
+
This example does not import the Discord bot. It resolves each argument into an
|
|
5
|
+
audio track, applies Veloura transition analysis, then pipes mixed PCM into
|
|
6
|
+
``ffplay``.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
|
|
10
|
+
python examples/streamer_player.py "./song-a.mp3" "./song-b.mp3"
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import asyncio
|
|
17
|
+
import shutil
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from veloura.audio import (
|
|
23
|
+
CHANNELS,
|
|
24
|
+
FRAME_RATE,
|
|
25
|
+
AudioTrack,
|
|
26
|
+
CrossfadeSession,
|
|
27
|
+
FileAnalysisCache,
|
|
28
|
+
prepare_smart_transition,
|
|
29
|
+
resolve_stream_track,
|
|
30
|
+
transition_preset,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def probe_duration(source: str) -> float:
|
|
35
|
+
ffprobe = shutil.which("ffprobe")
|
|
36
|
+
if not ffprobe:
|
|
37
|
+
return 0.0
|
|
38
|
+
result = subprocess.run(
|
|
39
|
+
[
|
|
40
|
+
ffprobe,
|
|
41
|
+
"-v",
|
|
42
|
+
"error",
|
|
43
|
+
"-show_entries",
|
|
44
|
+
"format=duration",
|
|
45
|
+
"-of",
|
|
46
|
+
"default=noprint_wrappers=1:nokey=1",
|
|
47
|
+
source,
|
|
48
|
+
],
|
|
49
|
+
stdout=subprocess.PIPE,
|
|
50
|
+
stderr=subprocess.DEVNULL,
|
|
51
|
+
text=True,
|
|
52
|
+
check=False,
|
|
53
|
+
)
|
|
54
|
+
try:
|
|
55
|
+
return float((result.stdout or "0").strip() or 0.0)
|
|
56
|
+
except ValueError:
|
|
57
|
+
return 0.0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def build_track(source: str, *, preset: str, cache: FileAnalysisCache | None) -> AudioTrack:
|
|
61
|
+
config = transition_preset(preset)
|
|
62
|
+
if Path(source).exists():
|
|
63
|
+
track = AudioTrack.from_source(
|
|
64
|
+
source,
|
|
65
|
+
title=Path(source).stem,
|
|
66
|
+
duration=probe_duration(source),
|
|
67
|
+
)
|
|
68
|
+
if cache:
|
|
69
|
+
return await asyncio.to_thread(cache.prepare_transition, track, config)
|
|
70
|
+
return await asyncio.to_thread(prepare_smart_transition, track, config)
|
|
71
|
+
return await resolve_stream_track(source, transition_config=config, analysis_cache=cache)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def build_tracks(
|
|
75
|
+
sources: list[str],
|
|
76
|
+
*,
|
|
77
|
+
preset: str,
|
|
78
|
+
cache: FileAnalysisCache | None,
|
|
79
|
+
) -> list[AudioTrack]:
|
|
80
|
+
return [await build_track(source, preset=preset, cache=cache) for source in sources]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def open_ffplay() -> subprocess.Popen:
|
|
84
|
+
ffplay = shutil.which("ffplay")
|
|
85
|
+
if not ffplay:
|
|
86
|
+
raise RuntimeError("ffplay was not found. Install FFmpeg with ffplay to run this example.")
|
|
87
|
+
return subprocess.Popen(
|
|
88
|
+
[
|
|
89
|
+
ffplay,
|
|
90
|
+
"-nodisp",
|
|
91
|
+
"-autoexit",
|
|
92
|
+
"-loglevel",
|
|
93
|
+
"warning",
|
|
94
|
+
"-f",
|
|
95
|
+
"s16le",
|
|
96
|
+
"-ar",
|
|
97
|
+
str(FRAME_RATE),
|
|
98
|
+
"-ac",
|
|
99
|
+
str(CHANNELS),
|
|
100
|
+
"-i",
|
|
101
|
+
"pipe:0",
|
|
102
|
+
],
|
|
103
|
+
stdin=subprocess.PIPE,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def play_tracks(tracks: list[AudioTrack], *, volume: float, crossfade: float):
|
|
108
|
+
session = CrossfadeSession(volume=volume, crossfade_seconds=crossfade)
|
|
109
|
+
for track in tracks:
|
|
110
|
+
session.source.enqueue(track)
|
|
111
|
+
|
|
112
|
+
player = open_ffplay()
|
|
113
|
+
try:
|
|
114
|
+
while True:
|
|
115
|
+
frame = session.source.read()
|
|
116
|
+
if not frame:
|
|
117
|
+
break
|
|
118
|
+
if not player.stdin:
|
|
119
|
+
break
|
|
120
|
+
player.stdin.write(frame)
|
|
121
|
+
finally:
|
|
122
|
+
session.stop()
|
|
123
|
+
if player.stdin:
|
|
124
|
+
player.stdin.close()
|
|
125
|
+
player.wait(timeout=5)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def main(argv: list[str] | None = None) -> int:
|
|
129
|
+
parser = argparse.ArgumentParser(description="Play a Veloura-powered streamer queue.")
|
|
130
|
+
parser.add_argument("sources", nargs="+", help="Local files, stream URLs, or yt-dlp search queries.")
|
|
131
|
+
parser.add_argument("--preset", default="streamer")
|
|
132
|
+
parser.add_argument("--volume", type=float, default=0.65)
|
|
133
|
+
parser.add_argument("--crossfade", type=float, default=8.0)
|
|
134
|
+
parser.add_argument("--cache-dir")
|
|
135
|
+
parser.add_argument("--no-cache", action="store_true")
|
|
136
|
+
args = parser.parse_args(argv)
|
|
137
|
+
|
|
138
|
+
cache = None if args.no_cache else FileAnalysisCache(args.cache_dir)
|
|
139
|
+
tracks = asyncio.run(build_tracks(args.sources, preset=args.preset, cache=cache))
|
|
140
|
+
play_tracks(tracks, volume=args.volume, crossfade=args.crossfade)
|
|
141
|
+
return 0
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "veloura-audio"
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "Reusable Python audio engine with crossfade and beat-aware transition planning."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
keywords = [
|
|
12
|
+
"audio",
|
|
13
|
+
"crossfade",
|
|
14
|
+
"ffmpeg",
|
|
15
|
+
"music",
|
|
16
|
+
"streaming",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Multimedia :: Sound/Audio",
|
|
26
|
+
"Topic :: Multimedia :: Sound/Audio :: Mixers",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
]
|
|
29
|
+
dependencies = []
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
veloura = "veloura.cli:main"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/VelouraAudio/veloura-audio"
|
|
36
|
+
Repository = "https://github.com/VelouraAudio/veloura-audio"
|
|
37
|
+
Issues = "https://github.com/VelouraAudio/veloura-audio/issues"
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
stream = [
|
|
41
|
+
"yt-dlp>=2026.3.17",
|
|
42
|
+
]
|
|
43
|
+
discord = [
|
|
44
|
+
"discord.py>=2.3.0",
|
|
45
|
+
"PyNaCl>=1.5.0",
|
|
46
|
+
]
|
|
47
|
+
all = [
|
|
48
|
+
"discord.py>=2.3.0",
|
|
49
|
+
"PyNaCl>=1.5.0",
|
|
50
|
+
"yt-dlp>=2026.3.17",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.packages.find]
|
|
54
|
+
include = ["veloura*"]
|
|
55
|
+
|
|
56
|
+
[tool.setuptools.package-data]
|
|
57
|
+
veloura = ["py.typed"]
|