apksearch 1.3.0__py3-none-any.whl → 1.3.5__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.
- apksearch/__init__.py +4 -6
- apksearch/cli.py +53 -58
- apksearch/sites/__init__.py +10 -0
- apksearch/sites/apkad.py +88 -55
- apksearch/sites/apkcombo.py +4 -3
- apksearch/sites/apkfab.py +6 -2
- apksearch/sites/apkmirror.py +6 -3
- apksearch/sites/apkpure.py +11 -10
- apksearch/sites/appteka.py +4 -2
- apksearch/sites/aptoide.py +3 -1
- {apksearch-1.3.0.dist-info → apksearch-1.3.5.dist-info}/METADATA +3 -1
- apksearch-1.3.5.dist-info/RECORD +17 -0
- apksearch/sites/apkmonk.py +0 -108
- apksearch-1.3.0.dist-info/RECORD +0 -18
- {apksearch-1.3.0.dist-info → apksearch-1.3.5.dist-info}/WHEEL +0 -0
- {apksearch-1.3.0.dist-info → apksearch-1.3.5.dist-info}/entry_points.txt +0 -0
- {apksearch-1.3.0.dist-info → apksearch-1.3.5.dist-info}/licenses/LICENSE +0 -0
- {apksearch-1.3.0.dist-info → apksearch-1.3.5.dist-info}/top_level.txt +0 -0
apksearch/__init__.py
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
from .sites.
|
2
|
-
from .sites.apkmirror import APKMirror
|
3
|
-
from .sites.appteka import AppTeka
|
1
|
+
from .sites.apkad import APKad
|
4
2
|
from .sites.apkcombo import APKCombo
|
5
3
|
from .sites.apkfab import APKFab
|
6
|
-
from .sites.
|
4
|
+
from .sites.apkmirror import APKMirror
|
5
|
+
from .sites.apkpure import APKPure
|
6
|
+
from .sites.appteka import AppTeka
|
7
7
|
from .sites.aptoide import Aptoide
|
8
|
-
from .sites.apkmonk import APKMonk
|
9
8
|
|
10
9
|
__all__ = [
|
11
10
|
"APKPure",
|
@@ -15,5 +14,4 @@ __all__ = [
|
|
15
14
|
"APKFab",
|
16
15
|
"APKad",
|
17
16
|
"Aptoide",
|
18
|
-
"APKMonk",
|
19
17
|
]
|
apksearch/cli.py
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
import argparse
|
2
|
-
|
3
2
|
from collections.abc import Callable
|
3
|
+
|
4
|
+
from requests.exceptions import ConnectionError, ConnectTimeout
|
5
|
+
|
4
6
|
from apksearch import (
|
5
|
-
|
6
|
-
APKMirror,
|
7
|
-
AppTeka,
|
7
|
+
APKad,
|
8
8
|
APKCombo,
|
9
9
|
APKFab,
|
10
|
-
|
10
|
+
APKMirror,
|
11
|
+
APKPure,
|
12
|
+
AppTeka,
|
11
13
|
Aptoide,
|
12
|
-
APKMonk,
|
13
14
|
)
|
14
|
-
from requests.exceptions import ConnectionError, ConnectTimeout
|
15
15
|
|
16
16
|
# Color codes
|
17
17
|
BOLD = "\033[1m"
|
@@ -22,7 +22,7 @@ NC = "\033[0m"
|
|
22
22
|
|
23
23
|
|
24
24
|
def search(
|
25
|
-
func: Callable[[str, str], object],
|
25
|
+
func: Callable[[str, str | None], object],
|
26
26
|
pkg_name: str,
|
27
27
|
version: str | None,
|
28
28
|
log_err: bool = False,
|
@@ -31,7 +31,8 @@ def search(
|
|
31
31
|
func(pkg_name, version)
|
32
32
|
except Exception as exc:
|
33
33
|
if log_err:
|
34
|
-
|
34
|
+
func_name = getattr(func, "__name__", func.__class__.__name__)
|
35
|
+
print(f"Error in {func_name}: {exc}")
|
35
36
|
else:
|
36
37
|
pass
|
37
38
|
|
@@ -91,16 +92,20 @@ def search_apkfab(pkg_name: str, version: str | None) -> None:
|
|
91
92
|
def search_apkad(pkg_name: str, version: str | None) -> None:
|
92
93
|
apkad = APKad(pkg_name)
|
93
94
|
try:
|
94
|
-
result_apkad: tuple[str, str] | None = apkad.search_apk()
|
95
|
+
result_apkad: tuple[str, list[tuple[str, str]]] | None = apkad.search_apk()
|
95
96
|
except (ConnectionError, ConnectTimeout):
|
96
97
|
result_apkad = None
|
97
98
|
print(f"{RED}Failed to resolve 'api.apk.ad'!{NC}")
|
98
99
|
if result_apkad:
|
99
|
-
title,
|
100
|
+
title, apk_links = result_apkad
|
100
101
|
print(f"{BOLD}APKAD:{NC} Found {GREEN}{title}{NC}") if title else None
|
101
|
-
|
102
|
-
f" ╰─> {BOLD}
|
103
|
-
|
102
|
+
if not version:
|
103
|
+
print(f" ╰─> {BOLD}Available APKs:{NC}")
|
104
|
+
for i, (filename, url) in enumerate(apk_links, 1):
|
105
|
+
print(f" {i}. {filename}")
|
106
|
+
print(f" ╰─> {BOLD}Download:{NC} {YELLOW}{url}{NC}")
|
107
|
+
else:
|
108
|
+
print(" ╰─> Doesn't support version search!")
|
104
109
|
else:
|
105
110
|
print(f"{BOLD}APKAD:{NC} No Results!")
|
106
111
|
|
@@ -142,7 +147,7 @@ def search_apkmirror(pkg_name: str, version: str | None) -> None:
|
|
142
147
|
print(f"{BOLD}APKMirror:{NC} Found {GREEN}{title}{NC}") if title else None
|
143
148
|
print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}") if not version else None
|
144
149
|
if version:
|
145
|
-
download_link = apkmirror.find_version(apk_link, version)
|
150
|
+
download_link = apkmirror.find_version(apk_link, version, title)
|
146
151
|
if download_link:
|
147
152
|
print(
|
148
153
|
f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{download_link}{NC}"
|
@@ -156,7 +161,7 @@ def search_apkmirror(pkg_name: str, version: str | None) -> None:
|
|
156
161
|
def search_appteka(pkg_name: str, version: str | None) -> None:
|
157
162
|
appteka = AppTeka(pkg_name)
|
158
163
|
try:
|
159
|
-
result_appteka: tuple[str, str] | None = appteka.search_apk(version)
|
164
|
+
result_appteka: tuple[str, str | None] | None = appteka.search_apk(version)
|
160
165
|
except (ConnectionError, ConnectTimeout):
|
161
166
|
result_appteka = None
|
162
167
|
print(f"{RED}Failed to resolve 'appteka.store'!{NC}")
|
@@ -200,32 +205,6 @@ def search_aptoide(pkg_name: str, version: str | None) -> None:
|
|
200
205
|
print(f"{BOLD}Aptoide:{NC} No Results!")
|
201
206
|
|
202
207
|
|
203
|
-
def search_apkmonk(pkg_name: str, version: str | None) -> None:
|
204
|
-
apkmonk = APKMonk(pkg_name)
|
205
|
-
try:
|
206
|
-
result_apkmonk: tuple[str, str] | None = apkmonk.search_apk()
|
207
|
-
except (ConnectionError, ConnectTimeout):
|
208
|
-
result_apkmonk = None
|
209
|
-
print(f"{RED}Failed to resolve 'apkmonk.com'!{NC}")
|
210
|
-
if result_apkmonk:
|
211
|
-
title, apk_link = result_apkmonk
|
212
|
-
print(f"{BOLD}APKMonk:{NC} Found {GREEN}{title}{NC}") if title else None
|
213
|
-
print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}") if not version else None
|
214
|
-
if version:
|
215
|
-
versions: list[tuple[str, str]] = apkmonk.find_versions(apk_link)
|
216
|
-
if versions:
|
217
|
-
for version_tuple in versions:
|
218
|
-
if version_tuple[0] == version:
|
219
|
-
print(
|
220
|
-
f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{version_tuple[1]}{NC}"
|
221
|
-
)
|
222
|
-
break
|
223
|
-
else:
|
224
|
-
print(f"{BOLD}APKMonk:{NC} Version {RED}{version}{NC} not found!")
|
225
|
-
else:
|
226
|
-
print(f"{BOLD}APKMonk:{NC} No Results!")
|
227
|
-
|
228
|
-
|
229
208
|
def main():
|
230
209
|
parser = argparse.ArgumentParser(
|
231
210
|
prog="APKSearch", description="Search for APKs on various websites"
|
@@ -235,28 +214,44 @@ def main():
|
|
235
214
|
parser.add_argument(
|
236
215
|
"--log_err", help="Enable error logs", action="store_true", required=False
|
237
216
|
)
|
217
|
+
parser.add_argument(
|
218
|
+
"--sites",
|
219
|
+
nargs="+",
|
220
|
+
choices=[
|
221
|
+
"apkpure",
|
222
|
+
"apkmirror",
|
223
|
+
"aptoide",
|
224
|
+
"appteka",
|
225
|
+
"apkcombo",
|
226
|
+
"apkfab",
|
227
|
+
"apkad",
|
228
|
+
],
|
229
|
+
help="Specify the sites to search on",
|
230
|
+
required=False,
|
231
|
+
)
|
238
232
|
args = parser.parse_args()
|
239
233
|
|
240
234
|
pkg_name = args.pkg_name
|
241
235
|
version = args.version
|
242
236
|
log_err = args.log_err
|
237
|
+
sites = args.sites
|
238
|
+
|
243
239
|
print(f"{BOLD}Searching for {YELLOW}{pkg_name}{NC}...")
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
search(search_apkmonk, pkg_name, version, log_err)
|
240
|
+
|
241
|
+
if not sites or "apkpure" in sites:
|
242
|
+
search(search_apkpure, pkg_name, version, log_err)
|
243
|
+
if not sites or "apkmirror" in sites:
|
244
|
+
search(search_apkmirror, pkg_name, version, log_err)
|
245
|
+
if not sites or "aptoide" in sites:
|
246
|
+
search(search_aptoide, pkg_name, version, log_err)
|
247
|
+
if not sites or "appteka" in sites:
|
248
|
+
search(search_appteka, pkg_name, version, log_err)
|
249
|
+
if not sites or "apkcombo" in sites:
|
250
|
+
search(search_apkcombo, pkg_name, version, log_err)
|
251
|
+
if not sites or "apkfab" in sites:
|
252
|
+
search(search_apkfab, pkg_name, version, log_err)
|
253
|
+
if not sites or "apkad" in sites:
|
254
|
+
search(search_apkad, pkg_name, version, log_err)
|
260
255
|
|
261
256
|
|
262
257
|
if __name__ == "__main__":
|
apksearch/sites/__init__.py
CHANGED
apksearch/sites/apkad.py
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
import base64
|
1
2
|
import json
|
3
|
+
|
2
4
|
from bs4 import BeautifulSoup
|
3
|
-
|
5
|
+
|
6
|
+
from apksearch.sites import requests
|
4
7
|
|
5
8
|
|
6
9
|
class APKad:
|
@@ -25,21 +28,19 @@ class APKad:
|
|
25
28
|
|
26
29
|
def __init__(self, pkg_name: str):
|
27
30
|
self.pkg_name = pkg_name
|
28
|
-
self.base_url = "https://
|
29
|
-
self.api_url = "https://api.
|
30
|
-
self.
|
31
|
-
|
32
|
-
+ f"/get?hl=en&package={self.pkg_name}&device=phone&arch=arm64-v8a&vc=&device_id="
|
33
|
-
)
|
31
|
+
self.base_url = "https://apkdownloader.pages.dev"
|
32
|
+
self.api_url = "https://api.mi9.com"
|
33
|
+
self.token_url = "https://token.mi9.com/"
|
34
|
+
self.search_url = self.api_url + "/get"
|
34
35
|
self.headers = {
|
35
36
|
"accept": "text/event-stream",
|
36
37
|
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
37
38
|
"cache-control": "no-cache",
|
38
39
|
"dnt": "1",
|
39
|
-
"origin": "https://
|
40
|
+
"origin": "https://apkdownloader.pages.dev",
|
40
41
|
"pragma": "no-cache",
|
41
42
|
"priority": "u=1, i",
|
42
|
-
"referer": "https://
|
43
|
+
"referer": "https://apkdownloader.pages.dev/",
|
43
44
|
"sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
44
45
|
"sec-ch-ua-mobile": "?0",
|
45
46
|
"sec-ch-ua-platform": '"Windows"',
|
@@ -50,60 +51,92 @@ class APKad:
|
|
50
51
|
}
|
51
52
|
self.session = requests.Session()
|
52
53
|
|
53
|
-
def
|
54
|
+
def get_token(self) -> tuple[str, int] | None:
|
54
55
|
"""
|
55
|
-
|
56
|
+
Retrieves a token from the token endpoint.
|
56
57
|
|
57
58
|
Returns:
|
58
|
-
|
59
|
-
|
59
|
+
tuple[str, int]: A tuple containing the token and timestamp if successful.
|
60
|
+
None: If the token retrieval fails.
|
60
61
|
"""
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
data = {
|
63
|
+
"package": self.pkg_name,
|
64
|
+
"device": "phone",
|
65
|
+
"arch": "arm64-v8a",
|
66
|
+
"vc": "",
|
67
|
+
"device_id": "",
|
68
|
+
"sdk": "default",
|
69
|
+
}
|
70
|
+
response = self.session.post(self.token_url, headers=self.headers, json=data)
|
71
|
+
if response.status_code == 200:
|
72
|
+
token_data = response.json()
|
73
|
+
if token_data.get("success"):
|
74
|
+
return token_data.get("token"), token_data.get("timestamp")
|
75
|
+
return None
|
76
|
+
|
77
|
+
def search_apk(self) -> None | tuple[str, list[tuple[str, str]]]:
|
78
|
+
token_tuple = self.get_token()
|
79
|
+
if not token_tuple:
|
80
|
+
return None
|
81
|
+
|
82
|
+
token, ts = token_tuple
|
83
|
+
data_json = json.dumps(
|
84
|
+
{
|
85
|
+
"hl": "en",
|
86
|
+
"package": self.pkg_name,
|
87
|
+
"device": "phone",
|
88
|
+
"arch": "arm64-v8a",
|
89
|
+
"vc": "",
|
90
|
+
"device_id": "",
|
91
|
+
"sdk": "default",
|
92
|
+
"timestamp": ts,
|
93
|
+
},
|
94
|
+
separators=(",", ":"),
|
95
|
+
)
|
96
|
+
|
97
|
+
data_b64 = base64.b64encode(data_json.encode("utf-8")).decode("utf-8")
|
98
|
+
|
99
|
+
params = {"token": token, "data": data_b64}
|
100
|
+
|
101
|
+
response = self.session.get(
|
102
|
+
self.search_url, headers=self.headers, params=params, stream=True
|
103
|
+
)
|
104
|
+
|
105
|
+
stream_response = None
|
64
106
|
for line in response.iter_lines():
|
65
|
-
if line:
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
107
|
+
if not line:
|
108
|
+
continue
|
109
|
+
line_response = line.decode("utf-8")
|
110
|
+
if line_response.startswith("data: "):
|
111
|
+
payload = line_response[6:]
|
112
|
+
try:
|
113
|
+
j = json.loads(payload)
|
114
|
+
if j.get("progress") == 100 and j.get("html"):
|
115
|
+
stream_response = j
|
116
|
+
break
|
117
|
+
except json.JSONDecodeError:
|
118
|
+
continue
|
119
|
+
|
71
120
|
if stream_response:
|
72
|
-
|
73
|
-
html_body = data["html"]
|
121
|
+
html_body = stream_response["html"]
|
74
122
|
soup = BeautifulSoup(html_body, "html.parser")
|
75
123
|
if not soup:
|
76
124
|
return None
|
125
|
+
|
77
126
|
title = soup.find("li", {"class": "_title"})
|
78
|
-
if title
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
t = zip_args[4] # time
|
94
|
-
apk_url = f"https://zip.apk.ad/compress?h={h}&p={p}&token={token}&ip={ip}&google_id={google_id}&t={t}"
|
95
|
-
apk_url_response = self.session.get(
|
96
|
-
url=apk_url, headers=self.headers, stream=True
|
97
|
-
)
|
98
|
-
for line in apk_url_response.iter_lines():
|
99
|
-
if line:
|
100
|
-
line_response = line.decode("utf-8")
|
101
|
-
if "File is ready for download." in line_response:
|
102
|
-
line_response = json.loads(line_response)
|
103
|
-
line_html = line_response["html"]
|
104
|
-
line_soup = BeautifulSoup(line_html, "html.parser")
|
105
|
-
download_link = line_soup.find("a")["href"]
|
106
|
-
if download_link.endswith("\n"):
|
107
|
-
download_link = download_link[:-1]
|
108
|
-
return title, download_link
|
127
|
+
title = title.text.strip() if title else self.pkg_name
|
128
|
+
|
129
|
+
apk_files_div = soup.find("div", {"id": "apkslist"})
|
130
|
+
if not apk_files_div:
|
131
|
+
return None
|
132
|
+
|
133
|
+
apk_links: list[tuple[str, str]] = []
|
134
|
+
for a in apk_files_div.find_all("a", href=True):
|
135
|
+
link = a["href"].strip()
|
136
|
+
filename = a.find("span", {"class": "der_name"})
|
137
|
+
filename = filename.text.strip() if filename else link.split("/")[-1]
|
138
|
+
apk_links.append((filename, link))
|
139
|
+
|
140
|
+
return title, apk_links
|
141
|
+
|
109
142
|
return None
|
apksearch/sites/apkcombo.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from bs4 import BeautifulSoup
|
2
|
-
|
2
|
+
|
3
|
+
from apksearch.sites import requests
|
3
4
|
|
4
5
|
|
5
6
|
class APKCombo:
|
@@ -27,7 +28,7 @@ class APKCombo:
|
|
27
28
|
|
28
29
|
def __init__(self, pkg_name: str):
|
29
30
|
self.pkg_name = pkg_name
|
30
|
-
self.base_url = "https://apkcombo.
|
31
|
+
self.base_url = "https://apkcombo.com"
|
31
32
|
self.search_url = self.base_url + "/search"
|
32
33
|
self.headers = {
|
33
34
|
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
@@ -37,7 +38,7 @@ class APKCombo:
|
|
37
38
|
"dnt": "1",
|
38
39
|
"pragma": "no-cache",
|
39
40
|
"priority": "u=0, i",
|
40
|
-
"referer": "https://apkcombo.
|
41
|
+
"referer": "https://apkcombo.com/",
|
41
42
|
"sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
42
43
|
"sec-ch-ua-mobile": "?0",
|
43
44
|
"sec-ch-ua-platform": '"Windows"',
|
apksearch/sites/apkfab.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from bs4 import BeautifulSoup
|
2
|
-
|
2
|
+
|
3
|
+
from apksearch.sites import requests, curl
|
3
4
|
|
4
5
|
|
5
6
|
class APKFab:
|
@@ -59,7 +60,10 @@ class APKFab:
|
|
59
60
|
"""
|
60
61
|
pkg_name = self.pkg_name
|
61
62
|
url = self.search_url + pkg_name
|
62
|
-
|
63
|
+
params = {"headers": self.headers}
|
64
|
+
if curl:
|
65
|
+
params["impersonate"] = "chrome"
|
66
|
+
response: requests.Response = self.session.get(url, **params)
|
63
67
|
soup = BeautifulSoup(response.text, "html.parser")
|
64
68
|
search_result = soup.find("div", {"class": "search-white"})
|
65
69
|
if search_result:
|
apksearch/sites/apkmirror.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
import
|
1
|
+
import re
|
2
|
+
|
3
|
+
from apksearch.sites import requests
|
2
4
|
|
3
5
|
|
4
6
|
class APKMirror:
|
@@ -59,7 +61,7 @@ class APKMirror:
|
|
59
61
|
return title, apk_link
|
60
62
|
return None
|
61
63
|
|
62
|
-
def find_version(self, apk_link: str, version: str) -> str:
|
64
|
+
def find_version(self, apk_link: str, version: str, title: str) -> str | None:
|
63
65
|
"""
|
64
66
|
Finds and returns the download link for the given APK link and version.
|
65
67
|
|
@@ -70,7 +72,8 @@ class APKMirror:
|
|
70
72
|
Returns:
|
71
73
|
str: The download link for the specified version of the APK.
|
72
74
|
"""
|
73
|
-
name =
|
75
|
+
name = re.sub(r"[(),]", "", title).lower().replace(" ", "-")
|
76
|
+
name = re.sub(r"-+", "-", name)
|
74
77
|
version = version.replace(".", "-")
|
75
78
|
url = apk_link + name + "-" + version + "-release"
|
76
79
|
response = self.session.get(url, headers=self.headers)
|
apksearch/sites/apkpure.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import re
|
2
|
+
|
2
3
|
from bs4 import BeautifulSoup
|
3
|
-
|
4
|
+
|
5
|
+
from apksearch.sites import requests
|
4
6
|
|
5
7
|
|
6
8
|
class APKPure:
|
@@ -107,14 +109,6 @@ class APKPure:
|
|
107
109
|
None: If no matching APK is found.
|
108
110
|
tuple[str, str]: A tuple containing the title and link of the matching APK if found.
|
109
111
|
"""
|
110
|
-
# Try API first
|
111
|
-
api_result = self._api_search()
|
112
|
-
if api_result:
|
113
|
-
title, versions = api_result
|
114
|
-
if versions:
|
115
|
-
return title, versions[0][1]
|
116
|
-
|
117
|
-
# Fall back to web scrapping
|
118
112
|
pkg_name = self.pkg_name
|
119
113
|
url = self.search_url + pkg_name
|
120
114
|
response: requests.Response = self.session.get(url, headers=self.headers)
|
@@ -138,7 +132,7 @@ class APKPure:
|
|
138
132
|
try:
|
139
133
|
location = response.headers.get("Location")
|
140
134
|
except AttributeError:
|
141
|
-
|
135
|
+
location = None
|
142
136
|
if location:
|
143
137
|
if location == "https://apkpure.com":
|
144
138
|
return None
|
@@ -152,6 +146,13 @@ class APKPure:
|
|
152
146
|
if content:
|
153
147
|
apk_title = content.split("filename=")[1].strip('"').split("_")[0]
|
154
148
|
return apk_title, location
|
149
|
+
|
150
|
+
api_result = self._api_search()
|
151
|
+
if api_result:
|
152
|
+
title, versions = api_result
|
153
|
+
if versions:
|
154
|
+
return title, versions[0][1]
|
155
|
+
|
155
156
|
return None
|
156
157
|
|
157
158
|
def find_versions(self, apk_link: str) -> list[tuple[str, str]]:
|
apksearch/sites/appteka.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import re
|
2
|
+
|
2
3
|
from bs4 import BeautifulSoup
|
3
|
-
|
4
|
+
|
5
|
+
from apksearch.sites import requests
|
4
6
|
|
5
7
|
|
6
8
|
class AppTeka:
|
@@ -48,7 +50,7 @@ class AppTeka:
|
|
48
50
|
}
|
49
51
|
self.session = requests.Session()
|
50
52
|
|
51
|
-
def search_apk(self, version: str = None) -> None | tuple[str, str]:
|
53
|
+
def search_apk(self, version: str | None = None) -> None | tuple[str, str | None]:
|
52
54
|
"""
|
53
55
|
Searches for the APK on AppTeka and returns the title and link if found.
|
54
56
|
|
apksearch/sites/aptoide.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: apksearch
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.5
|
4
4
|
Summary: Search for apks on varius websites
|
5
5
|
Author-email: Abhi <allinoneallinone00@gmail.com>
|
6
6
|
License: MIT License
|
@@ -49,6 +49,8 @@ Provides-Extra: dev
|
|
49
49
|
Requires-Dist: pytest>=7.4.3; extra == "dev"
|
50
50
|
Requires-Dist: black>=23.12.1; extra == "dev"
|
51
51
|
Requires-Dist: flake8>=6.1.0; extra == "dev"
|
52
|
+
Provides-Extra: curl
|
53
|
+
Requires-Dist: curl_cffi>=0.12.1b1; extra == "curl"
|
52
54
|
Dynamic: license-file
|
53
55
|
|
54
56
|
<h1 align="center">apksearch</h1>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
apksearch/__init__.py,sha256=PIO7B54j55_I5D7DEWcq_BVLK8Iwn48jB7uKjKhW83I,365
|
2
|
+
apksearch/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
|
3
|
+
apksearch/cli.py,sha256=gOvFyx35Byumt3VXct3gvwev0W5Nca2xfQulDd9CsYA,9582
|
4
|
+
apksearch/sites/__init__.py,sha256=IeT6HoIdeglXOkQuhfuZjlhADUI7L4bS2QIGG-zDgeM,147
|
5
|
+
apksearch/sites/apkad.py,sha256=UmVv4PL-L4SNJ5U4yERaRep-DG5V-QJ1A6hfy7DUwPQ,5072
|
6
|
+
apksearch/sites/apkcombo.py,sha256=WqiX9bt0Z8jx4JBkxyKzXrv32wxfZRKKO5RNn4Q74cw,4740
|
7
|
+
apksearch/sites/apkfab.py,sha256=DHnkDO96taEEvBxN8TVBW1flEqgAmkiU6MWcMUGJEFA,5623
|
8
|
+
apksearch/sites/apkmirror.py,sha256=slzj84w-WaiSQ9PjIC0EURceugbSC7u5bVgORDpVHtQ,3248
|
9
|
+
apksearch/sites/apkpure.py,sha256=7Ye9xNQylNj4RyqN8eG1cLqQjddvEOg1zVES1iFqmC4,8942
|
10
|
+
apksearch/sites/appteka.py,sha256=ivC8EQjy15HvLAQoqvOJQOso5Dv3Q3LTOnEc8OUXAc4,6409
|
11
|
+
apksearch/sites/aptoide.py,sha256=5_IkfYhEOvr2a1yxkEivqeO3PXPPj0HMwGefVBq95io,4839
|
12
|
+
apksearch-1.3.5.dist-info/licenses/LICENSE,sha256=Icu9iAY9cAaraq-HaAk8aWpXS1nE-3U_wfaOhN-HQUw,1061
|
13
|
+
apksearch-1.3.5.dist-info/METADATA,sha256=hj5WCRh0E8S6jK7e9JCZRJovOj0GU2YuIvkIca99hTg,8303
|
14
|
+
apksearch-1.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
+
apksearch-1.3.5.dist-info/entry_points.txt,sha256=FeAPgNPSU1tCwQNaKAmk6eQYbMHtsltcgeWSGUTxu0k,49
|
16
|
+
apksearch-1.3.5.dist-info/top_level.txt,sha256=VguZMODhlXWwDyJJNJms5syJ4EHmWSQOS45J9I5Cv5o,10
|
17
|
+
apksearch-1.3.5.dist-info/RECORD,,
|
apksearch/sites/apkmonk.py
DELETED
@@ -1,108 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
import requests
|
3
|
-
from bs4 import BeautifulSoup
|
4
|
-
|
5
|
-
|
6
|
-
class APKMonk:
|
7
|
-
"""
|
8
|
-
This class provides methods to search for an APK on APKMonk based on package name,
|
9
|
-
and to find available versions and their download links for a given APK link.
|
10
|
-
|
11
|
-
Parameters:
|
12
|
-
pkg_name (str): The package name of the APK to search for.
|
13
|
-
|
14
|
-
Attributes:
|
15
|
-
pkg_name (str): The package name of the APK to search for.
|
16
|
-
base_url (str): The base URL of the APKMonk website.
|
17
|
-
search_url (str): The URL used to search for APKs on APKMonk.
|
18
|
-
headers (dict): The headers used for making HTTP requests.
|
19
|
-
session (requests.Session): The session object used for making HTTP requests.
|
20
|
-
|
21
|
-
Methods:
|
22
|
-
search_apk() -> None | tuple[str, str]:
|
23
|
-
Searches for the APK on APKMonk and returns the title and link if found.
|
24
|
-
|
25
|
-
find_versions(apk_link: str) -> list[tuple[str, str]]:
|
26
|
-
Finds and returns a list of versions and their download links for the given APK link.
|
27
|
-
"""
|
28
|
-
|
29
|
-
def __init__(self, pkg_name: str):
|
30
|
-
self.pkg_name = pkg_name
|
31
|
-
self.base_url = "https://www.apkmonk.com"
|
32
|
-
self.search_url = self.base_url + "/ssearch?q="
|
33
|
-
self.headers = {
|
34
|
-
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
35
|
-
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
36
|
-
"cache-control": "no-cache",
|
37
|
-
"dnt": "1",
|
38
|
-
"pragma": "no-cache",
|
39
|
-
"priority": "u=0, i",
|
40
|
-
"referer": "https://www.apkmonk.com/",
|
41
|
-
"sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
42
|
-
"sec-ch-ua-mobile": "?0",
|
43
|
-
"sec-ch-ua-platform": '"Windows"',
|
44
|
-
"sec-fetch-dest": "document",
|
45
|
-
"sec-fetch-mode": "navigate",
|
46
|
-
"sec-fetch-site": "same-origin",
|
47
|
-
"sec-fetch-user": "?1",
|
48
|
-
"upgrade-insecure-requests": "1",
|
49
|
-
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0",
|
50
|
-
}
|
51
|
-
self.session = requests.Session()
|
52
|
-
|
53
|
-
def search_apk(self) -> None | tuple[str, str]:
|
54
|
-
"""
|
55
|
-
Searches for the APK on APKMonk and returns the title and link if found.
|
56
|
-
|
57
|
-
Returns:
|
58
|
-
None: If no matching APK is found.
|
59
|
-
tuple[str, str]: A tuple containing the title and link of the matching APK if found.
|
60
|
-
"""
|
61
|
-
pkg_name = self.pkg_name
|
62
|
-
url = self.search_url + pkg_name
|
63
|
-
response: requests.Response = self.session.get(url, headers=self.headers)
|
64
|
-
soup = BeautifulSoup(response.text, "html.parser")
|
65
|
-
search_results = soup.find("a", {"title": re.compile(".*apk$")})
|
66
|
-
if search_results:
|
67
|
-
link = search_results["href"]
|
68
|
-
title = search_results.find("span", {"class": "af-title truncate"}).text
|
69
|
-
if link == f"/app/{pkg_name}/":
|
70
|
-
return title, f"{self.base_url}{link}"
|
71
|
-
return None
|
72
|
-
|
73
|
-
def find_versions(self, apk_link: str) -> list[tuple[str, str]]:
|
74
|
-
"""
|
75
|
-
Finds and returns a list of versions and their download links for the given APK link.
|
76
|
-
|
77
|
-
Parameters:
|
78
|
-
apk_link (str): The link to the APK on the APKMonk website.
|
79
|
-
|
80
|
-
Returns:
|
81
|
-
list[tuple[str, str]]: A list of tuples, where each tuple contains the version number
|
82
|
-
and its corresponding download link. If no versions are found, an empty list is returned.
|
83
|
-
"""
|
84
|
-
versions_info = []
|
85
|
-
if apk_link.startswith(self.base_url):
|
86
|
-
url = apk_link
|
87
|
-
response: requests.Response = self.session.get(url, headers=self.headers)
|
88
|
-
soup = BeautifulSoup(response.text, "html.parser")
|
89
|
-
|
90
|
-
version_header = soup.find(
|
91
|
-
"div",
|
92
|
-
{"class": "box-title"},
|
93
|
-
text=re.compile("All Versions", re.IGNORECASE),
|
94
|
-
)
|
95
|
-
|
96
|
-
if version_header:
|
97
|
-
versions_table = version_header.find_next("table", {"class": "striped"})
|
98
|
-
|
99
|
-
if versions_table:
|
100
|
-
rows = versions_table.find_all("tr")
|
101
|
-
for row in rows:
|
102
|
-
version_link = row.find("a", href=True)
|
103
|
-
if version_link:
|
104
|
-
version_number = version_link.text.strip()
|
105
|
-
download_url = self.base_url + version_link["href"]
|
106
|
-
versions_info.append((version_number, download_url))
|
107
|
-
|
108
|
-
return versions_info
|
apksearch-1.3.0.dist-info/RECORD
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
apksearch/__init__.py,sha256=QvfqsYdMwBGKWiCEnADT1E1m74n5jjjSnrzvasbbZcs,415
|
2
|
-
apksearch/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
|
3
|
-
apksearch/cli.py,sha256=gpWmMXzcfGbLUnouSVUZzcxjP7ujWnf0sfqvdwkUIU8,10076
|
4
|
-
apksearch/sites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
apksearch/sites/apkad.py,sha256=x5nnI20Rj1LysZQc7-vFEbooxXb0vkmHzFpHoUvF_SA,4811
|
6
|
-
apksearch/sites/apkcombo.py,sha256=AiJhSb0subQLb51dr8MQaxgeEZ77wI99InfigYfrcl4,4718
|
7
|
-
apksearch/sites/apkfab.py,sha256=5f0fOBmQk0E30NcVE2cF80soJqI5ziOukU6_xMPtgFQ,5502
|
8
|
-
apksearch/sites/apkmirror.py,sha256=IpS6r7ovM3g2dZrkR_a9zQpHy-jqrU2nJ3ykVkWxers,3127
|
9
|
-
apksearch/sites/apkmonk.py,sha256=85bidRBz6RfZOaS9WEZfTXGnrMVsJEstMJT10XltWus,4657
|
10
|
-
apksearch/sites/apkpure.py,sha256=iXZPzecdBy8JlJmC_yFCQrPi3hXnulRbqg7xCSd3Ppk,8975
|
11
|
-
apksearch/sites/appteka.py,sha256=in04JYF043n0fUvBW1QQeXnNbYekGUIh4qeame951nA,6372
|
12
|
-
apksearch/sites/aptoide.py,sha256=5RruPDwyo5z0E2XesX4Mi41ONdn26SxN0dH36eRolNQ,4816
|
13
|
-
apksearch-1.3.0.dist-info/licenses/LICENSE,sha256=Icu9iAY9cAaraq-HaAk8aWpXS1nE-3U_wfaOhN-HQUw,1061
|
14
|
-
apksearch-1.3.0.dist-info/METADATA,sha256=FXDYFNkHiFEupZa-Y7II6pla6inP8cF6oAjHpUlB_IA,8230
|
15
|
-
apksearch-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
-
apksearch-1.3.0.dist-info/entry_points.txt,sha256=FeAPgNPSU1tCwQNaKAmk6eQYbMHtsltcgeWSGUTxu0k,49
|
17
|
-
apksearch-1.3.0.dist-info/top_level.txt,sha256=VguZMODhlXWwDyJJNJms5syJ4EHmWSQOS45J9I5Cv5o,10
|
18
|
-
apksearch-1.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|