From 75a8dc9ee4612f58241e9b7bce2529a3878ecdab Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sat, 2 Sep 2023 21:28:30 -0400 Subject: [PATCH 1/2] Add Aptoide as a Source (#843) --- README.md | 1 + lib/app_sources/aptoide.dart | 104 +++++++++++++++++++++++++++++ lib/providers/source_provider.dart | 9 ++- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 lib/app_sources/aptoide.dart diff --git a/README.md b/README.md index 915d851..cf57007 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Currently supported App sources: - [Signal](https://signal.org/) - [SourceForge](https://sourceforge.net/) - [SourceHut](https://git.sr.ht/) +- [Aptoide](https://aptoide.com/) - [APKMirror](https://apkmirror.com/) (Track-Only) - [APKPure](https://apkpure.com/) - [Huawei AppGallery](https://appgallery.huawei.com/) diff --git a/lib/app_sources/aptoide.dart b/lib/app_sources/aptoide.dart new file mode 100644 index 0000000..0b7f3f4 --- /dev/null +++ b/lib/app_sources/aptoide.dart @@ -0,0 +1,104 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:html/parser.dart'; +import 'package:http/http.dart'; +import 'package:obtainium/custom_errors.dart'; +import 'package:obtainium/providers/source_provider.dart'; + +class Aptoide extends AppSource { + Aptoide() { + host = 'aptoide.com'; + name = tr('Aptoide'); + allowSubDomains = true; + } + + @override + String sourceSpecificStandardizeURL(String url) { + RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host'); + RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); + if (match == null) { + throw InvalidURLError(name); + } + return url.substring(0, match.end); + } + + @override + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { + return (await getLatestAPKDetails(standardUrl, additionalSettings)).version; + } + + @override + Future getLatestAPKDetails( + String standardUrl, + Map additionalSettings, + ) async { + var res = await sourceRequest(standardUrl); + if (res.statusCode != 200) { + throw getObtainiumHttpError(res); + } + var idMatch = RegExp('"app":{"id":[0-9]+').firstMatch(res.body); + String? id; + if (idMatch != null) { + id = res.body.substring(idMatch.start + 12, idMatch.end); + } else { + throw NoReleasesError(); + } + var res2 = + await sourceRequest('https://ws2.aptoide.com/api/7/getApp/app_id/$id'); + if (res2.statusCode != 200) { + throw getObtainiumHttpError(res); + } + var appDetails = jsonDecode(res2.body)?['nodes']?['meta']?['data']; + String appName = appDetails?['name'] ?? tr('app'); + String author = appDetails?['developer']?['name'] ?? name; + String? dateStr = appDetails?['updated']; + String? version = appDetails?['file']?['vername']; + String? apkUrl = appDetails?['file']?['path']; + if (version == null) { + throw NoVersionError(); + } + if (apkUrl == null) { + throw NoAPKError(); + } + DateTime? relDate; + if (dateStr != null) { + relDate = DateTime.parse(dateStr); + } + + return APKDetails( + version, getApkUrlsFromUrls([apkUrl]), AppNames(author, appName), + releaseDate: relDate); + } + + @override + Future>> search(String query, + {Map querySettings = const {}}) async { + Response res = await sourceRequest( + 'https://search.$host/?q=${Uri.encodeQueryComponent(query)}'); + if (res.statusCode == 200) { + Map> urlsWithDescriptions = {}; + parse(res.body).querySelectorAll('.package-header').forEach((e) { + String? url = e.attributes['href']; + if (url != null) { + try { + standardizeUrl(url); + } catch (e) { + url = null; + } + } + if (url != null) { + urlsWithDescriptions[url] = [ + e.querySelector('.package-name')?.text.trim() ?? '', + e.querySelector('.package-summary')?.text.trim() ?? + tr('noDescription') + ]; + } + }); + return urlsWithDescriptions; + } else { + throw getObtainiumHttpError(res); + } + } +} diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 5494362..c9433b7 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -9,6 +9,7 @@ import 'package:html/dom.dart'; import 'package:http/http.dart'; import 'package:obtainium/app_sources/apkmirror.dart'; import 'package:obtainium/app_sources/apkpure.dart'; +import 'package:obtainium/app_sources/aptoide.dart'; import 'package:obtainium/app_sources/codeberg.dart'; import 'package:obtainium/app_sources/fdroid.dart'; import 'package:obtainium/app_sources/fdroidrepo.dart'; @@ -325,6 +326,7 @@ abstract class AppSource { bool enforceTrackOnly = false; bool changeLogIfAnyIsMarkDown = true; bool appIdInferIsOptional = false; + bool allowSubDomains = false; AppSource() { name = runtimeType.toString(); @@ -522,13 +524,14 @@ class SourceProvider { Jenkins(), SourceForge(), SourceHut(), + Aptoide(), APKMirror(), APKPure(), HuaweiAppGallery(), // APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden) Mullvad(), Signal(), - VLC(), // As of 2023-08-26 this site randomly messes up the 'latest' version (one minute it's 3.5.4, next minute back to 3.5.3) + VLC(), // WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date TelegramApp(), SteamMobile(), @@ -554,7 +557,9 @@ class SourceProvider { } AppSource? source; for (var s in sources.where((element) => element.host != null)) { - if (RegExp('://${s.host}(/|\\z)?').hasMatch(url)) { + if (RegExp( + '://${s.allowSubDomains ? '([^\\.]+\\.)*' : ''}${s.host}(/|\\z)?') + .hasMatch(url)) { source = s; break; } From 52d19f267ed57f4b7ad825153823e7590406eed6 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 3 Sep 2023 00:14:23 -0400 Subject: [PATCH 2/2] Upgrade packages, increment version --- lib/main.dart | 2 +- pubspec.lock | 24 ++++++++++++------------ pubspec.yaml | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index fb3a711..b3fdbd7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; // ignore: implementation_imports import 'package:easy_localization/src/localization.dart'; -const String currentVersion = '0.14.6'; +const String currentVersion = '0.14.7'; const String currentReleaseTag = 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES diff --git a/pubspec.lock b/pubspec.lock index f6ddf37..c394bd0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -46,10 +46,10 @@ packages: dependency: transitive description: name: archive - sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e" url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.3.8" args: dependency: transitive description: @@ -206,10 +206,10 @@ packages: dependency: "direct main" description: name: easy_localization - sha256: "30ebf25448ffe169e0bd9bc4b5da94faa8398967a2ad2ca09f438be8b6953645" + sha256: de63e3b422adfc97f256cbb3f8cf12739b6a4993d390f3cadb3f51837afaefe5 url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" easy_logger: dependency: transitive description: @@ -783,18 +783,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "38d8e783681bc342e92dc9799cbe4421d08c4d210a67ee9d61d0f7310491a465" + sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" url: "https://pub.dev" source: hosted - version: "6.1.13" + version: "6.1.14" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: ef7e34951ffa963fb7a65928deeb38d40fb3c5975baf93c1d631341ff7f2650a + sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 url: "https://pub.dev" source: hosted - version: "6.0.39" + version: "6.1.0" url_launcher_ios: dependency: transitive description: @@ -823,10 +823,10 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "4d0dae953f80dc06fa24c58d0f8381302139c22c2dad301417787ad96f5f73bd" + sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" url_launcher_web: dependency: transitive description: @@ -879,10 +879,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: "8545719bbf06f5c4b850f0d5f86da1fe837b1953c56b9af579a26be73627c98d" + sha256: "0d8f5ac96a155e672129bf94c7abf625de01241d44d269dbaff083f1b4deb1aa" url: "https://pub.dev" source: hosted - version: "3.9.4" + version: "3.9.5" webview_flutter_platform_interface: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 61b3d06..13a2449 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.14.6+198 # When changing this, update the tag in main() accordingly +version: 0.14.7+199 # When changing this, update the tag in main() accordingly environment: sdk: '>=2.18.2 <3.0.0'