mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-16 14:46:44 +02:00
Still no auto retry for rate-limit. Instead, rate-limit errors are ignored and the unchecked Apps have to wait until the next cycle. Even this needs more testing before release.
128 lines
4.0 KiB
Dart
128 lines
4.0 KiB
Dart
import 'dart:convert';
|
|
import 'package:http/http.dart';
|
|
import 'package:obtainium/components/generated_form.dart';
|
|
import 'package:obtainium/custom_errors.dart';
|
|
import 'package:obtainium/providers/source_provider.dart';
|
|
|
|
class GitHub implements AppSource {
|
|
@override
|
|
late String host = 'github.com';
|
|
|
|
@override
|
|
String standardizeURL(String url) {
|
|
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
|
if (match == null) {
|
|
throw notValidURL(runtimeType.toString());
|
|
}
|
|
return url.substring(0, match.end);
|
|
}
|
|
|
|
@override
|
|
Future<APKDetails> getLatestAPKDetails(
|
|
String standardUrl, List<String> additionalData) async {
|
|
var includePrereleases =
|
|
additionalData.isNotEmpty && additionalData[0] == 'true';
|
|
var fallbackToOlderReleases =
|
|
additionalData.length >= 2 && additionalData[1] == 'true';
|
|
var regexFilter = additionalData.length >= 3 && additionalData[2].isNotEmpty
|
|
? additionalData[2]
|
|
: null;
|
|
Response res = await get(Uri.parse(
|
|
'https://api.$host/repos${standardUrl.substring('https://$host'.length)}/releases'));
|
|
if (res.statusCode == 200) {
|
|
var releases = jsonDecode(res.body) as List<dynamic>;
|
|
|
|
List<String> getReleaseAPKUrls(dynamic release) =>
|
|
(release['assets'] as List<dynamic>?)
|
|
?.map((e) {
|
|
return e['browser_download_url'] != null
|
|
? e['browser_download_url'] as String
|
|
: '';
|
|
})
|
|
.where((element) => element.toLowerCase().endsWith('.apk'))
|
|
.toList() ??
|
|
[];
|
|
|
|
dynamic targetRelease;
|
|
|
|
for (int i = 0; i < releases.length; i++) {
|
|
if (!fallbackToOlderReleases && i > 0) break;
|
|
if (!includePrereleases && releases[i]['prerelease'] == true) {
|
|
continue;
|
|
}
|
|
if (regexFilter != null &&
|
|
!RegExp(regexFilter)
|
|
.hasMatch((releases[i]['name'] as String).trim())) {
|
|
continue;
|
|
}
|
|
var apkUrls = getReleaseAPKUrls(releases[i]);
|
|
if (apkUrls.isEmpty) {
|
|
continue;
|
|
}
|
|
targetRelease = releases[i];
|
|
targetRelease['apkUrls'] = apkUrls;
|
|
break;
|
|
}
|
|
if (targetRelease == null) {
|
|
throw couldNotFindReleases;
|
|
}
|
|
if ((targetRelease['apkUrls'] as List<String>).isEmpty) {
|
|
throw noAPKFound;
|
|
}
|
|
String? version = targetRelease['tag_name'];
|
|
if (version == null) {
|
|
throw couldNotFindLatestVersion;
|
|
}
|
|
return APKDetails(version, targetRelease['apkUrls']);
|
|
} else {
|
|
if (res.headers['x-ratelimit-remaining'] == '0') {
|
|
throw RateLimitError(
|
|
(int.parse(res.headers['x-ratelimit-reset'] ?? '1800000000') /
|
|
60000000)
|
|
.round());
|
|
}
|
|
|
|
throw couldNotFindReleases;
|
|
}
|
|
}
|
|
|
|
@override
|
|
AppNames getAppNames(String standardUrl) {
|
|
String temp = standardUrl.substring(standardUrl.indexOf('://') + 3);
|
|
List<String> names = temp.substring(temp.indexOf('/') + 1).split('/');
|
|
return AppNames(names[0], names[1]);
|
|
}
|
|
|
|
@override
|
|
List<List<GeneratedFormItem>> additionalDataFormItems = [
|
|
[GeneratedFormItem(label: 'Include prereleases', type: FormItemType.bool)],
|
|
[
|
|
GeneratedFormItem(
|
|
label: 'Fallback to older releases', type: FormItemType.bool)
|
|
],
|
|
[
|
|
GeneratedFormItem(
|
|
label: 'Filter Release Titles by Regular Expression',
|
|
type: FormItemType.string,
|
|
required: false,
|
|
additionalValidators: [
|
|
(value) {
|
|
if (value == null || value.isEmpty) {
|
|
return null;
|
|
}
|
|
try {
|
|
RegExp(value);
|
|
} catch (e) {
|
|
return 'Invalid regular expression';
|
|
}
|
|
return null;
|
|
}
|
|
])
|
|
]
|
|
];
|
|
|
|
@override
|
|
List<String> additionalDataDefaults = ['true', 'true', ''];
|
|
}
|