webscout 7.1__py3-none-any.whl → 7.3__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.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (154) hide show
  1. webscout/AIauto.py +191 -191
  2. webscout/AIbase.py +122 -122
  3. webscout/AIutel.py +440 -440
  4. webscout/Bard.py +343 -161
  5. webscout/DWEBS.py +489 -492
  6. webscout/Extra/YTToolkit/YTdownloader.py +995 -995
  7. webscout/Extra/YTToolkit/__init__.py +2 -2
  8. webscout/Extra/YTToolkit/transcriber.py +476 -479
  9. webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
  10. webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
  11. webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
  12. webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
  13. webscout/Extra/YTToolkit/ytapi/video.py +103 -103
  14. webscout/Extra/autocoder/__init__.py +9 -9
  15. webscout/Extra/autocoder/autocoder_utiles.py +199 -199
  16. webscout/Extra/autocoder/rawdog.py +5 -7
  17. webscout/Extra/autollama.py +230 -230
  18. webscout/Extra/gguf.py +3 -3
  19. webscout/Extra/weather.py +171 -171
  20. webscout/LLM.py +442 -442
  21. webscout/Litlogger/__init__.py +67 -681
  22. webscout/Litlogger/core/__init__.py +6 -0
  23. webscout/Litlogger/core/level.py +23 -0
  24. webscout/Litlogger/core/logger.py +166 -0
  25. webscout/Litlogger/handlers/__init__.py +12 -0
  26. webscout/Litlogger/handlers/console.py +33 -0
  27. webscout/Litlogger/handlers/file.py +143 -0
  28. webscout/Litlogger/handlers/network.py +173 -0
  29. webscout/Litlogger/styles/__init__.py +7 -0
  30. webscout/Litlogger/styles/colors.py +249 -0
  31. webscout/Litlogger/styles/formats.py +460 -0
  32. webscout/Litlogger/styles/text.py +87 -0
  33. webscout/Litlogger/utils/__init__.py +6 -0
  34. webscout/Litlogger/utils/detectors.py +154 -0
  35. webscout/Litlogger/utils/formatters.py +200 -0
  36. webscout/Provider/AISEARCH/DeepFind.py +250 -250
  37. webscout/Provider/AISEARCH/ISou.py +277 -0
  38. webscout/Provider/AISEARCH/__init__.py +2 -1
  39. webscout/Provider/Blackboxai.py +3 -3
  40. webscout/Provider/ChatGPTGratis.py +226 -0
  41. webscout/Provider/Cloudflare.py +3 -4
  42. webscout/Provider/DeepSeek.py +218 -0
  43. webscout/Provider/Deepinfra.py +40 -24
  44. webscout/Provider/Free2GPT.py +131 -124
  45. webscout/Provider/Gemini.py +100 -115
  46. webscout/Provider/Glider.py +3 -3
  47. webscout/Provider/Groq.py +5 -1
  48. webscout/Provider/Jadve.py +3 -3
  49. webscout/Provider/Marcus.py +191 -192
  50. webscout/Provider/Netwrck.py +3 -3
  51. webscout/Provider/PI.py +2 -2
  52. webscout/Provider/PizzaGPT.py +2 -3
  53. webscout/Provider/QwenLM.py +311 -0
  54. webscout/Provider/TTI/AiForce/__init__.py +22 -22
  55. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
  56. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
  57. webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -0
  58. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +206 -0
  59. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +192 -0
  60. webscout/Provider/TTI/Nexra/__init__.py +22 -22
  61. webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
  62. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
  63. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
  64. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
  65. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
  66. webscout/Provider/TTI/__init__.py +2 -1
  67. webscout/Provider/TTI/artbit/__init__.py +22 -22
  68. webscout/Provider/TTI/artbit/async_artbit.py +184 -184
  69. webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
  70. webscout/Provider/TTI/blackbox/__init__.py +4 -4
  71. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
  72. webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
  73. webscout/Provider/TTI/deepinfra/__init__.py +4 -4
  74. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
  75. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
  76. webscout/Provider/TTI/huggingface/__init__.py +22 -22
  77. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
  78. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
  79. webscout/Provider/TTI/imgninza/__init__.py +4 -4
  80. webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
  81. webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
  82. webscout/Provider/TTI/talkai/__init__.py +4 -4
  83. webscout/Provider/TTI/talkai/async_talkai.py +229 -229
  84. webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
  85. webscout/Provider/TTS/deepgram.py +182 -182
  86. webscout/Provider/TTS/elevenlabs.py +136 -136
  87. webscout/Provider/TTS/gesserit.py +150 -150
  88. webscout/Provider/TTS/murfai.py +138 -138
  89. webscout/Provider/TTS/parler.py +133 -134
  90. webscout/Provider/TTS/streamElements.py +360 -360
  91. webscout/Provider/TTS/utils.py +280 -280
  92. webscout/Provider/TTS/voicepod.py +116 -116
  93. webscout/Provider/TextPollinationsAI.py +28 -8
  94. webscout/Provider/WiseCat.py +193 -0
  95. webscout/Provider/__init__.py +146 -134
  96. webscout/Provider/cerebras.py +242 -227
  97. webscout/Provider/chatglm.py +204 -204
  98. webscout/Provider/dgaf.py +2 -3
  99. webscout/Provider/freeaichat.py +221 -0
  100. webscout/Provider/gaurish.py +2 -3
  101. webscout/Provider/geminiapi.py +208 -208
  102. webscout/Provider/granite.py +223 -0
  103. webscout/Provider/hermes.py +218 -218
  104. webscout/Provider/llama3mitril.py +179 -179
  105. webscout/Provider/llamatutor.py +3 -3
  106. webscout/Provider/llmchat.py +2 -3
  107. webscout/Provider/meta.py +794 -794
  108. webscout/Provider/multichat.py +331 -331
  109. webscout/Provider/typegpt.py +359 -359
  110. webscout/Provider/yep.py +3 -3
  111. webscout/__init__.py +1 -0
  112. webscout/__main__.py +5 -5
  113. webscout/cli.py +319 -319
  114. webscout/conversation.py +241 -242
  115. webscout/exceptions.py +328 -328
  116. webscout/litagent/__init__.py +28 -28
  117. webscout/litagent/agent.py +2 -3
  118. webscout/litprinter/__init__.py +0 -58
  119. webscout/scout/__init__.py +8 -8
  120. webscout/scout/core.py +884 -884
  121. webscout/scout/element.py +459 -459
  122. webscout/scout/parsers/__init__.py +69 -69
  123. webscout/scout/parsers/html5lib_parser.py +172 -172
  124. webscout/scout/parsers/html_parser.py +236 -236
  125. webscout/scout/parsers/lxml_parser.py +178 -178
  126. webscout/scout/utils.py +38 -38
  127. webscout/swiftcli/__init__.py +811 -811
  128. webscout/update_checker.py +2 -12
  129. webscout/version.py +1 -1
  130. webscout/webscout_search.py +87 -6
  131. webscout/webscout_search_async.py +58 -1
  132. webscout/yep_search.py +297 -0
  133. webscout/zeroart/__init__.py +54 -54
  134. webscout/zeroart/base.py +60 -60
  135. webscout/zeroart/effects.py +99 -99
  136. webscout/zeroart/fonts.py +816 -816
  137. {webscout-7.1.dist-info → webscout-7.3.dist-info}/METADATA +62 -22
  138. webscout-7.3.dist-info/RECORD +223 -0
  139. {webscout-7.1.dist-info → webscout-7.3.dist-info}/WHEEL +1 -1
  140. webstoken/__init__.py +30 -30
  141. webstoken/classifier.py +189 -189
  142. webstoken/keywords.py +216 -216
  143. webstoken/language.py +128 -128
  144. webstoken/ner.py +164 -164
  145. webstoken/normalizer.py +35 -35
  146. webstoken/processor.py +77 -77
  147. webstoken/sentiment.py +206 -206
  148. webstoken/stemmer.py +73 -73
  149. webstoken/tagger.py +60 -60
  150. webstoken/tokenizer.py +158 -158
  151. webscout-7.1.dist-info/RECORD +0 -198
  152. {webscout-7.1.dist-info → webscout-7.3.dist-info}/LICENSE.md +0 -0
  153. {webscout-7.1.dist-info → webscout-7.3.dist-info}/entry_points.txt +0 -0
  154. {webscout-7.1.dist-info → webscout-7.3.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,7 @@ import requests
15
15
  from packaging import version
16
16
  import re
17
17
 
18
- from webscout import LitLogger, ColorScheme
18
+ from webscout.Litlogger import Logger
19
19
  from importlib.metadata import version as get_package_version
20
20
  from importlib.metadata import PackageNotFoundError
21
21
  from importlib.metadata import metadata as get_package_metadata
@@ -23,19 +23,9 @@ from importlib.metadata import metadata as get_package_metadata
23
23
  # Setting up that clean logger format, no cap! 💯
24
24
  CUSTOM_FORMAT = """{message}"""
25
25
 
26
- logger = LitLogger(
26
+ logger = Logger(
27
27
  name="WebscoutUpdate",
28
28
  format=CUSTOM_FORMAT,
29
- color_scheme=ColorScheme.OCEAN,
30
- level_styles={
31
- "TRACE": "DIM",
32
- "DEBUG": "NORMAL",
33
- "INFO": "BOLD",
34
- "SUCCESS": "BOLD",
35
- "WARNING": "BOLD",
36
- "ERROR": "BOLD",
37
- "CRITICAL": "BOLD"
38
- }
39
29
  )
40
30
 
41
31
  def get_installed_version() -> Optional[str]:
webscout/version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "7.1"
1
+ __version__ = "7.3"
2
2
  __prog__ = "webscout"
@@ -1,6 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  # import logging
4
+ import json
5
+ from urllib.parse import quote
4
6
  import warnings
5
7
  from concurrent.futures import ThreadPoolExecutor
6
8
  from datetime import datetime, timezone
@@ -11,7 +13,7 @@ from random import choice, shuffle
11
13
  from threading import Event
12
14
  from time import sleep, time
13
15
  from types import TracebackType
14
- from typing import cast
16
+ from typing import Any, cast
15
17
 
16
18
  import primp # type: ignore
17
19
 
@@ -178,7 +180,8 @@ class WEBS:
178
180
  """
179
181
  models_deprecated = {
180
182
  "gpt-3.5": "gpt-4o-mini",
181
- "llama-3-70b": "llama-3.1-70b"
183
+ "llama-3.1-70b": "llama-3.3-70b",
184
+ "mixtral-8x7b": "mistral-24B"
182
185
  }
183
186
  if model in models_deprecated:
184
187
  # logger.info(f"{model=} is deprecated, using {models_deprecated[model]}")
@@ -186,9 +189,9 @@ class WEBS:
186
189
  models = {
187
190
  "claude-3-haiku": "claude-3-haiku-20240307",
188
191
  "gpt-4o-mini": "gpt-4o-mini",
189
- "llama-3.1-70b": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
190
- "mixtral-8x7b": "mistralai/Mixtral-8x7B-Instruct-v0.1",
191
- "o3-mini":"o3-mini"
192
+ "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct-Turbo",
193
+ "o3-mini":"o3-mini",
194
+ "mistral-24B": "mistralai/Mistral-Small-24B-Instruct-2501"
192
195
  }
193
196
  # vqd
194
197
  if not self._chat_vqd:
@@ -1139,4 +1142,82 @@ class WEBS:
1139
1142
  except Exception as e:
1140
1143
  raise e
1141
1144
 
1142
- return results
1145
+ return results
1146
+
1147
+ def weather(
1148
+ self,
1149
+ location: str,
1150
+ language: str = "en",
1151
+ ) -> dict[str, Any]:
1152
+ """Get weather information for a location from DuckDuckGo.
1153
+
1154
+ Args:
1155
+ location: Location to get weather for.
1156
+ language: Language code (e.g. 'en', 'es'). Defaults to "en".
1157
+
1158
+ Returns:
1159
+ Dictionary containing weather data with structure described in docstring.
1160
+
1161
+ Raises:
1162
+ WebscoutE: Base exception for webscout errors.
1163
+ RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
1164
+ TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
1165
+ """
1166
+ assert location, "location is mandatory"
1167
+ lang = language.split('-')[0]
1168
+ url = f"https://duckduckgo.com/js/spice/forecast/{quote(location)}/{lang}"
1169
+
1170
+ resp = self._get_url("GET", url)
1171
+ resp_text = resp.decode('utf-8')
1172
+
1173
+ if "ddg_spice_forecast(" not in resp_text:
1174
+ raise WebscoutE(f"No weather data found for {location}")
1175
+
1176
+ json_text = resp_text[resp_text.find('(') + 1:resp_text.rfind(')')]
1177
+ try:
1178
+ result = json.loads(json_text)
1179
+ except Exception as e:
1180
+ raise WebscoutE(f"Error parsing weather JSON: {e}")
1181
+
1182
+ if not result or 'currentWeather' not in result or 'forecastDaily' not in result:
1183
+ raise WebscoutE(f"Invalid weather data format for {location}")
1184
+
1185
+ formatted_data = {
1186
+ "location": result["currentWeather"]["metadata"].get("ddg-location", "Unknown"),
1187
+ "current": {
1188
+ "condition": result["currentWeather"].get("conditionCode"),
1189
+ "temperature_c": result["currentWeather"].get("temperature"),
1190
+ "feels_like_c": result["currentWeather"].get("temperatureApparent"),
1191
+ "humidity": result["currentWeather"].get("humidity"),
1192
+ "wind_speed_ms": result["currentWeather"].get("windSpeed"),
1193
+ "wind_direction": result["currentWeather"].get("windDirection"),
1194
+ "visibility_m": result["currentWeather"].get("visibility"),
1195
+ },
1196
+ "daily_forecast": [],
1197
+ "hourly_forecast": []
1198
+ }
1199
+
1200
+ for day in result["forecastDaily"]["days"]:
1201
+ formatted_data["daily_forecast"].append({
1202
+ "date": datetime.fromisoformat(day["forecastStart"].replace("Z", "+00:00")).strftime("%Y-%m-%d"),
1203
+ "condition": day["daytimeForecast"].get("conditionCode"),
1204
+ "max_temp_c": day["temperatureMax"],
1205
+ "min_temp_c": day["temperatureMin"],
1206
+ "sunrise": datetime.fromisoformat(day["sunrise"].replace("Z", "+00:00")).strftime("%H:%M"),
1207
+ "sunset": datetime.fromisoformat(day["sunset"].replace("Z", "+00:00")).strftime("%H:%M"),
1208
+ })
1209
+
1210
+ if 'forecastHourly' in result and 'hours' in result['forecastHourly']:
1211
+ for hour in result['forecastHourly']['hours']:
1212
+ formatted_data["hourly_forecast"].append({
1213
+ "time": datetime.fromisoformat(hour["forecastStart"].replace("Z", "+00:00")).strftime("%H:%M"),
1214
+ "condition": hour.get("conditionCode"),
1215
+ "temperature_c": hour.get("temperature"),
1216
+ "feels_like_c": hour.get("temperatureApparent"),
1217
+ "humidity": hour.get("humidity"),
1218
+ "wind_speed_ms": hour.get("windSpeed"),
1219
+ "wind_direction": hour.get("windDirection"),
1220
+ "visibility_m": hour.get("visibility"),
1221
+ })
1222
+
1223
+ return formatted_data
@@ -633,4 +633,61 @@ class AsyncWEBS:
633
633
  from_,
634
634
  to,
635
635
  )
636
- return result
636
+ return result
637
+
638
+ async def aweather(
639
+ self,
640
+ location: str,
641
+ language: str = "en",
642
+ ) -> dict[str, Any]:
643
+ """Async version of weather information retrieval from DuckDuckGo.
644
+
645
+ Args:
646
+ location: Location to get weather for.
647
+ language: Language code (e.g. 'en', 'es'). Defaults to "en".
648
+
649
+ Returns:
650
+ Dictionary containing weather data with the following structure:
651
+ {
652
+ "location": str,
653
+ "current": {
654
+ "condition": str,
655
+ "temperature_c": float,
656
+ "feels_like_c": float,
657
+ "humidity": float,
658
+ "wind_speed_ms": float,
659
+ "wind_direction": float,
660
+ "visibility_m": float
661
+ },
662
+ "daily_forecast": List[{
663
+ "date": str,
664
+ "condition": str,
665
+ "max_temp_c": float,
666
+ "min_temp_c": float,
667
+ "sunrise": str,
668
+ "sunset": str
669
+ }],
670
+ "hourly_forecast": List[{
671
+ "time": str,
672
+ "condition": str,
673
+ "temperature_c": float,
674
+ "feels_like_c": float,
675
+ "humidity": float,
676
+ "wind_speed_ms": float,
677
+ "wind_direction": float,
678
+ "visibility_m": float
679
+ }]
680
+ }
681
+
682
+ Raises:
683
+ WebscoutE: Base exception for webscout errors.
684
+ RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
685
+ TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
686
+ """
687
+ result = await self._loop.run_in_executor(
688
+ self._executor,
689
+ super().weather,
690
+ location,
691
+ language,
692
+ )
693
+ return result
webscout/yep_search.py ADDED
@@ -0,0 +1,297 @@
1
+ import cloudscraper
2
+ from urllib.parse import urlencode
3
+ from webscout.litagent import LitAgent
4
+ from typing import List, Dict, Optional, Tuple
5
+ from concurrent.futures import ThreadPoolExecutor
6
+ import json
7
+ class YepSearch:
8
+ """Yep.com search class to get search results."""
9
+
10
+ _executor: ThreadPoolExecutor = ThreadPoolExecutor()
11
+
12
+ def __init__(
13
+ self,
14
+ timeout: int = 20,
15
+ proxies: Dict[str, str] | None = None,
16
+ verify: bool = True,
17
+ ):
18
+ """Initialize YepSearch.
19
+
20
+ Args:
21
+ timeout: Timeout value for the HTTP client. Defaults to 20.
22
+ proxies: Proxy configuration for requests. Defaults to None.
23
+ verify: Verify SSL certificates. Defaults to True.
24
+ """
25
+ self.base_url = "https://api.yep.com/fs/2/search"
26
+ self.timeout = timeout
27
+ self.session = cloudscraper.create_scraper()
28
+ self.session.headers.update({
29
+ "Accept": "*/*",
30
+ "Accept-Language": "en-US,en;q=0.9,en-IN;q=0.8",
31
+ "DNT": "1",
32
+ "Origin": "https://yep.com",
33
+ "Referer": "https://yep.com/",
34
+ "Sec-Ch-Ua": '"Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"',
35
+ "Sec-Ch-Ua-Mobile": "?0",
36
+ "Sec-Ch-Ua-Platform": '"Windows"',
37
+ "Sec-Fetch-Dest": "empty",
38
+ "Sec-Fetch-Mode": "cors",
39
+ "Sec-Fetch-Site": "same-site",
40
+ "User-Agent": LitAgent().random()
41
+ })
42
+ if proxies:
43
+ self.session.proxies.update(proxies)
44
+ self.session.verify = verify
45
+
46
+ def _remove_html_tags(self, text: str) -> str:
47
+ """Remove HTML tags from text using simple string manipulation.
48
+
49
+ Args:
50
+ text: String containing HTML tags
51
+
52
+ Returns:
53
+ Clean text without HTML tags
54
+ """
55
+ result = ""
56
+ in_tag = False
57
+
58
+ for char in text:
59
+ if char == '<':
60
+ in_tag = True
61
+ elif char == '>':
62
+ in_tag = False
63
+ elif not in_tag:
64
+ result += char
65
+
66
+ # Replace common HTML entities
67
+ replacements = {
68
+ '&nbsp;': ' ',
69
+ '&amp;': '&',
70
+ '&lt;': '<',
71
+ '&gt;': '>',
72
+ '&quot;': '"',
73
+ '&apos;': "'",
74
+ }
75
+
76
+ for entity, replacement in replacements.items():
77
+ result = result.replace(entity, replacement)
78
+
79
+ return result.strip()
80
+
81
+ def format_results(self, raw_results: dict) -> List[Dict]:
82
+ """Format raw API results into a consistent structure."""
83
+ formatted_results = []
84
+
85
+ if not raw_results or len(raw_results) < 2:
86
+ return formatted_results
87
+
88
+ results = raw_results[1].get('results', [])
89
+
90
+ for result in results:
91
+ formatted_result = {
92
+ "title": self._remove_html_tags(result.get("title", "")),
93
+ "href": result.get("url", ""),
94
+ "body": self._remove_html_tags(result.get("snippet", "")),
95
+ "source": result.get("visual_url", ""),
96
+ "position": len(formatted_results) + 1,
97
+ "type": result.get("type", "organic"),
98
+ "first_seen": result.get("first_seen", None)
99
+ }
100
+
101
+ # Add sitelinks if they exist
102
+ if "sitelinks" in result:
103
+ sitelinks = []
104
+ if "full" in result["sitelinks"]:
105
+ sitelinks.extend(result["sitelinks"]["full"])
106
+ if "short" in result["sitelinks"]:
107
+ sitelinks.extend(result["sitelinks"]["short"])
108
+
109
+ if sitelinks:
110
+ formatted_result["sitelinks"] = [
111
+ {
112
+ "title": self._remove_html_tags(link.get("title", "")),
113
+ "href": link.get("url", "")
114
+ }
115
+ for link in sitelinks
116
+ ]
117
+
118
+ formatted_results.append(formatted_result)
119
+
120
+ return formatted_results
121
+
122
+ def text(
123
+ self,
124
+ keywords: str,
125
+ region: str = "all",
126
+ safesearch: str = "moderate",
127
+ max_results: Optional[int] = None,
128
+ ) -> List[Dict[str, str]]:
129
+ """Yep.com text search.
130
+
131
+ Args:
132
+ keywords: Search query string.
133
+ region: Region for search results. Defaults to "all".
134
+ safesearch: SafeSearch setting ("on", "moderate", "off"). Defaults to "moderate".
135
+ max_results: Maximum number of results to return. Defaults to None.
136
+
137
+ Returns:
138
+ List of dictionaries containing search results.
139
+ """
140
+ # Convert safesearch parameter
141
+ safe_search_map = {
142
+ "on": "on",
143
+ "moderate": "moderate",
144
+ "off": "off"
145
+ }
146
+ safe_setting = safe_search_map.get(safesearch.lower(), "moderate")
147
+
148
+ params = {
149
+ "client": "web",
150
+ "gl": region,
151
+ "limit": str(max_results) if max_results else "10",
152
+ "no_correct": "false",
153
+ "q": keywords,
154
+ "safeSearch": safe_setting,
155
+ "type": "web"
156
+ }
157
+
158
+ url = f"{self.base_url}?{urlencode(params)}"
159
+ try:
160
+ response = self.session.get(url, timeout=self.timeout)
161
+ response.raise_for_status()
162
+ raw_results = response.json()
163
+
164
+ formatted_results = self.format_results(raw_results)
165
+
166
+ if max_results:
167
+ return formatted_results[:max_results]
168
+ return formatted_results
169
+ except Exception as e:
170
+ raise Exception(f"Yep search failed: {str(e)}")
171
+
172
+ def images(
173
+ self,
174
+ keywords: str,
175
+ region: str = "all",
176
+ safesearch: str = "moderate",
177
+ max_results: Optional[int] = None,
178
+ ) -> List[Dict[str, str]]:
179
+ """Yep.com image search.
180
+
181
+ Args:
182
+ keywords: Search query string.
183
+ region: Region for search results. Defaults to "all".
184
+ safesearch: SafeSearch setting ("on", "moderate", "off"). Defaults to "moderate".
185
+ max_results: Maximum number of results to return. Defaults to None.
186
+
187
+ Returns:
188
+ List of dictionaries containing image search results with keys:
189
+ - title: Image title
190
+ - image: Full resolution image URL
191
+ - thumbnail: Thumbnail image URL
192
+ - url: Source page URL
193
+ - height: Image height
194
+ - width: Image width
195
+ - source: Source website domain
196
+ """
197
+ safe_search_map = {
198
+ "on": "on",
199
+ "moderate": "moderate",
200
+ "off": "off"
201
+ }
202
+ safe_setting = safe_search_map.get(safesearch.lower(), "moderate")
203
+
204
+ params = {
205
+ "client": "web",
206
+ "gl": region,
207
+ "limit": str(max_results) if max_results else "10",
208
+ "no_correct": "false",
209
+ "q": keywords,
210
+ "safeSearch": safe_setting,
211
+ "type": "images"
212
+ }
213
+
214
+ url = f"{self.base_url}?{urlencode(params)}"
215
+ try:
216
+ response = self.session.get(url, timeout=self.timeout)
217
+ response.raise_for_status()
218
+ raw_results = response.json()
219
+
220
+ if not raw_results or len(raw_results) < 2:
221
+ return []
222
+
223
+ formatted_results = []
224
+ results = raw_results[1].get('results', [])
225
+
226
+ for result in results:
227
+ if result.get("type") != "Image":
228
+ continue
229
+
230
+ formatted_result = {
231
+ "title": self._remove_html_tags(result.get("title", "")),
232
+ "image": result.get("image_id", ""),
233
+ "thumbnail": result.get("src", ""),
234
+ "url": result.get("host_page", ""),
235
+ "height": result.get("height", 0),
236
+ "width": result.get("width", 0),
237
+ "source": result.get("visual_url", "")
238
+ }
239
+
240
+ # Add high-res thumbnail if available
241
+ if "srcset" in result:
242
+ formatted_result["thumbnail_hd"] = result["srcset"].split(",")[1].strip().split(" ")[0]
243
+
244
+ formatted_results.append(formatted_result)
245
+
246
+ if max_results:
247
+ return formatted_results[:max_results]
248
+ return formatted_results
249
+
250
+ except Exception as e:
251
+ raise Exception(f"Yep image search failed: {str(e)}")
252
+
253
+ def suggestions(
254
+ self,
255
+ query: str,
256
+ region: str = "all",
257
+ ) -> List[str]:
258
+ """Get search suggestions from Yep.com autocomplete API.
259
+
260
+ Args:
261
+ query: Search query string to get suggestions for.
262
+ region: Region for suggestions. Defaults to "all".
263
+
264
+ Returns:
265
+ List of suggestion strings.
266
+
267
+ Example:
268
+ >>> yep = YepSearch()
269
+ >>> suggestions = yep.suggestions("ca")
270
+ >>> print(suggestions)
271
+ ['capital one', 'car wash', 'carmax', 'cafe', ...]
272
+ """
273
+ params = {
274
+ "query": query,
275
+ "type": "web",
276
+ "gl": region
277
+ }
278
+
279
+ url = f"https://api.yep.com/ac/?{urlencode(params)}"
280
+
281
+ try:
282
+ response = self.session.get(url, timeout=self.timeout)
283
+ response.raise_for_status()
284
+ data = response.json()
285
+ # Return suggestions list if response format is valid
286
+ if isinstance(data, list) and len(data) > 1 and isinstance(data[1], list):
287
+ return data[1]
288
+ return []
289
+
290
+ except Exception as e:
291
+ raise Exception(f"Yep suggestions failed: {str(e)}")
292
+
293
+
294
+ if __name__ == "__main__":
295
+ yep = YepSearch()
296
+ r = yep.suggestions("hi", region="all")
297
+ print(r)
@@ -1,55 +1,55 @@
1
- """
2
- ZeroArt: A zero-dependency ASCII art text generator
3
-
4
- Create awesome ASCII art text without external dependencies!
5
- """
6
-
7
- from .base import ZeroArtFont
8
- from .fonts import BlockFont, SlantFont, NeonFont, CyberFont
9
- from .effects import AsciiArtEffects
10
-
11
- def figlet_format(text, font='block'):
12
- """
13
- Generate ASCII art text
14
-
15
- :param text: Text to convert
16
- :param font: Font style (default: 'block')
17
- :return: ASCII art representation of text
18
- """
19
- font_map = {
20
- 'block': BlockFont(),
21
- 'slant': SlantFont(),
22
- 'neon': NeonFont(),
23
- 'cyber': CyberFont()
24
- }
25
-
26
- selected_font = font_map.get(font.lower(), BlockFont())
27
- return selected_font.render(text)
28
-
29
- def print_figlet(text, font='block'):
30
- """
31
- Print ASCII art text directly
32
-
33
- :param text: Text to convert and print
34
- :param font: Font style (default: 'block')
35
- """
36
- print(figlet_format(text, font))
37
-
38
- # Expose additional effects
39
- rainbow = AsciiArtEffects.rainbow_effect
40
- glitch = AsciiArtEffects.glitch_effect
41
- wrap_text = AsciiArtEffects.wrap_text
42
- outline = AsciiArtEffects.outline_effect
43
-
44
- __all__ = [
45
- 'figlet_format',
46
- 'print_figlet',
47
- 'rainbow',
48
- 'glitch',
49
- 'wrap_text',
50
- 'outline',
51
- 'BlockFont',
52
- 'SlantFont',
53
- 'NeonFont',
54
- 'CyberFont'
1
+ """
2
+ ZeroArt: A zero-dependency ASCII art text generator
3
+
4
+ Create awesome ASCII art text without external dependencies!
5
+ """
6
+
7
+ from .base import ZeroArtFont
8
+ from .fonts import BlockFont, SlantFont, NeonFont, CyberFont
9
+ from .effects import AsciiArtEffects
10
+
11
+ def figlet_format(text, font='block'):
12
+ """
13
+ Generate ASCII art text
14
+
15
+ :param text: Text to convert
16
+ :param font: Font style (default: 'block')
17
+ :return: ASCII art representation of text
18
+ """
19
+ font_map = {
20
+ 'block': BlockFont(),
21
+ 'slant': SlantFont(),
22
+ 'neon': NeonFont(),
23
+ 'cyber': CyberFont()
24
+ }
25
+
26
+ selected_font = font_map.get(font.lower(), BlockFont())
27
+ return selected_font.render(text)
28
+
29
+ def print_figlet(text, font='block'):
30
+ """
31
+ Print ASCII art text directly
32
+
33
+ :param text: Text to convert and print
34
+ :param font: Font style (default: 'block')
35
+ """
36
+ print(figlet_format(text, font))
37
+
38
+ # Expose additional effects
39
+ rainbow = AsciiArtEffects.rainbow_effect
40
+ glitch = AsciiArtEffects.glitch_effect
41
+ wrap_text = AsciiArtEffects.wrap_text
42
+ outline = AsciiArtEffects.outline_effect
43
+
44
+ __all__ = [
45
+ 'figlet_format',
46
+ 'print_figlet',
47
+ 'rainbow',
48
+ 'glitch',
49
+ 'wrap_text',
50
+ 'outline',
51
+ 'BlockFont',
52
+ 'SlantFont',
53
+ 'NeonFont',
54
+ 'CyberFont'
55
55
  ]