mirror of
https://github.com/venera-app/venera.git
synced 2025-12-16 07:01:16 +00:00
Improve updating following and fix potential crash.
This commit is contained in:
@@ -759,6 +759,7 @@ class ComicSourceParser {
|
|||||||
LoadComicFunc? _parseLoadComicFunc() {
|
LoadComicFunc? _parseLoadComicFunc() {
|
||||||
return (id) async {
|
return (id) async {
|
||||||
try {
|
try {
|
||||||
|
print("Loading comic $id from source $_key. Stacktrace:\n${StackTrace.current}");
|
||||||
var res = await JsEngine().runCode("""
|
var res = await JsEngine().runCode("""
|
||||||
ComicSource.sources.$_key.comic.loadInfo(${jsonEncode(id)})
|
ComicSource.sources.$_key.comic.loadInfo(${jsonEncode(id)})
|
||||||
""");
|
""");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:venera/foundation/favorites.dart';
|
import 'package:venera/foundation/favorites.dart';
|
||||||
import 'package:venera/foundation/log.dart';
|
import 'package:venera/foundation/log.dart';
|
||||||
|
import 'package:venera/utils/channel.dart';
|
||||||
|
|
||||||
class ComicUpdateResult {
|
class ComicUpdateResult {
|
||||||
final bool updated;
|
final bool updated;
|
||||||
@@ -62,6 +63,7 @@ Future<ComicUpdateResult> updateComic(
|
|||||||
return ComicUpdateResult(updated, null);
|
return ComicUpdateResult(updated, null);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Log.error("Check Updates", e, s);
|
Log.error("Check Updates", e, s);
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
retries--;
|
retries--;
|
||||||
if (retries == 0) {
|
if (retries == 0) {
|
||||||
return ComicUpdateResult(false, e.toString());
|
return ComicUpdateResult(false, e.toString());
|
||||||
@@ -114,23 +116,50 @@ void updateFolderBase(
|
|||||||
current = 0;
|
current = 0;
|
||||||
stream.add(UpdateProgress(total, current, errors, updated));
|
stream.add(UpdateProgress(total, current, errors, updated));
|
||||||
|
|
||||||
var futures = <Future>[];
|
var channel = Channel<FavoriteItemWithUpdateInfo>(10);
|
||||||
for (var comic in comicsToUpdate) {
|
|
||||||
var future = updateComic(comic, folder).then((result) {
|
// Producer
|
||||||
current++;
|
() async {
|
||||||
if (result.updated) {
|
var c = 0;
|
||||||
updated++;
|
for (var comic in comicsToUpdate) {
|
||||||
|
await channel.push(comic);
|
||||||
|
c++;
|
||||||
|
// Throttle
|
||||||
|
if (c % 5 == 0) {
|
||||||
|
var delay = c % 100 + 1;
|
||||||
|
if (delay > 10) {
|
||||||
|
delay = 10;
|
||||||
|
}
|
||||||
|
await Future.delayed(Duration(seconds: delay));
|
||||||
}
|
}
|
||||||
if (result.errorMessage != null) {
|
}
|
||||||
errors++;
|
channel.close();
|
||||||
|
}();
|
||||||
|
|
||||||
|
// Consumers
|
||||||
|
var updateFutures = <Future>[];
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
var f = () async {
|
||||||
|
while (true) {
|
||||||
|
var comic = await channel.pop();
|
||||||
|
if (comic == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var result = await updateComic(comic, folder);
|
||||||
|
current++;
|
||||||
|
if (result.updated) {
|
||||||
|
updated++;
|
||||||
|
}
|
||||||
|
if (result.errorMessage != null) {
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
stream.add(UpdateProgress(total, current, errors, updated, comic, result.errorMessage));
|
||||||
}
|
}
|
||||||
stream.add(
|
}();
|
||||||
UpdateProgress(total, current, errors, updated, comic, result.errorMessage));
|
updateFutures.add(f);
|
||||||
});
|
|
||||||
futures.add(future);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Future.wait(futures);
|
await Future.wait(updateFutures);
|
||||||
|
|
||||||
if (updated > 0) {
|
if (updated > 0) {
|
||||||
LocalFavoritesManager().notifyChanges();
|
LocalFavoritesManager().notifyChanges();
|
||||||
|
|||||||
58
lib/utils/channel.dart
Normal file
58
lib/utils/channel.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
class Channel<T> {
|
||||||
|
final Queue<T> _queue;
|
||||||
|
|
||||||
|
final int size;
|
||||||
|
|
||||||
|
Channel(this.size) : _queue = Queue<T>();
|
||||||
|
|
||||||
|
Completer? _releaseCompleter;
|
||||||
|
|
||||||
|
Completer? _pushCompleter;
|
||||||
|
|
||||||
|
var currentSize = 0;
|
||||||
|
|
||||||
|
var isClosed = false;
|
||||||
|
|
||||||
|
Future<void> push(T item) async {
|
||||||
|
if (currentSize >= size) {
|
||||||
|
_releaseCompleter ??= Completer();
|
||||||
|
return _releaseCompleter!.future.then((_) {
|
||||||
|
if (isClosed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_queue.addLast(item);
|
||||||
|
currentSize++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_queue.addLast(item);
|
||||||
|
currentSize++;
|
||||||
|
_pushCompleter?.complete();
|
||||||
|
_pushCompleter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T?> pop() async {
|
||||||
|
while (_queue.isEmpty) {
|
||||||
|
if (isClosed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
_pushCompleter ??= Completer();
|
||||||
|
await _pushCompleter!.future;
|
||||||
|
}
|
||||||
|
var item = _queue.removeFirst();
|
||||||
|
currentSize--;
|
||||||
|
if (_releaseCompleter != null && currentSize < size) {
|
||||||
|
_releaseCompleter!.complete();
|
||||||
|
_releaseCompleter = null;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
isClosed = true;
|
||||||
|
_pushCompleter?.complete();
|
||||||
|
_releaseCompleter?.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
115
test/channel_test.dart
Normal file
115
test/channel_test.dart
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:venera/utils/channel.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test("1-1-1", () async {
|
||||||
|
var channel = Channel<int>(1);
|
||||||
|
await channel.push(1);
|
||||||
|
var item = await channel.pop();
|
||||||
|
expect(item, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("1-3-1", () async {
|
||||||
|
var channel = Channel<int>(1);
|
||||||
|
|
||||||
|
// producer
|
||||||
|
() async {
|
||||||
|
await channel.push(1);
|
||||||
|
}();
|
||||||
|
() async {
|
||||||
|
await channel.push(2);
|
||||||
|
}();
|
||||||
|
() async {
|
||||||
|
await channel.push(3);
|
||||||
|
}();
|
||||||
|
|
||||||
|
// consumer
|
||||||
|
var results = <int>[];
|
||||||
|
for (var i = 0; i < 3; i++) {
|
||||||
|
var item = await channel.pop();
|
||||||
|
if (item != null) {
|
||||||
|
results.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(results.length, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("2-3-1", () async {
|
||||||
|
var channel = Channel<int>(2);
|
||||||
|
|
||||||
|
// producer
|
||||||
|
() async {
|
||||||
|
await channel.push(1);
|
||||||
|
}();
|
||||||
|
() async {
|
||||||
|
await channel.push(2);
|
||||||
|
}();
|
||||||
|
() async {
|
||||||
|
await channel.push(3);
|
||||||
|
}();
|
||||||
|
|
||||||
|
// consumer
|
||||||
|
var results = <int>[];
|
||||||
|
for (var i = 0; i < 3; i++) {
|
||||||
|
var item = await channel.pop();
|
||||||
|
if (item != null) {
|
||||||
|
results.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(results.length, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("1-1-3", () async {
|
||||||
|
var channel = Channel<int>(1);
|
||||||
|
|
||||||
|
// producer
|
||||||
|
() async {
|
||||||
|
print("push 1");
|
||||||
|
await channel.push(1);
|
||||||
|
print("push 2");
|
||||||
|
await channel.push(2);
|
||||||
|
print("push 3");
|
||||||
|
await channel.push(3);
|
||||||
|
print("push done");
|
||||||
|
channel.close();
|
||||||
|
}();
|
||||||
|
|
||||||
|
// consumer
|
||||||
|
var consumers = <Future>[];
|
||||||
|
var results = <int>[];
|
||||||
|
for (var i = 0; i < 3; i++) {
|
||||||
|
consumers.add(() async {
|
||||||
|
while (true) {
|
||||||
|
var item = await channel.pop();
|
||||||
|
if (item == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print("pop $item");
|
||||||
|
results.add(item);
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
await Future.wait(consumers);
|
||||||
|
expect(results.length, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("close", () async {
|
||||||
|
var channel = Channel<int>(2);
|
||||||
|
|
||||||
|
// producer
|
||||||
|
() async {
|
||||||
|
await channel.push(1);
|
||||||
|
await channel.push(2);
|
||||||
|
await channel.push(3);
|
||||||
|
channel.close();
|
||||||
|
}();
|
||||||
|
|
||||||
|
// consumer
|
||||||
|
await channel.pop();
|
||||||
|
await channel.pop();
|
||||||
|
await channel.pop();
|
||||||
|
var item4 = await channel.pop();
|
||||||
|
expect(item4, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user