webscout 8.2.9__py3-none-any.whl → 2026.1.19__py3-none-any.whl
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.
- webscout/AIauto.py +524 -251
- webscout/AIbase.py +247 -319
- webscout/AIutel.py +68 -703
- webscout/Bard.py +1072 -1026
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
- webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
- webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
- webscout/Extra/GitToolkit/gitapi/search.py +162 -0
- webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
- webscout/Extra/GitToolkit/gitapi/user.py +128 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
- webscout/Extra/YTToolkit/README.md +443 -375
- webscout/Extra/YTToolkit/YTdownloader.py +953 -957
- webscout/Extra/YTToolkit/__init__.py +3 -3
- webscout/Extra/YTToolkit/transcriber.py +595 -476
- webscout/Extra/YTToolkit/ytapi/README.md +230 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
- webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +178 -118
- webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
- webscout/Extra/YTToolkit/ytapi/https.py +89 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
- webscout/Extra/YTToolkit/ytapi/query.py +143 -40
- webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
- webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
- webscout/Extra/YTToolkit/ytapi/video.py +403 -232
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -684
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +28 -28
- webscout/Extra/tempmail/async_utils.py +143 -141
- webscout/Extra/tempmail/base.py +172 -161
- webscout/Extra/tempmail/cli.py +191 -187
- webscout/Extra/tempmail/emailnator.py +88 -84
- webscout/Extra/tempmail/mail_tm.py +378 -361
- webscout/Extra/tempmail/temp_mail_io.py +304 -292
- webscout/Extra/weather.py +196 -194
- webscout/Extra/weather_ascii.py +17 -15
- webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
- webscout/Provider/AISEARCH/Perplexity.py +292 -333
- webscout/Provider/AISEARCH/README.md +106 -279
- webscout/Provider/AISEARCH/__init__.py +16 -9
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +357 -410
- webscout/Provider/AISEARCH/monica_search.py +200 -220
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
- webscout/Provider/Algion.py +413 -0
- webscout/Provider/Andi.py +74 -69
- webscout/Provider/Apriel.py +313 -0
- webscout/Provider/Ayle.py +323 -0
- webscout/Provider/ChatSandbox.py +329 -342
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +467 -340
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +175 -169
- webscout/Provider/GithubChat.py +385 -369
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -801
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -375
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -291
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -384
- webscout/Provider/Netwrck.py +273 -270
- webscout/Provider/Nvidia.py +310 -0
- webscout/Provider/OPENAI/DeepAI.py +489 -0
- webscout/Provider/OPENAI/K2Think.py +423 -0
- webscout/Provider/OPENAI/PI.py +463 -0
- webscout/Provider/OPENAI/README.md +890 -952
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -357
- webscout/Provider/OPENAI/__init__.py +148 -40
- webscout/Provider/OPENAI/ai4chat.py +348 -293
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
- webscout/Provider/OPENAI/base.py +253 -249
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +870 -556
- webscout/Provider/OPENAI/chatsandbox.py +233 -173
- webscout/Provider/OPENAI/deepinfra.py +403 -322
- webscout/Provider/OPENAI/e2b.py +2370 -1414
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +452 -417
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -364
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +333 -308
- webscout/Provider/OPENAI/huggingface.py +321 -0
- webscout/Provider/OPENAI/ibm.py +425 -0
- webscout/Provider/OPENAI/llmchat.py +253 -0
- webscout/Provider/OPENAI/llmchatco.py +378 -335
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +374 -357
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -287
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -172
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +305 -304
- webscout/Provider/OPENAI/textpollinations.py +370 -339
- webscout/Provider/OPENAI/toolbaz.py +375 -413
- webscout/Provider/OPENAI/typefully.py +419 -355
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -318
- webscout/Provider/OPENAI/wisecat.py +359 -387
- webscout/Provider/OPENAI/writecream.py +185 -163
- webscout/Provider/OPENAI/x0gpt.py +462 -365
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -429
- webscout/Provider/QwenLM.py +346 -254
- webscout/Provider/STT/__init__.py +28 -0
- webscout/Provider/STT/base.py +303 -0
- webscout/Provider/STT/elevenlabs.py +264 -0
- webscout/Provider/Sambanova.py +317 -0
- webscout/Provider/TTI/README.md +69 -82
- webscout/Provider/TTI/__init__.py +37 -7
- webscout/Provider/TTI/base.py +147 -64
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -201
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -221
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -11
- webscout/Provider/TTS/README.md +186 -192
- webscout/Provider/TTS/__init__.py +43 -10
- webscout/Provider/TTS/base.py +523 -159
- webscout/Provider/TTS/deepgram.py +286 -156
- webscout/Provider/TTS/elevenlabs.py +189 -111
- webscout/Provider/TTS/freetts.py +218 -0
- webscout/Provider/TTS/murfai.py +288 -113
- webscout/Provider/TTS/openai_fm.py +364 -129
- webscout/Provider/TTS/parler.py +203 -111
- webscout/Provider/TTS/qwen.py +334 -0
- webscout/Provider/TTS/sherpa.py +286 -0
- webscout/Provider/TTS/speechma.py +693 -580
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +331 -308
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -475
- webscout/Provider/TypliAI.py +311 -305
- webscout/Provider/UNFINISHED/ChatHub.py +219 -209
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
- webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
- webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
- webscout/Provider/UNFINISHED/Youchat.py +347 -330
- webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
- webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
- webscout/Provider/UNFINISHED/liner.py +342 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +246 -263
- webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
- webscout/Provider/WiseCat.py +256 -233
- webscout/Provider/WrDoChat.py +390 -370
- webscout/Provider/__init__.py +115 -174
- webscout/Provider/ai4chat.py +181 -174
- webscout/Provider/akashgpt.py +330 -335
- webscout/Provider/cerebras.py +397 -290
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -283
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -325
- webscout/Provider/llama3mitril.py +230 -215
- webscout/Provider/llmchat.py +308 -258
- webscout/Provider/llmchatco.py +321 -306
- webscout/Provider/meta.py +996 -801
- webscout/Provider/oivscode.py +332 -309
- webscout/Provider/searchchat.py +316 -292
- webscout/Provider/sonus.py +264 -258
- webscout/Provider/toolbaz.py +359 -353
- webscout/Provider/turboseek.py +332 -266
- webscout/Provider/typefully.py +262 -202
- webscout/Provider/x0gpt.py +332 -299
- webscout/__init__.py +31 -39
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -524
- webscout/client.py +1497 -70
- webscout/conversation.py +140 -436
- webscout/exceptions.py +383 -362
- webscout/litagent/__init__.py +29 -29
- webscout/litagent/agent.py +492 -455
- webscout/litagent/constants.py +60 -60
- webscout/models.py +505 -181
- webscout/optimizers.py +74 -420
- webscout/prompt_manager.py +376 -288
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -404
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -210
- webscout/scout/core/scout.py +800 -607
- webscout/scout/core/search_result.py +51 -96
- webscout/scout/core/text_analyzer.py +64 -63
- webscout/scout/core/text_utils.py +412 -277
- webscout/scout/core/web_analyzer.py +54 -52
- webscout/scout/element.py +872 -478
- webscout/scout/parsers/__init__.py +70 -69
- webscout/scout/parsers/html5lib_parser.py +182 -172
- webscout/scout/parsers/html_parser.py +238 -236
- webscout/scout/parsers/lxml_parser.py +203 -178
- webscout/scout/utils.py +38 -37
- webscout/search/__init__.py +47 -0
- webscout/search/base.py +201 -0
- webscout/search/bing_main.py +45 -0
- webscout/search/brave_main.py +92 -0
- webscout/search/duckduckgo_main.py +57 -0
- webscout/search/engines/__init__.py +127 -0
- webscout/search/engines/bing/__init__.py +15 -0
- webscout/search/engines/bing/base.py +35 -0
- webscout/search/engines/bing/images.py +114 -0
- webscout/search/engines/bing/news.py +96 -0
- webscout/search/engines/bing/suggestions.py +36 -0
- webscout/search/engines/bing/text.py +109 -0
- webscout/search/engines/brave/__init__.py +19 -0
- webscout/search/engines/brave/base.py +47 -0
- webscout/search/engines/brave/images.py +213 -0
- webscout/search/engines/brave/news.py +353 -0
- webscout/search/engines/brave/suggestions.py +318 -0
- webscout/search/engines/brave/text.py +167 -0
- webscout/search/engines/brave/videos.py +364 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +80 -0
- webscout/search/engines/duckduckgo/base.py +189 -0
- webscout/search/engines/duckduckgo/images.py +100 -0
- webscout/search/engines/duckduckgo/maps.py +183 -0
- webscout/search/engines/duckduckgo/news.py +70 -0
- webscout/search/engines/duckduckgo/suggestions.py +22 -0
- webscout/search/engines/duckduckgo/text.py +221 -0
- webscout/search/engines/duckduckgo/translate.py +48 -0
- webscout/search/engines/duckduckgo/videos.py +80 -0
- webscout/search/engines/duckduckgo/weather.py +84 -0
- webscout/search/engines/mojeek.py +61 -0
- webscout/search/engines/wikipedia.py +77 -0
- webscout/search/engines/yahoo/__init__.py +41 -0
- webscout/search/engines/yahoo/answers.py +19 -0
- webscout/search/engines/yahoo/base.py +34 -0
- webscout/search/engines/yahoo/images.py +323 -0
- webscout/search/engines/yahoo/maps.py +19 -0
- webscout/search/engines/yahoo/news.py +258 -0
- webscout/search/engines/yahoo/suggestions.py +140 -0
- webscout/search/engines/yahoo/text.py +273 -0
- webscout/search/engines/yahoo/translate.py +19 -0
- webscout/search/engines/yahoo/videos.py +302 -0
- webscout/search/engines/yahoo/weather.py +220 -0
- webscout/search/engines/yandex.py +67 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +34 -0
- webscout/search/engines/yep/images.py +101 -0
- webscout/search/engines/yep/suggestions.py +38 -0
- webscout/search/engines/yep/text.py +99 -0
- webscout/search/http_client.py +172 -0
- webscout/search/results.py +141 -0
- webscout/search/yahoo_main.py +57 -0
- webscout/search/yep_main.py +48 -0
- webscout/server/__init__.py +48 -0
- webscout/server/config.py +78 -0
- webscout/server/exceptions.py +69 -0
- webscout/server/providers.py +286 -0
- webscout/server/request_models.py +131 -0
- webscout/server/request_processing.py +404 -0
- webscout/server/routes.py +642 -0
- webscout/server/server.py +351 -0
- webscout/server/ui_templates.py +1171 -0
- webscout/swiftcli/__init__.py +79 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +574 -297
- webscout/swiftcli/core/context.py +98 -104
- webscout/swiftcli/core/group.py +268 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +243 -221
- webscout/swiftcli/decorators/options.py +247 -220
- webscout/swiftcli/decorators/output.py +392 -252
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +134 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +58 -59
- webscout/swiftcli/utils/formatting.py +251 -252
- webscout/swiftcli/utils/parsing.py +368 -267
- webscout/update_checker.py +280 -136
- webscout/utils.py +28 -14
- webscout/version.py +2 -1
- webscout/version.py.bak +3 -0
- webscout/zeroart/__init__.py +218 -135
- webscout/zeroart/base.py +70 -66
- webscout/zeroart/effects.py +155 -101
- webscout/zeroart/fonts.py +1799 -1239
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
- webscout/DWEBS.py +0 -520
- webscout/Extra/Act.md +0 -309
- webscout/Extra/GitToolkit/gitapi/README.md +0 -110
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.md +0 -430
- webscout/Extra/weather.md +0 -281
- webscout/Litlogger/README.md +0 -10
- webscout/Litlogger/__init__.py +0 -15
- webscout/Litlogger/formats.py +0 -4
- webscout/Litlogger/handlers.py +0 -103
- webscout/Litlogger/levels.py +0 -13
- webscout/Litlogger/logger.py +0 -92
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/AISEARCH/felo_search.py +0 -202
- webscout/Provider/AISEARCH/genspark_search.py +0 -324
- webscout/Provider/AISEARCH/hika_search.py +0 -186
- webscout/Provider/AISEARCH/scira_search.py +0 -298
- webscout/Provider/Aitopia.py +0 -316
- webscout/Provider/AllenAI.py +0 -440
- webscout/Provider/Blackboxai.py +0 -791
- webscout/Provider/ChatGPTClone.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Cloudflare.py +0 -324
- webscout/Provider/ExaChat.py +0 -358
- webscout/Provider/Flowith.py +0 -217
- webscout/Provider/FreeGemini.py +0 -250
- webscout/Provider/Glider.py +0 -225
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -469
- webscout/Provider/Hunyuan.py +0 -283
- webscout/Provider/LambdaChat.py +0 -411
- webscout/Provider/Llama3.py +0 -259
- webscout/Provider/Nemotron.py +0 -218
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
- webscout/Provider/OPENAI/Cloudflare.py +0 -378
- webscout/Provider/OPENAI/FreeGemini.py +0 -283
- webscout/Provider/OPENAI/NEMOTRON.py +0 -232
- webscout/Provider/OPENAI/Qwen3.py +0 -283
- webscout/Provider/OPENAI/api.py +0 -969
- webscout/Provider/OPENAI/c4ai.py +0 -373
- webscout/Provider/OPENAI/chatgptclone.py +0 -494
- webscout/Provider/OPENAI/copilot.py +0 -242
- webscout/Provider/OPENAI/flowith.py +0 -162
- webscout/Provider/OPENAI/freeaichat.py +0 -359
- webscout/Provider/OPENAI/mcpcore.py +0 -389
- webscout/Provider/OPENAI/multichat.py +0 -376
- webscout/Provider/OPENAI/opkfc.py +0 -496
- webscout/Provider/OPENAI/scirachat.py +0 -477
- webscout/Provider/OPENAI/standardinput.py +0 -433
- webscout/Provider/OPENAI/typegpt.py +0 -364
- webscout/Provider/OPENAI/uncovrAI.py +0 -463
- webscout/Provider/OPENAI/venice.py +0 -431
- webscout/Provider/OPENAI/yep.py +0 -382
- webscout/Provider/OpenGPT.py +0 -209
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -290
- webscout/Provider/TTI/aiarta.py +0 -365
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/fastflux.py +0 -200
- webscout/Provider/TTI/piclumen.py +0 -203
- webscout/Provider/TTI/pixelmuse.py +0 -225
- webscout/Provider/TTS/gesserit.py +0 -128
- webscout/Provider/TTS/sthir.py +0 -94
- webscout/Provider/TeachAnything.py +0 -229
- webscout/Provider/UNFINISHED/puterjs.py +0 -635
- webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
- webscout/Provider/Venice.py +0 -258
- webscout/Provider/VercelAI.py +0 -253
- webscout/Provider/Writecream.py +0 -246
- webscout/Provider/WritingMate.py +0 -269
- webscout/Provider/asksteve.py +0 -220
- webscout/Provider/chatglm.py +0 -215
- webscout/Provider/copilot.py +0 -425
- webscout/Provider/freeaichat.py +0 -285
- webscout/Provider/granite.py +0 -235
- webscout/Provider/hermes.py +0 -266
- webscout/Provider/koala.py +0 -170
- webscout/Provider/lmarena.py +0 -198
- webscout/Provider/multichat.py +0 -364
- webscout/Provider/scira_chat.py +0 -299
- webscout/Provider/scnet.py +0 -243
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/typegpt.py +0 -289
- webscout/Provider/uncovr.py +0 -368
- webscout/Provider/yep.py +0 -389
- webscout/litagent/Readme.md +0 -276
- webscout/litprinter/__init__.py +0 -59
- webscout/swiftcli/Readme.md +0 -323
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1184
- webscout/webscout_search_async.py +0 -654
- webscout/yep_search.py +0 -347
- webscout/zeroart/README.md +0 -89
- webscout-8.2.9.dist-info/METADATA +0 -1033
- webscout-8.2.9.dist-info/RECORD +0 -289
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
webscout/Provider/TTS/base.py
CHANGED
|
@@ -1,159 +1,523 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Base class for TTS providers with
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
""
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
#
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
1
|
+
"""
|
|
2
|
+
Base class for TTS providers with OpenAI-compatible functionality.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import functools
|
|
7
|
+
import os
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Generator, List, Optional, Union, cast
|
|
11
|
+
|
|
12
|
+
from litprinter import ic
|
|
13
|
+
|
|
14
|
+
from webscout.AIbase import TTSProvider
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaseTTSProvider(TTSProvider):
|
|
18
|
+
"""
|
|
19
|
+
Base class for TTS providers with OpenAI-compatible functionality.
|
|
20
|
+
|
|
21
|
+
This class implements common methods and follows OpenAI TTS API patterns
|
|
22
|
+
for speech generation, streaming, and audio handling.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
required_auth = False
|
|
26
|
+
|
|
27
|
+
# Supported models (can be overridden by subclasses)
|
|
28
|
+
SUPPORTED_MODELS = [
|
|
29
|
+
"gpt-4o-mini-tts", # Latest intelligent realtime model
|
|
30
|
+
"tts-1", # Lower latency model
|
|
31
|
+
"tts-1-hd", # Higher quality model
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# Supported voices (can be overridden by subclasses)
|
|
35
|
+
SUPPORTED_VOICES = [
|
|
36
|
+
"alloy",
|
|
37
|
+
"ash",
|
|
38
|
+
"ballad",
|
|
39
|
+
"coral",
|
|
40
|
+
"echo",
|
|
41
|
+
"fable",
|
|
42
|
+
"nova",
|
|
43
|
+
"onyx",
|
|
44
|
+
"sage",
|
|
45
|
+
"shimmer",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
# Supported output formats
|
|
49
|
+
SUPPORTED_FORMATS = [
|
|
50
|
+
"mp3", # Default format
|
|
51
|
+
"opus", # Internet streaming, low latency
|
|
52
|
+
"aac", # Digital audio compression
|
|
53
|
+
"flac", # Lossless compression
|
|
54
|
+
"wav", # Uncompressed, low latency
|
|
55
|
+
"pcm", # Raw samples, 24kHz 16-bit
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
def __init__(self):
|
|
59
|
+
"""Initialize the base TTS provider."""
|
|
60
|
+
self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
|
|
61
|
+
self.default_model = "gpt-4o-mini-tts"
|
|
62
|
+
self.default_voice = "coral"
|
|
63
|
+
self.default_format = "mp3"
|
|
64
|
+
|
|
65
|
+
def validate_model(self, model: str) -> str:
|
|
66
|
+
"""
|
|
67
|
+
Validate and return the model name.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
model (str): Model name to validate
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
str: Validated model name
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ValueError: If model is not supported
|
|
77
|
+
"""
|
|
78
|
+
# If provider doesn't support models, return the model as-is
|
|
79
|
+
if self.SUPPORTED_MODELS is None:
|
|
80
|
+
return model
|
|
81
|
+
|
|
82
|
+
if model not in self.SUPPORTED_MODELS:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Model '{model}' not supported. Available models: {', '.join(self.SUPPORTED_MODELS)}"
|
|
85
|
+
)
|
|
86
|
+
return model
|
|
87
|
+
|
|
88
|
+
def validate_voice(self, voice: str) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Validate and return the voice name.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
voice (str): Voice name to validate
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
str: Validated voice name
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ValueError: If voice is not supported
|
|
100
|
+
"""
|
|
101
|
+
if voice not in self.SUPPORTED_VOICES:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"Voice '{voice}' not supported. Available voices: {', '.join(self.SUPPORTED_VOICES)}"
|
|
104
|
+
)
|
|
105
|
+
return voice
|
|
106
|
+
|
|
107
|
+
def validate_format(self, response_format: str) -> str:
|
|
108
|
+
"""
|
|
109
|
+
Validate and return the response format.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
response_format (str): Response format to validate
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
str: Validated response format
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
ValueError: If format is not supported
|
|
119
|
+
"""
|
|
120
|
+
if response_format not in self.SUPPORTED_FORMATS:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
f"Format '{response_format}' not supported. Available formats: {', '.join(self.SUPPORTED_FORMATS)}"
|
|
123
|
+
)
|
|
124
|
+
return response_format
|
|
125
|
+
|
|
126
|
+
def save_audio(
|
|
127
|
+
self, audio_file: str, destination: Optional[str] = None, verbose: bool = False
|
|
128
|
+
) -> str:
|
|
129
|
+
"""
|
|
130
|
+
Save audio to a specific destination.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
audio_file (str): Path to the source audio file
|
|
134
|
+
destination (str, optional): Destination path. Defaults to current directory with timestamp.
|
|
135
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
str: Path to the saved audio file
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
FileNotFoundError: If the audio file doesn't exist
|
|
142
|
+
"""
|
|
143
|
+
import shutil
|
|
144
|
+
import time
|
|
145
|
+
|
|
146
|
+
source_path = Path(audio_file)
|
|
147
|
+
|
|
148
|
+
if not source_path.exists():
|
|
149
|
+
raise FileNotFoundError(f"Audio file not found: {audio_file}")
|
|
150
|
+
|
|
151
|
+
if destination is None:
|
|
152
|
+
# Create a default destination with timestamp in current directory
|
|
153
|
+
timestamp = int(time.time())
|
|
154
|
+
destination = os.path.join(os.getcwd(), f"speech_{timestamp}{source_path.suffix}")
|
|
155
|
+
|
|
156
|
+
# Ensure the destination directory exists
|
|
157
|
+
os.makedirs(os.path.dirname(os.path.abspath(destination)), exist_ok=True)
|
|
158
|
+
|
|
159
|
+
# Copy the file
|
|
160
|
+
shutil.copy2(source_path, destination)
|
|
161
|
+
|
|
162
|
+
if verbose:
|
|
163
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
164
|
+
ic(f"Audio saved to {destination}")
|
|
165
|
+
|
|
166
|
+
return destination
|
|
167
|
+
|
|
168
|
+
def create_speech(
|
|
169
|
+
self,
|
|
170
|
+
input_text: str,
|
|
171
|
+
model: Optional[str] = None,
|
|
172
|
+
voice: Optional[str] = None,
|
|
173
|
+
response_format: Optional[str] = None,
|
|
174
|
+
instructions: Optional[str] = None,
|
|
175
|
+
verbose: bool = False,
|
|
176
|
+
) -> str:
|
|
177
|
+
"""
|
|
178
|
+
Create speech from input text (OpenAI-compatible interface).
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
input_text (str): The text to convert to speech
|
|
182
|
+
model (str, optional): The TTS model to use
|
|
183
|
+
voice (str, optional): The voice to use
|
|
184
|
+
response_format (str, optional): Audio format (mp3, opus, aac, flac, wav, pcm)
|
|
185
|
+
instructions (str, optional): Voice instructions for controlling speech aspects
|
|
186
|
+
verbose (bool, optional): Whether to print debug information
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
str: Path to the generated audio file
|
|
190
|
+
"""
|
|
191
|
+
# Use defaults if not provided
|
|
192
|
+
model = model or self.default_model
|
|
193
|
+
voice = voice or self.default_voice
|
|
194
|
+
response_format = response_format or self.default_format
|
|
195
|
+
|
|
196
|
+
# Validate parameters
|
|
197
|
+
self.validate_model(model)
|
|
198
|
+
self.validate_voice(voice)
|
|
199
|
+
self.validate_format(response_format)
|
|
200
|
+
|
|
201
|
+
# Call the provider-specific TTS implementation
|
|
202
|
+
return self.tts(
|
|
203
|
+
text=input_text,
|
|
204
|
+
model=model,
|
|
205
|
+
voice=voice,
|
|
206
|
+
response_format=response_format,
|
|
207
|
+
instructions=instructions,
|
|
208
|
+
verbose=verbose,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def stream_audio(
|
|
212
|
+
self,
|
|
213
|
+
text: str,
|
|
214
|
+
model: Optional[str] = None,
|
|
215
|
+
voice: Optional[str] = None,
|
|
216
|
+
response_format: Optional[str] = None,
|
|
217
|
+
instructions: Optional[str] = None,
|
|
218
|
+
chunk_size: int = 1024,
|
|
219
|
+
verbose: bool = False,
|
|
220
|
+
) -> Generator[bytes, None, None]:
|
|
221
|
+
"""
|
|
222
|
+
Stream audio in chunks with OpenAI-compatible parameters.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
text (str): The text to convert to speech
|
|
226
|
+
model (str, optional): The TTS model to use
|
|
227
|
+
voice (str, optional): The voice to use
|
|
228
|
+
response_format (str, optional): Audio format
|
|
229
|
+
instructions (str, optional): Voice instructions
|
|
230
|
+
chunk_size (int, optional): Size of audio chunks to yield. Defaults to 1024.
|
|
231
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
232
|
+
|
|
233
|
+
Yields:
|
|
234
|
+
Generator[bytes, None, None]: Audio data chunks
|
|
235
|
+
"""
|
|
236
|
+
# Generate the audio file using create_speech
|
|
237
|
+
audio_file = self.create_speech(
|
|
238
|
+
input_text=text,
|
|
239
|
+
model=model,
|
|
240
|
+
voice=voice,
|
|
241
|
+
response_format=response_format,
|
|
242
|
+
instructions=instructions,
|
|
243
|
+
verbose=verbose,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Stream the file in chunks
|
|
247
|
+
with open(audio_file, "rb") as f:
|
|
248
|
+
while chunk := f.read(chunk_size):
|
|
249
|
+
yield chunk
|
|
250
|
+
|
|
251
|
+
def tts(self, text: str, voice: Optional[str] = None, verbose: bool = False, **kwargs) -> str:
|
|
252
|
+
"""
|
|
253
|
+
Abstract method for text-to-speech conversion.
|
|
254
|
+
Must be implemented by subclasses.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
text (str): The text to convert to speech
|
|
258
|
+
**kwargs: Additional provider-specific parameters
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
str: Path to the generated audio file
|
|
262
|
+
|
|
263
|
+
Raises:
|
|
264
|
+
NotImplementedError: If not implemented by subclass
|
|
265
|
+
"""
|
|
266
|
+
raise NotImplementedError("Subclasses must implement the tts method")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class AsyncBaseTTSProvider:
|
|
270
|
+
"""
|
|
271
|
+
Base class for async TTS providers with OpenAI-compatible functionality.
|
|
272
|
+
|
|
273
|
+
This class implements common async methods following OpenAI TTS API patterns
|
|
274
|
+
for speech generation, streaming, and audio handling.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
required_auth = False
|
|
278
|
+
|
|
279
|
+
# Supported models (can be overridden by subclasses)
|
|
280
|
+
SUPPORTED_MODELS = [
|
|
281
|
+
"gpt-4o-mini-tts", # Latest intelligent realtime model
|
|
282
|
+
"tts-1", # Lower latency model
|
|
283
|
+
"tts-1-hd", # Higher quality model
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
# Supported voices (can be overridden by subclasses)
|
|
287
|
+
SUPPORTED_VOICES = [
|
|
288
|
+
"alloy",
|
|
289
|
+
"ash",
|
|
290
|
+
"ballad",
|
|
291
|
+
"coral",
|
|
292
|
+
"echo",
|
|
293
|
+
"fable",
|
|
294
|
+
"nova",
|
|
295
|
+
"onyx",
|
|
296
|
+
"sage",
|
|
297
|
+
"shimmer",
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
# Supported output formats
|
|
301
|
+
SUPPORTED_FORMATS = [
|
|
302
|
+
"mp3", # Default format
|
|
303
|
+
"opus", # Internet streaming, low latency
|
|
304
|
+
"aac", # Digital audio compression
|
|
305
|
+
"flac", # Lossless compression
|
|
306
|
+
"wav", # Uncompressed, low latency
|
|
307
|
+
"pcm", # Raw samples, 24kHz 16-bit
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
def __init__(self):
|
|
311
|
+
"""Initialize the async base TTS provider."""
|
|
312
|
+
self.temp_dir = tempfile.mkdtemp(prefix="webscout_tts_")
|
|
313
|
+
self.default_model = "gpt-4o-mini-tts"
|
|
314
|
+
self.default_voice = "coral"
|
|
315
|
+
self.default_format = "mp3"
|
|
316
|
+
|
|
317
|
+
async def validate_model(self, model: str) -> str:
|
|
318
|
+
"""
|
|
319
|
+
Validate and return the model name.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
model (str): Model name to validate
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
str: Validated model name
|
|
326
|
+
|
|
327
|
+
Raises:
|
|
328
|
+
ValueError: If model is not supported
|
|
329
|
+
"""
|
|
330
|
+
# If provider doesn't support models, return the model as-is
|
|
331
|
+
if self.SUPPORTED_MODELS is None:
|
|
332
|
+
return model
|
|
333
|
+
|
|
334
|
+
if model not in self.SUPPORTED_MODELS:
|
|
335
|
+
raise ValueError(
|
|
336
|
+
f"Model '{model}' not supported. Available models: {', '.join(self.SUPPORTED_MODELS)}"
|
|
337
|
+
)
|
|
338
|
+
return model
|
|
339
|
+
|
|
340
|
+
async def validate_voice(self, voice: str) -> str:
|
|
341
|
+
"""
|
|
342
|
+
Validate and return the voice name.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
voice (str): Voice name to validate
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
str: Validated voice name
|
|
349
|
+
|
|
350
|
+
Raises:
|
|
351
|
+
ValueError: If voice is not supported
|
|
352
|
+
"""
|
|
353
|
+
if voice not in self.SUPPORTED_VOICES:
|
|
354
|
+
raise ValueError(
|
|
355
|
+
f"Voice '{voice}' not supported. Available voices: {', '.join(self.SUPPORTED_VOICES)}"
|
|
356
|
+
)
|
|
357
|
+
return voice
|
|
358
|
+
|
|
359
|
+
async def validate_format(self, response_format: str) -> str:
|
|
360
|
+
"""
|
|
361
|
+
Validate and return the response format.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
response_format (str): Response format to validate
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
str: Validated response format
|
|
368
|
+
|
|
369
|
+
Raises:
|
|
370
|
+
ValueError: If format is not supported
|
|
371
|
+
"""
|
|
372
|
+
if response_format not in self.SUPPORTED_FORMATS:
|
|
373
|
+
raise ValueError(
|
|
374
|
+
f"Format '{response_format}' not supported. Available formats: {', '.join(self.SUPPORTED_FORMATS)}"
|
|
375
|
+
)
|
|
376
|
+
return response_format
|
|
377
|
+
|
|
378
|
+
async def save_audio(
|
|
379
|
+
self, audio_file: str, destination: Optional[str] = None, verbose: bool = False
|
|
380
|
+
) -> str:
|
|
381
|
+
"""
|
|
382
|
+
Save audio to a specific destination asynchronously.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
audio_file (str): Path to the source audio file
|
|
386
|
+
destination (str, optional): Destination path. Defaults to current directory with timestamp.
|
|
387
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
str: Path to the saved audio file
|
|
391
|
+
|
|
392
|
+
Raises:
|
|
393
|
+
FileNotFoundError: If the audio file doesn't exist
|
|
394
|
+
"""
|
|
395
|
+
import asyncio
|
|
396
|
+
import shutil
|
|
397
|
+
import time
|
|
398
|
+
|
|
399
|
+
source_path = Path(audio_file)
|
|
400
|
+
|
|
401
|
+
if not source_path.exists():
|
|
402
|
+
raise FileNotFoundError(f"Audio file not found: {audio_file}")
|
|
403
|
+
|
|
404
|
+
if destination is None:
|
|
405
|
+
# Create a default destination with timestamp in current directory
|
|
406
|
+
timestamp = int(time.time())
|
|
407
|
+
destination = os.path.join(os.getcwd(), f"speech_{timestamp}{source_path.suffix}")
|
|
408
|
+
|
|
409
|
+
# Ensure the destination directory exists
|
|
410
|
+
os.makedirs(os.path.dirname(os.path.abspath(destination)), exist_ok=True)
|
|
411
|
+
|
|
412
|
+
# Copy the file using asyncio to avoid blocking
|
|
413
|
+
await asyncio.to_thread(functools.partial(shutil.copy2, str(source_path), destination))
|
|
414
|
+
|
|
415
|
+
if verbose:
|
|
416
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
417
|
+
ic(f"Audio saved to {destination}")
|
|
418
|
+
|
|
419
|
+
return destination
|
|
420
|
+
|
|
421
|
+
async def create_speech(
|
|
422
|
+
self,
|
|
423
|
+
input_text: str,
|
|
424
|
+
model: Optional[str] = None,
|
|
425
|
+
voice: Optional[str] = None,
|
|
426
|
+
response_format: Optional[str] = None,
|
|
427
|
+
instructions: Optional[str] = None,
|
|
428
|
+
verbose: bool = False,
|
|
429
|
+
) -> str:
|
|
430
|
+
"""
|
|
431
|
+
Create speech from input text asynchronously (OpenAI-compatible interface).
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
input_text (str): The text to convert to speech
|
|
435
|
+
model (str, optional): The TTS model to use
|
|
436
|
+
voice (str, optional): The voice to use
|
|
437
|
+
response_format (str, optional): Audio format (mp3, opus, aac, flac, wav, pcm)
|
|
438
|
+
instructions (str, optional): Voice instructions for controlling speech aspects
|
|
439
|
+
verbose (bool, optional): Whether to print debug information
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
str: Path to the generated audio file
|
|
443
|
+
"""
|
|
444
|
+
# Use defaults if not provided
|
|
445
|
+
model = model or self.default_model
|
|
446
|
+
voice = voice or self.default_voice
|
|
447
|
+
response_format = response_format or self.default_format
|
|
448
|
+
|
|
449
|
+
# Validate parameters
|
|
450
|
+
await self.validate_model(model)
|
|
451
|
+
await self.validate_voice(voice)
|
|
452
|
+
await self.validate_format(response_format)
|
|
453
|
+
|
|
454
|
+
# Call the provider-specific TTS implementation
|
|
455
|
+
return await self.tts(
|
|
456
|
+
text=input_text,
|
|
457
|
+
model=model,
|
|
458
|
+
voice=voice,
|
|
459
|
+
response_format=response_format,
|
|
460
|
+
instructions=instructions,
|
|
461
|
+
verbose=verbose,
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
async def stream_audio(
|
|
465
|
+
self,
|
|
466
|
+
input_text: str,
|
|
467
|
+
model: Optional[str] = None,
|
|
468
|
+
voice: Optional[str] = None,
|
|
469
|
+
response_format: Optional[str] = None,
|
|
470
|
+
instructions: Optional[str] = None,
|
|
471
|
+
chunk_size: int = 1024,
|
|
472
|
+
verbose: bool = False,
|
|
473
|
+
):
|
|
474
|
+
"""
|
|
475
|
+
Stream audio in chunks asynchronously with OpenAI-compatible parameters.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
input_text (str): The text to convert to speech
|
|
479
|
+
model (str, optional): The TTS model to use
|
|
480
|
+
voice (str, optional): The voice to use
|
|
481
|
+
response_format (str, optional): Audio format
|
|
482
|
+
instructions (str, optional): Voice instructions
|
|
483
|
+
chunk_size (int, optional): Size of audio chunks to yield. Defaults to 1024.
|
|
484
|
+
verbose (bool, optional): Whether to print debug information. Defaults to False.
|
|
485
|
+
|
|
486
|
+
Yields:
|
|
487
|
+
AsyncGenerator[bytes, None]: Audio data chunks
|
|
488
|
+
"""
|
|
489
|
+
# Generate the audio file using create_speech
|
|
490
|
+
audio_file = await self.create_speech(
|
|
491
|
+
input_text=input_text,
|
|
492
|
+
model=model,
|
|
493
|
+
voice=voice,
|
|
494
|
+
response_format=response_format,
|
|
495
|
+
instructions=instructions,
|
|
496
|
+
verbose=verbose,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
# Stream the file in chunks using thread pool
|
|
500
|
+
def read_file_chunks(file_path, chunk_size):
|
|
501
|
+
with open(file_path, "rb") as f:
|
|
502
|
+
while chunk := f.read(chunk_size):
|
|
503
|
+
yield chunk
|
|
504
|
+
|
|
505
|
+
for chunk in await asyncio.to_thread(read_file_chunks, audio_file, chunk_size):
|
|
506
|
+
yield chunk
|
|
507
|
+
|
|
508
|
+
async def tts(self, text: str, **kwargs) -> str:
|
|
509
|
+
"""
|
|
510
|
+
Abstract async method for text-to-speech conversion.
|
|
511
|
+
Must be implemented by subclasses.
|
|
512
|
+
|
|
513
|
+
Args:
|
|
514
|
+
text (str): The text to convert to speech
|
|
515
|
+
**kwargs: Additional provider-specific parameters
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
str: Path to the generated audio file
|
|
519
|
+
|
|
520
|
+
Raises:
|
|
521
|
+
NotImplementedError: If not implemented by subclass
|
|
522
|
+
"""
|
|
523
|
+
raise NotImplementedError("Subclasses must implement the async tts method")
|