From b14dcd67f21b34d1bd9990b00a2c9f29ce4f9a4f Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Sat, 7 May 2022 19:06:09 +0200 Subject: [PATCH] common: Remove platform interface --- common/lib/musicus_common.dart | 3 +- common/lib/src/app.dart | 10 +--- common/lib/src/backend.dart | 16 ++--- common/lib/src/library.dart | 8 +-- common/lib/src/platform.dart | 82 -------------------------- common/lib/src/playback.dart | 28 +++++---- common/lib/src/screens/program.dart | 6 +- common/lib/src/screens/settings.dart | 6 +- common/lib/src/screens/work.dart | 5 +- common/lib/src/widgets/lists.dart | 3 - common/lib/src/widgets/player_bar.dart | 7 ++- common/pubspec.yaml | 2 + desktop/lib/main.dart | 2 - desktop/lib/platform.dart | 58 ------------------ desktop/lib/playback.dart | 11 ++-- desktop/pubspec.yaml | 1 + 16 files changed, 44 insertions(+), 204 deletions(-) delete mode 100644 common/lib/src/platform.dart delete mode 100644 desktop/lib/platform.dart diff --git a/common/lib/musicus_common.dart b/common/lib/musicus_common.dart index d85d4fa..4d0e25e 100644 --- a/common/lib/musicus_common.dart +++ b/common/lib/musicus_common.dart @@ -1,5 +1,4 @@ export 'src/app.dart'; export 'src/library.dart'; -export 'src/platform.dart'; export 'src/playback.dart'; -export 'src/settings.dart'; \ No newline at end of file +export 'src/settings.dart'; diff --git a/common/lib/src/app.dart b/common/lib/src/app.dart index e91ecbf..8897aa3 100644 --- a/common/lib/src/app.dart +++ b/common/lib/src/app.dart @@ -1,12 +1,11 @@ import 'dart:async'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'backend.dart'; import 'screens/home.dart'; import 'settings.dart'; -import 'platform.dart'; import 'playback.dart'; import 'widgets/player_bar.dart'; @@ -24,14 +23,10 @@ class MusicusApp extends StatelessWidget { /// An object handling playback. final MusicusPlayback playback; - /// An object handling platform dependent functionality. - final MusicusPlatform platform; - MusicusApp({ @required this.dbPath, @required this.settingsStorage, @required this.playback, - @required this.platform, }); @override @@ -39,7 +34,6 @@ class MusicusApp extends StatelessWidget { return MusicusBackend( settingsStorage: settingsStorage, playback: playback, - platform: platform, child: Builder( builder: (context) { final backend = MusicusBackend.of(context); @@ -91,7 +85,7 @@ class MusicusApp extends StatelessWidget { leading: const Icon(Icons.folder_open), title: Text('Choose path'), onTap: () async { - final uri = await platform.chooseBasePath(); + final uri = await FilePicker.platform.getDirectoryPath(); if (uri != null) { backend.settings.setMusicLibraryPath(uri); } diff --git a/common/lib/src/backend.dart b/common/lib/src/backend.dart index 4aeb96a..cca8ed0 100644 --- a/common/lib/src/backend.dart +++ b/common/lib/src/backend.dart @@ -2,7 +2,6 @@ import 'package:flutter/widgets.dart'; import 'package:musicus_database/musicus_database.dart'; import 'library.dart'; -import 'platform.dart'; import 'playback.dart'; import 'settings.dart'; @@ -43,9 +42,6 @@ class MusicusBackend extends StatefulWidget { /// An object handling playback. final MusicusPlayback playback; - /// An object handling platform dependent functionality. - final MusicusPlatform platform; - /// The first child below the backend widget. /// /// This widget should keep track of the current backend status and block @@ -56,7 +52,6 @@ class MusicusBackend extends StatefulWidget { MusicusBackend({ @required this.settingsStorage, @required this.playback, - @required this.platform, @required this.child, }); @@ -76,7 +71,6 @@ class MusicusBackendState extends State { MusicusPlayback playback; MusicusSettings settings; - MusicusPlatform platform; MusicusLibrary library; MusicusClientDatabase get db => library.db; @@ -90,7 +84,6 @@ class MusicusBackendState extends State { /// Initialize resources. Future _load() async { playback = widget.playback; - await playback.setup(); settings = MusicusSettings(widget.settingsStorage); await settings.load(); @@ -104,9 +97,6 @@ class MusicusBackendState extends State { final path = settings.musicLibraryPath.valueOrNull; - platform = widget.platform; - platform.setBasePath(path); - // This will change the status for us. _updateMusicLibrary(path); } @@ -118,9 +108,11 @@ class MusicusBackendState extends State { status = MusicusBackendStatus.setup; }); } else { - platform.setBasePath(path); - library = MusicusLibrary(path, platform); + library = MusicusLibrary(path); + await library.load(); + await playback.setup(library); + setState(() { status = MusicusBackendStatus.ready; }); diff --git a/common/lib/src/library.dart b/common/lib/src/library.dart index 23781fe..fbefb94 100644 --- a/common/lib/src/library.dart +++ b/common/lib/src/library.dart @@ -8,8 +8,6 @@ import 'package:drift/isolate.dart'; import 'package:drift/native.dart'; import 'package:musicus_database/musicus_database.dart'; -import 'platform.dart'; - /// Manager for all available tracks and their representation on disk. class MusicusLibrary { /// Starts the database isolate. @@ -19,6 +17,7 @@ class MusicusLibrary { static void _dbIsolateEntrypoint(_IsolateStartRequest request) { final executor = NativeDatabase(File(request.path)); + final driftIsolate = DriftIsolate.inCurrent(() => DatabaseConnection.fromExecutor(executor)); @@ -31,10 +30,7 @@ class MusicusLibrary { /// The actual music library database. MusicusClientDatabase db; - /// Access to platform dependent functionality. - final MusicusPlatform platform; - - MusicusLibrary(this.basePath, this.platform); + MusicusLibrary(this.basePath); /// Load all available tracks. /// diff --git a/common/lib/src/platform.dart b/common/lib/src/platform.dart deleted file mode 100644 index 4fe35c8..0000000 --- a/common/lib/src/platform.dart +++ /dev/null @@ -1,82 +0,0 @@ -/// Object representing a document in Storage Access Framework terms. -class Document { - /// Unique ID for the document. - /// - /// The platform implementation thould be able to get the content of the - /// document based on this value. - final String id; - - /// Name of the document (i.e. file name). - final String name; - - /// Document ID of the parent document. - final String parent; - - /// Whether this document represents a directory. - final bool isDirectory; - - Document({ - this.id, - this.name, - this.parent, - this.isDirectory, - }); - - // Use Map here, as we get casting errors otherwise. This - // won't be typesafe anyway. - Document.fromJson(Map json) - : id = json['id'], - name = json['name'], - parent = json['parent'], - isDirectory = json['isDirectory']; -} - -/// Platform dependent code for the Musicus backend. -abstract class MusicusPlatform { - /// An identifier for the root directory of the music library. - /// - /// This will be the string, that is stored as musicLibraryPath in the - /// settings object. - String basePath; - - MusicusPlatform(); - - /// This will be called, when the music library path was changed. - void setBasePath(String path) { - basePath = path; - } - - /// Choose a root level directory for the music library. - /// - /// This should return a string representation of the chosen directory - /// suitable for storage as [basePath]. - Future chooseBasePath(); - - /// Get all documents in a directory. - /// - /// [parentId] will be the ID of the directory document. If [parentId] is - /// null, the children of the root directory will be returned. - Future> getChildren(String parentId); - - /// Read the contents of a document by ID. - Future readDocument(String id); - - /// Read from a document by name. - /// - /// [parentId] is the document ID of the parent directory. - Future readDocumentByName(String parentId, String fileName); - - /// Get a string identifying a document. - /// - /// [parentId] is the document ID of the parent directory. The return value - /// should be a string, that the playback object can use to find and play the - /// file. It will be included in [InternalTrack] objects by the music - /// library. - Future getIdentifier(String parentId, String fileName); - - /// Write to a document by name. - /// - /// [parentId] is the document ID of the parent directory. - Future writeDocumentByName( - String parentId, String fileName, String contents); -} diff --git a/common/lib/src/playback.dart b/common/lib/src/playback.dart index da5ca5e..bf7f3f8 100644 --- a/common/lib/src/playback.dart +++ b/common/lib/src/playback.dart @@ -1,5 +1,5 @@ import 'package:meta/meta.dart'; -import 'package:musicus_database/musicus_database.dart'; +import 'package:musicus_common/musicus_common.dart'; import 'package:rxdart/rxdart.dart'; /// Base class for Musicus playback. @@ -13,7 +13,7 @@ abstract class MusicusPlayback { /// The current playlist. /// /// If the player is not active, this will be an empty list. - final playlist = BehaviorSubject.seeded([]); + final playlist = BehaviorSubject.seeded([]); /// Index of the currently played (or paused) track within the playlist. /// @@ -23,7 +23,7 @@ abstract class MusicusPlayback { /// The currently played track. /// /// This will be null, if there is no current track. - final currentTrack = BehaviorSubject.seeded(null); + final currentTrack = BehaviorSubject.seeded(null); /// Whether we are currently playing or not. /// @@ -46,10 +46,10 @@ abstract class MusicusPlayback { /// Initialize the player. /// /// This will be called after the database was initialized. - Future setup(); + Future setup(MusicusLibrary library); /// Add a list of tracks to the players playlist. - Future addTracks(List tracks); + Future addTracks(List tracks); /// Remove the track at [index] from the playlist. Future removeTrack(int index); @@ -96,16 +96,15 @@ abstract class MusicusPlayback { /// Update [position] and [normalizedPosition]. /// /// Requires [duration] to be up to date - void updatePosition(int positionMs) { - position.add(Duration(milliseconds: positionMs)); - _setNormalizedPosition(positionMs / duration.value.inMilliseconds); + void updatePosition(Duration pos) { + position.add(pos); + _setNormalizedPosition(pos.inMilliseconds / duration.value.inMilliseconds); } /// Update [position], [duration] and [normalizedPosition]. - void updateDuration(int positionMs, int durationMs) { - position.add(Duration(milliseconds: positionMs)); - duration.add(Duration(milliseconds: durationMs)); - _setNormalizedPosition(positionMs / durationMs); + void updateDuration(Duration dur) { + duration.add(dur); + _setNormalizedPosition(position.value.inMilliseconds / dur.inMilliseconds); } /// Update [normalizedPosition] ensuring its value is between 0.0 and 1.0. @@ -124,6 +123,9 @@ abstract class MusicusPlayback { /// Requires [playlist] to be up to date. void updateCurrentTrack(int index) { currentIndex.add(index); - currentTrack.add(playlist.value[index]); + + if (playlist.value != null && index >= 0 && index < playlist.value.length) { + currentTrack.add(playlist.value[index]); + } } } diff --git a/common/lib/src/screens/program.dart b/common/lib/src/screens/program.dart index d8c8507..4203ad7 100644 --- a/common/lib/src/screens/program.dart +++ b/common/lib/src/screens/program.dart @@ -17,7 +17,7 @@ class _ProgramScreenState extends State { StreamSubscription playerActiveSubscription; - StreamSubscription> playlistSubscription; + StreamSubscription> playlistSubscription; List widgets = []; StreamSubscription positionSubscription; @@ -63,7 +63,7 @@ class _ProgramScreenState extends State { } /// Go through the tracks of [playlist] and preprocess them for displaying. - Future updateProgram(List playlist) async { + Future updateProgram(List playlist) async { List newWidgets = []; // The following variables exist to adapt the resulting ProgramItem to its @@ -84,7 +84,7 @@ class _ProgramScreenState extends State { // The widgets displayed for this track. List children = []; - final track = playlist[i]; + final track = await backend.db.tracksById(playlist[i]).getSingle(); final recordingId = track.recording; final partIds = track.workParts; diff --git a/common/lib/src/screens/settings.dart b/common/lib/src/screens/settings.dart index 0b09e48..4a724d1 100644 --- a/common/lib/src/screens/settings.dart +++ b/common/lib/src/screens/settings.dart @@ -1,11 +1,9 @@ +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import '../backend.dart'; class SettingsScreen extends StatelessWidget { - static const _platform = MethodChannel('de.johrpan.musicus/platform'); - @override Widget build(BuildContext context) { final backend = MusicusBackend.of(context); @@ -25,7 +23,7 @@ class SettingsScreen extends StatelessWidget { subtitle: Text(snapshot.data ?? 'Choose folder'), isThreeLine: snapshot.hasData, onTap: () async { - final uri = await backend.platform.chooseBasePath(); + final uri = await FilePicker.platform.getDirectoryPath(); if (uri != null) { settings.setMusicLibraryPath(uri); diff --git a/common/lib/src/screens/work.dart b/common/lib/src/screens/work.dart index 9dd3976..7164d37 100644 --- a/common/lib/src/screens/work.dart +++ b/common/lib/src/screens/work.dart @@ -32,8 +32,9 @@ class WorkScreen extends StatelessWidget { performanceInfos: recordingInfo.performances, ), onTap: () async { - final tracks = await backend.db.tracksByRecording(recordingId).get(); - backend.playback.addTracks(tracks); + final tracks = + await backend.db.tracksByRecording(recordingId).get(); + backend.playback.addTracks(tracks.map((t) => t.id).toList()); }, ); }, diff --git a/common/lib/src/widgets/lists.dart b/common/lib/src/widgets/lists.dart index 84136b4..c21c132 100644 --- a/common/lib/src/widgets/lists.dart +++ b/common/lib/src/widgets/lists.dart @@ -2,9 +2,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import '../backend.dart'; -import '../widgets/texts.dart'; - /// A list view supporting pagination and searching. /// /// The [fetch] function will be called, when the user has scrolled to the end diff --git a/common/lib/src/widgets/player_bar.dart b/common/lib/src/widgets/player_bar.dart index 7c5c687..c4b3d9d 100644 --- a/common/lib/src/widgets/player_bar.dart +++ b/common/lib/src/widgets/player_bar.dart @@ -15,7 +15,7 @@ class PlayerBar extends StatefulWidget { class _PlayerBarState extends State { MusicusBackendState _backend; - StreamSubscription _currentTrackSubscribtion; + StreamSubscription _currentTrackSubscribtion; WorkInfo _workInfo; List _partIds; @@ -26,9 +26,10 @@ class _PlayerBarState extends State { _backend = MusicusBackend.of(context); _currentTrackSubscribtion?.cancel(); - _currentTrackSubscribtion = _backend.playback.currentTrack.listen((track) { + _currentTrackSubscribtion = + _backend.playback.currentTrack.listen((track) async { if (track != null) { - _setTrack(track); + _setTrack(await _backend.db.tracksById(track).getSingle()); } }); } diff --git a/common/pubspec.yaml b/common/pubspec.yaml index 4ac7e09..a801f66 100644 --- a/common/pubspec.yaml +++ b/common/pubspec.yaml @@ -1,12 +1,14 @@ name: musicus_common version: 0.1.0 description: Common building blocks for Musicus client apps. +publish_to: none environment: sdk: ">=2.3.0 <3.0.0" dependencies: drift: ^1.0.0 + file_picker: ^4.5.1 flutter: sdk: flutter flutter_markdown: diff --git a/desktop/lib/main.dart b/desktop/lib/main.dart index 834314a..8945db3 100644 --- a/desktop/lib/main.dart +++ b/desktop/lib/main.dart @@ -4,7 +4,6 @@ import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart' as pp; import 'settings.dart'; -import 'platform.dart'; import 'playback.dart'; Future main() async { @@ -16,7 +15,6 @@ Future main() async { runApp(MusicusApp( dbPath: dbPath, settingsStorage: SettingsStorage(), - platform: MusicusDesktopPlatform(), playback: MusicusDesktopPlayback(), )); } diff --git a/desktop/lib/platform.dart b/desktop/lib/platform.dart deleted file mode 100644 index 54c337c..0000000 --- a/desktop/lib/platform.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:musicus_common/musicus_common.dart'; -import 'package:path/path.dart' as p; - -class MusicusDesktopPlatform extends MusicusPlatform { - @override - Future chooseBasePath() async { - return await FilePicker.platform.getDirectoryPath(); - } - - @override - Future> getChildren(String parentId) async { - final List result = []; - - final parent = Directory(parentId ?? basePath); - await for (final fse in parent.list()) { - result.add(Document( - id: fse.path, - name: p.basename(fse.path), - parent: parentId, - isDirectory: fse is Directory, - )); - } - - return result; - } - - @override - Future getIdentifier(String parentId, String fileName) async { - return p.absolute(parentId, fileName); - } - - @override - Future readDocument(String id) async { - try { - return await File(id).readAsString(); - } on FileSystemException { - return null; - } - } - - @override - Future readDocumentByName(String parentId, String fileName) async { - try { - return await File(p.absolute(parentId, fileName)).readAsString(); - } on FileSystemException { - return null; - } - } - - @override - Future writeDocumentByName( - String parentId, String fileName, String contents) async { - await File(p.absolute(parentId, fileName)).writeAsString(contents); - } -} diff --git a/desktop/lib/playback.dart b/desktop/lib/playback.dart index 7d4c721..05ad74e 100644 --- a/desktop/lib/playback.dart +++ b/desktop/lib/playback.dart @@ -1,13 +1,12 @@ import 'package:musicus_common/musicus_common.dart'; -import 'package:musicus_database/musicus_database.dart'; class MusicusDesktopPlayback extends MusicusPlayback { @override - Future setup() async {} + Future setup(MusicusLibrary library) async {} @override - Future addTracks(List tracks) async { - final List newPlaylist = List.from(playlist.value); + Future addTracks(List tracks) async { + final List newPlaylist = List.from(playlist.value); newPlaylist.addAll(tracks); playlist.add(newPlaylist); active.add(true); @@ -20,7 +19,7 @@ class MusicusDesktopPlayback extends MusicusPlayback { @override Future removeTrack(int index) async { - final List tracks = List.from(playlist.value); + final List tracks = List.from(playlist.value); tracks.removeAt(index); playlist.add(tracks); } @@ -29,7 +28,7 @@ class MusicusDesktopPlayback extends MusicusPlayback { Future seekTo(double pos) async { if (active.value && pos >= 0.0 && pos <= 1.0) { final durationMs = duration.value.inMilliseconds; - updatePosition((pos * durationMs).floor()); + updatePosition(Duration(milliseconds: (pos * durationMs).floor())); } } diff --git a/desktop/pubspec.yaml b/desktop/pubspec.yaml index 727ba36..ec59d48 100644 --- a/desktop/pubspec.yaml +++ b/desktop/pubspec.yaml @@ -1,6 +1,7 @@ name: musicus_desktop version: 0.1.0 description: Desktop version of the classical music player and organizer. +publish_to: none environment: sdk: ">=2.3.0 <3.0.0"