captionwave 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wilfredo Guillén
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,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: captionwave
3
+ Version: 0.1.0
4
+ Summary: Genera audio + subtítulos animados sincronizados (.ass/.srt) a partir de texto, con emojis compatibles con iOS.
5
+ Author-email: Wilfredo Guillén <wilfredoguillensalazar@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Fortex-GT/Captionwave
8
+ Project-URL: Repository, https://github.com/Fortex-GT/Captionwave
9
+ Project-URL: Issues, https://github.com/Fortex-GT/Captionwave/issues
10
+ Keywords: subtitles,captions,tts,ass,shorts,reels,tiktok,karaoke,emoji
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Topic :: Multimedia :: Video
22
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: edge-tts>=7.0
27
+ Requires-Dist: emoji>=2.8
28
+ Provides-Extra: duration
29
+ Requires-Dist: mutagen>=1.45; extra == "duration"
30
+ Provides-Extra: video
31
+ Requires-Dist: moviepy>=2.0; extra == "video"
32
+ Provides-Extra: test
33
+ Requires-Dist: pytest>=7; extra == "test"
34
+ Dynamic: license-file
35
+
36
+ # captionwave
37
+
38
+ Genera **audio (voz)** y **subtítulos animados sincronizados** (`.ass` + `.srt`) a partir de una variable de texto. Pensado para Shorts / Reels / TikTok estilo "¿Sabías que…?".
39
+
40
+ La librería **no arma el video final**: te entrega los archivos (audio + subtítulos + emojis con tiempos) para que tú montes el video como prefieras (FFmpeg, tu editor, MoviePy…). Así tienes control total y no reprogramas los subtítulos en cada proyecto.
41
+
42
+ - ✅ Voz con **edge-tts** y tiempos **por palabra** → el audio y los subtítulos quedan pegados *por construcción* (no se desfasan).
43
+ - ✅ Subtítulos animados en **`.ass`** (karaoke, palabra activa, "pop", sticker estilo Hormozi, palabra-por-palabra…) que **FFmpeg/libass renderiza de forma nativa** (rápido).
44
+ - ✅ `.srt` de respaldo para plataformas que no aceptan `.ass`.
45
+ - ✅ **Emojis filtrados a los que existen en iOS** (sin "tofus"/cuadritos), con sus tiempos exportados para superponer tu propio arte.
46
+
47
+ ---
48
+
49
+ ## Instalación
50
+
51
+ ```bash
52
+ pip install captionwave # una vez publicado en PyPI
53
+ ```
54
+
55
+ Mientras tanto —o en notebooks como Google Colab— instálalo desde GitHub:
56
+
57
+ ```bash
58
+ pip install "git+https://github.com/Fortex-GT/Captionwave.git"
59
+ ```
60
+
61
+ O desde el código fuente (este repositorio):
62
+
63
+ ```bash
64
+ pip install . # o, para desarrollo: pip install -e .
65
+ ```
66
+
67
+ Requisitos:
68
+ - **Conexión a internet** para la voz (edge-tts usa el servicio de Microsoft Edge). Si solo quieres los subtítulos a partir de tiempos que ya tienes, puedes trabajar sin red con `build_from_words(...)` (ver más abajo y `examples/offline_sin_internet.py`).
69
+ - **FFmpeg** instalado si vas a quemar los subtítulos en el video (`ffmpeg -version`).
70
+ - Opcional, recomendado: `pip install captionwave[duration]` (usa *mutagen* para medir con exactitud la duración del audio y alinear el último subtítulo).
71
+
72
+ ---
73
+
74
+ ## Uso rápido
75
+
76
+ ```python
77
+ from captionwave import CaptionGenerator
78
+
79
+ gen = CaptionGenerator(
80
+ voice="es-MX-DaliaNeural", # cualquier voz de edge-tts
81
+ rate="+18%", # más rápido = más dinámico
82
+ style="hormozi", # ver estilos abajo
83
+ )
84
+
85
+ r = gen.generate(
86
+ "El Sol es una estrella que contiene el 99% de la masa del sistema solar.",
87
+ out_audio="voz.mp3",
88
+ out_ass="subs.ass",
89
+ out_srt="subs.srt", # opcional
90
+ out_emojis="emojis.json", # opcional (para superponer arte de emoji)
91
+ )
92
+
93
+ print(r["duration"], "segundos")
94
+ ```
95
+
96
+ Esto crea `voz.mp3`, `subs.ass`, `subs.srt` y `emojis.json`, listos para montar.
97
+
98
+ ### Con un gancho/intro a otro ritmo
99
+
100
+ ```python
101
+ r = gen.generate(
102
+ "el sol es una estrella enorme.",
103
+ intro="¿Sabías que...?", # se dice antes
104
+ intro_rate="+5%", # el gancho un poco más pausado
105
+ out_audio="voz.mp3", out_ass="subs.ass",
106
+ )
107
+ ```
108
+
109
+ ### Sin internet (a partir de tiempos que ya tienes)
110
+
111
+ Si ya tienes los tiempos por palabra (de otra fuente o para hacer pruebas),
112
+ puedes generar los subtítulos **sin TTS ni conexión** con `build_from_words`:
113
+
114
+ ```python
115
+ from captionwave import CaptionGenerator
116
+
117
+ words = [
118
+ {"word": "El", "start": 0.00, "dur": 0.18},
119
+ {"word": "Sol", "start": 0.18, "dur": 0.34},
120
+ {"word": "es", "start": 0.52, "dur": 0.16},
121
+ {"word": "una", "start": 0.68, "dur": 0.18},
122
+ {"word": "estrella", "start": 0.86, "dur": 0.52},
123
+ ]
124
+
125
+ gen = CaptionGenerator(style="hormozi")
126
+ r = gen.build_from_words(words, duration=1.5, out_ass="subs.ass", out_srt="subs.srt")
127
+ ```
128
+
129
+ Devuelve el mismo `dict` que `generate` (con `audio=None`). Ver `examples/offline_sin_internet.py`.
130
+
131
+ ---
132
+
133
+ ## Estilos disponibles
134
+
135
+ ![Muestra de estilos](estilos_muestra.png)
136
+
137
+ *(Palabra activa "ESTRELLA" resaltada en 4 de los estilos. El resaltado avanza palabra por palabra al ritmo de la voz.)*
138
+
139
+ ```python
140
+ from captionwave import list_styles
141
+ print(list_styles())
142
+ ```
143
+
144
+ | Estilo | Animación | Descripción |
145
+ |-------------|------------------|-------------|
146
+ | `hormozi` | sticker amarillo | Palabra activa con fondo sólido (clásico de Shorts). |
147
+ | `green` | sticker verde | Igual que hormozi pero en verde. |
148
+ | `karaoke` | barrido | El texto se "llena" de color al ritmo de la voz. |
149
+ | `pop` | rebote | La palabra activa crece y cambia de color. |
150
+ | `fire` | rebote naranja | Variante de `pop` en tono fuego. |
151
+ | `neon` | color + glow | Palabra activa cian con contorno azul. |
152
+ | `single` | palabra única | Una sola palabra a la vez, grande y centrada. |
153
+ | `clean` | color suave | Subtítulo abajo, sobrio (lower third). |
154
+
155
+ ### Personalizar cualquier estilo
156
+
157
+ Cada estilo es un `Style` que puedes copiar y modificar:
158
+
159
+ ```python
160
+ from captionwave import get_style, CaptionGenerator
161
+
162
+ mi_estilo = get_style("hormozi").copy(
163
+ active_color="#FF3366",
164
+ sticker_color="#FF3366",
165
+ sticker_text_color="#FFFFFF",
166
+ font="Montserrat", # debe estar instalada en el sistema que renderiza
167
+ font_size=96,
168
+ max_words=2, # menos palabras por línea
169
+ position="lower", # "center" | "lower" | "upper"
170
+ )
171
+
172
+ gen = CaptionGenerator(style=mi_estilo, rate="+20%")
173
+ ```
174
+
175
+ Campos útiles de `Style`: `base_color`, `active_color`, `outline_color`, `outline_w`, `sticker_color`, `sticker_text_color`, `sticker_bord`, `pop_scale`, `font`, `font_size`, `uppercase`, `max_words`, `max_chars`, `position`.
176
+
177
+ ---
178
+
179
+ ## Montar el video con FFmpeg
180
+
181
+ La librería te da `voz.mp3` + `subs.ass`. Para quemar los subtítulos sobre un fondo:
182
+
183
+ **Sobre un video de fondo** (gameplay, b-roll, etc.):
184
+ ```bash
185
+ ffmpeg -i fondo.mp4 -i voz.mp3 \
186
+ -vf "scale=1080:1920,ass=subs.ass" \
187
+ -map 0:v -map 1:a -shortest salida.mp4
188
+ ```
189
+
190
+ **Sobre una imagen fija**:
191
+ ```bash
192
+ ffmpeg -loop 1 -i fondo.jpg -i voz.mp3 \
193
+ -vf "scale=1080:1920,ass=subs.ass" \
194
+ -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest salida.mp4
195
+ ```
196
+
197
+ > El `.ass` ya trae la resolución (1080×1920 por defecto). Si cambias la resolución usa `resolution=(W, H)` en `CaptionGenerator`.
198
+
199
+ ---
200
+
201
+ ## ⚠️ Sobre los emojis (léelo)
202
+
203
+ La librería elige, para cada palabra/frase, un **emoji que sí existe en iOS** (filtra por versión de Unicode; ajustable con `emoji_max_version`). Esto evita que en un iPhone aparezcan cuadritos.
204
+
205
+ Dos cosas importantes sobre la **apariencia**:
206
+
207
+ 1. **No se incluye el arte de Apple.** Los emojis de Apple son propiedad de Apple y no se pueden empaquetar/redistribuir. La librería entrega el **carácter Unicode** correcto; el diseño con el que se ve lo pone el sistema donde se reproduce (en iPhone/Mac se verá con el estilo de Apple; en Linux con Noto/Twemoji).
208
+
209
+ 2. **libass no garantiza emojis a color.** Al quemar el `.ass` con FFmpeg, los emojis suelen salir **monocromos**. Por eso, si quieres el emoji a color (y con el look de Apple), la mejor ruta es **superponerlo como imagen** en tu editor o con FFmpeg, usando los tiempos que te da la librería:
210
+
211
+ ```python
212
+ r = gen.generate("...", out_emojis="emojis.json")
213
+ for e in r["emojis"]:
214
+ print(e) # {"word": "estrella", "emoji": "⭐", "start": 1.38, "dur": 0.58}
215
+ ```
216
+
217
+ Con eso colocas tu PNG de emoji (que tú aportas) en `start` durante `dur`. Así el carácter sale de la librería y el arte lo pones tú, sin problemas de copyright.
218
+
219
+ Si prefieres incrustar el carácter directamente en el `.ass` de todas formas, está activado por defecto (`emoji_in_ass=True`); ponlo en `False` si vas a usar overlay.
220
+
221
+ ---
222
+
223
+ ## Qué devuelve `generate(...)`
224
+
225
+ Un `dict` con:
226
+
227
+ ```python
228
+ {
229
+ "audio": "voz.mp3",
230
+ "ass": "subs.ass",
231
+ "srt": "subs.srt" | None,
232
+ "duration": 6.37, # segundos
233
+ "words": [{"word","start","dur"}, ...],
234
+ "lines": [{"text","start","end","emoji"}, ...],
235
+ "emojis": [{"word","emoji","start","dur"}, ...],
236
+ }
237
+ ```
238
+
239
+ ---
240
+
241
+ ## Desarrollo y tests
242
+
243
+ ```bash
244
+ pip install -e ".[test]"
245
+ pytest
246
+ ```
247
+
248
+ Los tests **no necesitan internet** (no llaman al TTS): cubren los estilos, el
249
+ troceado en líneas, la selección de emojis y la escritura de `.ass`/`.srt`.
250
+
251
+ ---
252
+
253
+ ## Publicar tu propia copia
254
+
255
+ > **Antes de subir a PyPI, verifica que el nombre `captionwave` esté libre** en https://pypi.org. Si no lo está, renómbralo: cambia la carpeta `src/captionwave/` y el campo `name` de `pyproject.toml`.
256
+
257
+ ```bash
258
+ pip install build twine
259
+ python -m build
260
+ twine upload dist/*
261
+ ```
262
+
263
+ Para GitHub solo inicializa el repo y súbelo normal.
264
+
265
+ ---
266
+
267
+ ## Licencia
268
+
269
+ MIT © 2026 Wilfredo Guillén — ver [LICENSE](LICENSE).
@@ -0,0 +1,234 @@
1
+ # captionwave
2
+
3
+ Genera **audio (voz)** y **subtítulos animados sincronizados** (`.ass` + `.srt`) a partir de una variable de texto. Pensado para Shorts / Reels / TikTok estilo "¿Sabías que…?".
4
+
5
+ La librería **no arma el video final**: te entrega los archivos (audio + subtítulos + emojis con tiempos) para que tú montes el video como prefieras (FFmpeg, tu editor, MoviePy…). Así tienes control total y no reprogramas los subtítulos en cada proyecto.
6
+
7
+ - ✅ Voz con **edge-tts** y tiempos **por palabra** → el audio y los subtítulos quedan pegados *por construcción* (no se desfasan).
8
+ - ✅ Subtítulos animados en **`.ass`** (karaoke, palabra activa, "pop", sticker estilo Hormozi, palabra-por-palabra…) que **FFmpeg/libass renderiza de forma nativa** (rápido).
9
+ - ✅ `.srt` de respaldo para plataformas que no aceptan `.ass`.
10
+ - ✅ **Emojis filtrados a los que existen en iOS** (sin "tofus"/cuadritos), con sus tiempos exportados para superponer tu propio arte.
11
+
12
+ ---
13
+
14
+ ## Instalación
15
+
16
+ ```bash
17
+ pip install captionwave # una vez publicado en PyPI
18
+ ```
19
+
20
+ Mientras tanto —o en notebooks como Google Colab— instálalo desde GitHub:
21
+
22
+ ```bash
23
+ pip install "git+https://github.com/Fortex-GT/Captionwave.git"
24
+ ```
25
+
26
+ O desde el código fuente (este repositorio):
27
+
28
+ ```bash
29
+ pip install . # o, para desarrollo: pip install -e .
30
+ ```
31
+
32
+ Requisitos:
33
+ - **Conexión a internet** para la voz (edge-tts usa el servicio de Microsoft Edge). Si solo quieres los subtítulos a partir de tiempos que ya tienes, puedes trabajar sin red con `build_from_words(...)` (ver más abajo y `examples/offline_sin_internet.py`).
34
+ - **FFmpeg** instalado si vas a quemar los subtítulos en el video (`ffmpeg -version`).
35
+ - Opcional, recomendado: `pip install captionwave[duration]` (usa *mutagen* para medir con exactitud la duración del audio y alinear el último subtítulo).
36
+
37
+ ---
38
+
39
+ ## Uso rápido
40
+
41
+ ```python
42
+ from captionwave import CaptionGenerator
43
+
44
+ gen = CaptionGenerator(
45
+ voice="es-MX-DaliaNeural", # cualquier voz de edge-tts
46
+ rate="+18%", # más rápido = más dinámico
47
+ style="hormozi", # ver estilos abajo
48
+ )
49
+
50
+ r = gen.generate(
51
+ "El Sol es una estrella que contiene el 99% de la masa del sistema solar.",
52
+ out_audio="voz.mp3",
53
+ out_ass="subs.ass",
54
+ out_srt="subs.srt", # opcional
55
+ out_emojis="emojis.json", # opcional (para superponer arte de emoji)
56
+ )
57
+
58
+ print(r["duration"], "segundos")
59
+ ```
60
+
61
+ Esto crea `voz.mp3`, `subs.ass`, `subs.srt` y `emojis.json`, listos para montar.
62
+
63
+ ### Con un gancho/intro a otro ritmo
64
+
65
+ ```python
66
+ r = gen.generate(
67
+ "el sol es una estrella enorme.",
68
+ intro="¿Sabías que...?", # se dice antes
69
+ intro_rate="+5%", # el gancho un poco más pausado
70
+ out_audio="voz.mp3", out_ass="subs.ass",
71
+ )
72
+ ```
73
+
74
+ ### Sin internet (a partir de tiempos que ya tienes)
75
+
76
+ Si ya tienes los tiempos por palabra (de otra fuente o para hacer pruebas),
77
+ puedes generar los subtítulos **sin TTS ni conexión** con `build_from_words`:
78
+
79
+ ```python
80
+ from captionwave import CaptionGenerator
81
+
82
+ words = [
83
+ {"word": "El", "start": 0.00, "dur": 0.18},
84
+ {"word": "Sol", "start": 0.18, "dur": 0.34},
85
+ {"word": "es", "start": 0.52, "dur": 0.16},
86
+ {"word": "una", "start": 0.68, "dur": 0.18},
87
+ {"word": "estrella", "start": 0.86, "dur": 0.52},
88
+ ]
89
+
90
+ gen = CaptionGenerator(style="hormozi")
91
+ r = gen.build_from_words(words, duration=1.5, out_ass="subs.ass", out_srt="subs.srt")
92
+ ```
93
+
94
+ Devuelve el mismo `dict` que `generate` (con `audio=None`). Ver `examples/offline_sin_internet.py`.
95
+
96
+ ---
97
+
98
+ ## Estilos disponibles
99
+
100
+ ![Muestra de estilos](estilos_muestra.png)
101
+
102
+ *(Palabra activa "ESTRELLA" resaltada en 4 de los estilos. El resaltado avanza palabra por palabra al ritmo de la voz.)*
103
+
104
+ ```python
105
+ from captionwave import list_styles
106
+ print(list_styles())
107
+ ```
108
+
109
+ | Estilo | Animación | Descripción |
110
+ |-------------|------------------|-------------|
111
+ | `hormozi` | sticker amarillo | Palabra activa con fondo sólido (clásico de Shorts). |
112
+ | `green` | sticker verde | Igual que hormozi pero en verde. |
113
+ | `karaoke` | barrido | El texto se "llena" de color al ritmo de la voz. |
114
+ | `pop` | rebote | La palabra activa crece y cambia de color. |
115
+ | `fire` | rebote naranja | Variante de `pop` en tono fuego. |
116
+ | `neon` | color + glow | Palabra activa cian con contorno azul. |
117
+ | `single` | palabra única | Una sola palabra a la vez, grande y centrada. |
118
+ | `clean` | color suave | Subtítulo abajo, sobrio (lower third). |
119
+
120
+ ### Personalizar cualquier estilo
121
+
122
+ Cada estilo es un `Style` que puedes copiar y modificar:
123
+
124
+ ```python
125
+ from captionwave import get_style, CaptionGenerator
126
+
127
+ mi_estilo = get_style("hormozi").copy(
128
+ active_color="#FF3366",
129
+ sticker_color="#FF3366",
130
+ sticker_text_color="#FFFFFF",
131
+ font="Montserrat", # debe estar instalada en el sistema que renderiza
132
+ font_size=96,
133
+ max_words=2, # menos palabras por línea
134
+ position="lower", # "center" | "lower" | "upper"
135
+ )
136
+
137
+ gen = CaptionGenerator(style=mi_estilo, rate="+20%")
138
+ ```
139
+
140
+ Campos útiles de `Style`: `base_color`, `active_color`, `outline_color`, `outline_w`, `sticker_color`, `sticker_text_color`, `sticker_bord`, `pop_scale`, `font`, `font_size`, `uppercase`, `max_words`, `max_chars`, `position`.
141
+
142
+ ---
143
+
144
+ ## Montar el video con FFmpeg
145
+
146
+ La librería te da `voz.mp3` + `subs.ass`. Para quemar los subtítulos sobre un fondo:
147
+
148
+ **Sobre un video de fondo** (gameplay, b-roll, etc.):
149
+ ```bash
150
+ ffmpeg -i fondo.mp4 -i voz.mp3 \
151
+ -vf "scale=1080:1920,ass=subs.ass" \
152
+ -map 0:v -map 1:a -shortest salida.mp4
153
+ ```
154
+
155
+ **Sobre una imagen fija**:
156
+ ```bash
157
+ ffmpeg -loop 1 -i fondo.jpg -i voz.mp3 \
158
+ -vf "scale=1080:1920,ass=subs.ass" \
159
+ -c:v libx264 -pix_fmt yuv420p -c:a aac -shortest salida.mp4
160
+ ```
161
+
162
+ > El `.ass` ya trae la resolución (1080×1920 por defecto). Si cambias la resolución usa `resolution=(W, H)` en `CaptionGenerator`.
163
+
164
+ ---
165
+
166
+ ## ⚠️ Sobre los emojis (léelo)
167
+
168
+ La librería elige, para cada palabra/frase, un **emoji que sí existe en iOS** (filtra por versión de Unicode; ajustable con `emoji_max_version`). Esto evita que en un iPhone aparezcan cuadritos.
169
+
170
+ Dos cosas importantes sobre la **apariencia**:
171
+
172
+ 1. **No se incluye el arte de Apple.** Los emojis de Apple son propiedad de Apple y no se pueden empaquetar/redistribuir. La librería entrega el **carácter Unicode** correcto; el diseño con el que se ve lo pone el sistema donde se reproduce (en iPhone/Mac se verá con el estilo de Apple; en Linux con Noto/Twemoji).
173
+
174
+ 2. **libass no garantiza emojis a color.** Al quemar el `.ass` con FFmpeg, los emojis suelen salir **monocromos**. Por eso, si quieres el emoji a color (y con el look de Apple), la mejor ruta es **superponerlo como imagen** en tu editor o con FFmpeg, usando los tiempos que te da la librería:
175
+
176
+ ```python
177
+ r = gen.generate("...", out_emojis="emojis.json")
178
+ for e in r["emojis"]:
179
+ print(e) # {"word": "estrella", "emoji": "⭐", "start": 1.38, "dur": 0.58}
180
+ ```
181
+
182
+ Con eso colocas tu PNG de emoji (que tú aportas) en `start` durante `dur`. Así el carácter sale de la librería y el arte lo pones tú, sin problemas de copyright.
183
+
184
+ Si prefieres incrustar el carácter directamente en el `.ass` de todas formas, está activado por defecto (`emoji_in_ass=True`); ponlo en `False` si vas a usar overlay.
185
+
186
+ ---
187
+
188
+ ## Qué devuelve `generate(...)`
189
+
190
+ Un `dict` con:
191
+
192
+ ```python
193
+ {
194
+ "audio": "voz.mp3",
195
+ "ass": "subs.ass",
196
+ "srt": "subs.srt" | None,
197
+ "duration": 6.37, # segundos
198
+ "words": [{"word","start","dur"}, ...],
199
+ "lines": [{"text","start","end","emoji"}, ...],
200
+ "emojis": [{"word","emoji","start","dur"}, ...],
201
+ }
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Desarrollo y tests
207
+
208
+ ```bash
209
+ pip install -e ".[test]"
210
+ pytest
211
+ ```
212
+
213
+ Los tests **no necesitan internet** (no llaman al TTS): cubren los estilos, el
214
+ troceado en líneas, la selección de emojis y la escritura de `.ass`/`.srt`.
215
+
216
+ ---
217
+
218
+ ## Publicar tu propia copia
219
+
220
+ > **Antes de subir a PyPI, verifica que el nombre `captionwave` esté libre** en https://pypi.org. Si no lo está, renómbralo: cambia la carpeta `src/captionwave/` y el campo `name` de `pyproject.toml`.
221
+
222
+ ```bash
223
+ pip install build twine
224
+ python -m build
225
+ twine upload dist/*
226
+ ```
227
+
228
+ Para GitHub solo inicializa el repo y súbelo normal.
229
+
230
+ ---
231
+
232
+ ## Licencia
233
+
234
+ MIT © 2026 Wilfredo Guillén — ver [LICENSE](LICENSE).
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "captionwave"
7
+ version = "0.1.0"
8
+ description = "Genera audio + subtítulos animados sincronizados (.ass/.srt) a partir de texto, con emojis compatibles con iOS."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Wilfredo Guillén", email = "wilfredoguillensalazar@gmail.com" }]
14
+ keywords = ["subtitles", "captions", "tts", "ass", "shorts", "reels", "tiktok", "karaoke", "emoji"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3 :: Only",
26
+ "Topic :: Multimedia :: Video",
27
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
28
+ ]
29
+ dependencies = [
30
+ "edge-tts>=7.0",
31
+ "emoji>=2.8",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ # Mejora la precisión de la duración del audio (recomendado).
36
+ duration = ["mutagen>=1.45"]
37
+ # Para los ejemplos que montan el video.
38
+ video = ["moviepy>=2.0"]
39
+ # Para ejecutar la batería de tests (offline).
40
+ test = ["pytest>=7"]
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/Fortex-GT/Captionwave"
44
+ Repository = "https://github.com/Fortex-GT/Captionwave"
45
+ Issues = "https://github.com/Fortex-GT/Captionwave/issues"
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ """
2
+ captionwave — genera audio + subtítulos animados sincronizados a partir de texto.
3
+
4
+ Salidas: .ass (animado), .srt (respaldo), audio (.mp3) y emojis con tiempos.
5
+ Tú montas el video con FFmpeg/MoviePy usando esos archivos.
6
+ """
7
+
8
+ from .core import CaptionGenerator, chunk_words
9
+ from .styles import Style, PRESETS, get_style, list_styles, hex_to_ass
10
+ from .emojis import EmojiPicker
11
+
12
+ __version__ = "0.1.0"
13
+ __all__ = [
14
+ "CaptionGenerator",
15
+ "chunk_words",
16
+ "Style",
17
+ "PRESETS",
18
+ "get_style",
19
+ "list_styles",
20
+ "hex_to_ass",
21
+ "EmojiPicker",
22
+ ]