Prepare Lunaris 1.1.2 experimental
This commit is contained in:
@@ -29,6 +29,25 @@ class DownloadManagerService {
|
||||
return task;
|
||||
}
|
||||
|
||||
Future<DownloadTask> startUrl({
|
||||
required String url,
|
||||
required String fileName,
|
||||
bool openAfterDownload = false,
|
||||
}) async {
|
||||
final id = 'url:$url:${DateTime.now().microsecondsSinceEpoch}';
|
||||
final task = DownloadTask(
|
||||
id: id,
|
||||
sourceUrl: url,
|
||||
fileName: fileName,
|
||||
progress: 0,
|
||||
status: DownloadTaskStatus.queued,
|
||||
openAfterDownload: openAfterDownload,
|
||||
);
|
||||
_set(task);
|
||||
unawaited(_run(task));
|
||||
return task;
|
||||
}
|
||||
|
||||
Future<void> retry(String taskId) async {
|
||||
final existing = _tasks[taskId];
|
||||
if (existing == null) return;
|
||||
@@ -51,18 +70,22 @@ class DownloadManagerService {
|
||||
if (_tasks[task.id]?.status == DownloadTaskStatus.canceled) return;
|
||||
_set(task.copyWith(status: DownloadTaskStatus.running));
|
||||
try {
|
||||
final saved = await _downloadService.downloadPost(
|
||||
task.post,
|
||||
onProgress: (received, total) {
|
||||
if (total <= 0) return;
|
||||
final current = _tasks[task.id];
|
||||
if (current == null ||
|
||||
current.status == DownloadTaskStatus.canceled) {
|
||||
return;
|
||||
}
|
||||
_set(current.copyWith(progress: received / total));
|
||||
},
|
||||
);
|
||||
final saved = task.sourceUrl == null
|
||||
? await _downloadService.downloadPost(
|
||||
task.post!,
|
||||
onProgress: (received, total) {
|
||||
_updateProgress(task.id, received, total);
|
||||
},
|
||||
)
|
||||
: await _downloadService.downloadUrl(
|
||||
task.sourceUrl!,
|
||||
fileName: task.fileName,
|
||||
mimeType: _mimeType(task.fileName),
|
||||
openAfterDownload: task.openAfterDownload,
|
||||
onProgress: (received, total) {
|
||||
_updateProgress(task.id, received, total);
|
||||
},
|
||||
);
|
||||
final current = _tasks[task.id];
|
||||
if (current == null || current.status == DownloadTaskStatus.canceled) {
|
||||
return;
|
||||
@@ -94,6 +117,15 @@ class DownloadManagerService {
|
||||
_controller.add(tasks);
|
||||
}
|
||||
|
||||
void _updateProgress(String taskId, int received, int total) {
|
||||
if (total <= 0) return;
|
||||
final current = _tasks[taskId];
|
||||
if (current == null || current.status == DownloadTaskStatus.canceled) {
|
||||
return;
|
||||
}
|
||||
_set(current.copyWith(progress: received / total));
|
||||
}
|
||||
|
||||
void _scheduleAutoRemove(String taskId, Duration delay) {
|
||||
Future<void>.delayed(delay, () {
|
||||
final current = _tasks[taskId];
|
||||
@@ -111,4 +143,18 @@ class DownloadManagerService {
|
||||
if (last.contains('.')) return last;
|
||||
return '${post.providerId}_${post.id}';
|
||||
}
|
||||
|
||||
String _mimeType(String fileName) {
|
||||
final lower = fileName.toLowerCase();
|
||||
if (lower.endsWith('.apk')) {
|
||||
return 'application/vnd.android.package-archive';
|
||||
}
|
||||
if (lower.endsWith('.exe')) {
|
||||
return 'application/vnd.microsoft.portable-executable';
|
||||
}
|
||||
if (lower.endsWith('.zip')) {
|
||||
return 'application/zip';
|
||||
}
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,54 @@ class DownloadService {
|
||||
return location.path;
|
||||
}
|
||||
|
||||
Future<String?> downloadUrl(
|
||||
String url, {
|
||||
required String fileName,
|
||||
String mimeType = 'application/octet-stream',
|
||||
bool openAfterDownload = false,
|
||||
void Function(int received, int total)? onProgress,
|
||||
}) async {
|
||||
if (Platform.isAndroid) {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final tempPath = p.join(tempDir.path, fileName);
|
||||
await _dio.download(url, tempPath, onReceiveProgress: onProgress);
|
||||
if (openAfterDownload) {
|
||||
await openFile(tempPath, mimeType: mimeType);
|
||||
return tempPath;
|
||||
}
|
||||
final saved = await _channel.invokeMethod<String>('saveToDownloads', {
|
||||
'path': tempPath,
|
||||
'fileName': fileName,
|
||||
'mimeType': mimeType,
|
||||
});
|
||||
return saved ?? fileName;
|
||||
}
|
||||
|
||||
final downloads =
|
||||
await getDownloadsDirectory() ?? await getTemporaryDirectory();
|
||||
final path = p.join(downloads.path, fileName);
|
||||
await _dio.download(url, path, onReceiveProgress: onProgress);
|
||||
if (openAfterDownload) await openFile(path, mimeType: mimeType);
|
||||
return path;
|
||||
}
|
||||
|
||||
Future<void> openFile(String path, {String? mimeType}) async {
|
||||
if (Platform.isAndroid) {
|
||||
await _channel.invokeMethod<void>('openFile', {
|
||||
'path': path,
|
||||
'mimeType': mimeType ?? _mimeType(path, ''),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
await Process.start('explorer.exe', [path]);
|
||||
return;
|
||||
}
|
||||
if (Platform.isLinux) {
|
||||
await Process.start('xdg-open', [path]);
|
||||
}
|
||||
}
|
||||
|
||||
String? _downloadUrl(Post post) {
|
||||
return MediaUrlSelector.download(post);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import '../../app/app_version.dart';
|
||||
import '../../core/errors/failure.dart';
|
||||
@@ -71,12 +72,14 @@ class UpdateService {
|
||||
final assets = (json['assets'] as List?) ?? const [];
|
||||
String? apkUrl;
|
||||
String? installerUrl;
|
||||
String? portableUrl;
|
||||
for (final item in assets.whereType<Map>()) {
|
||||
final name = (item['name'] ?? '').toString().toLowerCase();
|
||||
final url = (item['browser_download_url'] ?? '').toString();
|
||||
if (url.isEmpty) continue;
|
||||
if (name.endsWith('.apk')) apkUrl = url;
|
||||
if (name.endsWith('.exe')) installerUrl = url;
|
||||
if (name.endsWith('.zip')) portableUrl = url;
|
||||
}
|
||||
return AppUpdateInfo(
|
||||
version: _versionFromTag(tag),
|
||||
@@ -87,9 +90,30 @@ class UpdateService {
|
||||
publishedAt: DateTime.tryParse((json['published_at'] ?? '').toString()),
|
||||
apkUrl: apkUrl,
|
||||
windowsInstallerUrl: installerUrl,
|
||||
portableZipUrl: portableUrl,
|
||||
);
|
||||
}
|
||||
|
||||
String? assetUrlForCurrentPlatform(AppUpdateInfo info) {
|
||||
if (Platform.isAndroid) {
|
||||
return info.apkUrl;
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
return info.windowsInstallerUrl ?? info.portableZipUrl;
|
||||
}
|
||||
return info.portableZipUrl;
|
||||
}
|
||||
|
||||
String assetFileName(AppUpdateInfo info, String url) {
|
||||
final parsed = Uri.tryParse(url);
|
||||
final fromUrl =
|
||||
parsed?.pathSegments.isEmpty ?? true ? '' : parsed!.pathSegments.last;
|
||||
if (fromUrl.contains('.')) return fromUrl;
|
||||
if (Platform.isAndroid) return 'Lunaris-v${info.version}.apk';
|
||||
if (Platform.isWindows) return 'LunarisSetup-v${info.version}.exe';
|
||||
return 'LunarisPortable-v${info.version}.zip';
|
||||
}
|
||||
|
||||
static String _versionFromTag(String tag) {
|
||||
final match = RegExp(r'v?(\d+\.\d+\.\d+)').firstMatch(tag);
|
||||
return match?.group(1) ?? tag.replaceFirst(RegExp(r'^v'), '');
|
||||
|
||||
Reference in New Issue
Block a user