fix : progress bar for macrecovery and wget, graceful fallback for zsync

This commit is contained in:
Yannick Mauray 2021-10-27 20:57:49 +02:00
parent 661880bdbf
commit 51b6d576a0
No known key found for this signature in database
GPG Key ID: 67C4AAC5E99CB909
8 changed files with 71 additions and 22 deletions

View File

@ -2,13 +2,16 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:quickgui/src/app.dart'; import 'package:quickgui/src/app.dart';
import 'package:quickgui/src/globals.dart';
import 'package:quickgui/src/model/operating_system.dart'; import 'package:quickgui/src/model/operating_system.dart';
import 'package:quickgui/src/model/option.dart';
import 'package:quickgui/src/model/version.dart'; import 'package:quickgui/src/model/version.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import 'package:window_size/window_size.dart'; import 'package:window_size/window_size.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
Directory.current = gCurrentDirectoy;
setWindowTitle('Quickgui : a flutter frontend for Quickget and Quickemu'); setWindowTitle('Quickgui : a flutter frontend for Quickget and Quickemu');
setWindowMinSize(const Size(692, 580)); setWindowMinSize(const Size(692, 580));
setWindowMaxSize(const Size(692, 580)); setWindowMaxSize(const Size(692, 580));
@ -44,7 +47,7 @@ Future<List<OperatingSystem>> loadOperatingSystems(bool showUbuntus) async {
currentVersion = Version(supportedVersion.item3); currentVersion = Version(supportedVersion.item3);
currentOperatingSystem!.versions.add(currentVersion!); currentOperatingSystem!.versions.add(currentVersion!);
} }
currentVersion!.options.add(supportedVersion.item4); currentVersion!.options.add(Option(supportedVersion.item4, supportedVersion.item5));
}); });
return output; return output;

6
lib/src/globals.dart Normal file
View File

@ -0,0 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart';
var gIsSnap = Platform.environment['SNAP']?.isNotEmpty ?? false;
var gCurrentDirectoy = Directory(Platform.environment['HOME'] ?? Directory.current.absolute.path);

View File

@ -0,0 +1,6 @@
class Option {
Option(this.option, this.downloader);
final String option;
final String downloader;
}

View File

@ -1,6 +1,8 @@
import 'package:quickgui/src/model/option.dart';
class Version { class Version {
Version(this.version) : options = []; Version(this.version) : options = [];
final String version; final String version;
final List<String> options; final List<Option> options;
} }

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:quickgui/src/model/operating_system.dart'; import 'package:quickgui/src/model/operating_system.dart';
import 'package:quickgui/src/model/option.dart';
import 'package:quickgui/src/model/version.dart'; import 'package:quickgui/src/model/version.dart';
class Downloader extends StatefulWidget { class Downloader extends StatefulWidget {
@ -16,14 +17,15 @@ class Downloader extends StatefulWidget {
final OperatingSystem operatingSystem; final OperatingSystem operatingSystem;
final Version version; final Version version;
final String? option; final Option? option;
@override @override
_DownloaderState createState() => _DownloaderState(); _DownloaderState createState() => _DownloaderState();
} }
class _DownloaderState extends State<Downloader> { class _DownloaderState extends State<Downloader> {
final pattern = RegExp("( [0-9.]+%)"); final wgetPattern = RegExp("( [0-9.]+%)");
final macRecoveryPattern = RegExp("([0-9]+\\.[0-9])");
late final Stream<double> _progressStream; late final Stream<double> _progressStream;
bool _downloadFinished = false; bool _downloadFinished = false;
var controller = StreamController<double>(); var controller = StreamController<double>();
@ -34,9 +36,8 @@ class _DownloaderState extends State<Downloader> {
super.initState(); super.initState();
} }
void parseProgress(String line) { void parseWgetProgress(String line) {
print(line); var matches = wgetPattern.allMatches(line).toList();
var matches = pattern.allMatches(line).toList();
if (matches.isNotEmpty) { if (matches.isNotEmpty) {
var percent = matches[0].group(1); var percent = matches[0].group(1);
if (percent != null) { if (percent != null) {
@ -46,14 +47,33 @@ class _DownloaderState extends State<Downloader> {
} }
} }
void parseMacRecoveryProgress(String line) {
var matches = macRecoveryPattern.allMatches(line).toList();
if (matches.isNotEmpty) {
var size = matches[0].group(1);
print(size);
if (size != null) {
var value = double.parse(size);
print(value);
controller.add(value);
}
}
}
Stream<double> progressStream() { Stream<double> progressStream() {
var options = [widget.operatingSystem.code, widget.version.version]; var options = [widget.operatingSystem.code, widget.version.version];
if (widget.option != null) { if (widget.option != null) {
options.add(widget.option!); options.add(widget.option!.option);
} }
Process.start('quickget', options).then((process) { Process.start('quickget', options).then((process) {
process.stdout.transform(utf8.decoder).forEach(parseProgress); if (widget.option!.downloader == 'wget') {
process.stderr.transform(utf8.decoder).forEach(parseProgress); process.stderr.transform(utf8.decoder).forEach(parseWgetProgress);
} else if (widget.option!.downloader == 'zsync') {
controller.add(-1);
} else if (widget.option!.downloader == 'macrecovery') {
process.stdout.transform(utf8.decoder).forEach(parseMacRecoveryProgress);
}
process.exitCode.then((value) { process.exitCode.then((value) {
print("Process exited with exit code $value"); print("Process exited with exit code $value");
controller.close(); controller.close();
@ -69,7 +89,8 @@ class _DownloaderState extends State<Downloader> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Downloading ${widget.operatingSystem.name} ${widget.version.version}' + (widget.option != null ? ' (${widget.option})' : '')), title: Text(
'Downloading ${widget.operatingSystem.name} ${widget.version.version}' + (widget.option!.option.isNotEmpty ? ' (${widget.option!.option})' : '')),
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
), ),
body: Column( body: Column(
@ -78,6 +99,7 @@ class _DownloaderState extends State<Downloader> {
child: StreamBuilder( child: StreamBuilder(
stream: _progressStream, stream: _progressStream,
builder: (context, AsyncSnapshot<double> snapshot) { builder: (context, AsyncSnapshot<double> snapshot) {
var data = !snapshot.hasData || widget.option!.downloader != 'wget' ? null : snapshot.data;
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -86,7 +108,11 @@ class _DownloaderState extends State<Downloader> {
child: _downloadFinished child: _downloadFinished
? const Text('Download finished.') ? const Text('Download finished.')
: snapshot.hasData : snapshot.hasData
? Text('Downloading...${(snapshot.data! * 100).toInt()}%') ? widget.option!.downloader != 'zsync'
? widget.option!.downloader == 'wget'
? Text('Downloading...${(snapshot.data! * 100).toInt()}%')
: Text('${snapshot.data} Mbs downloaded')
: const Text("Downloading (no progress available)...")
: const Text('Waiting for download to start'), : const Text('Waiting for download to start'),
), ),
Padding( Padding(
@ -97,11 +123,15 @@ class _DownloaderState extends State<Downloader> {
value: _downloadFinished value: _downloadFinished
? 1 ? 1
: snapshot.hasData : snapshot.hasData
? snapshot.data! ? data
: null, : null,
), ),
), ),
), ),
Padding(
padding: const EdgeInsets.only(top: 32),
child: Text("Target folder : ${Directory.current}"),
),
], ],
); );
}, },

View File

@ -22,7 +22,7 @@ class _OptionSelectionState extends State<OptionSelection> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var list = widget.version.options.where((e) => e.toLowerCase().contains(term.toLowerCase())).toList(); var list = widget.version.options.where((e) => e.option.toLowerCase().contains(term.toLowerCase())).toList();
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@ -73,7 +73,7 @@ class _OptionSelectionState extends State<OptionSelection> {
var item = list[index]; var item = list[index];
return Card( return Card(
child: ListTile( child: ListTile(
title: Text(item), title: Text(item.option),
onTap: () { onTap: () {
Navigator.of(context).pop(item); Navigator.of(context).pop(item);
}, },

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:quickgui/src/model/operating_system.dart'; import 'package:quickgui/src/model/operating_system.dart';
import 'package:quickgui/src/model/option.dart';
import 'package:quickgui/src/model/version.dart'; import 'package:quickgui/src/model/version.dart';
import 'package:quickgui/src/pages/option_selection.dart'; import 'package:quickgui/src/pages/option_selection.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -34,15 +35,15 @@ class _VersionSelectionState extends State<VersionSelection> {
onTap: () { onTap: () {
if (widget.operatingSystem.versions[index].options.length > 1) { if (widget.operatingSystem.versions[index].options.length > 1) {
Navigator.of(context) Navigator.of(context)
.push<String>( .push<Option>(
MaterialPageRoute(fullscreenDialog: true, builder: (context) => OptionSelection(widget.operatingSystem.versions[index]))) MaterialPageRoute(fullscreenDialog: true, builder: (context) => OptionSelection(widget.operatingSystem.versions[index])))
.then((selection) { .then((selection) {
if (selection != null) { if (selection != null) {
Navigator.of(context).pop(Tuple2<Version, String?>(item, selection)); Navigator.of(context).pop(Tuple2<Version, Option?>(item, selection));
} }
}); });
} else { } else {
Navigator.of(context).pop(Tuple2<Version, String?>(item, null)); Navigator.of(context).pop(Tuple2<Version, Option?>(item, widget.operatingSystem.versions[index].options[0]));
} }
}, },
), ),

View File

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:quickgui/src/model/operating_system.dart'; import 'package:quickgui/src/model/operating_system.dart';
import 'package:quickgui/src/model/option.dart';
import 'package:quickgui/src/model/version.dart'; import 'package:quickgui/src/model/version.dart';
import 'package:quickgui/src/pages/downloader.dart'; import 'package:quickgui/src/pages/downloader.dart';
import 'package:quickgui/src/pages/operating_system_selection.dart'; import 'package:quickgui/src/pages/operating_system_selection.dart';
@ -20,13 +21,13 @@ class HomePageButtons extends StatefulWidget {
class _HomePageButtonsState extends State<HomePageButtons> { class _HomePageButtonsState extends State<HomePageButtons> {
OperatingSystem? _selectedOperatingSystem; OperatingSystem? _selectedOperatingSystem;
Version? _selectedVersion; Version? _selectedVersion;
String? _selectedOption; Option? _selectedOption;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var _versionButtonLabel = _selectedVersion?.version ?? 'Select...'; var _versionButtonLabel = _selectedVersion?.version ?? 'Select...';
if (_selectedOption != null) { if (_selectedOption?.option.isNotEmpty ?? false) {
_versionButtonLabel = "$_versionButtonLabel ($_selectedOption)"; _versionButtonLabel = "$_versionButtonLabel (${_selectedOption!.option})";
} }
return Row( return Row(
children: [ children: [
@ -53,7 +54,7 @@ class _HomePageButtonsState extends State<HomePageButtons> {
onPressed: (_selectedOperatingSystem != null) onPressed: (_selectedOperatingSystem != null)
? () { ? () {
Navigator.of(context) Navigator.of(context)
.push<Tuple2<Version, String?>>(MaterialPageRoute( .push<Tuple2<Version, Option?>>(MaterialPageRoute(
fullscreenDialog: true, fullscreenDialog: true,
builder: (context) => VersionSelection(operatingSystem: _selectedOperatingSystem!), builder: (context) => VersionSelection(operatingSystem: _selectedOperatingSystem!),
)) ))