mirror of
https://github.com/ImranR98/Obtainium.git
synced 2026-08-03 02:51:21 -04:00
HIGH severity fixes: - Fix indexOf -> startsWith for URL scheme detection to prevent corrupting non-http URLs (source_provider.dart) - Fix OOM risk: use XFile(path) instead of readAsBytes() for large APKs (apps_provider_install.dart) - Fix GeneratedFormSwitch.ensureType returning String instead of bool, causing runtime _CastError (generated_form_model.dart) - Fix initForm() called during build() triggering parent setState by moving init to didUpdateWidget + postFrameCallback (generated_form_renderer.dart) - Fix CI sed no-op: update regex to match actual signingConfig pattern in build.gradle.kts (release.yml) - Fix build.sh auto git push on no-args: require explicit arg to sync+build - Pin all git dependencies to commit SHAs instead of mutable branches MEDIUM severity fixes: - Sequence background service start/stop calls to prevent races (main.dart) - Move loadSystemFont() from build() to init to prevent visible font flash - Prevent concurrent bgUpdateCheck calls in onRepeatEvent with guard flag - Extend download retry to SocketException and TimeoutException - Cap install permission loop at 10 attempts to prevent infinite hang - Remove dead unused headers variable causing double bcrypt hash (coolapk.dart) - Remove HTTP 304 from redirect status check (huaweiappgallery.dart) - Guard result.files.single against empty file picker results (import_export.dart) - Add error logging to linkFn catch block (add_app.dart) - Disable continue button when no download URL available (app_detail_widgets.dart) - Fix DEVELOPER_GUIDE.md: list all git-pinned deps, correct android_package_manager LOW severity fixes: - Use 'final' instead of 'late' for FDroid field (izzyondroid.dart) - Use stable 'name' instead of runtime-type sourceIdentifier (neutroncode.dart) - Fix typo: finalUrlKey -> urlDataKey (uptodown.dart) - Fix aptoide constructor field order for consistency - Remove dead app?.app == null condition (app.dart) - Reuse SourceProvider instance instead of throwaway (apps.dart) - Support descending sort for 'as added' column (app_list_tile.dart) - Remove SDK >= 31 guard causing layout shift for material-you option (settings.dart) - Secure standardize.mjs: atomic write-then-rename, Object.hasOwn, error handling - Add flutter_launcher_icons to dev_dependencies
113 lines
3.4 KiB
Dart
113 lines
3.4 KiB
Dart
import 'package:html/parser.dart';
|
|
import 'package:http/http.dart';
|
|
import 'package:obtainium/custom_errors.dart';
|
|
import 'package:obtainium/providers/source_provider.dart';
|
|
|
|
class NeutronCode extends AppSource {
|
|
NeutronCode() {
|
|
name = 'NeutronCode';
|
|
hosts = ['neutroncode.com'];
|
|
showReleaseDateAsVersionToggle = true;
|
|
changeLogPageIsStandardUrl = true;
|
|
}
|
|
|
|
@override
|
|
String sourceSpecificStandardizeURL(
|
|
String url, {
|
|
bool forSelection = false,
|
|
}) => standardizeUrlWithRegex(
|
|
url,
|
|
subdomainPrefix: r'(www\.)?',
|
|
pathPattern: r'/downloads/file/[^/]+',
|
|
);
|
|
|
|
static const _monthMap = {
|
|
'january': '01',
|
|
'february': '02',
|
|
'march': '03',
|
|
'april': '04',
|
|
'may': '05',
|
|
'june': '06',
|
|
'july': '07',
|
|
'august': '08',
|
|
'september': '09',
|
|
'october': '10',
|
|
'november': '11',
|
|
'december': '12',
|
|
};
|
|
|
|
String monthNameToNumberString(String s) =>
|
|
_monthMap[s.toLowerCase()] ??
|
|
(throw ArgumentError('Invalid month name: $s'));
|
|
|
|
String? formatDateForParsing(String dateString) {
|
|
final List<String> parts = dateString.split(' ');
|
|
if (parts.length != 3) {
|
|
return null;
|
|
}
|
|
final monthIdx = parts.indexWhere((s) => int.tryParse(s) == null);
|
|
if (monthIdx < 0) return null;
|
|
final month = monthNameToNumberString(parts[monthIdx]);
|
|
final numericParts = [
|
|
for (var i = 0; i < 3; i++)
|
|
if (i != monthIdx) int.tryParse(parts[i]),
|
|
];
|
|
if (numericParts.contains(null) || numericParts.length != 2) return null;
|
|
final a = numericParts[0]!, b = numericParts[1]!;
|
|
final year = a > 31 ? a : (b > 31 ? b : (a.toString().length == 4 ? a : b));
|
|
final day = a == year ? b : a;
|
|
return '$year-$month-${day.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
@override
|
|
Future<APKDetails> getLatestAPKDetails(
|
|
String standardUrl,
|
|
Map<String, dynamic> additionalSettings,
|
|
) async {
|
|
try {
|
|
final Response res = await sourceRequest(standardUrl, additionalSettings);
|
|
if (res.statusCode == 200) {
|
|
final http = parse(res.body);
|
|
final name = http.querySelector('.pd-title')?.innerHtml;
|
|
final filename = http
|
|
.querySelector('.pd-filename .pd-float')
|
|
?.innerHtml;
|
|
if (filename == null) {
|
|
throw NoReleasesError();
|
|
}
|
|
final version = http
|
|
.querySelector('.pd-version-txt')
|
|
?.nextElementSibling
|
|
?.innerHtml;
|
|
if (version == null || version.isEmpty) {
|
|
throw NoVersionError();
|
|
}
|
|
final String apkUrl = 'https://${hosts[0]}/download/$filename';
|
|
final dateStringOriginal = http
|
|
.querySelector('.pd-date-txt')
|
|
?.nextElementSibling
|
|
?.innerHtml;
|
|
final dateString = dateStringOriginal != null
|
|
? (formatDateForParsing(dateStringOriginal))
|
|
: null;
|
|
final changeLogElements = http.querySelectorAll('.pd-fdesc p');
|
|
return APKDetails(
|
|
version,
|
|
getApkUrlsFromUrls([apkUrl]),
|
|
AppNames(this.name, name ?? standardUrl.split('/').last),
|
|
releaseDate: dateString != null
|
|
? DateTime.tryParse(dateString)
|
|
: null,
|
|
changeLog: changeLogElements.isNotEmpty
|
|
? changeLogElements.last.innerHtml
|
|
: null,
|
|
);
|
|
} else {
|
|
throw getObtainiumHttpError(res);
|
|
}
|
|
} catch (e) {
|
|
rethrowOrWrapError(e);
|
|
}
|
|
}
|
|
}
|