2020-04-17 17:25:25 +02:00
|
|
|
import 'dart:io';
|
|
|
|
|
import 'dart:isolate';
|
|
|
|
|
|
2019-12-02 21:05:49 +01:00
|
|
|
import 'package:flutter/widgets.dart';
|
2020-04-17 17:25:25 +02:00
|
|
|
import 'package:moor/isolate.dart';
|
|
|
|
|
import 'package:moor/moor.dart';
|
|
|
|
|
import 'package:moor_ffi/moor_ffi.dart';
|
2020-04-25 15:02:11 +02:00
|
|
|
import 'package:musicus_client/musicus_client.dart';
|
2020-04-24 22:41:52 +02:00
|
|
|
import 'package:musicus_database/musicus_database.dart';
|
2020-04-17 17:25:25 +02:00
|
|
|
import 'package:path/path.dart' as p;
|
|
|
|
|
import 'package:path_provider/path_provider.dart' as pp;
|
2019-12-02 21:05:49 +01:00
|
|
|
|
2020-04-18 23:41:08 +02:00
|
|
|
import 'music_library.dart';
|
2020-04-18 13:50:38 +02:00
|
|
|
import 'player.dart';
|
2020-05-01 17:48:23 +02:00
|
|
|
import 'settings.dart';
|
2019-12-03 12:44:24 +01:00
|
|
|
|
2020-04-17 17:25:25 +02:00
|
|
|
// The following code was taken from
|
|
|
|
|
// https://moor.simonbinder.eu/docs/advanced-features/isolates/ and just
|
|
|
|
|
// slightly modified.
|
|
|
|
|
|
|
|
|
|
Future<MoorIsolate> _createMoorIsolate() async {
|
|
|
|
|
// This method is called from the main isolate. Since we can't use
|
|
|
|
|
// getApplicationDocumentsDirectory on a background isolate, we calculate
|
|
|
|
|
// the database path in the foreground isolate and then inform the
|
|
|
|
|
// background isolate about the path.
|
|
|
|
|
final dir = await pp.getApplicationDocumentsDirectory();
|
|
|
|
|
final path = p.join(dir.path, 'db.sqlite');
|
|
|
|
|
final receivePort = ReceivePort();
|
|
|
|
|
|
|
|
|
|
await Isolate.spawn(
|
|
|
|
|
_startBackground,
|
|
|
|
|
_IsolateStartRequest(receivePort.sendPort, path),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// _startBackground will send the MoorIsolate to this ReceivePort.
|
|
|
|
|
return (await receivePort.first as MoorIsolate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _startBackground(_IsolateStartRequest request) {
|
|
|
|
|
// This is the entrypoint from the background isolate! Let's create
|
|
|
|
|
// the database from the path we received.
|
|
|
|
|
final executor = VmDatabase(File(request.targetPath));
|
|
|
|
|
// We're using MoorIsolate.inCurrent here as this method already runs on a
|
|
|
|
|
// background isolate. If we used MoorIsolate.spawn, a third isolate would be
|
|
|
|
|
// started which is not what we want!
|
|
|
|
|
final moorIsolate = MoorIsolate.inCurrent(
|
|
|
|
|
() => DatabaseConnection.fromExecutor(executor),
|
|
|
|
|
);
|
|
|
|
|
// Inform the starting isolate about this, so that it can call .connect().
|
|
|
|
|
request.sendMoorIsolate.send(moorIsolate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Used to bundle the SendPort and the target path, since isolate entrypoint
|
|
|
|
|
// functions can only take one parameter.
|
|
|
|
|
class _IsolateStartRequest {
|
|
|
|
|
final SendPort sendMoorIsolate;
|
|
|
|
|
final String targetPath;
|
|
|
|
|
|
|
|
|
|
_IsolateStartRequest(this.sendMoorIsolate, this.targetPath);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 09:16:42 +01:00
|
|
|
enum BackendStatus {
|
|
|
|
|
loading,
|
2020-03-28 10:18:52 +01:00
|
|
|
setup,
|
2020-03-28 09:16:42 +01:00
|
|
|
ready,
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 08:51:45 +01:00
|
|
|
class Backend extends StatefulWidget {
|
2019-12-02 21:05:49 +01:00
|
|
|
final Widget child;
|
|
|
|
|
|
2020-03-28 08:51:45 +01:00
|
|
|
Backend({
|
|
|
|
|
@required this.child,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
BackendState createState() => BackendState();
|
|
|
|
|
|
|
|
|
|
static BackendState of(BuildContext context) =>
|
|
|
|
|
context.dependOnInheritedWidgetOfExactType<_InheritedBackend>().state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class BackendState extends State<Backend> {
|
2020-04-18 13:50:38 +02:00
|
|
|
final player = Player();
|
2020-05-01 17:48:23 +02:00
|
|
|
final settings = Settings();
|
2019-12-02 21:05:49 +01:00
|
|
|
|
2020-03-28 09:16:42 +01:00
|
|
|
BackendStatus status = BackendStatus.loading;
|
2020-04-11 21:59:23 +02:00
|
|
|
Database db;
|
2020-04-25 15:02:11 +02:00
|
|
|
MusicusClient client;
|
2020-04-18 23:41:08 +02:00
|
|
|
MusicLibrary ml;
|
2020-03-28 10:18:52 +01:00
|
|
|
|
2020-04-17 17:25:25 +02:00
|
|
|
MoorIsolate _moorIsolate;
|
2020-03-28 09:16:42 +01:00
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
_load();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 08:51:45 +01:00
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return _InheritedBackend(
|
|
|
|
|
child: widget.child,
|
|
|
|
|
state: this,
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-12-02 21:05:49 +01:00
|
|
|
|
2020-03-28 09:16:42 +01:00
|
|
|
Future<void> _load() async {
|
2020-04-17 17:25:25 +02:00
|
|
|
_moorIsolate = await _createMoorIsolate();
|
|
|
|
|
final dbConnection = await _moorIsolate.connect();
|
2020-04-18 13:50:38 +02:00
|
|
|
player.setup();
|
2020-04-17 17:25:25 +02:00
|
|
|
db = Database.connect(dbConnection);
|
|
|
|
|
|
2020-05-01 17:48:23 +02:00
|
|
|
await settings.load();
|
2020-04-25 15:02:11 +02:00
|
|
|
|
2020-05-01 17:48:23 +02:00
|
|
|
_updateMusicLibrary(settings.musicLibraryUri.value);
|
|
|
|
|
settings.musicLibraryUri.listen((uri) {
|
|
|
|
|
_updateMusicLibrary(uri);
|
|
|
|
|
});
|
2020-04-18 13:50:38 +02:00
|
|
|
|
2020-05-01 17:48:23 +02:00
|
|
|
_updateClient(settings.server.value);
|
|
|
|
|
settings.server.listen((serverSettings) {
|
|
|
|
|
_updateClient(serverSettings);
|
|
|
|
|
});
|
2020-03-28 10:18:52 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-01 17:48:23 +02:00
|
|
|
Future<void> _updateMusicLibrary(String uri) async {
|
|
|
|
|
if (uri == null) {
|
2020-03-28 10:18:52 +01:00
|
|
|
setState(() {
|
|
|
|
|
status = BackendStatus.setup;
|
|
|
|
|
});
|
2020-03-28 09:54:04 +01:00
|
|
|
} else {
|
2020-05-01 17:48:23 +02:00
|
|
|
ml = MusicLibrary(uri);
|
2020-04-18 23:41:08 +02:00
|
|
|
await ml.load();
|
2020-03-28 09:54:04 +01:00
|
|
|
setState(() {
|
|
|
|
|
status = BackendStatus.ready;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-01 17:48:23 +02:00
|
|
|
Future<void> _updateClient(ServerSettings serverSettings) async {
|
|
|
|
|
client = MusicusClient(
|
|
|
|
|
host: serverSettings.host,
|
|
|
|
|
port: serverSettings.port,
|
|
|
|
|
basePath: serverSettings.basePath,
|
|
|
|
|
);
|
2020-04-25 15:02:11 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-17 17:25:25 +02:00
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
super.dispose();
|
2020-04-25 15:02:11 +02:00
|
|
|
client.dispose();
|
2020-04-17 17:25:25 +02:00
|
|
|
_moorIsolate.shutdownAll();
|
|
|
|
|
}
|
2020-03-28 08:51:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _InheritedBackend extends InheritedWidget {
|
|
|
|
|
final Widget child;
|
|
|
|
|
final BackendState state;
|
|
|
|
|
|
|
|
|
|
_InheritedBackend({
|
|
|
|
|
@required this.child,
|
|
|
|
|
@required this.state,
|
|
|
|
|
}) : super(child: child);
|
2019-12-02 21:05:49 +01:00
|
|
|
|
|
|
|
|
@override
|
2020-03-28 09:52:42 +01:00
|
|
|
bool updateShouldNotify(_InheritedBackend old) => true;
|
2019-12-02 21:05:49 +01:00
|
|
|
}
|