webscout 8.2.2__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 -143
- webscout/AIbase.py +247 -123
- webscout/AIutel.py +68 -132
- webscout/Bard.py +1072 -535
- webscout/Extra/GitToolkit/__init__.py +2 -2
- 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 -0
- 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 -0
- 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 -45
- 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 +189 -18
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -682
- webscout/Extra/tempmail/README.md +488 -0
- 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 +237 -304
- webscout/Provider/AISEARCH/README.md +106 -0
- webscout/Provider/AISEARCH/__init__.py +16 -10
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +130 -209
- webscout/Provider/AISEARCH/monica_search.py +200 -246
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -281
- 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 -0
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +343 -173
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +60 -54
- webscout/Provider/GithubChat.py +385 -367
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -670
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -233
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -266
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -381
- webscout/Provider/Netwrck.py +273 -228
- 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 -0
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -0
- webscout/Provider/OPENAI/__init__.py +148 -25
- webscout/Provider/OPENAI/ai4chat.py +348 -0
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/ayle.py +365 -0
- webscout/Provider/OPENAI/base.py +253 -46
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +514 -193
- webscout/Provider/OPENAI/chatsandbox.py +233 -0
- webscout/Provider/OPENAI/deepinfra.py +403 -272
- webscout/Provider/OPENAI/e2b.py +2370 -1350
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +186 -138
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -0
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +100 -104
- 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 -327
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +110 -84
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -0
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -0
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +126 -115
- webscout/Provider/OPENAI/textpollinations.py +218 -133
- webscout/Provider/OPENAI/toolbaz.py +136 -166
- webscout/Provider/OPENAI/typefully.py +419 -0
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -211
- webscout/Provider/OPENAI/wisecat.py +103 -125
- webscout/Provider/OPENAI/writecream.py +185 -156
- webscout/Provider/OPENAI/x0gpt.py +227 -136
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -344
- 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 -0
- webscout/Provider/TTI/__init__.py +37 -12
- webscout/Provider/TTI/base.py +147 -0
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -0
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -0
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -0
- webscout/Provider/TTS/README.md +186 -0
- webscout/Provider/TTS/__init__.py +43 -7
- webscout/Provider/TTS/base.py +523 -0
- 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 -0
- 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 -180
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +221 -121
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -199
- webscout/Provider/TypliAI.py +311 -0
- webscout/Provider/UNFINISHED/ChatHub.py +219 -0
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +160 -145
- webscout/Provider/UNFINISHED/GizAI.py +300 -0
- webscout/Provider/UNFINISHED/Marcus.py +218 -0
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/UNFINISHED/XenAI.py +330 -0
- webscout/Provider/{Youchat.py → UNFINISHED/Youchat.py} +64 -47
- 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 -0
- webscout/Provider/UNFINISHED/samurai.py +231 -0
- webscout/Provider/WiseCat.py +256 -196
- webscout/Provider/WrDoChat.py +390 -0
- webscout/Provider/__init__.py +115 -198
- webscout/Provider/ai4chat.py +181 -202
- webscout/Provider/akashgpt.py +330 -342
- webscout/Provider/cerebras.py +397 -242
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -234
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -266
- webscout/Provider/llama3mitril.py +230 -180
- webscout/Provider/llmchat.py +308 -213
- webscout/Provider/llmchatco.py +321 -311
- webscout/Provider/meta.py +996 -794
- webscout/Provider/oivscode.py +332 -0
- webscout/Provider/searchchat.py +316 -293
- webscout/Provider/sonus.py +264 -208
- webscout/Provider/toolbaz.py +359 -320
- webscout/Provider/turboseek.py +332 -219
- webscout/Provider/typefully.py +262 -280
- webscout/Provider/x0gpt.py +332 -256
- webscout/__init__.py +31 -38
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -293
- webscout/client.py +1497 -0
- webscout/conversation.py +140 -565
- webscout/exceptions.py +383 -339
- 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 +32 -378
- webscout/prompt_manager.py +376 -274
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -0
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -140
- webscout/scout/core/scout.py +800 -568
- 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 -460
- 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 -809
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +574 -0
- webscout/swiftcli/core/context.py +98 -0
- webscout/swiftcli/core/group.py +268 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +243 -0
- webscout/swiftcli/decorators/options.py +247 -0
- webscout/swiftcli/decorators/output.py +392 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +134 -0
- webscout/swiftcli/plugins/manager.py +269 -0
- webscout/swiftcli/utils/__init__.py +58 -0
- webscout/swiftcli/utils/formatting.py +251 -0
- webscout/swiftcli/utils/parsing.py +368 -0
- 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 -55
- webscout/zeroart/base.py +70 -60
- webscout/zeroart/effects.py +155 -99
- webscout/zeroart/fonts.py +1799 -816
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.2.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- webscout-2026.1.19.dist-info/entry_points.txt +4 -0
- webscout-2026.1.19.dist-info/top_level.txt +1 -0
- inferno/__init__.py +0 -6
- inferno/__main__.py +0 -9
- inferno/cli.py +0 -6
- webscout/DWEBS.py +0 -477
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -849
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/LLM.py +0 -442
- webscout/Litlogger/__init__.py +0 -67
- webscout/Litlogger/core/__init__.py +0 -6
- webscout/Litlogger/core/level.py +0 -23
- webscout/Litlogger/core/logger.py +0 -165
- webscout/Litlogger/handlers/__init__.py +0 -12
- webscout/Litlogger/handlers/console.py +0 -33
- webscout/Litlogger/handlers/file.py +0 -143
- webscout/Litlogger/handlers/network.py +0 -173
- webscout/Litlogger/styles/__init__.py +0 -7
- webscout/Litlogger/styles/colors.py +0 -249
- webscout/Litlogger/styles/formats.py +0 -458
- webscout/Litlogger/styles/text.py +0 -87
- webscout/Litlogger/utils/__init__.py +0 -6
- webscout/Litlogger/utils/detectors.py +0 -153
- webscout/Litlogger/utils/formatters.py +0 -200
- webscout/Local/__init__.py +0 -12
- webscout/Local/__main__.py +0 -9
- webscout/Local/api.py +0 -576
- webscout/Local/cli.py +0 -516
- webscout/Local/config.py +0 -75
- webscout/Local/llm.py +0 -287
- webscout/Local/model_manager.py +0 -253
- webscout/Local/server.py +0 -721
- webscout/Local/utils.py +0 -93
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -250
- webscout/Provider/AISEARCH/ISou.py +0 -256
- webscout/Provider/AISEARCH/felo_search.py +0 -228
- webscout/Provider/AISEARCH/genspark_search.py +0 -208
- webscout/Provider/AISEARCH/hika_search.py +0 -194
- webscout/Provider/AISEARCH/scira_search.py +0 -324
- webscout/Provider/Aitopia.py +0 -292
- webscout/Provider/AllenAI.py +0 -413
- webscout/Provider/Blackboxai.py +0 -229
- webscout/Provider/C4ai.py +0 -432
- webscout/Provider/ChatGPTClone.py +0 -226
- webscout/Provider/ChatGPTES.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Chatify.py +0 -175
- webscout/Provider/Cloudflare.py +0 -273
- webscout/Provider/DeepSeek.py +0 -196
- webscout/Provider/ElectronHub.py +0 -709
- webscout/Provider/ExaChat.py +0 -342
- webscout/Provider/Free2GPT.py +0 -241
- webscout/Provider/GPTWeb.py +0 -193
- webscout/Provider/Glider.py +0 -211
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -462
- webscout/Provider/Hunyuan.py +0 -272
- webscout/Provider/LambdaChat.py +0 -392
- webscout/Provider/Llama.py +0 -200
- webscout/Provider/Llama3.py +0 -204
- webscout/Provider/Marcus.py +0 -148
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/c4ai.py +0 -367
- webscout/Provider/OPENAI/chatgptclone.py +0 -460
- webscout/Provider/OPENAI/exachat.py +0 -433
- webscout/Provider/OPENAI/freeaichat.py +0 -352
- webscout/Provider/OPENAI/opkfc.py +0 -488
- webscout/Provider/OPENAI/scirachat.py +0 -463
- webscout/Provider/OPENAI/standardinput.py +0 -425
- webscout/Provider/OPENAI/typegpt.py +0 -346
- webscout/Provider/OPENAI/uncovrAI.py +0 -455
- webscout/Provider/OPENAI/venice.py +0 -413
- webscout/Provider/OPENAI/yep.py +0 -327
- webscout/Provider/OpenGPT.py +0 -199
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Phind.py +0 -535
- webscout/Provider/PizzaGPT.py +0 -198
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -278
- webscout/Provider/TTI/AiForce/__init__.py +0 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
- webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
- webscout/Provider/TTI/ImgSys/__init__.py +0 -23
- webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
- webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
- webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
- webscout/Provider/TTI/Nexra/__init__.py +0 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
- webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
- webscout/Provider/TTI/aiarta/__init__.py +0 -2
- webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
- webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
- webscout/Provider/TTI/artbit/__init__.py +0 -22
- webscout/Provider/TTI/artbit/async_artbit.py +0 -155
- webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
- webscout/Provider/TTI/fastflux/__init__.py +0 -22
- webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
- webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
- webscout/Provider/TTI/huggingface/__init__.py +0 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
- webscout/Provider/TTI/piclumen/__init__.py +0 -23
- webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
- webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
- webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
- webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
- webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
- webscout/Provider/TTI/talkai/__init__.py +0 -4
- webscout/Provider/TTI/talkai/async_talkai.py +0 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
- webscout/Provider/TTS/gesserit.py +0 -127
- webscout/Provider/TeachAnything.py +0 -187
- webscout/Provider/Venice.py +0 -219
- webscout/Provider/VercelAI.py +0 -234
- webscout/Provider/WebSim.py +0 -228
- webscout/Provider/Writecream.py +0 -211
- webscout/Provider/WritingMate.py +0 -197
- webscout/Provider/aimathgpt.py +0 -189
- webscout/Provider/askmyai.py +0 -158
- webscout/Provider/asksteve.py +0 -203
- webscout/Provider/bagoodex.py +0 -145
- webscout/Provider/chatglm.py +0 -205
- webscout/Provider/copilot.py +0 -428
- webscout/Provider/freeaichat.py +0 -271
- webscout/Provider/gaurish.py +0 -244
- webscout/Provider/geminiprorealtime.py +0 -160
- webscout/Provider/granite.py +0 -187
- webscout/Provider/hermes.py +0 -219
- webscout/Provider/koala.py +0 -268
- webscout/Provider/labyrinth.py +0 -340
- webscout/Provider/lepton.py +0 -194
- webscout/Provider/llamatutor.py +0 -192
- webscout/Provider/multichat.py +0 -325
- webscout/Provider/promptrefine.py +0 -193
- webscout/Provider/scira_chat.py +0 -277
- webscout/Provider/scnet.py +0 -187
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/tutorai.py +0 -252
- webscout/Provider/typegpt.py +0 -232
- webscout/Provider/uncovr.py +0 -312
- webscout/Provider/yep.py +0 -376
- webscout/litprinter/__init__.py +0 -59
- webscout/scout/core.py +0 -881
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1346
- webscout/webscout_search_async.py +0 -877
- webscout/yep_search.py +0 -297
- webscout-8.2.2.dist-info/METADATA +0 -734
- webscout-8.2.2.dist-info/RECORD +0 -309
- webscout-8.2.2.dist-info/entry_points.txt +0 -5
- webscout-8.2.2.dist-info/top_level.txt +0 -3
- webstoken/__init__.py +0 -30
- webstoken/classifier.py +0 -189
- webstoken/keywords.py +0 -216
- webstoken/language.py +0 -128
- webstoken/ner.py +0 -164
- webstoken/normalizer.py +0 -35
- webstoken/processor.py +0 -77
- webstoken/sentiment.py +0 -206
- webstoken/stemmer.py +0 -73
- webstoken/tagger.py +0 -60
- webstoken/tokenizer.py +0 -158
- {webscout-8.2.2.dist-info → webscout-2026.1.19.dist-info/licenses}/LICENSE.md +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
##################################################################################
|
|
2
|
+
## OpenAI.fm TTS Provider ##
|
|
3
|
+
##################################################################################
|
|
4
|
+
import pathlib
|
|
5
|
+
import tempfile
|
|
6
|
+
from typing import Any, Generator, List, Optional, Union, cast
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from litprinter import ic
|
|
10
|
+
|
|
11
|
+
from webscout import exceptions
|
|
12
|
+
from webscout.litagent import LitAgent
|
|
13
|
+
from webscout.Provider.TTS.base import BaseTTSProvider
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OpenAIFMTTS(BaseTTSProvider):
|
|
17
|
+
"""
|
|
18
|
+
Text-to-speech provider using the OpenAI.fm API with OpenAI-compatible interface.
|
|
19
|
+
|
|
20
|
+
This provider follows the OpenAI TTS API structure with support for:
|
|
21
|
+
- Multiple TTS models (gpt-4o-mini-tts, tts-1, tts-1-hd)
|
|
22
|
+
- 11 built-in voices optimized for English
|
|
23
|
+
- Voice instructions for controlling speech aspects
|
|
24
|
+
- Multiple output formats
|
|
25
|
+
- Streaming support
|
|
26
|
+
"""
|
|
27
|
+
required_auth = False
|
|
28
|
+
|
|
29
|
+
# Request headers
|
|
30
|
+
headers = {
|
|
31
|
+
"accept": "*/*",
|
|
32
|
+
"accept-language": "en-US,en;q=0.9",
|
|
33
|
+
"cache-control": "no-cache",
|
|
34
|
+
"pragma": "no-cache",
|
|
35
|
+
"sec-fetch-dest": "audio",
|
|
36
|
+
"sec-fetch-mode": "no-cors",
|
|
37
|
+
"sec-fetch-site": "same-origin",
|
|
38
|
+
"user-agent": LitAgent().random(),
|
|
39
|
+
"referer": "https://www.openai.fm"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Override supported models for OpenAI.fm
|
|
43
|
+
SUPPORTED_MODELS = [
|
|
44
|
+
"gpt-4o-mini-tts", # Latest intelligent realtime model
|
|
45
|
+
"tts-1", # Lower latency model
|
|
46
|
+
"tts-1-hd" # Higher quality model
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# OpenAI.fm supported voices (11 built-in voices)
|
|
50
|
+
SUPPORTED_VOICES = [
|
|
51
|
+
"alloy", # Neutral voice with balanced tone
|
|
52
|
+
"ash", # Calm and thoughtful male voice
|
|
53
|
+
"ballad", # Soft and melodic voice
|
|
54
|
+
"coral", # Warm and inviting female voice
|
|
55
|
+
"echo", # Clear and precise voice
|
|
56
|
+
"fable", # Authoritative and narrative voice
|
|
57
|
+
"nova", # Energetic and bright female voice
|
|
58
|
+
"onyx", # Deep and resonant male voice
|
|
59
|
+
"sage", # Measured and contemplative voice
|
|
60
|
+
"shimmer" # Bright and optimistic voice
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
# Voice mapping for API compatibility
|
|
64
|
+
voice_mapping = {
|
|
65
|
+
"alloy": "alloy",
|
|
66
|
+
"ash": "ash",
|
|
67
|
+
"ballad": "ballad",
|
|
68
|
+
"coral": "coral",
|
|
69
|
+
"echo": "echo",
|
|
70
|
+
"fable": "fable",
|
|
71
|
+
"nova": "nova",
|
|
72
|
+
"onyx": "onyx",
|
|
73
|
+
"sage": "sage",
|
|
74
|
+
"shimmer": "shimmer"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
def __init__(self, timeout: int = 20, proxies: Optional[dict] = None):
|
|
78
|
+
"""
|
|
79
|
+
Initialize the OpenAI.fm TTS client.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
timeout (int): Request timeout in seconds
|
|
83
|
+
proxies (dict): Proxy configuration
|
|
84
|
+
"""
|
|
85
|
+
super().__init__()
|
|
86
|
+
self.api_url = "https://www.openai.fm/api/generate"
|
|
87
|
+
self.session = requests.Session()
|
|
88
|
+
self.session.headers.update(self.headers)
|
|
89
|
+
if proxies:
|
|
90
|
+
self.session.proxies.update(proxies)
|
|
91
|
+
self.timeout = timeout
|
|
92
|
+
|
|
93
|
+
def tts(
|
|
94
|
+
self,
|
|
95
|
+
text: str,
|
|
96
|
+
voice: Optional[str] = None,
|
|
97
|
+
verbose: bool = False,
|
|
98
|
+
**kwargs
|
|
99
|
+
) -> str:
|
|
100
|
+
"""
|
|
101
|
+
Convert text to speech using OpenAI.fm API with OpenAI-compatible parameters.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
text (str): The text to convert to speech (max 10,000 characters)
|
|
105
|
+
model (str): The TTS model to use (gpt-4o-mini-tts, tts-1, tts-1-hd)
|
|
106
|
+
voice (str): The voice to use for TTS (alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer)
|
|
107
|
+
response_format (str): Audio format (mp3, opus, aac, flac, wav, pcm)
|
|
108
|
+
instructions (str): Voice instructions for controlling speech aspects like accent, tone, speed
|
|
109
|
+
verbose (bool): Whether to print debug information
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
str: Path to the generated audio file
|
|
113
|
+
|
|
114
|
+
Raises:
|
|
115
|
+
ValueError: If input parameters are invalid
|
|
116
|
+
exceptions.FailedToGenerateResponseError: If there is an error generating or saving the audio
|
|
117
|
+
"""
|
|
118
|
+
# Extract optional parameters from kwargs
|
|
119
|
+
model = kwargs.get("model", "gpt-4o-mini-tts")
|
|
120
|
+
response_format = kwargs.get("response_format", "mp3")
|
|
121
|
+
instructions = kwargs.get("instructions", None)
|
|
122
|
+
|
|
123
|
+
# Validate input parameters
|
|
124
|
+
if not text or not isinstance(text, str):
|
|
125
|
+
raise ValueError("Input text must be a non-empty string")
|
|
126
|
+
if len(text) > 10000:
|
|
127
|
+
raise ValueError("Input text exceeds maximum allowed length of 10,000 characters")
|
|
128
|
+
|
|
129
|
+
# Validate model, voice, and format using base class methods
|
|
130
|
+
model = self.validate_model(model)
|
|
131
|
+
voice = self.validate_voice(voice or "coral")
|
|
132
|
+
response_format = self.validate_format(response_format)
|
|
133
|
+
|
|
134
|
+
# Map voice to API format
|
|
135
|
+
voice_id = self.voice_mapping.get(voice, voice)
|
|
136
|
+
|
|
137
|
+
# Set default instructions if not provided
|
|
138
|
+
if instructions is None:
|
|
139
|
+
instructions = "Speak in a cheerful and positive tone."
|
|
140
|
+
|
|
141
|
+
# Create temporary file with appropriate extension
|
|
142
|
+
file_extension = f".{response_format}" if response_format != "pcm" else ".wav"
|
|
143
|
+
with tempfile.NamedTemporaryFile(suffix=file_extension, dir=self.temp_dir, delete=False) as temp_file:
|
|
144
|
+
filename = pathlib.Path(temp_file.name)
|
|
145
|
+
|
|
146
|
+
# Prepare parameters for the API request
|
|
147
|
+
params = {
|
|
148
|
+
"input": text,
|
|
149
|
+
"prompt": instructions,
|
|
150
|
+
"voice": voice_id,
|
|
151
|
+
"model": model,
|
|
152
|
+
"response_format": response_format
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
# Make the API request
|
|
157
|
+
response = self.session.get(
|
|
158
|
+
self.api_url,
|
|
159
|
+
params=params,
|
|
160
|
+
timeout=self.timeout
|
|
161
|
+
)
|
|
162
|
+
response.raise_for_status()
|
|
163
|
+
|
|
164
|
+
# Validate response content
|
|
165
|
+
if not response.content:
|
|
166
|
+
raise exceptions.FailedToGenerateResponseError("Empty response from API")
|
|
167
|
+
|
|
168
|
+
# Save the audio file
|
|
169
|
+
with open(filename, "wb") as f:
|
|
170
|
+
f.write(response.content)
|
|
171
|
+
|
|
172
|
+
if verbose:
|
|
173
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
174
|
+
ic("Speech generated successfully")
|
|
175
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
176
|
+
ic(f"Model: {model}")
|
|
177
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
178
|
+
ic(f"Voice: {voice}")
|
|
179
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
180
|
+
ic(f"Format: {response_format}")
|
|
181
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
182
|
+
ic(f"Audio saved to {filename}")
|
|
183
|
+
|
|
184
|
+
return filename.as_posix()
|
|
185
|
+
|
|
186
|
+
except requests.exceptions.RequestException as e:
|
|
187
|
+
if verbose:
|
|
188
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
189
|
+
ic(f"Failed to generate speech: {e}")
|
|
190
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
191
|
+
f"Failed to generate speech: {e}"
|
|
192
|
+
)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
if verbose:
|
|
195
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
196
|
+
ic(f"Unexpected error: {e}")
|
|
197
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
198
|
+
f"Unexpected error during speech generation: {e}"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def create_speech(
|
|
202
|
+
self,
|
|
203
|
+
input_text: str,
|
|
204
|
+
model: Optional[str] = "gpt-4o-mini-tts",
|
|
205
|
+
voice: Optional[str] = "alloy",
|
|
206
|
+
response_format: Optional[str] = "mp3",
|
|
207
|
+
instructions: Optional[str] = None,
|
|
208
|
+
verbose: bool = False,
|
|
209
|
+
**kwargs: Any
|
|
210
|
+
) -> str:
|
|
211
|
+
"""
|
|
212
|
+
OpenAI-compatible speech creation interface.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
input_text (str): The text to convert to speech
|
|
216
|
+
model (str): The TTS model to use
|
|
217
|
+
voice (str): The voice to use
|
|
218
|
+
response_format (str): Audio format
|
|
219
|
+
instructions (str): Voice instructions
|
|
220
|
+
verbose (bool): Whether to print debug information
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
str: Path to the generated audio file
|
|
224
|
+
"""
|
|
225
|
+
return self.tts(
|
|
226
|
+
text=input_text,
|
|
227
|
+
voice=voice or "alloy",
|
|
228
|
+
model=model or "gpt-4o-mini-tts",
|
|
229
|
+
response_format=response_format or "mp3",
|
|
230
|
+
verbose=verbose
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def with_streaming_response(self):
|
|
234
|
+
"""
|
|
235
|
+
Return a streaming response context manager (OpenAI-compatible).
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
StreamingResponseContextManager: Context manager for streaming responses
|
|
239
|
+
"""
|
|
240
|
+
return StreamingResponseContextManager(self)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class StreamingResponseContextManager:
|
|
244
|
+
"""
|
|
245
|
+
Context manager for streaming TTS responses (OpenAI-compatible).
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
def __init__(self, tts_provider: OpenAIFMTTS):
|
|
249
|
+
self.tts_provider = tts_provider
|
|
250
|
+
self.audio_file = None
|
|
251
|
+
|
|
252
|
+
def create(
|
|
253
|
+
self,
|
|
254
|
+
input_text: str,
|
|
255
|
+
model: Optional[str] = "gpt-4o-mini-tts",
|
|
256
|
+
voice: Optional[str] = "coral",
|
|
257
|
+
response_format: Optional[str] = "mp3",
|
|
258
|
+
instructions: Optional[str] = None
|
|
259
|
+
):
|
|
260
|
+
"""
|
|
261
|
+
Create speech with streaming capability.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
input_text (str): The text to convert to speech
|
|
265
|
+
model (str): The TTS model to use
|
|
266
|
+
voice (str): The voice to use
|
|
267
|
+
response_format (str): Audio format
|
|
268
|
+
instructions (str): Voice instructions
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
StreamingResponse: Streaming response object
|
|
272
|
+
"""
|
|
273
|
+
self.audio_file = self.tts_provider.create_speech(
|
|
274
|
+
input_text=input_text,
|
|
275
|
+
model=model,
|
|
276
|
+
voice=voice,
|
|
277
|
+
response_format=response_format,
|
|
278
|
+
instructions=instructions
|
|
279
|
+
)
|
|
280
|
+
return StreamingResponse(self.audio_file)
|
|
281
|
+
|
|
282
|
+
def __enter__(self):
|
|
283
|
+
return self
|
|
284
|
+
|
|
285
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
286
|
+
pass
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class StreamingResponse:
|
|
290
|
+
"""
|
|
291
|
+
Streaming response object for TTS audio (OpenAI-compatible).
|
|
292
|
+
"""
|
|
293
|
+
|
|
294
|
+
def __init__(self, audio_file: str):
|
|
295
|
+
self.audio_file = audio_file
|
|
296
|
+
|
|
297
|
+
def __enter__(self):
|
|
298
|
+
"""Enter the context manager."""
|
|
299
|
+
return self
|
|
300
|
+
|
|
301
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
302
|
+
"""Exit the context manager."""
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
def stream_to_file(self, file_path: str, chunk_size: int = 1024):
|
|
306
|
+
"""
|
|
307
|
+
Stream audio content to a file.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
file_path (str): Destination file path
|
|
311
|
+
chunk_size (int): Size of chunks to read/write
|
|
312
|
+
"""
|
|
313
|
+
import shutil
|
|
314
|
+
shutil.copy2(self.audio_file, file_path)
|
|
315
|
+
|
|
316
|
+
def iter_bytes(self, chunk_size: int = 1024):
|
|
317
|
+
"""
|
|
318
|
+
Iterate over audio bytes in chunks.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
chunk_size (int): Size of chunks to yield
|
|
322
|
+
|
|
323
|
+
Yields:
|
|
324
|
+
bytes: Audio data chunks
|
|
325
|
+
"""
|
|
326
|
+
with open(self.audio_file, 'rb') as f:
|
|
327
|
+
while chunk := f.read(chunk_size):
|
|
328
|
+
yield chunk
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
if __name__ == "__main__":
|
|
332
|
+
# Example usage demonstrating OpenAI-compatible interface
|
|
333
|
+
tts_provider = OpenAIFMTTS()
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
# Basic usage
|
|
337
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
338
|
+
ic("Testing basic speech generation...")
|
|
339
|
+
audio_file = tts_provider.create_speech(
|
|
340
|
+
input_text="Today is a wonderful day to build something people love!",
|
|
341
|
+
model="gpt-4o-mini-tts",
|
|
342
|
+
voice="coral",
|
|
343
|
+
instructions="Speak in a cheerful and positive tone."
|
|
344
|
+
)
|
|
345
|
+
print(f"Audio file generated: {audio_file}")
|
|
346
|
+
|
|
347
|
+
# Streaming usage
|
|
348
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
349
|
+
ic("Testing streaming response...")
|
|
350
|
+
with tts_provider.with_streaming_response().create(
|
|
351
|
+
input_text="This is a streaming test.",
|
|
352
|
+
voice="alloy",
|
|
353
|
+
response_format="wav"
|
|
354
|
+
) as response:
|
|
355
|
+
response.stream_to_file("streaming_test.wav")
|
|
356
|
+
ic.configureOutput(prefix='INFO| ')
|
|
357
|
+
ic("Streaming audio saved to streaming_test.wav")
|
|
358
|
+
|
|
359
|
+
except exceptions.FailedToGenerateResponseError as e:
|
|
360
|
+
ic.configureOutput(prefix='ERROR| ')
|
|
361
|
+
ic(f"Error: {e}")
|
|
362
|
+
except Exception as e:
|
|
363
|
+
ic.configureOutput(prefix='ERROR| ')
|
|
364
|
+
ic(f"Unexpected error: {e}")
|
webscout/Provider/TTS/parler.py
CHANGED
|
@@ -1,111 +1,203 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
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
|
-
|
|
1
|
+
##################################################################################
|
|
2
|
+
## ParlerTTS Provider ##
|
|
3
|
+
##################################################################################
|
|
4
|
+
import json
|
|
5
|
+
import pathlib
|
|
6
|
+
import random
|
|
7
|
+
import string
|
|
8
|
+
import tempfile
|
|
9
|
+
from typing import Any, Optional, Union, cast
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
from litprinter import ic
|
|
13
|
+
|
|
14
|
+
from webscout import exceptions
|
|
15
|
+
from webscout.litagent import LitAgent
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
from . import utils
|
|
19
|
+
from .base import BaseTTSProvider
|
|
20
|
+
except ImportError:
|
|
21
|
+
# Handle direct execution
|
|
22
|
+
import os
|
|
23
|
+
import sys
|
|
24
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
25
|
+
from webscout.Provider.TTS.base import BaseTTSProvider
|
|
26
|
+
|
|
27
|
+
class ParlerTTS(BaseTTSProvider):
|
|
28
|
+
"""
|
|
29
|
+
Text-to-speech provider using the Parler-TTS API (Hugging Face Spaces).
|
|
30
|
+
|
|
31
|
+
Features:
|
|
32
|
+
- High-fidelity speech generation
|
|
33
|
+
- Controllable via simple text prompts (description)
|
|
34
|
+
- Manual polling logic for robustness
|
|
35
|
+
"""
|
|
36
|
+
required_auth = False
|
|
37
|
+
|
|
38
|
+
BASE_URL = "https://parler-tts-parler-tts.hf.space"
|
|
39
|
+
|
|
40
|
+
# Request headers
|
|
41
|
+
headers: dict[str, str] = {
|
|
42
|
+
"User-Agent": LitAgent().random(),
|
|
43
|
+
"origin": BASE_URL,
|
|
44
|
+
"referer": f"{BASE_URL}/",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
SUPPORTED_MODELS = ["parler-mini-v1", "parler-large-v1"]
|
|
48
|
+
|
|
49
|
+
def __init__(self, timeout: int = 120, proxy: Optional[str] = None):
|
|
50
|
+
"""
|
|
51
|
+
Initialize the ParlerTTS client.
|
|
52
|
+
"""
|
|
53
|
+
super().__init__()
|
|
54
|
+
self.timeout = timeout
|
|
55
|
+
self.proxy = proxy
|
|
56
|
+
self.default_model = "parler-mini-v1"
|
|
57
|
+
|
|
58
|
+
def _generate_session_hash(self) -> str:
|
|
59
|
+
return "".join(random.choices(string.ascii_lowercase + string.digits, k=10))
|
|
60
|
+
|
|
61
|
+
def tts(self, text: str, voice: Optional[str] = None, verbose: bool = False, **kwargs) -> str:
|
|
62
|
+
"""
|
|
63
|
+
Convert text to speech using Parler-TTS API.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
text (str): The text to convert to speech
|
|
67
|
+
voice (str): The voice to use
|
|
68
|
+
verbose (bool): Whether to print debug information
|
|
69
|
+
**kwargs: Additional parameters
|
|
70
|
+
"""
|
|
71
|
+
# Extract parameters from kwargs with defaults
|
|
72
|
+
description = kwargs.get('description', "A female speaker delivers a slightly expressive and animated speech with a moderate speed. The recording features a low-pitch voice and very clear audio.")
|
|
73
|
+
use_large = kwargs.get('use_large', False)
|
|
74
|
+
response_format = kwargs.get('response_format', "wav")
|
|
75
|
+
verbose = verbose if verbose is not None else kwargs.get('verbose', True)
|
|
76
|
+
|
|
77
|
+
if not text:
|
|
78
|
+
raise ValueError("Input text must be a non-empty string")
|
|
79
|
+
|
|
80
|
+
session_hash = self._generate_session_hash()
|
|
81
|
+
filename = pathlib.Path(tempfile.NamedTemporaryFile(suffix=f".{response_format}", dir=self.temp_dir, delete=False).name)
|
|
82
|
+
|
|
83
|
+
if verbose:
|
|
84
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
85
|
+
ic(f"ParlerTTS: Generating speech for '{text[:20]}...'")
|
|
86
|
+
|
|
87
|
+
client_kwargs: dict[str, Any] = {"headers": self.headers, "timeout": self.timeout}
|
|
88
|
+
if self.proxy:
|
|
89
|
+
client_kwargs["proxy"] = self.proxy
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
with httpx.Client(**client_kwargs) as client:
|
|
93
|
+
# Step 1: Join the queue
|
|
94
|
+
join_url = f"{self.BASE_URL}/queue/join?__theme=system"
|
|
95
|
+
# fn_index 0 is for the main generation task
|
|
96
|
+
payload = {
|
|
97
|
+
"data": [text, description, use_large],
|
|
98
|
+
"event_data": None,
|
|
99
|
+
"fn_index": 0,
|
|
100
|
+
"trigger_id": 8,
|
|
101
|
+
"session_hash": session_hash
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
response = client.post(join_url, json=payload)
|
|
105
|
+
response.raise_for_status()
|
|
106
|
+
|
|
107
|
+
# Step 2: Poll for data
|
|
108
|
+
data_url = f"{self.BASE_URL}/queue/data?session_hash={session_hash}"
|
|
109
|
+
audio_url = None
|
|
110
|
+
|
|
111
|
+
# Gradio Spaces can take time to wake up or process
|
|
112
|
+
with client.stream("GET", data_url) as stream:
|
|
113
|
+
for line in stream.iter_lines():
|
|
114
|
+
if not line:
|
|
115
|
+
continue
|
|
116
|
+
if line.startswith("data: "):
|
|
117
|
+
try:
|
|
118
|
+
data = json.loads(line[6:])
|
|
119
|
+
except json.JSONDecodeError:
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
msg = data.get("msg")
|
|
123
|
+
if msg == "process_completed":
|
|
124
|
+
if data.get("success"):
|
|
125
|
+
output_data = data.get("output", {}).get("data", [])
|
|
126
|
+
if output_data:
|
|
127
|
+
audio_info = output_data[0]
|
|
128
|
+
path = audio_info["path"] if isinstance(audio_info, dict) else audio_info
|
|
129
|
+
audio_url = f"{self.BASE_URL}/file={path}"
|
|
130
|
+
break
|
|
131
|
+
else:
|
|
132
|
+
raise exceptions.FailedToGenerateResponseError(f"Generation failed: {data}")
|
|
133
|
+
elif msg == "queue_full":
|
|
134
|
+
raise exceptions.FailedToGenerateResponseError("Queue is full")
|
|
135
|
+
elif msg == "send_hash":
|
|
136
|
+
# Normal handshake
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
if not audio_url:
|
|
140
|
+
raise exceptions.FailedToGenerateResponseError("Failed to get audio URL from stream")
|
|
141
|
+
|
|
142
|
+
# Step 3: Download the audio file
|
|
143
|
+
audio_response = client.get(audio_url)
|
|
144
|
+
audio_response.raise_for_status()
|
|
145
|
+
|
|
146
|
+
with open(filename, "wb") as f:
|
|
147
|
+
f.write(audio_response.content)
|
|
148
|
+
|
|
149
|
+
if verbose:
|
|
150
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
151
|
+
ic(f"Speech generated successfully: {filename}")
|
|
152
|
+
|
|
153
|
+
return filename.as_posix()
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
if verbose:
|
|
157
|
+
ic.configureOutput(prefix='DEBUG| ')
|
|
158
|
+
ic(f"Error in ParlerTTS: {e}")
|
|
159
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to generate audio: {e}")
|
|
160
|
+
|
|
161
|
+
def create_speech(
|
|
162
|
+
self,
|
|
163
|
+
input_text: str,
|
|
164
|
+
model: Optional[str] = "parler-mini-v1",
|
|
165
|
+
voice: Optional[str] = None,
|
|
166
|
+
response_format: Optional[str] = "mp3",
|
|
167
|
+
instructions: Optional[str] = None,
|
|
168
|
+
verbose: bool = False
|
|
169
|
+
) -> str:
|
|
170
|
+
"""
|
|
171
|
+
OpenAI-compatible speech creation interface.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
input_text (str): The text to convert to speech
|
|
175
|
+
model (str): The TTS model to use
|
|
176
|
+
voice (str): The voice to use (not used by ParlerAI directly)
|
|
177
|
+
response_format (str): Audio format
|
|
178
|
+
instructions (str): Voice instructions (used as description)
|
|
179
|
+
verbose (bool): Whether to print debug information
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
str: Path to the generated audio file
|
|
183
|
+
"""
|
|
184
|
+
description = instructions or "A female speaker delivers a slightly expressive and animated speech with a moderate speed. The recording features a low-pitch voice and very clear audio."
|
|
185
|
+
use_large = (model == "parler-large-v1")
|
|
186
|
+
|
|
187
|
+
return self.tts(
|
|
188
|
+
text=input_text,
|
|
189
|
+
description=description,
|
|
190
|
+
use_large=use_large,
|
|
191
|
+
response_format=response_format or "mp3",
|
|
192
|
+
verbose=verbose
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if __name__ == "__main__":
|
|
196
|
+
tts = ParlerTTS()
|
|
197
|
+
try:
|
|
198
|
+
path = tts.tts("Testing Parler-TTS with manual polling.", verbose=True)
|
|
199
|
+
ic.configureOutput(prefix='INFO| ')
|
|
200
|
+
ic(f"Saved to {path}")
|
|
201
|
+
except Exception as e:
|
|
202
|
+
ic.configureOutput(prefix='ERROR| ')
|
|
203
|
+
ic(f"Error: {e}")
|