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/murfai.py
CHANGED
|
@@ -1,113 +1,288 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
from io import BytesIO
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
from
|
|
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
|
-
|
|
1
|
+
import pathlib
|
|
2
|
+
import tempfile
|
|
3
|
+
import time
|
|
4
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from typing import Generator, Optional
|
|
7
|
+
from urllib.parse import urlencode
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from litprinter import ic
|
|
11
|
+
|
|
12
|
+
from webscout import exceptions
|
|
13
|
+
from webscout.litagent import LitAgent
|
|
14
|
+
|
|
15
|
+
from . import utils
|
|
16
|
+
from .base import BaseTTSProvider
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MurfAITTS(BaseTTSProvider):
|
|
20
|
+
"""
|
|
21
|
+
Text-to-speech provider using the MurfAITTS API with OpenAI-compatible interface.
|
|
22
|
+
|
|
23
|
+
This provider follows the OpenAI TTS API structure with support for:
|
|
24
|
+
- Multiple TTS models (gpt-4o-mini-tts, tts-1, tts-1-hd)
|
|
25
|
+
- Multiple voices with OpenAI-style naming
|
|
26
|
+
- Voice instructions for controlling speech aspects
|
|
27
|
+
- Multiple output formats (mp3, wav, aac, flac, opus, pcm)
|
|
28
|
+
- Streaming support
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
required_auth = False
|
|
32
|
+
|
|
33
|
+
# Override supported models for MurfAI (set to None as requested)
|
|
34
|
+
SUPPORTED_MODELS = None
|
|
35
|
+
|
|
36
|
+
# Override supported voices with real MurfAI voice names
|
|
37
|
+
SUPPORTED_VOICES = [
|
|
38
|
+
"Hazel",
|
|
39
|
+
"Marcus",
|
|
40
|
+
"Samantha",
|
|
41
|
+
"Natalie",
|
|
42
|
+
"Michelle",
|
|
43
|
+
"Ken",
|
|
44
|
+
"Clint",
|
|
45
|
+
"Amit",
|
|
46
|
+
"Priya",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
# Override supported formats
|
|
50
|
+
SUPPORTED_FORMATS = [
|
|
51
|
+
"mp3", # Default format for MurfAI
|
|
52
|
+
"wav", # Alternative format
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
# Request headers
|
|
56
|
+
headers: dict[str, str] = {"User-Agent": LitAgent().random()}
|
|
57
|
+
|
|
58
|
+
# Voice mapping from real names to MurfAI voice IDs
|
|
59
|
+
voice_mapping: dict[str, str] = {
|
|
60
|
+
"Hazel": "en-UK-hazel",
|
|
61
|
+
"Marcus": "en-US-marcus",
|
|
62
|
+
"Samantha": "en-US-samantha",
|
|
63
|
+
"Natalie": "en-US-natalie",
|
|
64
|
+
"Michelle": "en-US-michelle",
|
|
65
|
+
"Ken": "en-US-ken",
|
|
66
|
+
"Clint": "en-US-clint",
|
|
67
|
+
"Amit": "en-IN-amit",
|
|
68
|
+
"Priya": "en-IN-priya",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def __init__(self, timeout: int = 20, proxies: Optional[dict] = None):
|
|
72
|
+
"""Initializes the MurfAITTS TTS client."""
|
|
73
|
+
super().__init__()
|
|
74
|
+
self.session = requests.Session()
|
|
75
|
+
self.session.headers.update(self.headers)
|
|
76
|
+
if proxies:
|
|
77
|
+
self.session.proxies.update(proxies)
|
|
78
|
+
self.timeout = timeout
|
|
79
|
+
|
|
80
|
+
def tts(self, text: str, voice: Optional[str] = None, verbose: bool = False, **kwargs) -> str:
|
|
81
|
+
"""
|
|
82
|
+
Converts text to speech using the MurfAITTS API and saves it to a file.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
text (str): The text to convert to speech
|
|
86
|
+
voice (str): The voice to use (default: "Hazel")
|
|
87
|
+
verbose (bool): Whether to print debug information
|
|
88
|
+
**kwargs: Additional parameters (model, response_format, instructions)
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
str: Path to the generated audio file
|
|
92
|
+
"""
|
|
93
|
+
# Validate input parameters
|
|
94
|
+
if not text or not isinstance(text, str):
|
|
95
|
+
raise ValueError("Input text must be a non-empty string")
|
|
96
|
+
if len(text) > 10000:
|
|
97
|
+
raise ValueError("Input text exceeds maximum allowed length of 10,000 characters")
|
|
98
|
+
|
|
99
|
+
# Use default voice if not provided
|
|
100
|
+
if voice is None:
|
|
101
|
+
voice = "Hazel"
|
|
102
|
+
|
|
103
|
+
# Validate voice using base class method
|
|
104
|
+
self.validate_voice(voice)
|
|
105
|
+
|
|
106
|
+
# Map real voice name to MurfAI voice ID
|
|
107
|
+
voice_id = self.voice_mapping.get(voice, "en-UK-hazel") # Default to Hazel
|
|
108
|
+
|
|
109
|
+
# Get response format from kwargs or use default
|
|
110
|
+
response_format = kwargs.get("response_format", "mp3")
|
|
111
|
+
response_format = self.validate_format(response_format)
|
|
112
|
+
|
|
113
|
+
# Create temporary file with appropriate extension
|
|
114
|
+
file_extension = f".{response_format}" if response_format != "pcm" else ".wav"
|
|
115
|
+
temp_file = tempfile.NamedTemporaryFile(suffix=file_extension, dir=self.temp_dir, delete=False)
|
|
116
|
+
temp_file.close()
|
|
117
|
+
filename = pathlib.Path(temp_file.name)
|
|
118
|
+
|
|
119
|
+
# Split text into sentences
|
|
120
|
+
sentences = utils.split_sentences(text)
|
|
121
|
+
|
|
122
|
+
# Function to request audio for each chunk
|
|
123
|
+
def generate_audio_for_chunk(part_text: str, part_number: int):
|
|
124
|
+
while True:
|
|
125
|
+
try:
|
|
126
|
+
params: dict[str, str] = {"name": voice_id, "text": part_text}
|
|
127
|
+
encode_param: str = urlencode(params)
|
|
128
|
+
response = self.session.get(
|
|
129
|
+
f"https://murf.ai/Prod/anonymous-tts/audio?{encode_param}",
|
|
130
|
+
headers=self.headers,
|
|
131
|
+
timeout=self.timeout,
|
|
132
|
+
)
|
|
133
|
+
response.raise_for_status()
|
|
134
|
+
|
|
135
|
+
# Check if the request was successful
|
|
136
|
+
if response.ok and response.status_code == 200:
|
|
137
|
+
if verbose:
|
|
138
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
139
|
+
ic(f"Chunk {part_number} processed successfully")
|
|
140
|
+
return part_number, response.content
|
|
141
|
+
else:
|
|
142
|
+
if verbose:
|
|
143
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
144
|
+
ic(f"No data received for chunk {part_number}. Retrying...")
|
|
145
|
+
except requests.RequestException as e:
|
|
146
|
+
if verbose:
|
|
147
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
148
|
+
ic(f"Error for chunk {part_number}: {e}. Retrying...")
|
|
149
|
+
time.sleep(1)
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
# Using ThreadPoolExecutor to handle requests concurrently
|
|
153
|
+
with ThreadPoolExecutor() as executor:
|
|
154
|
+
futures = {
|
|
155
|
+
executor.submit(
|
|
156
|
+
generate_audio_for_chunk, sentence.strip(), chunk_num
|
|
157
|
+
): chunk_num
|
|
158
|
+
for chunk_num, sentence in enumerate(sentences, start=1)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# Dictionary to store results with order preserved
|
|
162
|
+
audio_chunks = {}
|
|
163
|
+
|
|
164
|
+
for future in as_completed(futures):
|
|
165
|
+
chunk_num = futures[future]
|
|
166
|
+
try:
|
|
167
|
+
part_number, audio_data = future.result()
|
|
168
|
+
audio_chunks[part_number] = (
|
|
169
|
+
audio_data # Store the audio data in correct sequence
|
|
170
|
+
)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
if verbose:
|
|
173
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
174
|
+
ic(f"Failed to generate audio for chunk {chunk_num}: {e}")
|
|
175
|
+
|
|
176
|
+
# Combine audio chunks in the correct sequence
|
|
177
|
+
combined_audio = BytesIO()
|
|
178
|
+
for part_number in sorted(audio_chunks.keys()):
|
|
179
|
+
combined_audio.write(audio_chunks[part_number])
|
|
180
|
+
if verbose:
|
|
181
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
182
|
+
ic(f"Added chunk {part_number} to the combined file.")
|
|
183
|
+
|
|
184
|
+
# Save the combined audio data to a single file
|
|
185
|
+
with open(filename, "wb") as f:
|
|
186
|
+
f.write(combined_audio.getvalue())
|
|
187
|
+
if verbose:
|
|
188
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
189
|
+
ic(f"Final Audio Saved as {filename}")
|
|
190
|
+
return filename.as_posix()
|
|
191
|
+
|
|
192
|
+
except requests.exceptions.RequestException as e:
|
|
193
|
+
if verbose:
|
|
194
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
195
|
+
ic(f"Failed to perform the operation: {e}")
|
|
196
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to perform the operation: {e}")
|
|
197
|
+
|
|
198
|
+
def create_speech(
|
|
199
|
+
self,
|
|
200
|
+
input_text: str,
|
|
201
|
+
model: Optional[str] = "gpt-4o-mini-tts",
|
|
202
|
+
voice: Optional[str] = "Hazel",
|
|
203
|
+
response_format: Optional[str] = "mp3",
|
|
204
|
+
instructions: Optional[str] = None,
|
|
205
|
+
verbose: bool = False,
|
|
206
|
+
) -> str:
|
|
207
|
+
"""
|
|
208
|
+
OpenAI-compatible speech creation interface.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
input_text (str): The text to convert to speech
|
|
212
|
+
model (str): The TTS model to use
|
|
213
|
+
voice (str): The voice to use
|
|
214
|
+
response_format (str): Audio format
|
|
215
|
+
instructions (str): Voice instructions (not used by MurfAI)
|
|
216
|
+
verbose (bool): Whether to print debug information
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
str: Path to the generated audio file
|
|
220
|
+
"""
|
|
221
|
+
return self.tts(
|
|
222
|
+
text=input_text,
|
|
223
|
+
voice=voice or "Hazel",
|
|
224
|
+
response_format=response_format or "mp3",
|
|
225
|
+
verbose=verbose,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def stream_audio(
|
|
229
|
+
self,
|
|
230
|
+
text: str,
|
|
231
|
+
model: Optional[str] = None,
|
|
232
|
+
voice: Optional[str] = None,
|
|
233
|
+
response_format: Optional[str] = None,
|
|
234
|
+
instructions: Optional[str] = None,
|
|
235
|
+
chunk_size: int = 1024,
|
|
236
|
+
verbose: bool = False,
|
|
237
|
+
) -> Generator[bytes, None, None]:
|
|
238
|
+
"""
|
|
239
|
+
Stream audio response in chunks.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
text (str): The text to convert to speech
|
|
243
|
+
model (str): The TTS model to use
|
|
244
|
+
voice (str): The voice to use
|
|
245
|
+
response_format (str): Audio format
|
|
246
|
+
instructions (str): Voice instructions
|
|
247
|
+
chunk_size (int): Size of audio chunks to yield
|
|
248
|
+
verbose (bool): Whether to print debug information
|
|
249
|
+
|
|
250
|
+
Yields:
|
|
251
|
+
bytes: Audio data chunks
|
|
252
|
+
"""
|
|
253
|
+
# Generate the audio file using create_speech
|
|
254
|
+
audio_file = self.create_speech(
|
|
255
|
+
input_text=text,
|
|
256
|
+
model=model,
|
|
257
|
+
voice=voice,
|
|
258
|
+
response_format=response_format,
|
|
259
|
+
instructions=instructions,
|
|
260
|
+
verbose=verbose,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Stream the file in chunks
|
|
264
|
+
with open(audio_file, "rb") as f:
|
|
265
|
+
while chunk := f.read(chunk_size):
|
|
266
|
+
yield chunk
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Example usage
|
|
270
|
+
if __name__ == "__main__":
|
|
271
|
+
murfai = MurfAITTS()
|
|
272
|
+
text = "This is a test of the MurfAITTS text-to-speech API. It supports multiple sentences and advanced logging."
|
|
273
|
+
|
|
274
|
+
ic.configureOutput(prefix="DEBUG| ")
|
|
275
|
+
ic("Generating audio...")
|
|
276
|
+
try:
|
|
277
|
+
audio_file = murfai.create_speech(
|
|
278
|
+
input_text=text,
|
|
279
|
+
model="gpt-4o-mini-tts",
|
|
280
|
+
voice="Hazel",
|
|
281
|
+
response_format="mp3",
|
|
282
|
+
verbose=True,
|
|
283
|
+
)
|
|
284
|
+
ic.configureOutput(prefix="INFO| ")
|
|
285
|
+
ic(f"Audio saved to: {audio_file}")
|
|
286
|
+
except Exception as e:
|
|
287
|
+
ic.configureOutput(prefix="ERROR| ")
|
|
288
|
+
ic(f"Error: {e}")
|