apksearch 1.0.0__py3-none-any.whl → 1.2.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
apksearch/__init__.py CHANGED
@@ -1,4 +1,6 @@
1
1
  from .sites.apkpure import APKPure
2
2
  from .sites.apkmirror import APKMirror
3
+ from .sites.appteka import AppTeka
4
+ from .sites.apkcombo import APKCombo
3
5
 
4
- __all__ = ["APKPure", "APKMirror"]
6
+ __all__ = ["APKPure", "APKMirror", "AppTeka", "APKCombo"]
apksearch/cli.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import argparse
2
2
 
3
- from apksearch import APKPure, APKMirror
3
+ from apksearch import APKPure, APKMirror, AppTeka, APKCombo
4
4
  from requests.exceptions import ConnectionError, ConnectTimeout
5
5
 
6
6
  # Color codes
@@ -36,15 +36,40 @@ def search_apkpure(pkg_name: str, version: str | None) -> None:
36
36
  print(f"{BOLD}APKPure:{NC} No Results!")
37
37
 
38
38
 
39
+ def search_apkcombo(pkg_name: str, version: str | None) -> None:
40
+ apkcombo = APKCombo(pkg_name)
41
+ try:
42
+ result_apkcombo: tuple[str, str] | None = apkcombo.search_apk()
43
+ except (ConnectionError, ConnectTimeout):
44
+ result_apkcombo = None
45
+ print(f"{RED}Failed to resolve 'apkcombo.app'!{NC}")
46
+ if result_apkcombo:
47
+ title, apk_link = result_apkcombo
48
+ print(f"{BOLD}APKCombo:{NC} Found {GREEN}{title}{NC}") if title else None
49
+ print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}") if not version else None
50
+ versions: list[tuple[str, str]] = apkcombo.find_versions(apk_link)
51
+ if version:
52
+ for version_tuple in versions:
53
+ if version_tuple[0] == version:
54
+ print(
55
+ f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{version_tuple[1]}{NC}"
56
+ )
57
+ break
58
+ else:
59
+ print(f"{BOLD}APKCombo:{NC} Version {RED}{version}{NC} not found!")
60
+ else:
61
+ print(f"{BOLD}APKCombo:{NC} No Results!")
62
+
63
+
39
64
  def search_apkmirror(pkg_name: str, version: str | None) -> None:
40
65
  apkmirror = APKMirror(pkg_name)
41
66
  try:
42
- result_apkpure: tuple[str, str] | None = apkmirror.search_apk()
67
+ result_apkmirror: tuple[str, str] | None = apkmirror.search_apk()
43
68
  except (ConnectionError, ConnectTimeout):
44
- result_apkpure = None
69
+ result_apkmirror = None
45
70
  print(f"{RED}Failed to resolve 'apkmirror.com'!{NC}")
46
- if result_apkpure:
47
- title, apk_link = result_apkpure
71
+ if result_apkmirror:
72
+ title, apk_link = result_apkmirror
48
73
  print(f"{BOLD}APKMirror:{NC} Found {GREEN}{title}{NC}") if title else None
49
74
  print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}") if not version else None
50
75
  if version:
@@ -59,6 +84,29 @@ def search_apkmirror(pkg_name: str, version: str | None) -> None:
59
84
  print(f"{BOLD}APKMirror:{NC} No Results!")
60
85
 
61
86
 
87
+ def search_appteka(pkg_name: str, version: str | None) -> None:
88
+ appteka = AppTeka(pkg_name)
89
+ try:
90
+ result_appteka: tuple[str, str] | None = appteka.search_apk(version)
91
+ except (ConnectionError, ConnectTimeout):
92
+ result_appteka = None
93
+ print(f"{RED}Failed to resolve 'appteka.store'!{NC}")
94
+ if result_appteka:
95
+ title, apk_link = result_appteka
96
+ print(f"{BOLD}AppTeka:{NC} Found {GREEN}{title}{NC}") if title else None
97
+ if version:
98
+ if apk_link:
99
+ print(
100
+ f" ╰─> {BOLD}Version: {GREEN}{version}{NC} - {YELLOW}{apk_link}{NC}"
101
+ )
102
+ else:
103
+ print(f"{BOLD}AppTeka:{NC} Version {RED}{version}{NC} not found!")
104
+ else:
105
+ print(f" ╰─> {BOLD}Link: {YELLOW}{apk_link}{NC}")
106
+ else:
107
+ print(f"{BOLD}AppTeka:{NC} No Results!")
108
+
109
+
62
110
  def main():
63
111
  parser = argparse.ArgumentParser(
64
112
  prog="APKSearch", description="Search for APKs on various websites"
@@ -74,6 +122,11 @@ def main():
74
122
  search_apkpure(pkg_name, version)
75
123
  # Initiate search on apkmirror
76
124
  search_apkmirror(pkg_name, version)
125
+ # Initiate search on appteka
126
+ search_appteka(pkg_name, version)
127
+ # Initiate search on apkcombo
128
+ search_apkcombo(pkg_name, version)
129
+
77
130
 
78
131
  if __name__ == "__main__":
79
- main()
132
+ main()
@@ -0,0 +1,104 @@
1
+ from bs4 import BeautifulSoup
2
+ import requests
3
+
4
+
5
+ class APKCombo:
6
+ """
7
+ This class provides methods to search for an APK on APKCombo based on package name,
8
+ and to find available versions and their download links for a given APK link.
9
+
10
+ Parameters:
11
+ pkg_name (str): The package name of the APK to search for.
12
+
13
+ Attributes:
14
+ pkg_name (str): The package name of the APK to search for.
15
+ base_url (str): The base URL of the APKCombo website.
16
+ search_url (str): The URL used to search for APKs on APKCombo.
17
+ headers (dict): The headers used for making HTTP requests.
18
+ session (requests.Session): The session object used for making HTTP requests.
19
+
20
+ Methods:
21
+ search_apk() -> None | tuple[str, str]:
22
+ Searches for the APK on APKCombo and returns the title and link if found.
23
+
24
+ find_versions(apk_link: str) -> list[tuple[str, str]]:
25
+ Finds and returns a list of versions and their download links for the given APK link.
26
+ """
27
+
28
+ def __init__(self, pkg_name: str):
29
+ self.pkg_name = pkg_name
30
+ self.base_url = "https://apkcombo.app"
31
+ self.search_url = self.base_url + "/search/"
32
+ self.headers = {
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
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
35
+ "cache-control": "no-cache",
36
+ "cookie": "__apkcombo_lang=en",
37
+ "dnt": "1",
38
+ "pragma": "no-cache",
39
+ "priority": "u=0, i",
40
+ "referer": "https://apkcombo.app/",
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 APKCombo 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(
64
+ url, headers=self.headers, allow_redirects=False
65
+ )
66
+ # Redirect to the APK page if there's only one result. i.e, apk found.
67
+ if response.status_code == 302:
68
+ url = self.search_url + response.headers["Location"]
69
+ response: requests.Response = self.session.get(url, headers=self.headers)
70
+ elif response.status_code == 200: # Package name not found or multiple results.
71
+ return None
72
+ else:
73
+ raise Exception(f"Error: {response.status_code}")
74
+ soup = BeautifulSoup(response.text, "html.parser")
75
+ title = soup.find("div", {"class": "app_name"}).text.strip()
76
+ apk_link = url
77
+ return title, apk_link
78
+
79
+ def find_versions(self, apk_link: str) -> list[tuple[str, str]]:
80
+ """
81
+ Finds and returns a list of versions and their download links for the given APK link.
82
+
83
+ Parameters:
84
+ apk_link (str): The link to the APK on the APKCombo website.
85
+
86
+ Returns:
87
+ list[tuple[str, str]]: A list of tuples, where each tuple contains the version number
88
+ and its corresponding download link. If no versions are found, an empty list is returned.
89
+ """
90
+ url = apk_link + "/old-versions"
91
+ response: requests.Response = self.session.get(url, headers=self.headers)
92
+ soup = BeautifulSoup(response.text, "html.parser")
93
+ versions_list = soup.find("ul", {"class": "list-versions"})
94
+ versions_info = []
95
+
96
+ if versions_list:
97
+ versions = versions_list.find_all("a", {"class": "ver-item"})
98
+ for version in versions:
99
+ version_number = version.find("span", {"class": "vername"}).text
100
+ version_number = version_number.split(" ")[-1]
101
+ download_url = self.base_url + version["href"]
102
+ versions_info.append((version_number, download_url))
103
+
104
+ return versions_info
@@ -12,7 +12,7 @@ class APKMirror:
12
12
  Attributes:
13
13
  pkg_name (str): The package name of the APK to search for.
14
14
  base_url (str): The base URL of the APKMirror website.
15
- search_url (str): The URL used to search for APKs on APKMirror.
15
+ api_url (str): The base URL for the APKMirror API.
16
16
  headers (dict): The headers used for making HTTP requests.
17
17
  session (requests.Session): The session object used for making HTTP requests.
18
18
 
@@ -0,0 +1,124 @@
1
+ import re
2
+ from bs4 import BeautifulSoup
3
+ import requests
4
+
5
+
6
+ class AppTeka:
7
+ """
8
+ This class provides methods to search for an APK on AppTeka 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 AppTeka website.
17
+ search_url (str): The URL used to search for APKs on AppTeka.
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(version) -> None | tuple[str, str]:
23
+ Searches for an APK on AppTeka based on the package name and version.
24
+ Returns a tuple containing the APK name and download link if found, otherwise None.
25
+ """
26
+
27
+ def __init__(self, pkg_name: str):
28
+ self.pkg_name = pkg_name
29
+ self.base_url = "https://appteka.store"
30
+ self.search_url = self.base_url + "/list/?query="
31
+ self.headers = {
32
+ "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",
33
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
34
+ "cache-control": "no-cache",
35
+ "dnt": "1",
36
+ "connection": "keep-alive",
37
+ "pragma": "no-cache",
38
+ "referer": "https://appteka.store/list/",
39
+ "sec-ch-ua": '"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
40
+ "sec-ch-ua-mobile": "?0",
41
+ "sec-ch-ua-platform": '"Windows"',
42
+ "sec-fetch-dest": "document",
43
+ "sec-fetch-mode": "navigate",
44
+ "sec-fetch-site": "same-origin",
45
+ "sec-fetch-user": "?1",
46
+ "upgrade-insecure-requests": "1",
47
+ "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",
48
+ }
49
+ self.session = requests.Session()
50
+
51
+ def search_apk(self, version: str = None) -> None | tuple[str, str]:
52
+ """
53
+ Searches for the APK on AppTeka and returns the title and link if found.
54
+
55
+ Parameters:
56
+ version (str, optional): The version of the APK to search for.
57
+
58
+ Returns:
59
+ None: If no matching APK is found.
60
+ tuple[str, str]: A tuple containing the title and link of the matching APK if found.
61
+ """
62
+ pkg_name = self.pkg_name
63
+ url = self.search_url + pkg_name
64
+ response: requests.Response = self.session.get(url, headers=self.headers)
65
+ soup = BeautifulSoup(response.text, "html.parser")
66
+ search_results = soup.find("div", {"class": "list-group"})
67
+ if search_results:
68
+ apk_items = search_results.find_all(
69
+ "a",
70
+ {"class": "list-group-item list-group-item-action d-flex gap-3 py-3"},
71
+ )
72
+ if apk_items:
73
+ for apk_item in apk_items:
74
+ apk_link = self.base_url + apk_item["href"]
75
+ apk_title = apk_item.find(
76
+ "strong", {"class": "text-gray-dark"}
77
+ ).text.strip()
78
+ # Unfortunately, AppTeka does not provide a package name in the search results.
79
+ # So, we can't compare the package names here.
80
+ # We can instead do a workaround by doing a request to the apk_link and check the package name there.
81
+ new_url = apk_link
82
+ new_response: requests.Response = self.session.get(
83
+ new_url, headers=self.headers
84
+ )
85
+ new_soup = BeautifulSoup(new_response.text, "html.parser")
86
+ rows = new_soup.find_all("dl", {"class": "row"})
87
+ for row in rows:
88
+ dt_tags = row.find_all("dt")
89
+ dd_tags = row.find_all("dd")
90
+ for dt, dd in zip(dt_tags, dd_tags):
91
+ if dt.text.strip() == "Package":
92
+ package_name = dd.text.strip()
93
+ if package_name == pkg_name:
94
+ # Appteka also stores the list of all the versions on same page
95
+ # So, if the version param is given then we can check if the version is available or not
96
+ if version:
97
+ version_modal = new_soup.find(
98
+ "div", {"id": "versionsModal"}
99
+ )
100
+ if version_modal:
101
+ version_links = version_modal.find_all(
102
+ "a",
103
+ {
104
+ "class": re.compile(
105
+ "^list-group-item list-group-item-action*"
106
+ )
107
+ },
108
+ )
109
+ for link in version_links:
110
+ version_text = (
111
+ link.find("p", {"class": "m-1"})
112
+ .text.strip()
113
+ .split("\xa0")[0]
114
+ )
115
+ if version_text == version:
116
+ apk_link = (
117
+ self.base_url + link["href"]
118
+ )
119
+ return apk_title, apk_link
120
+ return apk_title, None
121
+ else:
122
+ return apk_title, apk_link
123
+
124
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apksearch
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Search for apks on varius websites
5
5
  Author-email: Abhi <allinoneallinone00@gmail.com>
6
6
  License: MIT License
@@ -129,6 +129,17 @@ if result:
129
129
  - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKMirror and returns the title and link if found.
130
130
  - **`find_version(self, apk_link: str, version: str) -> str`**: Finds and returns the download link for the given APK link and version.
131
131
 
132
+ #### `AppTeka`
133
+
134
+ - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
135
+ - **`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.
136
+
137
+ #### `APKCombo`
138
+
139
+ - **`__init__(self, pkg_name: str)`**: Initializes with the package name.
140
+ - **`search_apk(self) -> None | tuple[str, str]`**: Searches for the APK on APKCombo and returns the title and link if found.
141
+ - **`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.
142
+
132
143
  ### Testing
133
144
 
134
145
  The project includes tests for the `sites` classes. To run the tests, use the following command:
@@ -0,0 +1,14 @@
1
+ apksearch/__init__.py,sha256=7gSvDwU4DieNIkOHP3m8rnpFt_54XitPiWBzuZ-kkf4,205
2
+ apksearch/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
3
+ apksearch/cli.py,sha256=iQe1riJhBUjByyb1WBgbiSxN_WHvOLATwUZkxU4X0Gs,5079
4
+ apksearch/sites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ apksearch/sites/apkcombo.py,sha256=zhlbsD6I97QTWFRTR1aOzM7gGW9_--qwJ1kUHSp8_Ik,4641
6
+ apksearch/sites/apkmirror.py,sha256=IpS6r7ovM3g2dZrkR_a9zQpHy-jqrU2nJ3ykVkWxers,3127
7
+ apksearch/sites/apkpure.py,sha256=oUH-D1Xh04Bdb9eWQlX74B8OR3wm9lOEWqvvCC0-dUo,4785
8
+ apksearch/sites/appteka.py,sha256=in04JYF043n0fUvBW1QQeXnNbYekGUIh4qeame951nA,6372
9
+ apksearch-1.2.0.dist-info/LICENSE,sha256=Icu9iAY9cAaraq-HaAk8aWpXS1nE-3U_wfaOhN-HQUw,1061
10
+ apksearch-1.2.0.dist-info/METADATA,sha256=99vlfqVCjL8L2jB23uaaKCrMG7lhE8bgSyi64N9ynJs,6309
11
+ apksearch-1.2.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
12
+ apksearch-1.2.0.dist-info/entry_points.txt,sha256=FeAPgNPSU1tCwQNaKAmk6eQYbMHtsltcgeWSGUTxu0k,49
13
+ apksearch-1.2.0.dist-info/top_level.txt,sha256=VguZMODhlXWwDyJJNJms5syJ4EHmWSQOS45J9I5Cv5o,10
14
+ apksearch-1.2.0.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- apksearch/__init__.py,sha256=wcIkiu89CfORbGITlj8sEFcoyDeSfDR6mgtiKohwPUU,110
2
- apksearch/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
3
- apksearch/cli.py,sha256=tMKufR_O1sVRTliL1uKeEdLi2IxSgggTzqD70gmA-jU,2917
4
- apksearch/sites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- apksearch/sites/apkmirror.py,sha256=O-8tQyf7Zd53iK5JR6ZAwsA2MI96I9v15gVR6tReqJY,3140
6
- apksearch/sites/apkpure.py,sha256=oUH-D1Xh04Bdb9eWQlX74B8OR3wm9lOEWqvvCC0-dUo,4785
7
- apksearch-1.0.0.dist-info/LICENSE,sha256=Icu9iAY9cAaraq-HaAk8aWpXS1nE-3U_wfaOhN-HQUw,1061
8
- apksearch-1.0.0.dist-info/METADATA,sha256=7LovCj595LowiM8Be-q51-iS9fd8n_p2V8wRMzY0Ki4,5492
9
- apksearch-1.0.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
10
- apksearch-1.0.0.dist-info/entry_points.txt,sha256=FeAPgNPSU1tCwQNaKAmk6eQYbMHtsltcgeWSGUTxu0k,49
11
- apksearch-1.0.0.dist-info/top_level.txt,sha256=VguZMODhlXWwDyJJNJms5syJ4EHmWSQOS45J9I5Cv5o,10
12
- apksearch-1.0.0.dist-info/RECORD,,