Infer GitHub App ID where possible

This commit is contained in:
Imran Remtulla
2023-05-27 20:36:29 -04:00
parent 4c6303f783
commit 833ece1ef5
6 changed files with 62 additions and 17 deletions

View File

@ -19,8 +19,8 @@ class APKCombo extends AppSource {
} }
@override @override
String? tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) { {Map<String, dynamic> additionalSettings = const {}}) async {
return Uri.parse(standardUrl).pathSegments.last; return Uri.parse(standardUrl).pathSegments.last;
} }
@ -83,7 +83,7 @@ class APKCombo extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String appId = tryInferringAppId(standardUrl)!; String appId = (await tryInferringAppId(standardUrl))!;
var preres = await sourceRequest(standardUrl); var preres = await sourceRequest(standardUrl);
if (preres.statusCode != 200) { if (preres.statusCode != 200) {
throw getObtainiumHttpError(preres); throw getObtainiumHttpError(preres);

View File

@ -24,8 +24,8 @@ class APKPure extends AppSource {
} }
@override @override
String? tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) { {Map<String, dynamic> additionalSettings = const {}}) async {
return Uri.parse(standardUrl).pathSegments.last; return Uri.parse(standardUrl).pathSegments.last;
} }
@ -34,7 +34,7 @@ class APKPure extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String appId = tryInferringAppId(standardUrl)!; String appId = (await tryInferringAppId(standardUrl))!;
String host = Uri.parse(standardUrl).host; String host = Uri.parse(standardUrl).host;
var res = await sourceRequest('$standardUrl/download'); var res = await sourceRequest('$standardUrl/download');
if (res.statusCode == 200) { if (res.statusCode == 200) {

View File

@ -31,8 +31,8 @@ class FDroid extends AppSource {
} }
@override @override
String? tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) { {Map<String, dynamic> additionalSettings = const {}}) async {
return Uri.parse(standardUrl).pathSegments.last; return Uri.parse(standardUrl).pathSegments.last;
} }
@ -63,7 +63,7 @@ class FDroid extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String? appId = tryInferringAppId(standardUrl); String? appId = await tryInferringAppId(standardUrl);
String host = Uri.parse(standardUrl).host; String host = Uri.parse(standardUrl).host;
return getAPKUrlsFromFDroidPackagesAPIResponse( return getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest('https://$host/api/v1/packages/$appId'), await sourceRequest('https://$host/api/v1/packages/$appId'),

View File

@ -6,6 +6,7 @@ import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/apps_provider.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/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
@ -79,6 +80,44 @@ class GitHub extends AppSource {
canSearch = true; canSearch = true;
} }
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> 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 @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+'); RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
@ -97,6 +136,12 @@ class GitHub extends AppSource {
return creds != null && creds.isNotEmpty ? '$creds@' : ''; return creds != null && creds.isNotEmpty ? '$creds@' : '';
} }
Future<String> getAPIHost() async =>
'https://${await getCredentialPrefixIfAny()}api.$host';
Future<String> convertStandardUrlToAPIUrl(String standardUrl) async =>
'${await getAPIHost()}/repos${standardUrl.substring('https://$host'.length)}';
@override @override
String? changeLogPageFromStandardUrl(String standardUrl) => String? changeLogPageFromStandardUrl(String standardUrl) =>
'$standardUrl/releases'; '$standardUrl/releases';
@ -239,7 +284,7 @@ class GitHub extends AppSource {
) async { ) async {
return await getLatestAPKDetailsCommon2(standardUrl, additionalSettings, return await getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
(bool useTagUrl) async { (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) { }, (Response res) {
rateLimitErrorCheck(res); rateLimitErrorCheck(res);
}); });
@ -281,7 +326,7 @@ class GitHub extends AppSource {
Future<Map<String, List<String>>> search(String query) async { Future<Map<String, List<String>>> search(String query) async {
return searchCommon( return searchCommon(
query, 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) { 'items', onHttpErrorCode: (Response res) {
rateLimitErrorCheck(res); rateLimitErrorCheck(res);
}); });

View File

@ -18,8 +18,8 @@ class IzzyOnDroid extends AppSource {
} }
@override @override
String? tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) { {Map<String, dynamic> additionalSettings = const {}}) async {
return FDroid().tryInferringAppId(standardUrl); return FDroid().tryInferringAppId(standardUrl);
} }
@ -28,7 +28,7 @@ class IzzyOnDroid extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
String? appId = tryInferringAppId(standardUrl); String? appId = await tryInferringAppId(standardUrl);
return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse( return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest( await sourceRequest(
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'), 'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),

View File

@ -434,8 +434,8 @@ abstract class AppSource {
throw NotImplementedError(); throw NotImplementedError();
} }
String? tryInferringAppId(String standardUrl, Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) { {Map<String, dynamic> additionalSettings = const {}}) async {
return null; return null;
} }
} }
@ -592,7 +592,7 @@ class SourceProvider {
: apk.names.name[0].toUpperCase() + apk.names.name.substring(1); : apk.names.name[0].toUpperCase() + apk.names.name.substring(1);
return App( return App(
currentApp?.id ?? currentApp?.id ??
source.tryInferringAppId(standardUrl, await source.tryInferringAppId(standardUrl,
additionalSettings: additionalSettings) ?? additionalSettings: additionalSettings) ??
generateTempID(standardUrl, additionalSettings), generateTempID(standardUrl, additionalSettings),
standardUrl, standardUrl,