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.
Files changed (413) hide show
  1. webscout/AIauto.py +524 -251
  2. webscout/AIbase.py +247 -319
  3. webscout/AIutel.py +68 -703
  4. webscout/Bard.py +1072 -1026
  5. webscout/Extra/GitToolkit/__init__.py +10 -10
  6. webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
  7. webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
  8. webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
  9. webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
  10. webscout/Extra/GitToolkit/gitapi/search.py +162 -0
  11. webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
  12. webscout/Extra/GitToolkit/gitapi/user.py +128 -96
  13. webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
  14. webscout/Extra/YTToolkit/README.md +443 -375
  15. webscout/Extra/YTToolkit/YTdownloader.py +953 -957
  16. webscout/Extra/YTToolkit/__init__.py +3 -3
  17. webscout/Extra/YTToolkit/transcriber.py +595 -476
  18. webscout/Extra/YTToolkit/ytapi/README.md +230 -44
  19. webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
  20. webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
  21. webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
  22. webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
  23. webscout/Extra/YTToolkit/ytapi/extras.py +178 -118
  24. webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
  25. webscout/Extra/YTToolkit/ytapi/https.py +89 -88
  26. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
  27. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
  28. webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
  29. webscout/Extra/YTToolkit/ytapi/query.py +143 -40
  30. webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
  31. webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
  32. webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
  33. webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
  34. webscout/Extra/YTToolkit/ytapi/video.py +403 -232
  35. webscout/Extra/__init__.py +2 -3
  36. webscout/Extra/gguf.py +1298 -684
  37. webscout/Extra/tempmail/README.md +487 -487
  38. webscout/Extra/tempmail/__init__.py +28 -28
  39. webscout/Extra/tempmail/async_utils.py +143 -141
  40. webscout/Extra/tempmail/base.py +172 -161
  41. webscout/Extra/tempmail/cli.py +191 -187
  42. webscout/Extra/tempmail/emailnator.py +88 -84
  43. webscout/Extra/tempmail/mail_tm.py +378 -361
  44. webscout/Extra/tempmail/temp_mail_io.py +304 -292
  45. webscout/Extra/weather.py +196 -194
  46. webscout/Extra/weather_ascii.py +17 -15
  47. webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
  48. webscout/Provider/AISEARCH/Perplexity.py +292 -333
  49. webscout/Provider/AISEARCH/README.md +106 -279
  50. webscout/Provider/AISEARCH/__init__.py +16 -9
  51. webscout/Provider/AISEARCH/brave_search.py +298 -0
  52. webscout/Provider/AISEARCH/iask_search.py +357 -410
  53. webscout/Provider/AISEARCH/monica_search.py +200 -220
  54. webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
  55. webscout/Provider/Algion.py +413 -0
  56. webscout/Provider/Andi.py +74 -69
  57. webscout/Provider/Apriel.py +313 -0
  58. webscout/Provider/Ayle.py +323 -0
  59. webscout/Provider/ChatSandbox.py +329 -342
  60. webscout/Provider/ClaudeOnline.py +365 -0
  61. webscout/Provider/Cohere.py +232 -208
  62. webscout/Provider/DeepAI.py +367 -0
  63. webscout/Provider/Deepinfra.py +467 -340
  64. webscout/Provider/EssentialAI.py +217 -0
  65. webscout/Provider/ExaAI.py +274 -261
  66. webscout/Provider/Gemini.py +175 -169
  67. webscout/Provider/GithubChat.py +385 -369
  68. webscout/Provider/Gradient.py +286 -0
  69. webscout/Provider/Groq.py +556 -801
  70. webscout/Provider/HadadXYZ.py +323 -0
  71. webscout/Provider/HeckAI.py +392 -375
  72. webscout/Provider/HuggingFace.py +387 -0
  73. webscout/Provider/IBM.py +340 -0
  74. webscout/Provider/Jadve.py +317 -291
  75. webscout/Provider/K2Think.py +306 -0
  76. webscout/Provider/Koboldai.py +221 -384
  77. webscout/Provider/Netwrck.py +273 -270
  78. webscout/Provider/Nvidia.py +310 -0
  79. webscout/Provider/OPENAI/DeepAI.py +489 -0
  80. webscout/Provider/OPENAI/K2Think.py +423 -0
  81. webscout/Provider/OPENAI/PI.py +463 -0
  82. webscout/Provider/OPENAI/README.md +890 -952
  83. webscout/Provider/OPENAI/TogetherAI.py +405 -0
  84. webscout/Provider/OPENAI/TwoAI.py +255 -357
  85. webscout/Provider/OPENAI/__init__.py +148 -40
  86. webscout/Provider/OPENAI/ai4chat.py +348 -293
  87. webscout/Provider/OPENAI/akashgpt.py +436 -0
  88. webscout/Provider/OPENAI/algion.py +303 -0
  89. webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
  90. webscout/Provider/OPENAI/base.py +253 -249
  91. webscout/Provider/OPENAI/cerebras.py +296 -0
  92. webscout/Provider/OPENAI/chatgpt.py +870 -556
  93. webscout/Provider/OPENAI/chatsandbox.py +233 -173
  94. webscout/Provider/OPENAI/deepinfra.py +403 -322
  95. webscout/Provider/OPENAI/e2b.py +2370 -1414
  96. webscout/Provider/OPENAI/elmo.py +278 -0
  97. webscout/Provider/OPENAI/exaai.py +452 -417
  98. webscout/Provider/OPENAI/freeassist.py +446 -0
  99. webscout/Provider/OPENAI/gradient.py +448 -0
  100. webscout/Provider/OPENAI/groq.py +380 -364
  101. webscout/Provider/OPENAI/hadadxyz.py +292 -0
  102. webscout/Provider/OPENAI/heckai.py +333 -308
  103. webscout/Provider/OPENAI/huggingface.py +321 -0
  104. webscout/Provider/OPENAI/ibm.py +425 -0
  105. webscout/Provider/OPENAI/llmchat.py +253 -0
  106. webscout/Provider/OPENAI/llmchatco.py +378 -335
  107. webscout/Provider/OPENAI/meta.py +541 -0
  108. webscout/Provider/OPENAI/netwrck.py +374 -357
  109. webscout/Provider/OPENAI/nvidia.py +317 -0
  110. webscout/Provider/OPENAI/oivscode.py +348 -287
  111. webscout/Provider/OPENAI/openrouter.py +328 -0
  112. webscout/Provider/OPENAI/pydantic_imports.py +1 -172
  113. webscout/Provider/OPENAI/sambanova.py +397 -0
  114. webscout/Provider/OPENAI/sonus.py +305 -304
  115. webscout/Provider/OPENAI/textpollinations.py +370 -339
  116. webscout/Provider/OPENAI/toolbaz.py +375 -413
  117. webscout/Provider/OPENAI/typefully.py +419 -355
  118. webscout/Provider/OPENAI/typliai.py +279 -0
  119. webscout/Provider/OPENAI/utils.py +314 -318
  120. webscout/Provider/OPENAI/wisecat.py +359 -387
  121. webscout/Provider/OPENAI/writecream.py +185 -163
  122. webscout/Provider/OPENAI/x0gpt.py +462 -365
  123. webscout/Provider/OPENAI/zenmux.py +380 -0
  124. webscout/Provider/OpenRouter.py +386 -0
  125. webscout/Provider/Openai.py +337 -496
  126. webscout/Provider/PI.py +443 -429
  127. webscout/Provider/QwenLM.py +346 -254
  128. webscout/Provider/STT/__init__.py +28 -0
  129. webscout/Provider/STT/base.py +303 -0
  130. webscout/Provider/STT/elevenlabs.py +264 -0
  131. webscout/Provider/Sambanova.py +317 -0
  132. webscout/Provider/TTI/README.md +69 -82
  133. webscout/Provider/TTI/__init__.py +37 -7
  134. webscout/Provider/TTI/base.py +147 -64
  135. webscout/Provider/TTI/claudeonline.py +393 -0
  136. webscout/Provider/TTI/magicstudio.py +292 -201
  137. webscout/Provider/TTI/miragic.py +180 -0
  138. webscout/Provider/TTI/pollinations.py +331 -221
  139. webscout/Provider/TTI/together.py +334 -0
  140. webscout/Provider/TTI/utils.py +14 -11
  141. webscout/Provider/TTS/README.md +186 -192
  142. webscout/Provider/TTS/__init__.py +43 -10
  143. webscout/Provider/TTS/base.py +523 -159
  144. webscout/Provider/TTS/deepgram.py +286 -156
  145. webscout/Provider/TTS/elevenlabs.py +189 -111
  146. webscout/Provider/TTS/freetts.py +218 -0
  147. webscout/Provider/TTS/murfai.py +288 -113
  148. webscout/Provider/TTS/openai_fm.py +364 -129
  149. webscout/Provider/TTS/parler.py +203 -111
  150. webscout/Provider/TTS/qwen.py +334 -0
  151. webscout/Provider/TTS/sherpa.py +286 -0
  152. webscout/Provider/TTS/speechma.py +693 -580
  153. webscout/Provider/TTS/streamElements.py +275 -333
  154. webscout/Provider/TTS/utils.py +280 -280
  155. webscout/Provider/TextPollinationsAI.py +331 -308
  156. webscout/Provider/TogetherAI.py +450 -0
  157. webscout/Provider/TwoAI.py +309 -475
  158. webscout/Provider/TypliAI.py +311 -305
  159. webscout/Provider/UNFINISHED/ChatHub.py +219 -209
  160. webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
  161. webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
  162. webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
  163. webscout/Provider/UNFINISHED/Qodo.py +481 -0
  164. webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
  165. webscout/Provider/UNFINISHED/Youchat.py +347 -330
  166. webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
  167. webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
  168. webscout/Provider/UNFINISHED/liner.py +342 -0
  169. webscout/Provider/UNFINISHED/liner_api_request.py +246 -263
  170. webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
  171. webscout/Provider/WiseCat.py +256 -233
  172. webscout/Provider/WrDoChat.py +390 -370
  173. webscout/Provider/__init__.py +115 -174
  174. webscout/Provider/ai4chat.py +181 -174
  175. webscout/Provider/akashgpt.py +330 -335
  176. webscout/Provider/cerebras.py +397 -290
  177. webscout/Provider/cleeai.py +236 -213
  178. webscout/Provider/elmo.py +291 -283
  179. webscout/Provider/geminiapi.py +343 -208
  180. webscout/Provider/julius.py +245 -223
  181. webscout/Provider/learnfastai.py +333 -325
  182. webscout/Provider/llama3mitril.py +230 -215
  183. webscout/Provider/llmchat.py +308 -258
  184. webscout/Provider/llmchatco.py +321 -306
  185. webscout/Provider/meta.py +996 -801
  186. webscout/Provider/oivscode.py +332 -309
  187. webscout/Provider/searchchat.py +316 -292
  188. webscout/Provider/sonus.py +264 -258
  189. webscout/Provider/toolbaz.py +359 -353
  190. webscout/Provider/turboseek.py +332 -266
  191. webscout/Provider/typefully.py +262 -202
  192. webscout/Provider/x0gpt.py +332 -299
  193. webscout/__init__.py +31 -39
  194. webscout/__main__.py +5 -5
  195. webscout/cli.py +585 -524
  196. webscout/client.py +1497 -70
  197. webscout/conversation.py +140 -436
  198. webscout/exceptions.py +383 -362
  199. webscout/litagent/__init__.py +29 -29
  200. webscout/litagent/agent.py +492 -455
  201. webscout/litagent/constants.py +60 -60
  202. webscout/models.py +505 -181
  203. webscout/optimizers.py +74 -420
  204. webscout/prompt_manager.py +376 -288
  205. webscout/sanitize.py +1514 -0
  206. webscout/scout/README.md +452 -404
  207. webscout/scout/__init__.py +8 -8
  208. webscout/scout/core/__init__.py +7 -7
  209. webscout/scout/core/crawler.py +330 -210
  210. webscout/scout/core/scout.py +800 -607
  211. webscout/scout/core/search_result.py +51 -96
  212. webscout/scout/core/text_analyzer.py +64 -63
  213. webscout/scout/core/text_utils.py +412 -277
  214. webscout/scout/core/web_analyzer.py +54 -52
  215. webscout/scout/element.py +872 -478
  216. webscout/scout/parsers/__init__.py +70 -69
  217. webscout/scout/parsers/html5lib_parser.py +182 -172
  218. webscout/scout/parsers/html_parser.py +238 -236
  219. webscout/scout/parsers/lxml_parser.py +203 -178
  220. webscout/scout/utils.py +38 -37
  221. webscout/search/__init__.py +47 -0
  222. webscout/search/base.py +201 -0
  223. webscout/search/bing_main.py +45 -0
  224. webscout/search/brave_main.py +92 -0
  225. webscout/search/duckduckgo_main.py +57 -0
  226. webscout/search/engines/__init__.py +127 -0
  227. webscout/search/engines/bing/__init__.py +15 -0
  228. webscout/search/engines/bing/base.py +35 -0
  229. webscout/search/engines/bing/images.py +114 -0
  230. webscout/search/engines/bing/news.py +96 -0
  231. webscout/search/engines/bing/suggestions.py +36 -0
  232. webscout/search/engines/bing/text.py +109 -0
  233. webscout/search/engines/brave/__init__.py +19 -0
  234. webscout/search/engines/brave/base.py +47 -0
  235. webscout/search/engines/brave/images.py +213 -0
  236. webscout/search/engines/brave/news.py +353 -0
  237. webscout/search/engines/brave/suggestions.py +318 -0
  238. webscout/search/engines/brave/text.py +167 -0
  239. webscout/search/engines/brave/videos.py +364 -0
  240. webscout/search/engines/duckduckgo/__init__.py +25 -0
  241. webscout/search/engines/duckduckgo/answers.py +80 -0
  242. webscout/search/engines/duckduckgo/base.py +189 -0
  243. webscout/search/engines/duckduckgo/images.py +100 -0
  244. webscout/search/engines/duckduckgo/maps.py +183 -0
  245. webscout/search/engines/duckduckgo/news.py +70 -0
  246. webscout/search/engines/duckduckgo/suggestions.py +22 -0
  247. webscout/search/engines/duckduckgo/text.py +221 -0
  248. webscout/search/engines/duckduckgo/translate.py +48 -0
  249. webscout/search/engines/duckduckgo/videos.py +80 -0
  250. webscout/search/engines/duckduckgo/weather.py +84 -0
  251. webscout/search/engines/mojeek.py +61 -0
  252. webscout/search/engines/wikipedia.py +77 -0
  253. webscout/search/engines/yahoo/__init__.py +41 -0
  254. webscout/search/engines/yahoo/answers.py +19 -0
  255. webscout/search/engines/yahoo/base.py +34 -0
  256. webscout/search/engines/yahoo/images.py +323 -0
  257. webscout/search/engines/yahoo/maps.py +19 -0
  258. webscout/search/engines/yahoo/news.py +258 -0
  259. webscout/search/engines/yahoo/suggestions.py +140 -0
  260. webscout/search/engines/yahoo/text.py +273 -0
  261. webscout/search/engines/yahoo/translate.py +19 -0
  262. webscout/search/engines/yahoo/videos.py +302 -0
  263. webscout/search/engines/yahoo/weather.py +220 -0
  264. webscout/search/engines/yandex.py +67 -0
  265. webscout/search/engines/yep/__init__.py +13 -0
  266. webscout/search/engines/yep/base.py +34 -0
  267. webscout/search/engines/yep/images.py +101 -0
  268. webscout/search/engines/yep/suggestions.py +38 -0
  269. webscout/search/engines/yep/text.py +99 -0
  270. webscout/search/http_client.py +172 -0
  271. webscout/search/results.py +141 -0
  272. webscout/search/yahoo_main.py +57 -0
  273. webscout/search/yep_main.py +48 -0
  274. webscout/server/__init__.py +48 -0
  275. webscout/server/config.py +78 -0
  276. webscout/server/exceptions.py +69 -0
  277. webscout/server/providers.py +286 -0
  278. webscout/server/request_models.py +131 -0
  279. webscout/server/request_processing.py +404 -0
  280. webscout/server/routes.py +642 -0
  281. webscout/server/server.py +351 -0
  282. webscout/server/ui_templates.py +1171 -0
  283. webscout/swiftcli/__init__.py +79 -95
  284. webscout/swiftcli/core/__init__.py +7 -7
  285. webscout/swiftcli/core/cli.py +574 -297
  286. webscout/swiftcli/core/context.py +98 -104
  287. webscout/swiftcli/core/group.py +268 -241
  288. webscout/swiftcli/decorators/__init__.py +28 -28
  289. webscout/swiftcli/decorators/command.py +243 -221
  290. webscout/swiftcli/decorators/options.py +247 -220
  291. webscout/swiftcli/decorators/output.py +392 -252
  292. webscout/swiftcli/exceptions.py +21 -21
  293. webscout/swiftcli/plugins/__init__.py +9 -9
  294. webscout/swiftcli/plugins/base.py +134 -135
  295. webscout/swiftcli/plugins/manager.py +269 -269
  296. webscout/swiftcli/utils/__init__.py +58 -59
  297. webscout/swiftcli/utils/formatting.py +251 -252
  298. webscout/swiftcli/utils/parsing.py +368 -267
  299. webscout/update_checker.py +280 -136
  300. webscout/utils.py +28 -14
  301. webscout/version.py +2 -1
  302. webscout/version.py.bak +3 -0
  303. webscout/zeroart/__init__.py +218 -135
  304. webscout/zeroart/base.py +70 -66
  305. webscout/zeroart/effects.py +155 -101
  306. webscout/zeroart/fonts.py +1799 -1239
  307. webscout-2026.1.19.dist-info/METADATA +638 -0
  308. webscout-2026.1.19.dist-info/RECORD +312 -0
  309. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
  310. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
  311. webscout/DWEBS.py +0 -520
  312. webscout/Extra/Act.md +0 -309
  313. webscout/Extra/GitToolkit/gitapi/README.md +0 -110
  314. webscout/Extra/autocoder/__init__.py +0 -9
  315. webscout/Extra/autocoder/autocoder.py +0 -1105
  316. webscout/Extra/autocoder/autocoder_utiles.py +0 -332
  317. webscout/Extra/gguf.md +0 -430
  318. webscout/Extra/weather.md +0 -281
  319. webscout/Litlogger/README.md +0 -10
  320. webscout/Litlogger/__init__.py +0 -15
  321. webscout/Litlogger/formats.py +0 -4
  322. webscout/Litlogger/handlers.py +0 -103
  323. webscout/Litlogger/levels.py +0 -13
  324. webscout/Litlogger/logger.py +0 -92
  325. webscout/Provider/AI21.py +0 -177
  326. webscout/Provider/AISEARCH/DeepFind.py +0 -254
  327. webscout/Provider/AISEARCH/felo_search.py +0 -202
  328. webscout/Provider/AISEARCH/genspark_search.py +0 -324
  329. webscout/Provider/AISEARCH/hika_search.py +0 -186
  330. webscout/Provider/AISEARCH/scira_search.py +0 -298
  331. webscout/Provider/Aitopia.py +0 -316
  332. webscout/Provider/AllenAI.py +0 -440
  333. webscout/Provider/Blackboxai.py +0 -791
  334. webscout/Provider/ChatGPTClone.py +0 -237
  335. webscout/Provider/ChatGPTGratis.py +0 -194
  336. webscout/Provider/Cloudflare.py +0 -324
  337. webscout/Provider/ExaChat.py +0 -358
  338. webscout/Provider/Flowith.py +0 -217
  339. webscout/Provider/FreeGemini.py +0 -250
  340. webscout/Provider/Glider.py +0 -225
  341. webscout/Provider/HF_space/__init__.py +0 -0
  342. webscout/Provider/HF_space/qwen_qwen2.py +0 -206
  343. webscout/Provider/HuggingFaceChat.py +0 -469
  344. webscout/Provider/Hunyuan.py +0 -283
  345. webscout/Provider/LambdaChat.py +0 -411
  346. webscout/Provider/Llama3.py +0 -259
  347. webscout/Provider/Nemotron.py +0 -218
  348. webscout/Provider/OLLAMA.py +0 -396
  349. webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
  350. webscout/Provider/OPENAI/Cloudflare.py +0 -378
  351. webscout/Provider/OPENAI/FreeGemini.py +0 -283
  352. webscout/Provider/OPENAI/NEMOTRON.py +0 -232
  353. webscout/Provider/OPENAI/Qwen3.py +0 -283
  354. webscout/Provider/OPENAI/api.py +0 -969
  355. webscout/Provider/OPENAI/c4ai.py +0 -373
  356. webscout/Provider/OPENAI/chatgptclone.py +0 -494
  357. webscout/Provider/OPENAI/copilot.py +0 -242
  358. webscout/Provider/OPENAI/flowith.py +0 -162
  359. webscout/Provider/OPENAI/freeaichat.py +0 -359
  360. webscout/Provider/OPENAI/mcpcore.py +0 -389
  361. webscout/Provider/OPENAI/multichat.py +0 -376
  362. webscout/Provider/OPENAI/opkfc.py +0 -496
  363. webscout/Provider/OPENAI/scirachat.py +0 -477
  364. webscout/Provider/OPENAI/standardinput.py +0 -433
  365. webscout/Provider/OPENAI/typegpt.py +0 -364
  366. webscout/Provider/OPENAI/uncovrAI.py +0 -463
  367. webscout/Provider/OPENAI/venice.py +0 -431
  368. webscout/Provider/OPENAI/yep.py +0 -382
  369. webscout/Provider/OpenGPT.py +0 -209
  370. webscout/Provider/Perplexitylabs.py +0 -415
  371. webscout/Provider/Reka.py +0 -214
  372. webscout/Provider/StandardInput.py +0 -290
  373. webscout/Provider/TTI/aiarta.py +0 -365
  374. webscout/Provider/TTI/artbit.py +0 -0
  375. webscout/Provider/TTI/fastflux.py +0 -200
  376. webscout/Provider/TTI/piclumen.py +0 -203
  377. webscout/Provider/TTI/pixelmuse.py +0 -225
  378. webscout/Provider/TTS/gesserit.py +0 -128
  379. webscout/Provider/TTS/sthir.py +0 -94
  380. webscout/Provider/TeachAnything.py +0 -229
  381. webscout/Provider/UNFINISHED/puterjs.py +0 -635
  382. webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
  383. webscout/Provider/Venice.py +0 -258
  384. webscout/Provider/VercelAI.py +0 -253
  385. webscout/Provider/Writecream.py +0 -246
  386. webscout/Provider/WritingMate.py +0 -269
  387. webscout/Provider/asksteve.py +0 -220
  388. webscout/Provider/chatglm.py +0 -215
  389. webscout/Provider/copilot.py +0 -425
  390. webscout/Provider/freeaichat.py +0 -285
  391. webscout/Provider/granite.py +0 -235
  392. webscout/Provider/hermes.py +0 -266
  393. webscout/Provider/koala.py +0 -170
  394. webscout/Provider/lmarena.py +0 -198
  395. webscout/Provider/multichat.py +0 -364
  396. webscout/Provider/scira_chat.py +0 -299
  397. webscout/Provider/scnet.py +0 -243
  398. webscout/Provider/talkai.py +0 -194
  399. webscout/Provider/typegpt.py +0 -289
  400. webscout/Provider/uncovr.py +0 -368
  401. webscout/Provider/yep.py +0 -389
  402. webscout/litagent/Readme.md +0 -276
  403. webscout/litprinter/__init__.py +0 -59
  404. webscout/swiftcli/Readme.md +0 -323
  405. webscout/tempid.py +0 -128
  406. webscout/webscout_search.py +0 -1184
  407. webscout/webscout_search_async.py +0 -654
  408. webscout/yep_search.py +0 -347
  409. webscout/zeroart/README.md +0 -89
  410. webscout-8.2.9.dist-info/METADATA +0 -1033
  411. webscout-8.2.9.dist-info/RECORD +0 -289
  412. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
  413. {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
@@ -1,580 +1,693 @@
1
- ##################################################################################
2
- ## Modified version of code written by t.me/infip1217 ##
3
- ##################################################################################
4
- import time
5
- import requests
6
- import pathlib
7
- import tempfile
8
- from io import BytesIO
9
- from webscout import exceptions
10
- from webscout.litagent import LitAgent
11
- from concurrent.futures import ThreadPoolExecutor, as_completed
12
- from webscout.Provider.TTS import utils
13
- from webscout.Provider.TTS.base import BaseTTSProvider
14
-
15
- class SpeechMaTTS(BaseTTSProvider):
16
- """
17
- Text-to-speech provider using the SpeechMa API.
18
- """
19
- # Request headers
20
- headers = {
21
- "accept": "*/*",
22
- "accept-language": "en-IN,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,en-AU;q=0.6",
23
- "content-type": "application/json",
24
- "origin": "https://speechma.com",
25
- "priority": "u=1, i",
26
- "User-Agent": LitAgent().random()
27
- }
28
-
29
- # Available voices with their IDs
30
- all_voices = {
31
- # Multilingual voices
32
- "Andrew Multilingual": "voice-107", # Male, Multilingual, United States
33
- "Ava Multilingual": "voice-110", # Female, Multilingual, United States
34
- "Brian Multilingual": "voice-112", # Male, Multilingual, United States
35
- "Emma Multilingual": "voice-115", # Female, Multilingual, United States
36
- "Remy Multilingual": "voice-142", # Male, Multilingual, France
37
- "Vivienne Multilingual": "voice-143", # Female, Multilingual, France
38
- "Florian Multilingual": "voice-154", # Male, Multilingual, Germany
39
- "Seraphina Multilingual": "voice-157", # Female, Multilingual, Germany
40
- "Giuseppe Multilingual": "voice-177", # Male, Multilingual, Italy
41
- "Hyunsu Multilingual": "voice-189", # Male, Multilingual, South Korea
42
- "Thalita Multilingual": "voice-222", # Female, Multilingual, Brazil
43
- # English (US)
44
- "Ana": "voice-106", # Female, English, United States
45
- "Andrew": "voice-108", # Male, English, United States
46
- "Aria": "voice-109", # Female, English, United States
47
- "Ava": "voice-111", # Female, English, United States
48
- "Brian": "voice-113", # Male, English, United States
49
- "Christopher": "voice-114", # Male, English, United States
50
- "Emma": "voice-116", # Female, English, United States
51
- "Eric": "voice-117", # Male, English, United States
52
- "Guy": "voice-118", # Male, English, United States
53
- "Jenny": "voice-119", # Female, English, United States
54
- "Michelle": "voice-120", # Female, English, United States
55
- "Roger": "voice-121", # Male, English, United States
56
- "Steffan": "voice-122", # Male, English, United States
57
- # English (UK)
58
- "Libby": "voice-82", # Female, English, United Kingdom
59
- "Maisie": "voice-83", # Female, English, United Kingdom
60
- "Ryan": "voice-84", # Male, English, United Kingdom
61
- "Sonia": "voice-85", # Female, English, United Kingdom
62
- "Thomas": "voice-86", # Male, English, United Kingdom
63
- # English (Australia)
64
- "Natasha": "voice-78", # Female, English, Australia
65
- "William": "voice-79", # Male, English, Australia
66
- # English (Canada)
67
- "Clara": "voice-80", # Female, English, Canada
68
- "Liam": "voice-81", # Male, English, Canada
69
- # English (India)
70
- "Neerja Expressive": "voice-91", # Female, English, India
71
- "Neerja": "voice-92", # Female, English, India
72
- "Prabhat": "voice-93", # Male, English, India
73
- # English (Hong Kong)
74
- "Sam": "voice-87", # Male, English, Hong Kong
75
- "Yan": "voice-88", # Female, English, Hong Kong
76
- # English (Ireland)
77
- "Connor": "voice-89", # Male, English, Ireland
78
- "Emily": "voice-90", # Female, English, Ireland
79
- # English (Kenya)
80
- "Asilia": "voice-94", # Female, English, Kenya
81
- "Chilemba": "voice-95", # Male, English, Kenya
82
- # English (Nigeria)
83
- "Abeo": "voice-96", # Male, English, Nigeria
84
- "Ezinne": "voice-97", # Female, English, Nigeria
85
- # English (New Zealand)
86
- "Mitchell": "voice-98", # Male, English, New Zealand
87
- "Molly": "voice-99", # Female, English, New Zealand
88
- # English (Philippines)
89
- "James": "voice-100", # Male, English, Philippines
90
- "Rosa": "voice-101", # Female, English, Philippines
91
- # English (Singapore)
92
- "Luna": "voice-102", # Female, English, Singapore
93
- "Wayne": "voice-103", # Male, English, Singapore
94
- # English (Tanzania)
95
- "Elimu": "voice-104", # Male, English, Tanzania
96
- "Imani": "voice-105", # Female, English, Tanzania
97
- # English (South Africa)
98
- "Leah": "voice-123", # Female, English, South Africa
99
- "Luke": "voice-124", # Male, English, South Africa
100
- # Spanish (Argentina)
101
- "Elena": "voice-239", # Female, Spanish, Argentina
102
- "Tomas": "voice-240", # Male, Spanish, Argentina
103
- # Spanish (Bolivia)
104
- "Marcelo": "voice-241", # Male, Spanish, Bolivia
105
- "Sofia": "voice-242", # Female, Spanish, Bolivia
106
- # Spanish (Chile)
107
- "Catalina": "voice-243", # Female, Spanish, Chile
108
- "Lorenzo": "voice-244", # Male, Spanish, Chile
109
- # Spanish (Colombia)
110
- "Gonzalo": "voice-245", # Male, Spanish, Colombia
111
- "Salome": "voice-246", # Female, Spanish, Colombia
112
- # Spanish (Costa Rica)
113
- "Juan": "voice-247", # Male, Spanish, Costa Rica
114
- "Maria": "voice-248", # Female, Spanish, Costa Rica
115
- # Spanish (Cuba)
116
- "Belkys": "voice-249", # Female, Spanish, Cuba
117
- "Manuel": "voice-250", # Male, Spanish, Cuba
118
- # Spanish (Dominican Republic)
119
- "Emilio": "voice-251", # Male, Spanish, Dominican Republic
120
- "Ramona": "voice-252", # Female, Spanish, Dominican Republic
121
- # Spanish (Ecuador)
122
- "Andrea": "voice-253", # Female, Spanish, Ecuador
123
- "Luis": "voice-254", # Male, Spanish, Ecuador
124
- # Spanish (Spain)
125
- "Alvaro": "voice-255", # Male, Spanish, Spain
126
- "Elvira": "voice-256", # Female, Spanish, Spain
127
- "Ximena": "voice-257", # Female, Spanish, Spain
128
- # Spanish (Equatorial Guinea)
129
- "Javier": "voice-258", # Male, Spanish, Equatorial Guinea
130
- "Teresa": "voice-259", # Female, Spanish, Equatorial Guinea
131
- # Spanish (Guatemala)
132
- "Andres": "voice-260", # Male, Spanish, Guatemala
133
- "Marta": "voice-261", # Female, Spanish, Guatemala
134
- # Spanish (Honduras)
135
- "Carlos": "voice-262", # Male, Spanish, Honduras
136
- "Karla": "voice-263", # Female, Spanish, Honduras
137
- # Spanish (Mexico)
138
- "Dalia": "voice-264", # Female, Spanish, Mexico
139
- "Jorge": "voice-265", # Male, Spanish, Mexico
140
- # Spanish (Nicaragua)
141
- "Federico": "voice-266", # Male, Spanish, Nicaragua
142
- "Yolanda": "voice-267", # Female, Spanish, Nicaragua
143
- # Spanish (Panama)
144
- "Margarita": "voice-268", # Female, Spanish, Panama
145
- "Roberto": "voice-269", # Male, Spanish, Panama
146
- # Spanish (Peru)
147
- "Alex": "voice-270", # Male, Spanish, Peru
148
- "Camila": "voice-271", # Female, Spanish, Peru
149
- # Spanish (Puerto Rico)
150
- "Karina": "voice-272", # Female, Spanish, Puerto Rico
151
- "Victor": "voice-273", # Male, Spanish, Puerto Rico
152
- # Spanish (Paraguay)
153
- "Mario": "voice-274", # Male, Spanish, Paraguay
154
- "Tania": "voice-275", # Female, Spanish, Paraguay
155
- # Spanish (El Salvador)
156
- "Lorena": "voice-276", # Female, Spanish, El Salvador
157
- "Rodrigo": "voice-277", # Male, Spanish, El Salvador
158
- # Spanish (United States)
159
- "Alonso": "voice-278", # Male, Spanish, United States
160
- "Paloma": "voice-279", # Female, Spanish, United States
161
- # Spanish (Uruguay)
162
- "Mateo": "voice-280", # Male, Spanish, Uruguay
163
- "Valentina": "voice-281", # Female, Spanish, Uruguay
164
- # Spanish (Venezuela)
165
- "Paola": "voice-282", # Female, Spanish, Venezuela
166
- "Sebastian": "voice-283", # Male, Spanish, Venezuela
167
- # Chinese (China)
168
- "Xiaoxiao": "voice-53", # Female, Chinese, China
169
- "Xiaoyi": "voice-54", # Female, Chinese, China
170
- "Yunjian": "voice-55", # Male, Chinese, China
171
- "Yunxi": "voice-56", # Male, Chinese, China
172
- "Yunxia": "voice-57", # Male, Chinese, China
173
- "Yunyang": "voice-58", # Male, Chinese, China
174
- "Xiaobei": "voice-59", # Female, Chinese, China
175
- "Xiaoni": "voice-60", # Female, Chinese, China
176
- # Chinese (Hong Kong)
177
- "HiuGaai": "voice-61", # Female, Chinese, Hong Kong
178
- "HiuMaan": "voice-62", # Female, Chinese, Hong Kong
179
- "WanLung": "voice-63", # Male, Chinese, Hong Kong
180
- # Chinese (Taiwan)
181
- "HsiaoChen": "voice-64", # Female, Chinese, Taiwan
182
- "HsiaoYu": "voice-65", # Female, Chinese, Taiwan
183
- "YunJhe": "voice-66", # Male, Chinese, Taiwan
184
- # French (Belgium)
185
- "Charline": "voice-131", # Female, French, Belgium
186
- "Gerard": "voice-132", # Male, French, Belgium
187
- # French (Canada)
188
- "Antoine": "voice-133", # Male, French, Canada
189
- "Jean": "voice-134", # Male, French, Canada
190
- "Sylvie": "voice-135", # Female, French, Canada
191
- "Thierry": "voice-136", # Male, French, Canada
192
- # French (Switzerland)
193
- "Ariane": "voice-137", # Female, French, Switzerland
194
- "Fabrice": "voice-138", # Male, French, Switzerland
195
- # French (France)
196
- "Denise": "voice-139", # Female, French, France
197
- "Eloise": "voice-140", # Female, French, France
198
- "Henri": "voice-141", # Male, French, France
199
- # German (Austria)
200
- "Ingrid": "voice-148", # Female, German, Austria
201
- "Jonas": "voice-149", # Male, German, Austria
202
- # German (Switzerland)
203
- "Jan": "voice-150", # Male, German, Switzerland
204
- "Leni": "voice-151", # Female, German, Switzerland
205
- # German (Germany)
206
- "Amala": "voice-152", # Female, German, Germany
207
- "Conrad": "voice-153", # Male, German, Germany
208
- "Katja": "voice-155", # Female, German, Germany
209
- "Killian": "voice-156", # Male, German, Germany
210
- # Arabic (United Arab Emirates)
211
- "Fatima": "voice-7", # Female, Arabic, United Arab Emirates
212
- "Hamdan": "voice-8", # Male, Arabic, United Arab Emirates
213
- # Arabic (Bahrain)
214
- "Ali": "voice-9", # Male, Arabic, Bahrain
215
- "Laila": "voice-10", # Female, Arabic, Bahrain
216
- # Arabic (Algeria)
217
- "Amina": "voice-11", # Female, Arabic, Algeria
218
- "Ismael": "voice-12", # Male, Arabic, Algeria
219
- # Arabic (Egypt)
220
- "Salma": "voice-13", # Female, Arabic, Egypt
221
- "Shakir": "voice-14", # Male, Arabic, Egypt
222
- # Arabic (Iraq)
223
- "Bassel": "voice-15", # Male, Arabic, Iraq
224
- "Rana": "voice-16", # Female, Arabic, Iraq
225
- # Arabic (Jordan)
226
- "Sana": "voice-17", # Female, Arabic, Jordan
227
- "Taim": "voice-18", # Male, Arabic, Jordan
228
- # Arabic (Kuwait)
229
- "Fahed": "voice-19", # Male, Arabic, Kuwait
230
- "Noura": "voice-20", # Female, Arabic, Kuwait
231
- # Arabic (Lebanon)
232
- "Layla": "voice-21", # Female, Arabic, Lebanon
233
- "Rami": "voice-22", # Male, Arabic, Lebanon
234
- # Arabic (Libya)
235
- "Iman": "voice-23", # Female, Arabic, Libya
236
- "Omar": "voice-24", # Male, Arabic, Libya
237
- # Arabic (Morocco)
238
- "Jamal": "voice-25", # Male, Arabic, Morocco
239
- "Mouna": "voice-26", # Female, Arabic, Morocco
240
- # Arabic (Oman)
241
- "Abdullah": "voice-27", # Male, Arabic, Oman
242
- "Aysha": "voice-28", # Female, Arabic, Oman
243
- # Arabic (Qatar)
244
- "Amal": "voice-29", # Female, Arabic, Qatar
245
- "Moaz": "voice-30", # Male, Arabic, Qatar
246
- # Arabic (Saudi Arabia)
247
- "Hamed": "voice-31", # Male, Arabic, Saudi Arabia
248
- "Zariyah": "voice-32", # Female, Arabic, Saudi Arabia
249
- # Arabic (Syria)
250
- "Amany": "voice-33", # Female, Arabic, Syria
251
- "Laith": "voice-34", # Male, Arabic, Syria
252
- # Arabic (Tunisia)
253
- "Hedi": "voice-35", # Male, Arabic, Tunisia
254
- "Reem": "voice-36", # Female, Arabic, Tunisia
255
- # Arabic (Yemen)
256
- "Maryam": "voice-37", # Female, Arabic, Yemen
257
- "Saleh": "voice-38", # Male, Arabic, Yemen
258
- # Afrikaans (South Africa)
259
- "Adri": "voice-1", # Female, Afrikaans, South Africa
260
- "Willem": "voice-2", # Male, Afrikaans, South Africa
261
- # Albanian (Albania)
262
- "Anila": "voice-3", # Female, Albanian, Albania
263
- "Ilir": "voice-4", # Male, Albanian, Albania
264
- # Amharic (Ethiopia)
265
- "Ameha": "voice-5", # Male, Amharic, Ethiopia
266
- "Mekdes": "voice-6", # Female, Amharic, Ethiopia
267
- # Azerbaijani (Azerbaijan)
268
- "Babek": "voice-39", # Male, Azerbaijani, Azerbaijan
269
- "Banu": "voice-40", # Female, Azerbaijani, Azerbaijan
270
- # Bengali (Bangladesh)
271
- "Nabanita": "voice-41", # Female, Bengali, Bangladesh
272
- "Pradeep": "voice-42", # Male, Bengali, Bangladesh
273
- # Bengali (India)
274
- "Bashkar": "voice-43", # Male, Bengali, India
275
- "Tanishaa": "voice-44", # Female, Bengali, India
276
- # Bosnian (Bosnia and Herzegovina)
277
- "Goran": "voice-45", # Male, Bosnian, Bosnia and Herzegovina
278
- "Vesna": "voice-46", # Female, Bosnian, Bosnia and Herzegovina
279
- # Bulgarian (Bulgaria)
280
- "Borislav": "voice-47", # Male, Bulgarian, Bulgaria
281
- "Kalina": "voice-48", # Female, Bulgarian, Bulgaria
282
- # Burmese (Myanmar)
283
- "Nilar": "voice-49", # Female, Burmese, Myanmar
284
- "Thiha": "voice-50", # Male, Burmese, Myanmar
285
- # Catalan (Spain)
286
- "Enric": "voice-51", # Male, Catalan, Spain
287
- "Joana": "voice-52", # Female, Catalan, Spain
288
- # Croatian (Croatia)
289
- "Gabrijela": "voice-67", # Female, Croatian, Croatia
290
- "Srecko": "voice-68", # Male, Croatian, Croatia
291
- # Czech (Czech Republic)
292
- "Antonin": "voice-69", # Male, Czech, Czech Republic
293
- "Vlasta": "voice-70", # Female, Czech, Czech Republic
294
- # Danish (Denmark)
295
- "Christel": "voice-71", # Female, Danish, Denmark
296
- "Jeppe": "voice-72", # Male, Danish, Denmark
297
- # Dutch (Belgium)
298
- "Arnaud": "voice-73", # Male, Dutch, Belgium
299
- "Dena": "voice-74", # Female, Dutch, Belgium
300
- # Dutch (Netherlands)
301
- "Colette": "voice-75", # Female, Dutch, Netherlands
302
- "Fenna": "voice-76", # Female, Dutch, Netherlands
303
- "Maarten": "voice-77", # Male, Dutch, Netherlands
304
- # Estonian (Estonia)
305
- "Anu": "voice-125", # Female, Estonian, Estonia
306
- "Kert": "voice-126", # Male, Estonian, Estonia
307
- # Filipino (Philippines)
308
- "Angelo": "voice-127", # Male, Filipino, Philippines
309
- "Blessica": "voice-128", # Female, Filipino, Philippines
310
- # Finnish (Finland)
311
- "Harri": "voice-129", # Male, Finnish, Finland
312
- "Noora": "voice-130", # Female, Finnish, Finland
313
- # Galician (Spain)
314
- "Roi": "voice-144", # Male, Galician, Spain
315
- "Sabela": "voice-145", # Female, Galician, Spain
316
- # Georgian (Georgia)
317
- "Eka": "voice-146", # Female, Georgian, Georgia
318
- "Giorgi": "voice-147", # Male, Georgian, Georgia
319
- # Greek (Greece)
320
- "Athina": "voice-158", # Female, Greek, Greece
321
- "Nestoras": "voice-159", # Male, Greek, Greece (Note: voice-160 is a duplicate name)
322
- # Gujarati (India)
323
- "Dhwani": "voice-161", # Female, Gujarati, India
324
- "Niranjan": "voice-162", # Male, Gujarati, India
325
- # Hebrew (Israel)
326
- "Avri": "voice-163", # Male, Hebrew, Israel
327
- "Hila": "voice-164", # Female, Hebrew, Israel
328
- # Hindi (India)
329
- "Madhur": "voice-165", # Male, Hindi, India
330
- "Swara": "voice-166", # Female, Hindi, India
331
- # Hungarian (Hungary)
332
- "Noemi": "voice-167", # Female, Hungarian, Hungary
333
- "Tamas": "voice-168", # Male, Hungarian, Hungary
334
- # Icelandic (Iceland)
335
- "Gudrun": "voice-169", # Female, Icelandic, Iceland
336
- "Gunnar": "voice-170", # Male, Icelandic, Iceland
337
- # Indonesian (Indonesia)
338
- "Ardi": "voice-171", # Male, Indonesian, Indonesia
339
- "Gadis": "voice-172", # Female, Indonesian, Indonesia
340
- # Irish (Ireland)
341
- "Colm": "voice-173", # Male, Irish, Ireland
342
- "Orla": "voice-174", # Female, Irish, Ireland
343
- # Italian (Italy)
344
- "Diego": "voice-175", # Male, Italian, Italy
345
- "Elsa": "voice-176", # Female, Italian, Italy
346
- "Isabella": "voice-178", # Female, Italian, Italy
347
- # Japanese (Japan)
348
- "Keita": "voice-179", # Male, Japanese, Japan
349
- "Nanami": "voice-180", # Female, Japanese, Japan
350
- # Javanese (Indonesia)
351
- "Dimas": "voice-181", # Male, Javanese, Indonesia
352
- "Siti": "voice-182", # Female, Javanese, Indonesia
353
- # Kannada (India)
354
- "Gagan": "voice-183", # Male, Kannada, India
355
- "Sapna": "voice-184", # Female, Kannada, India
356
- # Kazakh (Kazakhstan)
357
- "Aigul": "voice-185", # Female, Kazakh, Kazakhstan
358
- "Daulet": "voice-186", # Male, Kazakh, Kazakhstan
359
- # Khmer (Cambodia)
360
- "Piseth": "voice-187", # Male, Khmer, Cambodia
361
- "Sreymom": "voice-188", # Female, Khmer, Cambodia
362
- # Korean (South Korea)
363
- "InJoon": "voice-190", # Male, Korean, South Korea
364
- "SunHi": "voice-191", # Female, Korean, South Korea
365
- # Lao (Laos)
366
- "Chanthavong": "voice-192", # Male, Lao, Laos
367
- "Keomany": "voice-193", # Female, Lao, Laos
368
- # Latvian (Latvia)
369
- "Everita": "voice-194", # Female, Latvian, Latvia
370
- "Nils": "voice-195", # Male, Latvian, Latvia
371
- # Lithuanian (Lithuania)
372
- "Leonas": "voice-196", # Male, Lithuanian, Lithuania
373
- "Ona": "voice-197", # Female, Lithuanian, Lithuania
374
- # Macedonian (North Macedonia)
375
- "Aleksandar": "voice-198", # Male, Macedonian, North Macedonia
376
- "Marija": "voice-199", # Female, Macedonian, North Macedonia
377
- # Malay (Malaysia)
378
- "Osman": "voice-200", # Male, Malay, Malaysia
379
- "Yasmin": "voice-201", # Female, Malay, Malaysia
380
- # Malayalam (India)
381
- "Midhun": "voice-202", # Male, Malayalam, India
382
- "Sobhana": "voice-203", # Female, Malayalam, India
383
- # Maltese (Malta)
384
- "Grace": "voice-204", # Female, Maltese, Malta
385
- "Joseph": "voice-205", # Male, Maltese, Malta
386
- # Marathi (India)
387
- "Aarohi": "voice-206", # Female, Marathi, India
388
- "Manohar": "voice-207", # Male, Marathi, India
389
- # Mongolian (Mongolia)
390
- "Bataa": "voice-208", # Male, Mongolian, Mongolia
391
- "Yesui": "voice-209", # Female, Mongolian, Mongolia
392
- # Nepali (Nepal)
393
- "Hemkala": "voice-210", # Female, Nepali, Nepal
394
- "Sagar": "voice-211", # Male, Nepali, Nepal
395
- # Norwegian (Norway)
396
- "Finn": "voice-212", # Male, Norwegian, Norway
397
- "Pernille": "voice-213", # Female, Norwegian, Norway
398
- # Pashto (Afghanistan)
399
- "GulNawaz": "voice-214", # Male, Pashto, Afghanistan
400
- "Latifa": "voice-215", # Female, Pashto, Afghanistan
401
- # Persian (Iran)
402
- "Dilara": "voice-216", # Female, Persian, Iran
403
- "Farid": "voice-217", # Male, Persian, Iran
404
- # Polish (Poland)
405
- "Marek": "voice-218", # Male, Polish, Poland
406
- "Zofia": "voice-219", # Female, Polish, Poland
407
- # Portuguese (Brazil)
408
- "Antonio": "voice-220", # Male, Portuguese, Brazil
409
- "Francisca": "voice-221", # Female, Portuguese, Brazil
410
- # Portuguese (Portugal)
411
- "Duarte": "voice-223", # Male, Portuguese, Portugal
412
- "Raquel": "voice-224", # Female, Portuguese, Portugal
413
- # Romanian (Romania)
414
- "Alina": "voice-225", # Female, Romanian, Romania
415
- "Emil": "voice-226", # Male, Romanian, Romania
416
- # Russian (Russia)
417
- "Dmitry": "voice-227", # Male, Russian, Russia
418
- "Svetlana": "voice-228", # Female, Russian, Russia
419
- # Serbian (Serbia)
420
- "Nicholas": "voice-229", # Male, Serbian, Serbia
421
- "Sophie": "voice-230", # Female, Serbian, Serbia
422
- # Sinhala (Sri Lanka)
423
- "Sameera": "voice-231", # Male, Sinhala, Sri Lanka
424
- "Thilini": "voice-232", # Female, Sinhala, Sri Lanka
425
- # Slovak (Slovakia)
426
- "Lukas": "voice-233", # Male, Slovak, Slovakia
427
- "Viktoria": "voice-234", # Female, Slovak, Slovakia
428
- # Slovenian (Slovenia)
429
- "Petra": "voice-235", # Female, Slovenian, Slovenia
430
- "Rok": "voice-236", # Male, Slovenian, Slovenia
431
- # Somali (Somalia)
432
- "Muuse": "voice-237", # Male, Somali, Somalia
433
- "Ubax": "voice-238", # Female, Somali, Somalia
434
- # Sundanese (Indonesia)
435
- "Jajang": "voice-284", # Male, Sundanese, Indonesia
436
- "Tuti": "voice-285", # Female, Sundanese, Indonesia
437
- # Swahili (Kenya)
438
- "Rafiki": "voice-286", # Male, Swahili, Kenya
439
- "Zuri": "voice-287", # Female, Swahili, Kenya
440
- # Swahili (Tanzania)
441
- "Daudi": "voice-288", # Male, Swahili, Tanzania
442
- "Rehema": "voice-289", # Female, Swahili, Tanzania
443
- # Swedish (Sweden)
444
- "Mattias": "voice-290", # Male, Swedish, Sweden
445
- "Sofie": "voice-291", # Female, Swedish, Sweden
446
- # Tamil (India)
447
- "Pallavi": "voice-292", # Female, Tamil, India
448
- "Valluvar": "voice-293", # Male, Tamil, India
449
- # Tamil (Sri Lanka)
450
- "Kumar": "voice-294", # Male, Tamil, Sri Lanka
451
- "Saranya": "voice-295", # Female, Tamil, Sri Lanka
452
- # Tamil (Malaysia)
453
- "Kani": "voice-296", # Female, Tamil, Malaysia
454
- "Surya": "voice-297", # Male, Tamil, Malaysia
455
- # Tamil (Singapore)
456
- "Anbu": "voice-298", # Male, Tamil, Singapore
457
- "Venba": "voice-299", # Female, Tamil, Singapore
458
- # Telugu (India)
459
- "Mohan": "voice-300", # Male, Telugu, India
460
- "Shruti": "voice-301", # Female, Telugu, India
461
- # Thai (Thailand)
462
- "Niwat": "voice-302", # Male, Thai, Thailand
463
- "Premwadee": "voice-303", # Female, Thai, Thailand
464
- # Turkish (Turkey)
465
- "Ahmet": "voice-304", # Male, Turkish, Turkey
466
- "Emel": "voice-305", # Female, Turkish, Turkey
467
- # Ukrainian (Ukraine)
468
- "Ostap": "voice-306", # Male, Ukrainian, Ukraine
469
- "Polina": "voice-307", # Female, Ukrainian, Ukraine
470
- # Urdu (India)
471
- "Gul": "voice-308", # Female, Urdu, India
472
- "Salman": "voice-309", # Male, Urdu, India
473
- # Urdu (Pakistan)
474
- "Asad": "voice-310", # Male, Urdu, Pakistan
475
- "Uzma": "voice-311", # Female, Urdu, Pakistan
476
- # Uzbek (Uzbekistan)
477
- "Madina": "voice-312", # Female, Uzbek, Uzbekistan
478
- "Sardor": "voice-313", # Male, Uzbek, Uzbekistan
479
- # Vietnamese (Vietnam)
480
- "HoaiMy": "voice-314", # Female, Vietnamese, Vietnam
481
- "NamMinh": "voice-315", # Male, Vietnamese, Vietnam
482
- # Welsh (United Kingdom)
483
- "Aled": "voice-316", # Male, Welsh, United Kingdom
484
- "Nia": "voice-317", # Female, Welsh, United Kingdom
485
- # Zulu (South Africa)
486
- "Thando": "voice-318", # Female, Zulu, South Africa
487
- "Themba": "voice-319", # Male, Zulu, South Africa
488
- }
489
-
490
- def __init__(self, timeout: int = 20, proxies: dict = None):
491
- """Initializes the SpeechMa TTS client."""
492
- super().__init__()
493
- self.api_url = "https://speechma.com/com.api/tts-api.php"
494
- self.session = requests.Session()
495
- self.session.headers.update(self.headers)
496
- if proxies:
497
- self.session.proxies.update(proxies)
498
- self.timeout = timeout
499
-
500
- def tts(self, text: str, voice: str = "Emma", pitch: int = 0, rate: int = 0) -> str:
501
- """
502
- Converts text to speech using the SpeechMa API and saves it to a file.
503
-
504
- Args:
505
- text (str): The text to convert to speech
506
- voice (str): The voice to use for TTS (default: "Emma")
507
- pitch (int): Voice pitch adjustment (-10 to 10, default: 0)
508
- rate (int): Voice rate/speed adjustment (-10 to 10, default: 0)
509
-
510
- Returns:
511
- str: Path to the generated audio file
512
-
513
- Raises:
514
- exceptions.FailedToGenerateResponseError: If there is an error generating or saving the audio.
515
- """
516
- assert (
517
- voice in self.all_voices
518
- ), f"Voice '{voice}' not one of [{', '.join(self.all_voices.keys())}]"
519
-
520
- filename = pathlib.Path(tempfile.mktemp(suffix=".mp3", dir=self.temp_dir))
521
- voice_id = self.all_voices[voice]
522
-
523
- # Prepare payload for the job-based API
524
- payload = {
525
- "text": text,
526
- "voice": voice_id,
527
- "pitch": pitch,
528
- "rate": rate,
529
- "volume": 100
530
- }
531
-
532
- try:
533
- response = self.session.post(
534
- self.api_url,
535
- headers=self.headers,
536
- json=payload,
537
- timeout=self.timeout
538
- )
539
- response.raise_for_status()
540
- resp_json = response.json()
541
- if not resp_json.get("success") or "data" not in resp_json or "job_id" not in resp_json["data"]:
542
- raise exceptions.FailedToGenerateResponseError(f"SpeechMa API error: {resp_json}")
543
- job_id = resp_json["data"]["job_id"]
544
-
545
- # Poll for job completion
546
- status_url = f"https://speechma.com/com.api/tts-api.php/status/{job_id}"
547
- for _ in range(30): # up to ~30 seconds
548
- status_resp = self.session.get(
549
- status_url,
550
- headers=self.headers,
551
- timeout=self.timeout
552
- )
553
- status_resp.raise_for_status()
554
- status_json = status_resp.json()
555
- if status_json.get("success") and status_json.get("data", {}).get("status") == "completed":
556
- break
557
- time.sleep(1)
558
- else:
559
- raise exceptions.FailedToGenerateResponseError("TTS job did not complete in time.")
560
-
561
- # Download the audio file (API provides a URL in the status response)
562
- data = status_json["data"]
563
- audio_url = f"https://speechma.com/com.api/tts-api.php/audio/{job_id}"
564
- audio_resp = self.session.get(audio_url, timeout=self.timeout)
565
- audio_resp.raise_for_status()
566
- with open(filename, 'wb') as f:
567
- f.write(audio_resp.content)
568
- return filename.as_posix()
569
-
570
- except requests.exceptions.RequestException as e:
571
- raise exceptions.FailedToGenerateResponseError(
572
- f"Failed to perform the operation: {e}"
573
- )
574
-
575
- # Example usage
576
- if __name__ == "__main__":
577
- speechma = SpeechMaTTS()
578
- text = "This is a test of the SpeechMa text-to-speech API. It supports multiple sentences."
579
- audio_file = speechma.tts(text, voice="Emma")
580
- print(f"Audio saved to: {audio_file}")
1
+ ##################################################################################
2
+ ## Modified version of code written by t.me/infip1217 ##
3
+ ##################################################################################
4
+ import pathlib
5
+ import tempfile
6
+ import time
7
+ from concurrent.futures import ThreadPoolExecutor, as_completed
8
+ from io import BytesIO
9
+ from typing import Any, Generator, Optional, Union, cast
10
+
11
+ import requests
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
+
25
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", ".."))
26
+ from webscout.Provider.TTS import utils
27
+ from webscout.Provider.TTS.base import BaseTTSProvider
28
+
29
+
30
+ class SpeechMaTTS(BaseTTSProvider):
31
+ """
32
+ Text-to-speech provider using the SpeechMa API with OpenAI-compatible interface.
33
+
34
+ This provider follows the OpenAI TTS API structure with support for:
35
+ - Multiple TTS models (gpt-4o-mini-tts, tts-1, tts-1-hd)
36
+ - Multilingual voices with pitch and rate control
37
+ - Voice instructions for controlling speech aspects
38
+ - Multiple output formats
39
+ - Streaming support
40
+ """
41
+
42
+ required_auth = False
43
+
44
+ # Request headers
45
+ headers = {
46
+ "authority": "speechma.com",
47
+ "origin": "https://speechma.com",
48
+ "referer": "https://speechma.com/",
49
+ "content-type": "application/json",
50
+ **LitAgent().generate_fingerprint(),
51
+ }
52
+
53
+ # SpeechMa doesn't support different models - set to None
54
+ SUPPORTED_MODELS = None
55
+
56
+ # All supported voices from SpeechMa API
57
+ SUPPORTED_VOICES = [
58
+ "aditi",
59
+ "amy",
60
+ "astrid",
61
+ "bianca",
62
+ "carla",
63
+ "carmen",
64
+ "celine",
65
+ "chant",
66
+ "conchita",
67
+ "cristiano",
68
+ "dora",
69
+ "enrique",
70
+ "ewa",
71
+ "filiz",
72
+ "geraint",
73
+ "giorgio",
74
+ "gwyneth",
75
+ "hans",
76
+ "ines",
77
+ "ivy",
78
+ "jacek",
79
+ "jan",
80
+ "joanna",
81
+ "joey",
82
+ "justin",
83
+ "karl",
84
+ "kendra",
85
+ "kimberly",
86
+ "lea",
87
+ "liv",
88
+ "lotte",
89
+ "lucia",
90
+ "lupe",
91
+ "mads",
92
+ "maja",
93
+ "marlene",
94
+ "mathieu",
95
+ "matthew",
96
+ "maxim",
97
+ "mia",
98
+ "miguel",
99
+ "mizuki",
100
+ "naja",
101
+ "nicole",
102
+ "penelope",
103
+ "raveena",
104
+ "ricardo",
105
+ "ruben",
106
+ "russell",
107
+ "salli",
108
+ "seoyeon",
109
+ "takumi",
110
+ "tatyana",
111
+ "vicki",
112
+ "vitoria",
113
+ "zeina",
114
+ "zhiyu",
115
+ "aditi-neural",
116
+ "amy-neural",
117
+ "aria-neural",
118
+ "ayanda-neural",
119
+ "brian-neural",
120
+ "emma-neural",
121
+ "jenny-neural",
122
+ "joey-neural",
123
+ "justin-neural",
124
+ "kendra-neural",
125
+ "kimberly-neural",
126
+ "matthew-neural",
127
+ "olivia-neural",
128
+ "ruth-neural",
129
+ "salli-neural",
130
+ "stephen-neural",
131
+ "suvi-neural",
132
+ "camila-neural",
133
+ "lupe-neural",
134
+ "pedro-neural",
135
+ "natasha-neural",
136
+ "william-neural",
137
+ "clara-neural",
138
+ "liam-neural",
139
+ "libby-neural",
140
+ "maisie-neural",
141
+ "ryan-neural",
142
+ "sonia-neural",
143
+ "thomas-neural",
144
+ "aria-multilingual",
145
+ "andrew-multilingual",
146
+ "brian-multilingual",
147
+ "emma-multilingual",
148
+ "jenny-multilingual",
149
+ "ryan-multilingual",
150
+ "adam-multilingual",
151
+ "liam-multilingual",
152
+ "aria-turbo",
153
+ "andrew-turbo",
154
+ "brian-turbo",
155
+ "emma-turbo",
156
+ "jenny-turbo",
157
+ "ryan-turbo",
158
+ "adam-turbo",
159
+ "liam-turbo",
160
+ "aria-hd",
161
+ "andrew-hd",
162
+ "brian-hd",
163
+ "emma-hd",
164
+ "jenny-hd",
165
+ "andrew-hd-2",
166
+ "aria-hd-2",
167
+ "adam-hd",
168
+ "ava-hd",
169
+ "davis-hd",
170
+ "brian-hd-2",
171
+ "christopher-hd",
172
+ "coral-hd",
173
+ "emma-hd-2",
174
+ "eric-hd",
175
+ "fable-hd",
176
+ "jenny-hd-2",
177
+ "michelle-hd",
178
+ "roger-hd",
179
+ "sage-hd",
180
+ "vale-hd",
181
+ "verse-hd",
182
+ # Legacy voice names for backward compatibility
183
+ "emma",
184
+ "ava",
185
+ "brian",
186
+ "andrew",
187
+ "aria",
188
+ "christopher",
189
+ "eric",
190
+ "jenny",
191
+ "michelle",
192
+ "roger",
193
+ "libby",
194
+ "ryan",
195
+ "sonia",
196
+ "thomas",
197
+ "natasha",
198
+ "william",
199
+ "clara",
200
+ "liam",
201
+ ]
202
+
203
+ # Voice mapping for SpeechMa API compatibility (lowercase keys for all voices)
204
+ voice_mapping = {
205
+ # Standard voices
206
+ "aditi": "voice-1",
207
+ "amy": "voice-2",
208
+ "astrid": "voice-3",
209
+ "bianca": "voice-4",
210
+ "carla": "voice-5",
211
+ "carmen": "voice-6",
212
+ "celine": "voice-7",
213
+ "chant": "voice-8",
214
+ "conchita": "voice-9",
215
+ "cristiano": "voice-10",
216
+ "dora": "voice-11",
217
+ "enrique": "voice-12",
218
+ "ewa": "voice-13",
219
+ "filiz": "voice-14",
220
+ "geraint": "voice-15",
221
+ "giorgio": "voice-16",
222
+ "gwyneth": "voice-17",
223
+ "hans": "voice-18",
224
+ "ines": "voice-19",
225
+ "ivy": "voice-20",
226
+ "jacek": "voice-21",
227
+ "jan": "voice-22",
228
+ "joanna": "voice-23",
229
+ "joey": "voice-24",
230
+ "justin": "voice-25",
231
+ "karl": "voice-26",
232
+ "kendra": "voice-27",
233
+ "kimberly": "voice-28",
234
+ "lea": "voice-29",
235
+ "liv": "voice-30",
236
+ "lotte": "voice-31",
237
+ "lucia": "voice-32",
238
+ "lupe": "voice-33",
239
+ "mads": "voice-34",
240
+ "maja": "voice-35",
241
+ "marlene": "voice-36",
242
+ "mathieu": "voice-37",
243
+ "matthew": "voice-38",
244
+ "maxim": "voice-39",
245
+ "mia": "voice-40",
246
+ "miguel": "voice-41",
247
+ "mizuki": "voice-42",
248
+ "naja": "voice-43",
249
+ "nicole": "voice-44",
250
+ "penelope": "voice-45",
251
+ "raveena": "voice-46",
252
+ "ricardo": "voice-47",
253
+ "ruben": "voice-48",
254
+ "russell": "voice-49",
255
+ "salli": "voice-50",
256
+ "seoyeon": "voice-51",
257
+ "takumi": "voice-52",
258
+ "tatyana": "voice-53",
259
+ "vicki": "voice-54",
260
+ "vitoria": "voice-55",
261
+ "zeina": "voice-56",
262
+ "zhiyu": "voice-57",
263
+ # Neural voices
264
+ "aditi-neural": "voice-58",
265
+ "amy-neural": "voice-59",
266
+ "aria-neural": "voice-60",
267
+ "ayanda-neural": "voice-61",
268
+ "brian-neural": "voice-62",
269
+ "emma-neural": "voice-63",
270
+ "jenny-neural": "voice-64",
271
+ "joey-neural": "voice-65",
272
+ "justin-neural": "voice-66",
273
+ "kendra-neural": "voice-67",
274
+ "kimberly-neural": "voice-68",
275
+ "matthew-neural": "voice-69",
276
+ "olivia-neural": "voice-70",
277
+ "ruth-neural": "voice-71",
278
+ "salli-neural": "voice-72",
279
+ "stephen-neural": "voice-73",
280
+ "suvi-neural": "voice-74",
281
+ "camila-neural": "voice-75",
282
+ "lupe-neural": "voice-76",
283
+ "pedro-neural": "voice-77",
284
+ "natasha-neural": "voice-78",
285
+ "william-neural": "voice-79",
286
+ "clara-neural": "voice-80",
287
+ "liam-neural": "voice-81",
288
+ "libby-neural": "voice-82",
289
+ "maisie-neural": "voice-83",
290
+ "ryan-neural": "voice-84",
291
+ "sonia-neural": "voice-85",
292
+ "thomas-neural": "voice-86",
293
+ # Multilingual voices
294
+ "aria-multilingual": "voice-87",
295
+ "andrew-multilingual": "voice-88",
296
+ "brian-multilingual": "voice-89",
297
+ "emma-multilingual": "voice-90",
298
+ "jenny-multilingual": "voice-91",
299
+ "ryan-multilingual": "voice-92",
300
+ "adam-multilingual": "voice-93",
301
+ "liam-multilingual": "voice-94",
302
+ # Turbo voices
303
+ "aria-turbo": "voice-95",
304
+ "andrew-turbo": "voice-96",
305
+ "brian-turbo": "voice-97",
306
+ "emma-turbo": "voice-98",
307
+ "jenny-turbo": "voice-99",
308
+ "ryan-turbo": "voice-100",
309
+ "adam-turbo": "voice-101",
310
+ "liam-turbo": "voice-102",
311
+ # HD voices
312
+ "aria-hd": "voice-103",
313
+ "andrew-hd": "voice-104",
314
+ "brian-hd": "voice-105",
315
+ "emma-hd": "voice-106",
316
+ "jenny-hd": "voice-107",
317
+ "andrew-hd-2": "voice-108",
318
+ "aria-hd-2": "voice-109",
319
+ "adam-hd": "voice-110",
320
+ "ava-hd": "voice-111",
321
+ "davis-hd": "voice-112",
322
+ "brian-hd-2": "voice-113",
323
+ "christopher-hd": "voice-114",
324
+ "coral-hd": "voice-115",
325
+ "emma-hd-2": "voice-116",
326
+ "eric-hd": "voice-117",
327
+ "fable-hd": "voice-118",
328
+ "jenny-hd-2": "voice-119",
329
+ "michelle-hd": "voice-120",
330
+ "roger-hd": "voice-121",
331
+ "sage-hd": "voice-122",
332
+ "vale-hd": "voice-123",
333
+ "verse-hd": "voice-124",
334
+ # Legacy compatibility mappings (lowercase)
335
+ "emma": "voice-116",
336
+ "ava": "voice-111",
337
+ "brian": "voice-113",
338
+ "andrew": "voice-108",
339
+ "aria": "voice-109",
340
+ "christopher": "voice-114",
341
+ "eric": "voice-117",
342
+ "jenny": "voice-119",
343
+ "michelle": "voice-120",
344
+ "roger": "voice-121",
345
+ "libby": "voice-82",
346
+ "ryan": "voice-84",
347
+ "sonia": "voice-85",
348
+ "thomas": "voice-86",
349
+ "natasha": "voice-78",
350
+ "william": "voice-79",
351
+ "clara": "voice-80",
352
+ "liam": "voice-81",
353
+ }
354
+
355
+ # Legacy voice mapping for backward compatibility
356
+ all_voices = voice_mapping
357
+
358
+ def __init__(self, timeout: int = 20, proxies: Optional[dict] = None):
359
+ """
360
+ Initialize the SpeechMa TTS client.
361
+
362
+ Args:
363
+ timeout (int): Request timeout in seconds
364
+ proxies (dict): Proxy configuration
365
+ """
366
+ super().__init__()
367
+ self.api_url = "https://speechma.com/com.api/tts-api.php"
368
+ self.session = requests.Session()
369
+ self.session.headers.update(self.headers)
370
+ if proxies:
371
+ self.session.proxies.update(proxies)
372
+ self.timeout = timeout
373
+ # Override defaults for SpeechMa
374
+ self.default_voice = "emma"
375
+ self.default_model = "gpt-4o-mini-tts"
376
+
377
+ def create_speech(
378
+ self,
379
+ input_text: str,
380
+ model: Optional[str] = "gpt-4o-mini-tts",
381
+ voice: Optional[str] = "emma",
382
+ response_format: Optional[str] = "mp3",
383
+ instructions: Optional[str] = None,
384
+ verbose: bool = False,
385
+ ) -> str:
386
+ """
387
+ Create speech from text using OpenAI-compatible interface.
388
+
389
+ Args:
390
+ input_text (str): The text to convert to speech
391
+ voice (str): Voice to use for generation
392
+ model (str): TTS model to use
393
+ response_format (str): Audio format (mp3, opus, aac, flac, wav, pcm)
394
+ instructions (str): Voice instructions (not used by SpeechMa)
395
+ verbose (bool): Whether to print debug information
396
+
397
+ Returns:
398
+ str: Path to the generated audio file
399
+
400
+ Raises:
401
+ ValueError: If input parameters are invalid
402
+ exceptions.FailedToGenerateResponseError: If generation fails
403
+ """
404
+ return self.tts(
405
+ text=input_text,
406
+ voice=voice or "emma",
407
+ model=model or "gpt-4o-mini-tts",
408
+ response_format=response_format or "mp3",
409
+ instructions=instructions,
410
+ verbose=verbose,
411
+ )
412
+
413
+ def with_streaming_response(self):
414
+ """
415
+ Return a context manager for streaming responses.
416
+
417
+ Returns:
418
+ SpeechMaStreamingResponse: Context manager for streaming
419
+ """
420
+ return SpeechMaStreamingResponse(self)
421
+
422
+ def tts(self, text: str, voice: Optional[str] = None, verbose: bool = False, **kwargs) -> str:
423
+ """
424
+ Convert text to speech using SpeechMa API with OpenAI-compatible parameters.
425
+
426
+ Args:
427
+ text (str): The text to convert to speech (max 10,000 characters)
428
+ **kwargs: Additional parameters (model, voice, response_format, instructions, pitch, rate, verbose)
429
+ """
430
+ # Extract parameters from kwargs with defaults
431
+ model = kwargs.get("model", self.default_model)
432
+ voice = voice or kwargs.get("voice", "emma")
433
+ response_format = kwargs.get("response_format", "mp3")
434
+ pitch = kwargs.get("pitch", 0)
435
+ rate = kwargs.get("rate", 0)
436
+ verbose = verbose if verbose is not None else kwargs.get("verbose", True)
437
+ # Validate input parameters
438
+ if not text or not isinstance(text, str):
439
+ raise ValueError("Input text must be a non-empty string")
440
+ if len(text) > 10000:
441
+ raise ValueError("Input text exceeds maximum allowed length of 10,000 characters")
442
+
443
+ # Validate model, voice, and format using base class methods
444
+ model = self.validate_model(model)
445
+ voice = self.validate_voice(voice)
446
+ response_format = self.validate_format(response_format)
447
+
448
+ # Map voice to SpeechMa API format
449
+ speechma_voice = self.voice_mapping.get(voice, voice)
450
+ if speechma_voice not in self.all_voices.values():
451
+ # Fallback to legacy voice mapping
452
+ speechma_voice = self.all_voices.get(
453
+ voice.title(), self.all_voices.get("Emma", "voice-116")
454
+ )
455
+
456
+ # Create temporary file with appropriate extension
457
+ file_extension = f".{response_format}" if response_format != "pcm" else ".wav"
458
+ filename = pathlib.Path(
459
+ tempfile.NamedTemporaryFile(suffix=file_extension, dir=self.temp_dir, delete=False).name
460
+ )
461
+
462
+ # Split text into sentences using the utils module for better processing
463
+ sentences = utils.split_sentences(text)
464
+ if verbose:
465
+ ic.configureOutput(prefix="DEBUG| ")
466
+ ic(f"Processing {len(sentences)} sentences")
467
+ ic.configureOutput(prefix="DEBUG| ")
468
+ ic(f"Model: {model}")
469
+ ic.configureOutput(prefix="DEBUG| ")
470
+ ic(f"Voice: {voice} -> {speechma_voice}")
471
+ ic.configureOutput(prefix="DEBUG| ")
472
+ ic(f"Format: {response_format}")
473
+
474
+ def generate_audio_for_chunk(part_text: str, part_number: int):
475
+ """
476
+ Generate audio for a single chunk of text.
477
+
478
+ Args:
479
+ part_text (str): The text chunk to convert
480
+ part_number (int): The chunk number for ordering
481
+
482
+ Returns:
483
+ tuple: (part_number, audio_data)
484
+
485
+ Raises:
486
+ requests.RequestException: If there's an API error
487
+ """
488
+ max_retries = 3
489
+ retry_count = 0
490
+
491
+ while retry_count < max_retries:
492
+ try:
493
+ payload = {
494
+ "text": part_text,
495
+ "voice": speechma_voice,
496
+ "pitch": pitch,
497
+ "rate": rate,
498
+ "volume": 100,
499
+ # Add model parameter for future SpeechMa API compatibility
500
+ "tts_model": model,
501
+ }
502
+ response = self.session.post(
503
+ url=self.api_url, headers=self.headers, json=payload, timeout=self.timeout
504
+ )
505
+ response.raise_for_status()
506
+
507
+ # Check if response is audio data
508
+ content_type = response.headers.get("content-type", "").lower()
509
+ if (
510
+ "audio" in content_type
511
+ or response.content.startswith(b"\xff\xfb")
512
+ or response.content.startswith(b"ID3")
513
+ or b"LAME" in response.content[:100]
514
+ ):
515
+ if verbose:
516
+ ic.configureOutput(prefix="DEBUG| ")
517
+ ic(f"Chunk {part_number} processed successfully")
518
+ return part_number, response.content
519
+ else:
520
+ raise exceptions.FailedToGenerateResponseError(
521
+ f"Unexpected response format. Content-Type: {content_type}"
522
+ )
523
+
524
+ except requests.exceptions.RequestException as e:
525
+ retry_count += 1
526
+ if retry_count >= max_retries:
527
+ raise exceptions.FailedToGenerateResponseError(
528
+ f"Failed to generate audio for chunk {part_number} after {max_retries} retries: {e}"
529
+ )
530
+ if verbose:
531
+ ic.configureOutput(prefix="DEBUG| ")
532
+ ic(f"Retrying chunk {part_number} (attempt {retry_count + 1})")
533
+ time.sleep(1) # Brief delay before retry
534
+
535
+ # Process chunks concurrently for better performance
536
+ audio_chunks = []
537
+ if len(sentences) > 1:
538
+ with ThreadPoolExecutor(max_workers=3) as executor:
539
+ future_to_chunk = {
540
+ executor.submit(generate_audio_for_chunk, sentence, i): i
541
+ for i, sentence in enumerate(sentences)
542
+ }
543
+
544
+ for future in as_completed(future_to_chunk):
545
+ try:
546
+ chunk_number, audio_data = future.result()
547
+ audio_chunks.append((chunk_number, audio_data))
548
+ except Exception as e:
549
+ if verbose:
550
+ ic.configureOutput(prefix="DEBUG| ")
551
+ ic(f"Error processing chunk: {e}")
552
+ raise
553
+ else:
554
+ # Single sentence, process directly
555
+ chunk_number, audio_data = generate_audio_for_chunk(sentences[0], 0)
556
+ audio_chunks.append((chunk_number, audio_data))
557
+
558
+ # Sort chunks by their original order and combine
559
+ audio_chunks.sort(key=lambda x: x[0])
560
+ combined_audio = b"".join([chunk[1] for chunk in audio_chunks])
561
+
562
+ # Save combined audio to file
563
+ try:
564
+ with open(filename, "wb") as f:
565
+ f.write(combined_audio)
566
+ if verbose:
567
+ ic.configureOutput(prefix="DEBUG| ")
568
+ ic(f"Audio saved to: {filename}")
569
+ return filename.as_posix()
570
+ except IOError as e:
571
+ raise exceptions.FailedToGenerateResponseError(f"Failed to save audio file: {e}")
572
+
573
+
574
+ class SpeechMaStreamingResponse:
575
+ """Context manager for streaming SpeechMa TTS responses."""
576
+
577
+ def __init__(self, client: SpeechMaTTS):
578
+ self.client = client
579
+
580
+ def __enter__(self):
581
+ return self
582
+
583
+ def __exit__(self, exc_type, exc_val, exc_tb):
584
+ pass
585
+
586
+ def create_speech(
587
+ self,
588
+ input_text: str,
589
+ voice: Optional[str] = "emma",
590
+ model: Optional[str] = "gpt-4o-mini-tts",
591
+ response_format: Optional[str] = "mp3",
592
+ instructions: Optional[str] = None,
593
+ verbose: bool = False,
594
+ ):
595
+ """
596
+ Create speech with streaming response simulation.
597
+
598
+ Note: SpeechMa doesn't support true streaming, so this returns
599
+ the complete audio data wrapped in a BytesIO object.
600
+
601
+ Args:
602
+ input_text (str): Text to convert to speech
603
+ voice (str): Voice to use
604
+ model (str): TTS model
605
+ response_format (str): Audio format
606
+ instructions (str): Voice instructions
607
+ verbose (bool): Whether to print debug information
608
+
609
+ Returns:
610
+ BytesIO: Audio data stream
611
+ """
612
+ audio_file = self.client.create_speech(
613
+ input_text=input_text,
614
+ voice=voice,
615
+ model=model,
616
+ response_format=response_format,
617
+ instructions=instructions,
618
+ verbose=verbose,
619
+ )
620
+ with open(audio_file, "rb") as f:
621
+ return BytesIO(f.read())
622
+
623
+
624
+ # Example usage and testing
625
+ if __name__ == "__main__":
626
+ # Initialize the SpeechMa TTS client
627
+ speechma = SpeechMaTTS()
628
+
629
+ # Example 1: Basic usage with legacy method
630
+ print("=== Example 1: Basic TTS ===")
631
+ text = "Hello, this is a test of the SpeechMa text-to-speech API."
632
+ try:
633
+ audio_file = speechma.tts(text, voice="emma", verbose=True)
634
+ print(f"Audio saved to: {audio_file}")
635
+ except Exception as e:
636
+ print(f"Error: {e}")
637
+
638
+ # Example 2: OpenAI-compatible interface
639
+ print("\n=== Example 2: OpenAI-compatible interface ===")
640
+ try:
641
+ audio_file_path = speechma.create_speech(
642
+ input_text="This demonstrates the OpenAI-compatible interface.",
643
+ voice="brian",
644
+ model="tts-1-hd",
645
+ response_format="mp3",
646
+ )
647
+ print(f"Generated audio file at: {audio_file_path}")
648
+
649
+ # Read the file to get bytes for the example
650
+ with open(audio_file_path, "rb") as f:
651
+ audio_data = f.read()
652
+ print(f"Generated {len(audio_data)} bytes of audio data")
653
+
654
+ # Save to file
655
+ with open("openai_compatible_test.mp3", "wb") as f:
656
+ f.write(audio_data)
657
+ print("Audio saved to: openai_compatible_test.mp3")
658
+ except Exception as e:
659
+ print(f"Error: {e}")
660
+
661
+ # Example 3: Streaming response context manager
662
+ print("\n=== Example 3: Streaming response ===")
663
+ try:
664
+ with speechma.with_streaming_response() as streaming:
665
+ audio_stream = streaming.create_speech(
666
+ input_text="This demonstrates streaming response handling.",
667
+ voice="aria",
668
+ model="gpt-4o-mini-tts",
669
+ )
670
+ audio_data = audio_stream.read()
671
+ print(f"Streamed {len(audio_data)} bytes of audio data")
672
+ except Exception as e:
673
+ print(f"Error: {e}")
674
+
675
+ # Example 4: Voice and model validation
676
+ print("\n=== Example 4: Parameter validation ===")
677
+ try:
678
+ # Test supported voices
679
+ print("Supported voices:", speechma.SUPPORTED_VOICES[:5], "...")
680
+ print("Supported models:", speechma.SUPPORTED_MODELS)
681
+
682
+ # Test with different parameters
683
+ audio_file = speechma.tts(
684
+ text="Testing different voice parameters.",
685
+ voice="christopher",
686
+ model="tts-1",
687
+ pitch=2,
688
+ rate=-1,
689
+ verbose=True,
690
+ )
691
+ print(f"Audio with custom parameters saved to: {audio_file}")
692
+ except Exception as e:
693
+ print(f"Error: {e}")