apksearch 1.2.5__tar.gz → 1.2.7__tar.gz

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 (28) hide show
  1. {apksearch-1.2.5 → apksearch-1.2.7}/PKG-INFO +32 -11
  2. {apksearch-1.2.5 → apksearch-1.2.7}/README.md +30 -9
  3. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/__init__.py +3 -1
  4. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/cli.py +71 -6
  5. apksearch-1.2.7/apksearch/sites/apkad.py +109 -0
  6. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/sites/apkcombo.py +2 -2
  7. apksearch-1.2.7/apksearch/sites/aptoide.py +102 -0
  8. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch.egg-info/PKG-INFO +32 -11
  9. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch.egg-info/SOURCES.txt +4 -1
  10. {apksearch-1.2.5 → apksearch-1.2.7}/pyproject.toml +1 -1
  11. {apksearch-1.2.5 → apksearch-1.2.7}/tests/test_apkmirror.py +1 -1
  12. apksearch-1.2.7/tests/test_aptoide.py +29 -0
  13. {apksearch-1.2.5 → apksearch-1.2.7}/LICENSE +0 -0
  14. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/__main__.py +0 -0
  15. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/sites/__init__.py +0 -0
  16. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/sites/apkfab.py +0 -0
  17. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/sites/apkmirror.py +0 -0
  18. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/sites/apkpure.py +0 -0
  19. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch/sites/appteka.py +0 -0
  20. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch.egg-info/dependency_links.txt +0 -0
  21. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch.egg-info/entry_points.txt +0 -0
  22. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch.egg-info/requires.txt +0 -0
  23. {apksearch-1.2.5 → apksearch-1.2.7}/apksearch.egg-info/top_level.txt +0 -0
  24. {apksearch-1.2.5 → apksearch-1.2.7}/setup.cfg +0 -0
  25. {apksearch-1.2.5 → apksearch-1.2.7}/tests/test_apkcombo.py +0 -0
  26. {apksearch-1.2.5 → apksearch-1.2.7}/tests/test_apkfab.py +0 -0
  27. {apksearch-1.2.5 → apksearch-1.2.7}/tests/test_apkpure.py +0 -0
  28. {apksearch-1.2.5 → apksearch-1.2.7}/tests/test_appteka.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: apksearch
3
- Version: 1.2.5
3
+ Version: 1.2.7
4
4
  Summary: Search for apks on varius websites
5
5
  Author-email: Abhi <allinoneallinone00@gmail.com>
6
6
  License: MIT License
@@ -50,26 +50,37 @@ 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
52
 
53
- # apksearch
53
+ <h1 align="center">apksearch</h1>
54
54
 
55
55
  `apksearch` is a Python library designed to search for APK files on different APK websites, such as APKPure and APKMirror. It allows users to find APKs, check for available versions, and retrieve download links.
56
56
 
57
57
  **The Inspiration:**
58
58
  There were countless occasions when I needed a specific APK for a package name, only to find it unavailable on popular platforms. This led to the tedious task of manually visiting multiple websites and searching one by one.
59
+ <details>
60
+ <summary>screenshot</summary>
61
+ <p align="center">
62
+ <img width="500" src="https://github.com/user-attachments/assets/cd54eaeb-a56b-40b3-835f-b48b1e7772f3"></img><br>
63
+ As you can see, Roblox version <code>2.647.716</code> is not available on APKPure and APKCombo, this helped me avoid going through these sites.
64
+ </p>
65
+ </details>
66
+
67
+ **P.S:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
59
68
 
60
69
  # Features
61
70
 
62
71
  - **Search APKs:** The library provides methods to search for APKs using package names.
63
- - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from APKPure and APKMirror.
72
+ - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from various websites.
64
73
  - **Command-Line Interface:** A CLI is available for users to search for APKs directly from the command line.
65
74
 
66
75
  ## Supported Websites
67
76
 
68
77
  - [APKPure](https://apkpure.net/)
69
78
  - [APKMirror](https://www.apkmirror.com/)
79
+ - [Aptoide](https://en.aptoide.com/)
70
80
  - [APKCombo](https://apkcombo.app/)
71
81
  - [APKFab](https://apkfab.com/)
72
82
  - [Appteka](https://appteka.store/)
83
+ - [APKAD](https://apk.ad/)
73
84
 
74
85
  > [!NOTE]
75
86
  > **For site owners:**
@@ -79,7 +90,7 @@ There were countless occasions when I needed a specific APK for a package name,
79
90
 
80
91
  ## Installation
81
92
 
82
- To install the `apksearch` library, use the following command:
93
+ To install/upgrade the `apksearch` library, use the following command:
83
94
 
84
95
  ```sh
85
96
  pip install -U git+https://github.com/AbhiTheModder/apksearch.git
@@ -91,6 +102,12 @@ OR, through pip:
91
102
  pip install -U apksearch
92
103
  ```
93
104
 
105
+ OR, if you've cloned the repository locally you can do so via:
106
+
107
+ ```sh
108
+ pip install -U . # or path to the local clone
109
+ ```
110
+
94
111
  ## Usage
95
112
 
96
113
  ### Command-Line Interface
@@ -134,19 +151,25 @@ if result:
134
151
  #### `APKPure` | `APKCombo` | `APKFab`
135
152
 
136
153
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
137
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKPure and returns the title and link if found.
154
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
138
155
  - **`find_versions(self, apk_link: str) -> list[tuple[str, str]]`**: Finds and returns a list of versions and their download links for the given APK link.
139
156
 
140
157
  #### `APKMirror`
141
158
 
142
159
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
143
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKMirror and returns the title and link if found.
160
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
144
161
  - **`find_version(self, apk_link: str, version: str) -> str`**: Finds and returns the download link for the given APK link and version.
145
162
 
146
- #### `AppTeka`
163
+ #### `AppTeka` | `APKAD`
147
164
 
148
165
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
149
- - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK on AppTeka and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
166
+ - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
167
+
168
+ #### `Aptoide`
169
+
170
+ - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
171
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
172
+ - **`find_versions(self, apk_link: str, version: str) -> list[str]`**: Finds and returns the download links for the given APK link and versions list.
150
173
 
151
174
  ### Testing
152
175
 
@@ -165,8 +188,6 @@ pytest
165
188
  - [APKUpdater](https://github.com/rumboalla/apkupdater) for APKMirror API.
166
189
  - [apkeep](https://github.com/EFForg/apkeep) for APKPure API.
167
190
 
168
- **Recommendation:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
169
-
170
191
  ## License
171
192
 
172
193
  This project is licensed under the MIT License. See the [LICENSE](https://github.com/AbhiTheModder/apksearch/blob/main/LICENSE) file for more details.
@@ -1,23 +1,34 @@
1
- # apksearch
1
+ <h1 align="center">apksearch</h1>
2
2
 
3
3
  `apksearch` is a Python library designed to search for APK files on different APK websites, such as APKPure and APKMirror. It allows users to find APKs, check for available versions, and retrieve download links.
4
4
 
5
5
  **The Inspiration:**
6
6
  There were countless occasions when I needed a specific APK for a package name, only to find it unavailable on popular platforms. This led to the tedious task of manually visiting multiple websites and searching one by one.
7
+ <details>
8
+ <summary>screenshot</summary>
9
+ <p align="center">
10
+ <img width="500" src="https://github.com/user-attachments/assets/cd54eaeb-a56b-40b3-835f-b48b1e7772f3"></img><br>
11
+ As you can see, Roblox version <code>2.647.716</code> is not available on APKPure and APKCombo, this helped me avoid going through these sites.
12
+ </p>
13
+ </details>
14
+
15
+ **P.S:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
7
16
 
8
17
  # Features
9
18
 
10
19
  - **Search APKs:** The library provides methods to search for APKs using package names.
11
- - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from APKPure and APKMirror.
20
+ - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from various websites.
12
21
  - **Command-Line Interface:** A CLI is available for users to search for APKs directly from the command line.
13
22
 
14
23
  ## Supported Websites
15
24
 
16
25
  - [APKPure](https://apkpure.net/)
17
26
  - [APKMirror](https://www.apkmirror.com/)
27
+ - [Aptoide](https://en.aptoide.com/)
18
28
  - [APKCombo](https://apkcombo.app/)
19
29
  - [APKFab](https://apkfab.com/)
20
30
  - [Appteka](https://appteka.store/)
31
+ - [APKAD](https://apk.ad/)
21
32
 
22
33
  > [!NOTE]
23
34
  > **For site owners:**
@@ -27,7 +38,7 @@ There were countless occasions when I needed a specific APK for a package name,
27
38
 
28
39
  ## Installation
29
40
 
30
- To install the `apksearch` library, use the following command:
41
+ To install/upgrade the `apksearch` library, use the following command:
31
42
 
32
43
  ```sh
33
44
  pip install -U git+https://github.com/AbhiTheModder/apksearch.git
@@ -39,6 +50,12 @@ OR, through pip:
39
50
  pip install -U apksearch
40
51
  ```
41
52
 
53
+ OR, if you've cloned the repository locally you can do so via:
54
+
55
+ ```sh
56
+ pip install -U . # or path to the local clone
57
+ ```
58
+
42
59
  ## Usage
43
60
 
44
61
  ### Command-Line Interface
@@ -82,19 +99,25 @@ if result:
82
99
  #### `APKPure` | `APKCombo` | `APKFab`
83
100
 
84
101
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
85
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKPure and returns the title and link if found.
102
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
86
103
  - **`find_versions(self, apk_link: str) -> list[tuple[str, str]]`**: Finds and returns a list of versions and their download links for the given APK link.
87
104
 
88
105
  #### `APKMirror`
89
106
 
90
107
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
91
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKMirror and returns the title and link if found.
108
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
92
109
  - **`find_version(self, apk_link: str, version: str) -> str`**: Finds and returns the download link for the given APK link and version.
93
110
 
94
- #### `AppTeka`
111
+ #### `AppTeka` | `APKAD`
95
112
 
96
113
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
97
- - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK on AppTeka and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
114
+ - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
115
+
116
+ #### `Aptoide`
117
+
118
+ - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
119
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
120
+ - **`find_versions(self, apk_link: str, version: str) -> list[str]`**: Finds and returns the download links for the given APK link and versions list.
98
121
 
99
122
  ### Testing
100
123
 
@@ -113,8 +136,6 @@ pytest
113
136
  - [APKUpdater](https://github.com/rumboalla/apkupdater) for APKMirror API.
114
137
  - [apkeep](https://github.com/EFForg/apkeep) for APKPure API.
115
138
 
116
- **Recommendation:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
117
-
118
139
  ## License
119
140
 
120
141
  This project is licensed under the MIT License. See the [LICENSE](https://github.com/AbhiTheModder/apksearch/blob/main/LICENSE) file for more details.
@@ -3,5 +3,7 @@ from .sites.apkmirror import APKMirror
3
3
  from .sites.appteka import AppTeka
4
4
  from .sites.apkcombo import APKCombo
5
5
  from .sites.apkfab import APKFab
6
+ from .sites.apkad import APKad
7
+ from .sites.aptoide import Aptoide
6
8
 
7
- __all__ = ["APKPure", "APKMirror", "AppTeka", "APKCombo", "APKFab"]
9
+ __all__ = ["APKPure", "APKMirror", "AppTeka", "APKCombo", "APKFab", "APKad", "Aptoide"]
@@ -1,6 +1,7 @@
1
1
  import argparse
2
2
 
3
- from apksearch import APKPure, APKMirror, AppTeka, APKCombo, APKFab
3
+ from collections.abc import Callable
4
+ from apksearch import APKPure, APKMirror, AppTeka, APKCombo, APKFab, APKad, Aptoide
4
5
  from requests.exceptions import ConnectionError, ConnectTimeout
5
6
 
6
7
  # Color codes
@@ -11,6 +12,21 @@ YELLOW = "\033[93m"
11
12
  NC = "\033[0m"
12
13
 
13
14
 
15
+ def search(
16
+ func: Callable[[str, str], object],
17
+ pkg_name: str,
18
+ version: str | None,
19
+ log_err: bool = False,
20
+ ) -> None:
21
+ try:
22
+ func(pkg_name, version)
23
+ except Exception as exc:
24
+ if log_err:
25
+ print(f"Error in {func.__name__}: {exc}")
26
+ else:
27
+ pass
28
+
29
+
14
30
  def search_apkpure(pkg_name: str, version: str | None) -> None:
15
31
  apkpure = APKPure(pkg_name)
16
32
  try:
@@ -63,6 +79,23 @@ def search_apkfab(pkg_name: str, version: str | None) -> None:
63
79
  print(f"{BOLD}APKFab:{NC} No Results!")
64
80
 
65
81
 
82
+ def search_apkad(pkg_name: str, version: str | None) -> None:
83
+ apkad = APKad(pkg_name)
84
+ try:
85
+ result_apkad: tuple[str, str] | None = apkad.search_apk()
86
+ except (ConnectionError, ConnectTimeout):
87
+ result_apkad = None
88
+ print(f"{RED}Failed to resolve 'api.apk.ad'!{NC}")
89
+ if result_apkad:
90
+ title, apk_link = result_apkad
91
+ print(f"{BOLD}APKAD:{NC} Found {GREEN}{title}{NC}") if title else None
92
+ print(
93
+ f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}"
94
+ ) if not version else print(" ╰─> Doesn't support version search!")
95
+ else:
96
+ print(f"{BOLD}APKAD:{NC} No Results!")
97
+
98
+
66
99
  def search_apkcombo(pkg_name: str, version: str | None) -> None:
67
100
  apkcombo = APKCombo(pkg_name)
68
101
  try:
@@ -134,27 +167,59 @@ def search_appteka(pkg_name: str, version: str | None) -> None:
134
167
  print(f"{BOLD}AppTeka:{NC} No Results!")
135
168
 
136
169
 
170
+ def search_aptoide(pkg_name: str, version: str | None) -> None:
171
+ aptoide = Aptoide(pkg_name)
172
+ try:
173
+ result_aptoide: tuple[str, str] | None = aptoide.search_apk()
174
+ except (ConnectionError, ConnectTimeout):
175
+ result_aptoide = None
176
+ print(f"{RED}Failed to resolve 'aptoide.com'!{NC}")
177
+ if result_aptoide:
178
+ title, apk_link = result_aptoide
179
+ print(f"{BOLD}Aptoide:{NC} Found {GREEN}{title}{NC}") if title else None
180
+ print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}") if not version else None
181
+ if version:
182
+ versions: list[str | None] = aptoide.find_versions(apk_link)
183
+ if versions:
184
+ if version in versions:
185
+ print(
186
+ f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{apk_link}versions{NC}"
187
+ )
188
+ else:
189
+ print(f"{BOLD}Aptoide:{NC} Version {RED}{version}{NC} not found!")
190
+ else:
191
+ print(f"{BOLD}Aptoide:{NC} No Results!")
192
+
193
+
137
194
  def main():
138
195
  parser = argparse.ArgumentParser(
139
196
  prog="APKSearch", description="Search for APKs on various websites"
140
197
  )
141
198
  parser.add_argument("pkg_name", help="The package name of the APK")
142
199
  parser.add_argument("--version", help="The version of the APK", required=False)
200
+ parser.add_argument(
201
+ "--log_err", help="Enable error logs", action="store_true", required=False
202
+ )
143
203
  args = parser.parse_args()
144
204
 
145
205
  pkg_name = args.pkg_name
146
206
  version = args.version
207
+ log_err = args.log_err
147
208
  print(f"{BOLD}Searching for {YELLOW}{pkg_name}{NC}...")
148
209
  # Initiate search on apkpure
149
- search_apkpure(pkg_name, version)
210
+ search(search_apkpure, pkg_name, version, log_err)
150
211
  # Initiate search on apkmirror
151
- search_apkmirror(pkg_name, version)
212
+ search(search_apkmirror, pkg_name, version, log_err)
213
+ # Initiate search on aptoide
214
+ search(search_aptoide, pkg_name, version, log_err)
152
215
  # Initiate search on appteka
153
- search_appteka(pkg_name, version)
216
+ search(search_appteka, pkg_name, version, log_err)
154
217
  # Initiate search on apkcombo
155
- search_apkcombo(pkg_name, version)
218
+ search(search_apkcombo, pkg_name, version, log_err)
156
219
  # Initiate search on apkfab
157
- search_apkfab(pkg_name, version)
220
+ search(search_apkfab, pkg_name, version, log_err)
221
+ # Initiate search on apkad
222
+ search(search_apkad, pkg_name, version, log_err)
158
223
 
159
224
 
160
225
  if __name__ == "__main__":
@@ -0,0 +1,109 @@
1
+ import json
2
+ from bs4 import BeautifulSoup
3
+ import requests
4
+
5
+
6
+ class APKad:
7
+ """
8
+ This class provides methods to search for an APK on APKAD 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 APKAD website.
17
+ search_url (str): The URL used to search for APKs on APKAD.
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 APKAD and returns the title and link if found.
24
+ """
25
+
26
+ def __init__(self, pkg_name: str):
27
+ self.pkg_name = pkg_name
28
+ self.base_url = "https://downloader.apk.ad"
29
+ self.api_url = "https://api.apk.ad"
30
+ self.search_url = (
31
+ self.api_url
32
+ + f"/get?hl=en&package={self.pkg_name}&device=phone&arch=arm64-v8a&vc=&device_id="
33
+ )
34
+ self.headers = {
35
+ "accept": "text/event-stream",
36
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
37
+ "cache-control": "no-cache",
38
+ "dnt": "1",
39
+ "origin": "https://downloader.apk.ad",
40
+ "pragma": "no-cache",
41
+ "priority": "u=1, i",
42
+ "referer": "https://downloader.apk.ad/",
43
+ "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
44
+ "sec-ch-ua-mobile": "?0",
45
+ "sec-ch-ua-platform": '"Windows"',
46
+ "sec-fetch-dest": "empty",
47
+ "sec-fetch-mode": "cors",
48
+ "sec-fetch-site": "same-site",
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 APKAD 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
+ url = self.search_url
62
+ response = self.session.get(url, headers=self.headers, stream=True)
63
+ stream_response = ""
64
+ for line in response.iter_lines():
65
+ if line:
66
+ line_response = line.decode("utf-8")
67
+ if '"progress":100' in line_response:
68
+ line_response = line_response[6:]
69
+ stream_response += line_response
70
+ break
71
+ if stream_response:
72
+ data = json.loads(stream_response)
73
+ html_body = data["html"]
74
+ soup = BeautifulSoup(html_body, "html.parser")
75
+ if not soup:
76
+ return None
77
+ title = soup.find("li", {"class": "_title"})
78
+ if title:
79
+ title = title.text.strip()
80
+ button = soup.find(
81
+ "button", {"onclick": True, "id": "downloadButtonapk"}
82
+ )["onclick"]
83
+ if button:
84
+ zip_args = [
85
+ arg.strip("'")
86
+ for arg in button.split("zip(")[1].split(")")[0].split(",")
87
+ ]
88
+ h = zip_args[0] # hash
89
+ p = zip_args[-1] # type
90
+ token = zip_args[1] # token
91
+ ip = zip_args[2] # ip
92
+ google_id = zip_args[3] # package_name
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
109
+ return None
@@ -28,7 +28,7 @@ class APKCombo:
28
28
  def __init__(self, pkg_name: str):
29
29
  self.pkg_name = pkg_name
30
30
  self.base_url = "https://apkcombo.app"
31
- self.search_url = self.base_url + "/search/"
31
+ self.search_url = self.base_url + "/search"
32
32
  self.headers = {
33
33
  "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",
34
34
  "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
@@ -59,7 +59,7 @@ class APKCombo:
59
59
  tuple[str, str]: A tuple containing the title and link of the matching APK if found.
60
60
  """
61
61
  pkg_name = self.pkg_name
62
- url = self.search_url + pkg_name
62
+ url = self.search_url + "/" + pkg_name
63
63
  response: requests.Response = self.session.get(
64
64
  url, headers=self.headers, allow_redirects=False
65
65
  )
@@ -0,0 +1,102 @@
1
+ import re
2
+ from bs4 import BeautifulSoup
3
+ import requests
4
+
5
+
6
+ class Aptoide:
7
+ """
8
+ This class provides methods to search for an APK on Aptoide 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
+ api_url (str): The base URL for the Aptoide API.
17
+ search_url (str): The URL used to search for APKs on Aptoide.
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 Aptoide and returns the title and link if found.
24
+
25
+ find_versions(apk_link: str) -> list[str | None]:
26
+ Finds and returns a list of versions for the given APK link.
27
+ """
28
+
29
+ def __init__(self, pkg_name: str):
30
+ self.pkg_name = pkg_name
31
+ self.api_url = "https://ws75.aptoide.com/api/7"
32
+ self.search_url = f"{self.api_url}/apps/search?limit=1&query="
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://en.aptoide.com/",
41
+ "sec-ch-ua": '"Not A(Brand";v="8", "Chromium";v="132", "Microsoft Edge";v="132"',
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 Aptoide 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
+ data = response.json()
65
+ if data and data["info"]["status"] == "OK":
66
+ lis = data["datalist"]["list"]
67
+ if lis:
68
+ package = data["datalist"]["list"][0]["package"]
69
+ apk_title = data["datalist"]["list"][0]["name"]
70
+ if package == pkg_name:
71
+ app_id: int = data["datalist"]["list"][0]["id"]
72
+ meta_url = self.api_url + f"/app/getMeta?app_id={app_id}"
73
+ meta_response: requests.Response = self.session.get(
74
+ meta_url, headers=self.headers
75
+ )
76
+ meta_data = meta_response.json()
77
+ if meta_data and meta_data["info"]["status"] == "OK":
78
+ url = meta_data["data"]["urls"]["w"].split("?")[0]
79
+ return apk_title, url
80
+ return None
81
+
82
+ def find_versions(self, apk_link: str) -> list[str | None]:
83
+ """
84
+ Finds and returns a list of versions for the given APK link.
85
+
86
+ Parameters:
87
+ apk_link (str): The link to the APK on the Aptoide website.
88
+
89
+ Returns:
90
+ list[str | None]: A list of version strings for the given APK link.
91
+ """
92
+ versions_info = []
93
+
94
+ url = apk_link + "/versions"
95
+ response: requests.Response = self.session.get(url, headers=self.headers)
96
+ soup = BeautifulSoup(response.content, "html.parser")
97
+ version_spans = soup.find_all("span", string=re.compile(r"^\d+\.\d+\.\d+$"))
98
+ for span in version_spans:
99
+ version = span.text
100
+ versions_info.append(version)
101
+
102
+ return versions_info
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: apksearch
3
- Version: 1.2.5
3
+ Version: 1.2.7
4
4
  Summary: Search for apks on varius websites
5
5
  Author-email: Abhi <allinoneallinone00@gmail.com>
6
6
  License: MIT License
@@ -50,26 +50,37 @@ 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
52
 
53
- # apksearch
53
+ <h1 align="center">apksearch</h1>
54
54
 
55
55
  `apksearch` is a Python library designed to search for APK files on different APK websites, such as APKPure and APKMirror. It allows users to find APKs, check for available versions, and retrieve download links.
56
56
 
57
57
  **The Inspiration:**
58
58
  There were countless occasions when I needed a specific APK for a package name, only to find it unavailable on popular platforms. This led to the tedious task of manually visiting multiple websites and searching one by one.
59
+ <details>
60
+ <summary>screenshot</summary>
61
+ <p align="center">
62
+ <img width="500" src="https://github.com/user-attachments/assets/cd54eaeb-a56b-40b3-835f-b48b1e7772f3"></img><br>
63
+ As you can see, Roblox version <code>2.647.716</code> is not available on APKPure and APKCombo, this helped me avoid going through these sites.
64
+ </p>
65
+ </details>
66
+
67
+ **P.S:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
59
68
 
60
69
  # Features
61
70
 
62
71
  - **Search APKs:** The library provides methods to search for APKs using package names.
63
- - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from APKPure and APKMirror.
72
+ - **Retrieve APK Versions and Download Links:** It can fetch available versions and their download links for a given APK from various websites.
64
73
  - **Command-Line Interface:** A CLI is available for users to search for APKs directly from the command line.
65
74
 
66
75
  ## Supported Websites
67
76
 
68
77
  - [APKPure](https://apkpure.net/)
69
78
  - [APKMirror](https://www.apkmirror.com/)
79
+ - [Aptoide](https://en.aptoide.com/)
70
80
  - [APKCombo](https://apkcombo.app/)
71
81
  - [APKFab](https://apkfab.com/)
72
82
  - [Appteka](https://appteka.store/)
83
+ - [APKAD](https://apk.ad/)
73
84
 
74
85
  > [!NOTE]
75
86
  > **For site owners:**
@@ -79,7 +90,7 @@ There were countless occasions when I needed a specific APK for a package name,
79
90
 
80
91
  ## Installation
81
92
 
82
- To install the `apksearch` library, use the following command:
93
+ To install/upgrade the `apksearch` library, use the following command:
83
94
 
84
95
  ```sh
85
96
  pip install -U git+https://github.com/AbhiTheModder/apksearch.git
@@ -91,6 +102,12 @@ OR, through pip:
91
102
  pip install -U apksearch
92
103
  ```
93
104
 
105
+ OR, if you've cloned the repository locally you can do so via:
106
+
107
+ ```sh
108
+ pip install -U . # or path to the local clone
109
+ ```
110
+
94
111
  ## Usage
95
112
 
96
113
  ### Command-Line Interface
@@ -134,19 +151,25 @@ if result:
134
151
  #### `APKPure` | `APKCombo` | `APKFab`
135
152
 
136
153
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
137
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKPure and returns the title and link if found.
154
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
138
155
  - **`find_versions(self, apk_link: str) -> list[tuple[str, str]]`**: Finds and returns a list of versions and their download links for the given APK link.
139
156
 
140
157
  #### `APKMirror`
141
158
 
142
159
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
143
- - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKMirror and returns the title and link if found.
160
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
144
161
  - **`find_version(self, apk_link: str, version: str) -> str`**: Finds and returns the download link for the given APK link and version.
145
162
 
146
- #### `AppTeka`
163
+ #### `AppTeka` | `APKAD`
147
164
 
148
165
  - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
149
- - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK on AppTeka and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
166
+ - **`search_apk(self, version: str = None) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found. If a version is provided, it checks if that version is available and returns the corresponding download link, None otherwise. If no version is provided, it returns the link for the latest version available.
167
+
168
+ #### `Aptoide`
169
+
170
+ - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
171
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK and returns the title and link if found.
172
+ - **`find_versions(self, apk_link: str, version: str) -> list[str]`**: Finds and returns the download links for the given APK link and versions list.
150
173
 
151
174
  ### Testing
152
175
 
@@ -165,8 +188,6 @@ pytest
165
188
  - [APKUpdater](https://github.com/rumboalla/apkupdater) for APKMirror API.
166
189
  - [apkeep](https://github.com/EFForg/apkeep) for APKPure API.
167
190
 
168
- **Recommendation:** If you're looking for an APK downloader, I highly recommend using [apkeep](https://github.com/EFForg/apkeep).
169
-
170
191
  ## License
171
192
 
172
193
  This project is licensed under the MIT License. See the [LICENSE](https://github.com/AbhiTheModder/apksearch/blob/main/LICENSE) file for more details.
@@ -11,13 +11,16 @@ apksearch.egg-info/entry_points.txt
11
11
  apksearch.egg-info/requires.txt
12
12
  apksearch.egg-info/top_level.txt
13
13
  apksearch/sites/__init__.py
14
+ apksearch/sites/apkad.py
14
15
  apksearch/sites/apkcombo.py
15
16
  apksearch/sites/apkfab.py
16
17
  apksearch/sites/apkmirror.py
17
18
  apksearch/sites/apkpure.py
18
19
  apksearch/sites/appteka.py
20
+ apksearch/sites/aptoide.py
19
21
  tests/test_apkcombo.py
20
22
  tests/test_apkfab.py
21
23
  tests/test_apkmirror.py
22
24
  tests/test_apkpure.py
23
- tests/test_appteka.py
25
+ tests/test_appteka.py
26
+ tests/test_aptoide.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "apksearch"
7
- version = "1.2.5"
7
+ version = "1.2.7"
8
8
  description = "Search for apks on varius websites"
9
9
  authors = [{ name = "Abhi", email = "allinoneallinone00@gmail.com" }]
10
10
  license = { file = "LICENSE" }
@@ -31,7 +31,7 @@ def test_find_versions_nfound():
31
31
 
32
32
  def test_find_versions_found():
33
33
  query = "com.roblox.client"
34
- version = "2.652.765"
34
+ version = "2.654.474"
35
35
  apkmirror = APKMirror(query)
36
36
  result = apkmirror.search_apk()
37
37
 
@@ -0,0 +1,29 @@
1
+ from apksearch.sites.aptoide import Aptoide
2
+
3
+
4
+ def test_search_apk():
5
+ query = "com.roblox.client"
6
+ aptoide = Aptoide(query)
7
+ result = aptoide.search_apk()
8
+
9
+ assert result is not None, "No APK found for the query."
10
+ assert isinstance(result, tuple), "Result should be a tuple."
11
+ assert len(result) == 2, "Tuple should contain two elements."
12
+ assert isinstance(result[0], str), "First element of the tuple should be a string."
13
+ assert isinstance(result[1], str), "Second element of the tuple should be a string."
14
+
15
+
16
+ def test_find_versions():
17
+ query = "com.roblox.client"
18
+ aptoide = Aptoide(query)
19
+ result = aptoide.search_apk()
20
+
21
+ if result:
22
+ apk_link = result[1]
23
+ versions = aptoide.find_versions(apk_link)
24
+
25
+ assert isinstance(versions, list), "Versions should be a list."
26
+ assert len(versions) > 0, "No versions found."
27
+ assert all(
28
+ isinstance(version, str) for version in versions
29
+ ), "Each version should be a string."
File without changes
File without changes