diff --git a/lib/app_sources/apkcombo.dart b/lib/app_sources/apkcombo.dart index 16e21c5..c90b0cb 100644 --- a/lib/app_sources/apkcombo.dart +++ b/lib/app_sources/apkcombo.dart @@ -19,8 +19,8 @@ class APKCombo extends AppSource { } @override - String? tryInferringAppId(String standardUrl, - {Map additionalSettings = const {}}) { + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { return Uri.parse(standardUrl).pathSegments.last; } @@ -83,7 +83,7 @@ class APKCombo extends AppSource { String standardUrl, Map additionalSettings, ) async { - String appId = tryInferringAppId(standardUrl)!; + String appId = (await tryInferringAppId(standardUrl))!; var preres = await sourceRequest(standardUrl); if (preres.statusCode != 200) { throw getObtainiumHttpError(preres); diff --git a/lib/app_sources/apkpure.dart b/lib/app_sources/apkpure.dart index 20028a4..f3cef4e 100644 --- a/lib/app_sources/apkpure.dart +++ b/lib/app_sources/apkpure.dart @@ -24,8 +24,8 @@ class APKPure extends AppSource { } @override - String? tryInferringAppId(String standardUrl, - {Map additionalSettings = const {}}) { + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { return Uri.parse(standardUrl).pathSegments.last; } @@ -34,7 +34,7 @@ class APKPure extends AppSource { String standardUrl, Map additionalSettings, ) async { - String appId = tryInferringAppId(standardUrl)!; + String appId = (await tryInferringAppId(standardUrl))!; String host = Uri.parse(standardUrl).host; var res = await sourceRequest('$standardUrl/download'); if (res.statusCode == 200) { diff --git a/lib/app_sources/fdroid.dart b/lib/app_sources/fdroid.dart index 28d45cc..0602f6d 100644 --- a/lib/app_sources/fdroid.dart +++ b/lib/app_sources/fdroid.dart @@ -31,8 +31,8 @@ class FDroid extends AppSource { } @override - String? tryInferringAppId(String standardUrl, - {Map additionalSettings = const {}}) { + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { return Uri.parse(standardUrl).pathSegments.last; } @@ -63,7 +63,7 @@ class FDroid extends AppSource { String standardUrl, Map additionalSettings, ) async { - String? appId = tryInferringAppId(standardUrl); + String? appId = await tryInferringAppId(standardUrl); String host = Uri.parse(standardUrl).host; return getAPKUrlsFromFDroidPackagesAPIResponse( await sourceRequest('https://$host/api/v1/packages/$appId'), diff --git a/lib/app_sources/github.dart b/lib/app_sources/github.dart index a34bda2..0d1c6a9 100644 --- a/lib/app_sources/github.dart +++ b/lib/app_sources/github.dart @@ -6,6 +6,7 @@ import 'package:obtainium/app_sources/html.dart'; import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/providers/apps_provider.dart'; +import 'package:obtainium/providers/logs_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/source_provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; @@ -79,6 +80,44 @@ class GitHub extends AppSource { canSearch = true; } + @override + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { + const possibleBuildGradleLocations = [ + '/app/build.gradle', + 'android/app/build.gradle', + 'src/app/build.gradle' + ]; + for (var path in possibleBuildGradleLocations) { + try { + var res = await sourceRequest( + '${await convertStandardUrlToAPIUrl(standardUrl)}/contents/$path'); + if (res.statusCode == 200) { + try { + var body = jsonDecode(res.body); + var appId = utf8 + .decode(base64 + .decode(body['content'].toString().split('\n').join(''))) + .split('\n') + .map((e) => e.trim()) + .where((l) => l.startsWith('applicationId "')) + .first + .split('"')[1]; + if (appId.isNotEmpty) { + return appId; + } + } catch (err) { + LogsProvider().add( + 'Error parsing build.gradle from ${res.request!.url.toString()}: ${err.toString()}'); + } + } + } catch (err) { + // Ignore - ID will be extracted from the APK + } + } + return null; + } + @override String sourceSpecificStandardizeURL(String url) { RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); @@ -97,6 +136,12 @@ class GitHub extends AppSource { return creds != null && creds.isNotEmpty ? '$creds@' : ''; } + Future getAPIHost() async => + 'https://${await getCredentialPrefixIfAny()}api.$host'; + + Future convertStandardUrlToAPIUrl(String standardUrl) async => + '${await getAPIHost()}/repos${standardUrl.substring('https://$host'.length)}'; + @override String? changeLogPageFromStandardUrl(String standardUrl) => '$standardUrl/releases'; @@ -239,7 +284,7 @@ class GitHub extends AppSource { ) async { return await getLatestAPKDetailsCommon2(standardUrl, additionalSettings, (bool useTagUrl) async { - return 'https://${await getCredentialPrefixIfAny()}api.$host/repos${standardUrl.substring('https://$host'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100'; + return '${await convertStandardUrlToAPIUrl(standardUrl)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100'; }, (Response res) { rateLimitErrorCheck(res); }); @@ -281,7 +326,7 @@ class GitHub extends AppSource { Future>> search(String query) async { return searchCommon( query, - 'https://${await getCredentialPrefixIfAny()}api.$host/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100', + '${await getAPIHost()}/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100', 'items', onHttpErrorCode: (Response res) { rateLimitErrorCheck(res); }); diff --git a/lib/app_sources/izzyondroid.dart b/lib/app_sources/izzyondroid.dart index 1773558..d0fcfe8 100644 --- a/lib/app_sources/izzyondroid.dart +++ b/lib/app_sources/izzyondroid.dart @@ -18,8 +18,8 @@ class IzzyOnDroid extends AppSource { } @override - String? tryInferringAppId(String standardUrl, - {Map additionalSettings = const {}}) { + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { return FDroid().tryInferringAppId(standardUrl); } @@ -28,7 +28,7 @@ class IzzyOnDroid extends AppSource { String standardUrl, Map additionalSettings, ) async { - String? appId = tryInferringAppId(standardUrl); + String? appId = await tryInferringAppId(standardUrl); return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse( await sourceRequest( 'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'), diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index d980e3c..fc06a03 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -434,8 +434,8 @@ abstract class AppSource { throw NotImplementedError(); } - String? tryInferringAppId(String standardUrl, - {Map additionalSettings = const {}}) { + Future tryInferringAppId(String standardUrl, + {Map additionalSettings = const {}}) async { return null; } } @@ -592,7 +592,7 @@ class SourceProvider { : apk.names.name[0].toUpperCase() + apk.names.name.substring(1); return App( currentApp?.id ?? - source.tryInferringAppId(standardUrl, + await source.tryInferringAppId(standardUrl, additionalSettings: additionalSettings) ?? generateTempID(standardUrl, additionalSettings), standardUrl,