webscout 7.5__py3-none-any.whl → 7.6__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 (118) hide show
  1. webscout/AIauto.py +5 -53
  2. webscout/AIutel.py +8 -318
  3. webscout/DWEBS.py +460 -489
  4. webscout/Extra/YTToolkit/YTdownloader.py +14 -53
  5. webscout/Extra/YTToolkit/transcriber.py +12 -13
  6. webscout/Extra/YTToolkit/ytapi/video.py +0 -1
  7. webscout/Extra/__init__.py +0 -1
  8. webscout/Extra/autocoder/autocoder_utiles.py +0 -4
  9. webscout/Extra/autocoder/rawdog.py +13 -41
  10. webscout/Extra/gguf.py +652 -428
  11. webscout/Extra/weather.py +178 -156
  12. webscout/Extra/weather_ascii.py +70 -17
  13. webscout/Litlogger/core/logger.py +1 -2
  14. webscout/Litlogger/handlers/file.py +1 -1
  15. webscout/Litlogger/styles/formats.py +0 -2
  16. webscout/Litlogger/utils/detectors.py +0 -1
  17. webscout/Provider/AISEARCH/DeepFind.py +0 -1
  18. webscout/Provider/AISEARCH/ISou.py +1 -1
  19. webscout/Provider/AISEARCH/felo_search.py +0 -1
  20. webscout/Provider/AllenAI.py +24 -9
  21. webscout/Provider/C4ai.py +29 -11
  22. webscout/Provider/ChatGPTGratis.py +24 -56
  23. webscout/Provider/DeepSeek.py +25 -17
  24. webscout/Provider/Deepinfra.py +115 -48
  25. webscout/Provider/Gemini.py +1 -1
  26. webscout/Provider/Glider.py +25 -8
  27. webscout/Provider/HF_space/qwen_qwen2.py +2 -2
  28. webscout/Provider/HeckAI.py +23 -7
  29. webscout/Provider/Jadve.py +20 -5
  30. webscout/Provider/Netwrck.py +42 -19
  31. webscout/Provider/PI.py +4 -2
  32. webscout/Provider/Perplexitylabs.py +26 -6
  33. webscout/Provider/PizzaGPT.py +10 -51
  34. webscout/Provider/TTI/AiForce/async_aiforce.py +4 -37
  35. webscout/Provider/TTI/AiForce/sync_aiforce.py +41 -38
  36. webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -9
  37. webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +206 -206
  38. webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +192 -192
  39. webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
  40. webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
  41. webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
  42. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +5 -24
  43. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +2 -22
  44. webscout/Provider/TTI/__init__.py +2 -3
  45. webscout/Provider/TTI/aiarta/async_aiarta.py +14 -14
  46. webscout/Provider/TTI/aiarta/sync_aiarta.py +52 -21
  47. webscout/Provider/TTI/fastflux/__init__.py +22 -0
  48. webscout/Provider/TTI/fastflux/async_fastflux.py +257 -0
  49. webscout/Provider/TTI/fastflux/sync_fastflux.py +247 -0
  50. webscout/Provider/TTS/__init__.py +2 -2
  51. webscout/Provider/TTS/deepgram.py +12 -39
  52. webscout/Provider/TTS/elevenlabs.py +14 -40
  53. webscout/Provider/TTS/gesserit.py +11 -35
  54. webscout/Provider/TTS/murfai.py +13 -39
  55. webscout/Provider/TTS/parler.py +17 -40
  56. webscout/Provider/TTS/speechma.py +180 -0
  57. webscout/Provider/TTS/streamElements.py +17 -44
  58. webscout/Provider/TextPollinationsAI.py +39 -59
  59. webscout/Provider/Venice.py +25 -8
  60. webscout/Provider/WiseCat.py +27 -5
  61. webscout/Provider/Youchat.py +64 -37
  62. webscout/Provider/__init__.py +0 -6
  63. webscout/Provider/akashgpt.py +20 -5
  64. webscout/Provider/flowith.py +20 -5
  65. webscout/Provider/freeaichat.py +32 -45
  66. webscout/Provider/koala.py +20 -5
  67. webscout/Provider/llamatutor.py +1 -1
  68. webscout/Provider/llmchat.py +30 -8
  69. webscout/Provider/multichat.py +65 -9
  70. webscout/Provider/talkai.py +1 -0
  71. webscout/Provider/turboseek.py +3 -0
  72. webscout/Provider/tutorai.py +2 -0
  73. webscout/Provider/typegpt.py +154 -64
  74. webscout/Provider/x0gpt.py +3 -1
  75. webscout/Provider/yep.py +102 -20
  76. webscout/__init__.py +3 -0
  77. webscout/cli.py +4 -40
  78. webscout/conversation.py +1 -10
  79. webscout/litagent/__init__.py +2 -2
  80. webscout/litagent/agent.py +351 -20
  81. webscout/litagent/constants.py +34 -5
  82. webscout/litprinter/__init__.py +0 -3
  83. webscout/models.py +181 -0
  84. webscout/optimizers.py +1 -1
  85. webscout/prompt_manager.py +2 -8
  86. webscout/scout/core/scout.py +1 -4
  87. webscout/scout/core/search_result.py +1 -1
  88. webscout/scout/core/text_utils.py +1 -1
  89. webscout/scout/core.py +2 -5
  90. webscout/scout/element.py +1 -1
  91. webscout/scout/parsers/html_parser.py +1 -1
  92. webscout/scout/utils.py +0 -1
  93. webscout/swiftcli/__init__.py +1 -3
  94. webscout/tempid.py +1 -1
  95. webscout/update_checker.py +1 -3
  96. webscout/version.py +1 -1
  97. webscout/webscout_search_async.py +1 -2
  98. webscout/yep_search.py +297 -297
  99. {webscout-7.5.dist-info → webscout-7.6.dist-info}/LICENSE.md +4 -4
  100. {webscout-7.5.dist-info → webscout-7.6.dist-info}/METADATA +101 -390
  101. {webscout-7.5.dist-info → webscout-7.6.dist-info}/RECORD +104 -110
  102. webscout/Extra/autollama.py +0 -231
  103. webscout/Provider/Amigo.py +0 -274
  104. webscout/Provider/Bing.py +0 -243
  105. webscout/Provider/DiscordRocks.py +0 -253
  106. webscout/Provider/TTI/blackbox/__init__.py +0 -4
  107. webscout/Provider/TTI/blackbox/async_blackbox.py +0 -212
  108. webscout/Provider/TTI/blackbox/sync_blackbox.py +0 -199
  109. webscout/Provider/TTI/deepinfra/__init__.py +0 -4
  110. webscout/Provider/TTI/deepinfra/async_deepinfra.py +0 -227
  111. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +0 -199
  112. webscout/Provider/TTI/imgninza/__init__.py +0 -4
  113. webscout/Provider/TTI/imgninza/async_ninza.py +0 -214
  114. webscout/Provider/TTI/imgninza/sync_ninza.py +0 -209
  115. webscout/Provider/TTS/voicepod.py +0 -117
  116. {webscout-7.5.dist-info → webscout-7.6.dist-info}/WHEEL +0 -0
  117. {webscout-7.5.dist-info → webscout-7.6.dist-info}/entry_points.txt +0 -0
  118. {webscout-7.5.dist-info → webscout-7.6.dist-info}/top_level.txt +0 -0
webscout/yep_search.py CHANGED
@@ -1,297 +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
+ 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,4 +1,4 @@
1
- Apache License
1
+
2
2
  Version 2.0, March 2025
3
3
  Webscout License
4
4
 
@@ -87,7 +87,7 @@ Apache License
87
87
 
88
88
  (e) You must include the following attribution notice prominently
89
89
  displayed in any Derivative Works:
90
- "Based on Webscout (https://github.com/webscout)"
90
+ "Based on Webscout (https://github.com/OE-LUCIFER/webscout)"
91
91
 
92
92
  5. API Usage and Third-Party Services. When using the APIs and services
93
93
  accessed through this Work, you must:
@@ -133,11 +133,11 @@ END OF TERMS AND CONDITIONS
133
133
 
134
134
  Copyright 2024-2025 Webscout Contributors
135
135
 
136
- Licensed under the Apache License, Version 2.0 (the "License");
136
+ Licensed under the Webscout License (the "License");
137
137
  you may not use this file except in compliance with the License.
138
138
  You may obtain a copy of the License at
139
139
 
140
- http://www.apache.org/licenses/LICENSE-2.0
140
+ https://github.com/OE-LUCIFER/webscout/blob/main/LICENSE.md
141
141
 
142
142
  Unless required by applicable law or agreed to in writing, software
143
143
  distributed under the License is distributed on an "AS IS" BASIS,