From 988f9a6f9fc307ce67465b75fd094057b815ffde Mon Sep 17 00:00:00 2001
From: UjuiUjuMandan <125150101+UjuiUjuMandan@users.noreply.github.com>
Date: Thu, 13 Mar 2025 12:54:28 +0000
Subject: [PATCH 1/5] Add CoolApk
---
README.md | 1 +
assets/translations/en.json | 1 +
assets/translations/zh.json | 1 +
.../android/en-US/full_description.txt | 1 +
.../metadata/android/ru/full_description.txt | 1 +
lib/app_sources/coolapk.dart | 197 ++++++++++++++++++
lib/providers/source_provider.dart | 2 +
pubspec.lock | 8 +
pubspec.yaml | 1 +
9 files changed, 213 insertions(+)
create mode 100644 lib/app_sources/coolapk.dart
diff --git a/README.md b/README.md
index 36bfccc..829570d 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ Currently supported App sources:
- [Uptodown](https://uptodown.com/)
- [Huawei AppGallery](https://appgallery.huawei.com/)
- [Tencent App Store](https://sj.qq.com/)
+ - [CoolApk](https://coolapk.com/)
- [RuStore](https://rustore.ru/)
- Jenkins Jobs
- [APKMirror](https://apkmirror.com/) (Track-Only)
diff --git a/assets/translations/en.json b/assets/translations/en.json
index e745dfd..1642588 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -319,6 +319,7 @@
"stayOneVersionBehind": "Stay one version behind latest",
"refreshBeforeDownload": "Refresh app details before download",
"tencentAppStore": "Tencent App Store",
+ "coolApk": "CoolApk",
"name": "Name",
"smartname": "Name (Smart)",
"sortMethod": "Sort Method",
diff --git a/assets/translations/zh.json b/assets/translations/zh.json
index 40884ff..10e6c39 100644
--- a/assets/translations/zh.json
+++ b/assets/translations/zh.json
@@ -319,6 +319,7 @@
"stayOneVersionBehind": "比最新版本晚一个版本",
"refreshBeforeDownload": "下载前刷新应用程序详细信息",
"tencentAppStore": "腾讯应用宝",
+ "coolApk": "酷安",
"name": "名称",
"smartname": "姓名(智能)",
"sortMethod": "排序方法",
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index 90025e2..d620444 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -25,6 +25,7 @@
APKMirror (Track-Only)
Huawei AppGallery
Tencent App Store
+ CoolApk
Jenkins Jobs
RuStore
diff --git a/fastlane/metadata/android/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt
index e762156..7249abd 100644
--- a/fastlane/metadata/android/ru/full_description.txt
+++ b/fastlane/metadata/android/ru/full_description.txt
@@ -25,6 +25,7 @@
APKMirror (Track-Only)
Huawei AppGallery
Tencent App Store
+ CoolApk
Jenkins Jobs
RuStore
diff --git a/lib/app_sources/coolapk.dart b/lib/app_sources/coolapk.dart
new file mode 100644
index 0000000..976f7f2
--- /dev/null
+++ b/lib/app_sources/coolapk.dart
@@ -0,0 +1,197 @@
+import 'dart:convert';
+import 'package:bcrypt/bcrypt.dart';
+import 'package:crypto/crypto.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:obtainium/custom_errors.dart';
+import 'package:obtainium/providers/source_provider.dart';
+import 'dart:math';
+
+// kanged from https://github.com/DUpdateSystem/UpgradeAll/blob/b2f92c9/core-websdk/src/main/java/net/xzos/upgradeall/core/websdk/api/client_proxy/hubs/CoolApk.kt
+class CoolApk extends AppSource {
+ CoolApk() {
+ name = tr('coolApk');
+ hosts = ['www.coolapk.com', 'api2.coolapk.com'];
+ allowSubDomains = true;
+ naiveStandardVersionDetection = true;
+ }
+
+ @override
+ String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) {
+ print('Debug: Standardizing URL: $url');
+ RegExp standardUrlRegEx = RegExp(
+ r'^https?://(www\.)?coolapk\.com/apk/[^/]+',
+ caseSensitive: false);
+ var match = standardUrlRegEx.firstMatch(url);
+ if (match == null) {
+ print('Debug: Invalid URL for CoolApk: $url');
+ throw InvalidURLError(name);
+ }
+ String standardizedUrl = match.group(0)!;
+ print('Debug: Standardized URL: $standardizedUrl');
+ return standardizedUrl;
+ }
+
+ @override
+ Future tryInferringAppId(String standardUrl,
+ {Map additionalSettings = const {}}) async {
+ print('Debug: Inferring appId from URL: $standardUrl');
+ String appId = Uri.parse(standardUrl).pathSegments.last;
+ print('Debug: Inferred appId: $appId');
+ return appId;
+ }
+
+ @override
+ Future getLatestAPKDetails(
+ String standardUrl,
+ Map additionalSettings,
+ ) async {
+ String appId = (await tryInferringAppId(standardUrl))!;
+ String apiUrl = 'https://api2.coolapk.com';
+ print('Debug: Fetching details for appId: $appId');
+
+ // get latest
+ var detailUrl = '$apiUrl/v6/apk/detail?id=$appId';
+ var headers = await getRequestHeaders(additionalSettings);
+ print('Debug: Requesting URL: $detailUrl with headers: $headers');
+ var res = await sourceRequest(detailUrl, additionalSettings);
+
+ print('Debug: Response status code: ${res.statusCode}');
+ if (res.statusCode != 200) {
+ print('Debug: HTTP error: ${res.statusCode} - ${res.body}');
+ throw getObtainiumHttpError(res);
+ }
+
+ var json = jsonDecode(res.body);
+ print('Debug: Parsed JSON: $json');
+ if (json['status'] == -2 || json['data'] == null) {
+ print('Debug: No releases found or invalid status: ${json['status']}');
+ throw NoReleasesError();
+ }
+
+ var detail = json['data'];
+ String version = detail['apkversionname'].toString();
+ String appName = detail['title'].toString();
+ String author = detail['developername']?.toString() ?? 'CoolApk';
+ String changelog = detail['changelog']?.toString() ?? '';
+ int? releaseDate = detail['lastupdate'] != null
+ ? (detail['lastupdate'] is int
+ ? detail['lastupdate'] * 1000
+ : int.parse(detail['lastupdate'].toString()) * 1000)
+ : null;
+ String aid = detail['id'].toString();
+
+ print('Debug: Version: $version, AppName: $appName, Author: $author, AID: $aid');
+
+ // get apk url
+ String apkUrl = await _getLatestApkUrl(apiUrl, appId, aid, version, headers);
+ if (apkUrl.isEmpty) {
+ print('Debug: No APK URL found for $appId');
+ throw NoAPKError();
+ }
+ print('Debug: APK URL: $apkUrl');
+
+ String apkName = '${appId}_$version.apk';
+
+ return APKDetails(
+ version,
+ [MapEntry(apkName, apkUrl)],
+ AppNames(author, appName),
+ releaseDate: releaseDate != null
+ ? DateTime.fromMillisecondsSinceEpoch(releaseDate)
+ : null,
+ changeLog: changelog,
+ );
+ }
+
+ Future _getLatestApkUrl(String apiUrl, String appId, String aid,
+ String version, Map? headers) async {
+ String url = '$apiUrl/v6/apk/download?pn=$appId&aid=$aid';
+ print('Debug: Fetching APK URL: $url');
+ var res = await sourceRequest(url, {}, followRedirects: false);
+ print('Debug: APK request status code: ${res.statusCode}');
+ if (res.statusCode >= 300 && res.statusCode < 400) {
+ String location = res.headers['location'] ?? '';
+ print('Debug: Redirect location: $location');
+ return location;
+ }
+ print('Debug: No redirect found for APK URL');
+ return '';
+ }
+
+ @override
+ Future