From a916167be34fbc9e5273781a2bafd380bec8bbfe Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 25 Sep 2022 03:21:57 -0400 Subject: [PATCH] Added basic SourceForge support --- lib/app_sources/sourceforge.dart | 69 ++++++++++++++++++++++++++++++ lib/pages/add_app.dart | 66 ++++++++++++++-------------- lib/providers/source_provider.dart | 4 +- 3 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 lib/app_sources/sourceforge.dart diff --git a/lib/app_sources/sourceforge.dart b/lib/app_sources/sourceforge.dart new file mode 100644 index 00000000..6d63c7b5 --- /dev/null +++ b/lib/app_sources/sourceforge.dart @@ -0,0 +1,69 @@ +import 'package:html/parser.dart'; +import 'package:http/http.dart'; +import 'package:obtainium/app_sources/github.dart'; +import 'package:obtainium/components/generated_form.dart'; +import 'package:obtainium/providers/source_provider.dart'; + +class SourceForge implements AppSource { + @override + late String host = 'sourceforge.net'; + + @override + String standardizeURL(String url) { + RegExp standardUrlRegEx = RegExp('^https?://$host/projects/[^/]+'); + RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); + if (match == null) { + throw notValidURL(runtimeType.toString()); + } + return url.substring(0, match.end); + } + + @override + Future getLatestAPKDetails( + String standardUrl, List additionalData) async { + Response res = await get(Uri.parse('$standardUrl/rss?path=/')); + if (res.statusCode == 200) { + var parsedHtml = parse(res.body); + var allDownloadLinks = + parsedHtml.querySelectorAll('guid').map((e) => e.innerHtml).toList(); + getVersion(String url) { + try { + var tokens = url.split('/'); + return tokens[tokens.length - 3]; + } catch (e) { + return null; + } + } + + String? version = getVersion(allDownloadLinks[0]); + if (version == null) { + throw couldNotFindLatestVersion; + } + var apkUrlListAllReleases = allDownloadLinks + .where((element) => element.toLowerCase().endsWith('.apk/download')) + .toList(); + var apkUrlList = + apkUrlListAllReleases // This can be used skipped for fallback support later + .where((element) => getVersion(element) == version) + .toList(); + if (apkUrlList.isEmpty) { + throw noAPKFound; + } + return APKDetails(version, apkUrlList); + } else { + throw couldNotFindReleases; + } + } + + @override + AppNames getAppNames(String standardUrl) { + return AppNames(runtimeType.toString(), + standardUrl.substring(standardUrl.lastIndexOf('/') + 1)); + } + + @override + List> additionalDataFormItems = []; + + @override + List additionalDataDefaults = []; +} diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index b019439a..173eab4f 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -142,8 +142,7 @@ class _AddAppPageState extends State { child: const Text('Add')) ], ), - if (pickedSource != null && - (pickedSource!.additionalDataFormItems.isNotEmpty)) + if (pickedSource != null) Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -158,39 +157,38 @@ class _AddAppPageState extends State { const SizedBox( height: 16, ), - GeneratedForm( - items: pickedSource!.additionalDataFormItems, - onValueChanges: (values, valid) { - setState(() { - additionalData = values; - validAdditionalData = valid; - }); - }, - defaultValues: - pickedSource!.additionalDataDefaults), + if (pickedSource! + .additionalDataFormItems.isNotEmpty) + GeneratedForm( + items: pickedSource!.additionalDataFormItems, + onValueChanges: (values, valid) { + setState(() { + additionalData = values; + validAdditionalData = valid; + }); + }, + defaultValues: + pickedSource!.additionalDataDefaults), + if (pickedSource! + .additionalDataFormItems.isNotEmpty) + const SizedBox( + height: 8, + ), if (pickedSource != null) - Column( - crossAxisAlignment: - CrossAxisAlignment.stretch, - children: [ - const SizedBox( - height: 8, - ), - GeneratedForm( - items: [ - [ - GeneratedFormItem( - label: 'Custom App Name', - required: false) - ] - ], - onValueChanges: (values, valid) { - setState(() { - customName = values[0]; - }); - }, - defaultValues: [customName]) - ]), + GeneratedForm( + items: [ + [ + GeneratedFormItem( + label: 'Custom App Name', + required: false) + ] + ], + onValueChanges: (values, valid) { + setState(() { + customName = values[0]; + }); + }, + defaultValues: [customName]) ], ) else diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 856118e2..5da451f8 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -10,6 +10,7 @@ import 'package:obtainium/app_sources/gitlab.dart'; import 'package:obtainium/app_sources/izzyondroid.dart'; import 'package:obtainium/app_sources/mullvad.dart'; import 'package:obtainium/app_sources/signal.dart'; +import 'package:obtainium/app_sources/sourceforge.dart'; import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/mass_app_sources/githubstars.dart'; @@ -144,7 +145,8 @@ class SourceProvider { FDroid(), IzzyOnDroid(), Mullvad(), - Signal() + Signal(), + SourceForge() ]; // Add more mass source classes here so they are available via the service