diff --git a/assets/plugins/spotube-plugin-musicbrainz-listenbrainz/plugin.smplug b/assets/plugins/spotube-plugin-musicbrainz-listenbrainz/plugin.smplug new file mode 100644 index 00000000..41be05a4 Binary files /dev/null and b/assets/plugins/spotube-plugin-musicbrainz-listenbrainz/plugin.smplug differ diff --git a/assets/plugins/spotube-plugin-youtube-audio/plugin.smplug b/assets/plugins/spotube-plugin-youtube-audio/plugin.smplug new file mode 100644 index 00000000..4b93b31b Binary files /dev/null and b/assets/plugins/spotube-plugin-youtube-audio/plugin.smplug differ diff --git a/lib/models/database/database.dart b/lib/models/database/database.dart index 387bcdb7..8df70968 100644 --- a/lib/models/database/database.dart +++ b/lib/models/database/database.dart @@ -22,6 +22,7 @@ import 'package:drift/native.dart'; import 'package:spotube/services/youtube_engine/newpipe_engine.dart'; import 'package:spotube/services/youtube_engine/youtube_explode_engine.dart'; import 'package:spotube/services/youtube_engine/yt_dlp_engine.dart'; +import 'package:spotube/utils/platform.dart'; import 'package:sqlite3/sqlite3.dart'; import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart'; diff --git a/lib/models/database/tables/preferences.dart b/lib/models/database/tables/preferences.dart index cc810ae7..3029e2a8 100644 --- a/lib/models/database/tables/preferences.dart +++ b/lib/models/database/tables/preferences.dart @@ -111,7 +111,9 @@ class PreferencesTable extends Table { localLibraryLocation: [], themeMode: ThemeMode.system, audioSourceId: null, - youtubeClientEngine: YoutubeClientEngine.youtubeExplode, + youtubeClientEngine: kIsIOS + ? YoutubeClientEngine.youtubeExplode + : YoutubeClientEngine.newPipe, discordPresence: true, endlessPlayback: true, enableConnect: false, diff --git a/lib/provider/metadata_plugin/metadata_plugin_provider.dart b/lib/provider/metadata_plugin/metadata_plugin_provider.dart index ab3c8547..cdc96c41 100644 --- a/lib/provider/metadata_plugin/metadata_plugin_provider.dart +++ b/lib/provider/metadata_plugin/metadata_plugin_provider.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:drift/drift.dart'; +import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; @@ -101,7 +102,11 @@ class MetadataPluginNotifier extends AsyncNotifier { final plugins = await database.pluginsTable.select().get(); - return await toStatePlugins(plugins); + final pluginState = await toStatePlugins(plugins); + + await _loadDefaultPlugins(pluginState); + + return pluginState; } Future toStatePlugins( @@ -171,6 +176,45 @@ class MetadataPluginNotifier extends AsyncNotifier { ); } + Future _loadDefaultPlugins(MetadataPluginState pluginState) async { + const plugins = [ + "spotube-plugin-musicbrainz-listenbrainz", + "spotube-plugin-youtube-audio", + ]; + + for (final plugin in plugins) { + final byteData = await rootBundle.load( + "assets/plugins/$plugin/plugin.smplug", + ); + final pluginConfig = + await extractPluginArchive(byteData.buffer.asUint8List()); + try { + await addPlugin(pluginConfig); + } on MetadataPluginException catch (e) { + if (e.errorCode == MetadataPluginErrorCode.duplicatePlugin && + await isPluginUpdate(pluginConfig)) { + final oldConfig = pluginState.plugins + .firstWhereOrNull((p) => p.slug == pluginConfig.slug); + if (oldConfig == null) continue; + final isDefaultMetadata = + oldConfig == pluginState.defaultMetadataPluginConfig; + final isDefaultAudioSource = + oldConfig == pluginState.defaultAudioSourcePluginConfig; + + await removePlugin(pluginConfig); + await addPlugin(pluginConfig); + + if (isDefaultMetadata) { + await setDefaultMetadataPlugin(pluginConfig); + } + if (isDefaultAudioSource) { + await setDefaultAudioSourcePlugin(pluginConfig); + } + } + } + } + } + Uri _getGithubReleasesUrl(String repoUrl) { final parsedUri = Uri.parse(repoUrl); final uri = parsedUri.replace( @@ -373,11 +417,19 @@ class MetadataPluginNotifier extends AsyncNotifier { repository: Value(plugin.repository), // Setting the very first plugin as the default plugin selectedForMetadata: Value( - (state.valueOrNull?.plugins.isEmpty ?? true) && + (state.valueOrNull?.plugins + .where( + (d) => d.abilities.contains(PluginAbilities.metadata)) + .isEmpty ?? + true) && plugin.abilities.contains(PluginAbilities.metadata), ), selectedForAudioSource: Value( - (state.valueOrNull?.plugins.isEmpty ?? true) && + (state.valueOrNull?.plugins + .where((d) => + d.abilities.contains(PluginAbilities.audioSource)) + .isEmpty ?? + true) && plugin.abilities.contains(PluginAbilities.audioSource), ), ), @@ -420,6 +472,27 @@ class MetadataPluginNotifier extends AsyncNotifier { } } + Future isPluginUpdate(PluginConfiguration newPlugin) async { + final pluginRes = await (database.pluginsTable.select() + ..where( + (tbl) => + tbl.name.equals(newPlugin.name) & + tbl.author.equals(newPlugin.author), + ) + ..limit(1)) + .get(); + + if (pluginRes.isEmpty) { + return false; + } + + final oldPlugin = pluginRes.first; + final oldPluginApiVersion = Version.parse(oldPlugin.pluginApiVersion); + final newPluginApiVersion = Version.parse(newPlugin.pluginApiVersion); + + return newPluginApiVersion > oldPluginApiVersion; + } + Future updatePlugin( PluginConfiguration plugin, PluginUpdateAvailable update, diff --git a/pubspec.yaml b/pubspec.yaml index 0c31a0cb..fd3d78c5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -227,6 +227,8 @@ flutter: - assets/branding/spotube-logo.png - assets/branding/spotube-logo-light.png - assets/branding/spotube-logo.ico + - assets/plugins/spotube-plugin-musicbrainz-listenbrainz/plugin.smplug + - assets/plugins/spotube-plugin-youtube-audio/plugin.smplug - LICENSE - packages/flutter_undraw/assets/undraw/access_denied.svg - packages/flutter_undraw/assets/undraw/fixing_bugs.svg