feat: add default plugin loading capability

This commit is contained in:
Kingkor Roy Tirtho 2025-11-07 22:51:48 +06:00
parent 7c632c8f06
commit fda2257119
6 changed files with 82 additions and 4 deletions

View File

@ -22,6 +22,7 @@ import 'package:drift/native.dart';
import 'package:spotube/services/youtube_engine/newpipe_engine.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/youtube_explode_engine.dart';
import 'package:spotube/services/youtube_engine/yt_dlp_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/sqlite3.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart'; import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';

View File

@ -111,7 +111,9 @@ class PreferencesTable extends Table {
localLibraryLocation: [], localLibraryLocation: [],
themeMode: ThemeMode.system, themeMode: ThemeMode.system,
audioSourceId: null, audioSourceId: null,
youtubeClientEngine: YoutubeClientEngine.youtubeExplode, youtubeClientEngine: kIsIOS
? YoutubeClientEngine.youtubeExplode
: YoutubeClientEngine.newPipe,
discordPresence: true, discordPresence: true,
endlessPlayback: true, endlessPlayback: true,
enableConnect: false, enableConnect: false,

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:path/path.dart'; import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
@ -101,7 +102,11 @@ class MetadataPluginNotifier extends AsyncNotifier<MetadataPluginState> {
final plugins = await database.pluginsTable.select().get(); final plugins = await database.pluginsTable.select().get();
return await toStatePlugins(plugins); final pluginState = await toStatePlugins(plugins);
await _loadDefaultPlugins(pluginState);
return pluginState;
} }
Future<MetadataPluginState> toStatePlugins( Future<MetadataPluginState> toStatePlugins(
@ -171,6 +176,45 @@ class MetadataPluginNotifier extends AsyncNotifier<MetadataPluginState> {
); );
} }
Future<void> _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) { Uri _getGithubReleasesUrl(String repoUrl) {
final parsedUri = Uri.parse(repoUrl); final parsedUri = Uri.parse(repoUrl);
final uri = parsedUri.replace( final uri = parsedUri.replace(
@ -373,11 +417,19 @@ class MetadataPluginNotifier extends AsyncNotifier<MetadataPluginState> {
repository: Value(plugin.repository), repository: Value(plugin.repository),
// Setting the very first plugin as the default plugin // Setting the very first plugin as the default plugin
selectedForMetadata: Value( selectedForMetadata: Value(
(state.valueOrNull?.plugins.isEmpty ?? true) && (state.valueOrNull?.plugins
.where(
(d) => d.abilities.contains(PluginAbilities.metadata))
.isEmpty ??
true) &&
plugin.abilities.contains(PluginAbilities.metadata), plugin.abilities.contains(PluginAbilities.metadata),
), ),
selectedForAudioSource: Value( selectedForAudioSource: Value(
(state.valueOrNull?.plugins.isEmpty ?? true) && (state.valueOrNull?.plugins
.where((d) =>
d.abilities.contains(PluginAbilities.audioSource))
.isEmpty ??
true) &&
plugin.abilities.contains(PluginAbilities.audioSource), plugin.abilities.contains(PluginAbilities.audioSource),
), ),
), ),
@ -420,6 +472,27 @@ class MetadataPluginNotifier extends AsyncNotifier<MetadataPluginState> {
} }
} }
Future<bool> 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<void> updatePlugin( Future<void> updatePlugin(
PluginConfiguration plugin, PluginConfiguration plugin,
PluginUpdateAvailable update, PluginUpdateAvailable update,

View File

@ -227,6 +227,8 @@ flutter:
- assets/branding/spotube-logo.png - assets/branding/spotube-logo.png
- assets/branding/spotube-logo-light.png - assets/branding/spotube-logo-light.png
- assets/branding/spotube-logo.ico - assets/branding/spotube-logo.ico
- assets/plugins/spotube-plugin-musicbrainz-listenbrainz/plugin.smplug
- assets/plugins/spotube-plugin-youtube-audio/plugin.smplug
- LICENSE - LICENSE
- packages/flutter_undraw/assets/undraw/access_denied.svg - packages/flutter_undraw/assets/undraw/access_denied.svg
- packages/flutter_undraw/assets/undraw/fixing_bugs.svg - packages/flutter_undraw/assets/undraw/fixing_bugs.svg