From 294327bde460f75b185f3af31e06b75300cb6a40 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Tue, 13 Sep 2022 21:42:06 -0400 Subject: [PATCH] FIXED GITHUB ISSUE --- README.md | 2 +- lib/main.dart | 2 +- lib/providers/source_provider.dart | 99 +++++++++++++++++------------- pubspec.yaml | 2 +- 4 files changed, 60 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 1a0a52d..79b02ae 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Currently supported App sources: ## Limitations - App installs are assumed to have succeeded; failures and cancelled installs cannot be detected. - Auto (unattended) updates are unsupported due to a lack of any capable Flutter plugin. -- For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods are either unavailable (e.g. Mullvad), insufficient (e.g. GitHub RSS) or subject to rate limits (e.g. GitHub API). +- For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable. ## Screenshots diff --git a/lib/main.dart b/lib/main.dart index cce1ff8..654418c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,7 +12,7 @@ import 'package:dynamic_color/dynamic_color.dart'; import 'package:device_info_plus/device_info_plus.dart'; const String currentReleaseTag = - 'v0.1.8-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES + 'v0.1.9-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES @pragma('vm:entry-point') void bgTaskCallback() { diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 8e6fab2..640a5fb 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -71,6 +71,12 @@ escapeRegEx(String s) { }); } +const String couldNotFindReleases = 'Unable to fetch release info'; +const String couldNotFindLatestVersion = + 'Could not determine latest release version'; +const String notValidURL = 'Not a valid URL'; +const String noAPKFound = 'No APK found'; + List getLinksFromParsedHTML( Document dom, RegExp hrefPattern, String prependToLinks) => dom @@ -98,44 +104,53 @@ class GitHub implements AppSource { RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); if (match == null) { - throw 'Not a valid URL'; + throw notValidURL; } return url.substring(0, match.end); } @override Future getLatestAPKDetails(String standardUrl) async { - Response res = await get(Uri.parse('$standardUrl/releases/latest')); + Response res = await get(Uri.parse( + 'https://api.$host/repos${standardUrl.substring('https://$host'.length)}/releases')); if (res.statusCode == 200) { - var standardUri = Uri.parse(standardUrl); - var parsedHtml = parse(res.body); - var apkUrlList = getLinksFromParsedHTML( - parsedHtml, - RegExp( - '^${escapeRegEx(standardUri.path)}/releases/download/[^/]+/[^/]+\\.apk\$', - caseSensitive: false), - standardUri.origin); - if (apkUrlList.isEmpty) { - throw 'No APK found'; + var releases = jsonDecode(res.body) as List; + // Right now, the latest non-prerelease version is picked + // If none exists, the latest prerelease version is picked + // In the future, the user could be given a choice + var nonPrereleaseReleases = + releases.where((element) => element['prerelease'] != true).toList(); + var latestRelease = nonPrereleaseReleases.isNotEmpty + ? nonPrereleaseReleases[0] + : releases.isNotEmpty + ? releases[0] + : null; + if (latestRelease == null) { + throw couldNotFindReleases; } - String getTag(String url) { - List parts = url.split('/'); - return parts[parts.length - 2]; + List? assets = latestRelease['assets']; + List? apkUrlList = assets + ?.map((e) { + return e['browser_download_url'] != null + ? e['browser_download_url'] as String + : ''; + }) + .where((element) => element.toLowerCase().endsWith('.apk')) + .toList(); + if (apkUrlList == null || apkUrlList.isEmpty) { + throw noAPKFound; + } + String? version = latestRelease['tag_name']; + if (version == null) { + throw couldNotFindLatestVersion; + } + return APKDetails(version, apkUrlList); + } else { + if (res.headers['x-ratelimit-remaining'] == '0') { + throw 'Rate limit reached - try again in ${(int.parse(res.headers['x-ratelimit-reset'] ?? '1800000000') / 60000000).toString()} minutes'; } - String latestTag = getTag(apkUrlList[0]); - String? version = parsedHtml - .querySelector('.octicon-tag') - ?.nextElementSibling - ?.innerHtml - .trim(); - if (version == null) { - throw 'Could not determine latest release version'; - } - return APKDetails(version, - apkUrlList.where((element) => getTag(element) == latestTag).toList()); - } else { - throw 'Unable to fetch release info'; + throw couldNotFindReleases; } } @@ -156,7 +171,7 @@ class GitLab implements AppSource { RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); if (match == null) { - throw 'Not a valid URL'; + throw notValidURL; } return url.substring(0, match.end); } @@ -184,18 +199,18 @@ class GitLab implements AppSource { .toList() ]; if (apkUrlList.isEmpty) { - throw 'No APK found'; + throw noAPKFound; } var entryId = entry?.querySelector('id')?.innerHtml; var version = entryId == null ? null : Uri.parse(entryId).pathSegments.last; if (version == null) { - throw 'Could not determine latest release version'; + throw couldNotFindLatestVersion; } return APKDetails(version, apkUrlList); } else { - throw 'Unable to fetch release info'; + throw couldNotFindReleases; } } @@ -223,15 +238,15 @@ class Signal implements AppSource { var json = jsonDecode(res.body); String? apkUrl = json['url']; if (apkUrl == null) { - throw 'No APK found'; + throw noAPKFound; } String? version = json['versionName']; if (version == null) { - throw 'Could not determine latest release version'; + throw couldNotFindLatestVersion; } return APKDetails(version, [apkUrl]); } else { - throw 'Unable to fetch release info'; + throw couldNotFindReleases; } } @@ -248,7 +263,7 @@ class FDroid implements AppSource { RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/packages/[^/]+'); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); if (match == null) { - throw 'Not a valid URL'; + throw notValidURL; } return url.substring(0, match.end); } @@ -263,7 +278,7 @@ class FDroid implements AppSource { ?.querySelector('.package-version-download a') ?.attributes['href']; if (apkUrl == null) { - throw 'No APK found'; + throw noAPKFound; } var version = latestReleaseDiv ?.querySelector('.package-version-header b') @@ -271,11 +286,11 @@ class FDroid implements AppSource { .split(' ') .last; if (version == null) { - throw 'Could not determine latest release version'; + throw couldNotFindLatestVersion; } return APKDetails(version, [apkUrl]); } else { - throw 'Unable to fetch release info'; + throw couldNotFindReleases; } } @@ -295,7 +310,7 @@ class Mullvad implements AppSource { RegExp standardUrlRegEx = RegExp('^https?://$host'); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); if (match == null) { - throw 'Not a valid URL'; + throw notValidURL; } return url.substring(0, match.end); } @@ -311,12 +326,12 @@ class Mullvad implements AppSource { ?.split('/') .last; if (version == null) { - throw 'Could not determine the latest release version'; + throw couldNotFindLatestVersion; } return APKDetails( version, ['https://mullvad.net/download/app/apk/latest']); } else { - throw 'Unable to fetch release info'; + throw couldNotFindReleases; } } diff --git a/pubspec.yaml b/pubspec.yaml index 833f4f5..a703a16 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.1.8+9 # When changing this, update the tag in main() accordingly +version: 0.1.9+10 # When changing this, update the tag in main() accordingly environment: sdk: '>=2.19.0-79.0.dev <3.0.0'