chore: use frb based plugins from git

This commit is contained in:
Kingkor Roy Tirtho 2024-08-01 14:15:40 +06:00
parent b211813213
commit 0eb78d14ca
9 changed files with 118 additions and 400 deletions

View File

@ -82,6 +82,10 @@ jobs:
- name: Set up Docker Buildx
if: ${{matrix.platform == 'linux_arm'}}
uses: docker/setup-buildx-action@v3
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Install ${{matrix.platform}} dependencies
run: |

View File

@ -12,6 +12,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:local_notifier/local_notifier.dart';
import 'package:media_kit/media_kit.dart';
import 'package:metadata_god/metadata_god.dart';
import 'package:smtc_windows/smtc_windows.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/initializers.dart';
import 'package:spotube/collections/routes.dart';
@ -91,6 +92,10 @@ Future<void> main(List<String> rawArgs) async {
await FlutterDiscordRPC.initialize(Env.discordAppId);
}
if(kIsWindows){
await SMTCWindows.initialize();
}
await KVStoreService.initialize();
await EncryptedKvStoreService.initialize();

View File

@ -70,7 +70,7 @@ class DownloadManagerProvider extends ChangeNotifier {
trackNumber: track.trackNumber,
discNumber: track.discNumber,
durationMs: track.durationMs?.toDouble() ?? 0.0,
fileSize: await file.length(),
fileSize: BigInt.from(await file.length()),
trackTotal: track.album?.tracks?.length ?? 0,
picture: imageBytes != null
? Picture(

View File

@ -14,7 +14,7 @@ import 'package:spotube/extensions/track.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
// ignore: depend_on_referenced_packages
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart' show FfiException;
import 'package:flutter_rust_bridge/flutter_rust_bridge.dart' show FrbException;
const supportedAudioTypes = [
"audio/webm",
@ -37,7 +37,7 @@ final localTracksProvider =
FutureProvider<Map<String, List<LocalTrack>>>((ref) async {
try {
if (kIsWeb) return {};
final Map<String, List<LocalTrack>> tracks = {};
final Map<String, List<LocalTrack>> libraryToTracks = {};
final downloadLocation = ref.watch(
userPreferencesProvider.select((s) => s.downloadLocation),
@ -52,59 +52,61 @@ final localTracksProvider =
for (final location in [downloadLocation, ...localLibraryLocations]) {
if (location.isEmpty) continue;
final entities = <FileSystemEntity>[];
final entities = <File>[];
if (await Directory(location).exists()) {
try {
entities.addAll(Directory(location).listSync(recursive: true));
final dirEntities =
await Directory(location).list(recursive: true).toList();
entities.addAll(
dirEntities
.where(
(e) =>
e is File &&
supportedAudioTypes.contains(lookupMimeType(e.path)),
)
.cast<File>(),
);
} catch (e, stack) {
AppLogger.reportError(e, stack);
}
}
final filesWithMetadata = (await Future.wait(
entities.map((e) => File(e.path)).where((file) {
final mimetype = lookupMimeType(file.path);
return mimetype != null && supportedAudioTypes.contains(mimetype);
}).map(
(file) async {
try {
final metadata = await MetadataGod.readMetadata(file: file.path)
.timeout(const Duration(seconds: 10));
final List<Map<dynamic, dynamic>> filesWithMetadata = [];
final imageFile = File(join(
(await getTemporaryDirectory()).path,
"spotube",
basenameWithoutExtension(file.path) +
imgMimeToExt[metadata.picture?.mimeType ?? "image/jpeg"]!,
));
if (!await imageFile.exists() && metadata.picture != null) {
await imageFile.create(recursive: true);
await imageFile.writeAsBytes(
metadata.picture?.data ?? [],
mode: FileMode.writeOnly,
);
}
for (final file in entities) {
try {
final metadata = await MetadataGod.readMetadata(file: file.path);
return {
"metadata": metadata,
"file": file,
"art": imageFile.path
};
} catch (e, stack) {
if (e case FfiException() || TimeoutException()) {
return {"file": file};
}
AppLogger.reportError(e, stack);
return {};
}
},
),
))
.where((e) => e.isNotEmpty)
.toList();
await Future.delayed(const Duration(milliseconds: 50));
// ignore: no_leading_underscores_for_local_identifiers
final _tracks = filesWithMetadata
final imageFile = File(join(
(await getTemporaryDirectory()).path,
"spotube",
basenameWithoutExtension(file.path) +
imgMimeToExt[metadata.picture?.mimeType ?? "image/jpeg"]!,
));
if (!await imageFile.exists() && metadata.picture != null) {
await imageFile.create(recursive: true);
await imageFile.writeAsBytes(
metadata.picture?.data ?? [],
mode: FileMode.writeOnly,
);
}
filesWithMetadata.add(
{"metadata": metadata, "file": file, "art": imageFile.path},
);
} catch (e, stack) {
if (e case FrbException() || TimeoutException()) {
filesWithMetadata.add({"file": file});
}
AppLogger.reportError(e, stack);
continue;
}
}
final tracksFromMetadata = filesWithMetadata
.map(
(fileWithMetadata) => LocalTrack.fromTrack(
track: Track().fromFile(
@ -117,9 +119,9 @@ final localTracksProvider =
)
.toList();
tracks[location] = _tracks;
libraryToTracks[location] = tracksFromMetadata;
}
return tracks;
return libraryToTracks;
} catch (e, stack) {
AppLogger.reportError(e, stack);
return {};

View File

@ -1,276 +0,0 @@
// ignore_for_file: constant_identifier_names
class MusicMetadata {
final String? title;
final String? artist;
final String? album;
final String? albumArtist;
final String? thumbnail;
const MusicMetadata({
this.title,
this.artist,
this.album,
this.albumArtist,
this.thumbnail,
});
}
enum PlaybackStatus {
Closed,
Changing,
Stopped,
Playing,
Paused,
}
enum PressedButton {
play,
pause,
next,
previous,
fastForward,
rewind,
stop,
record,
channelUp,
channelDown;
static PressedButton fromString(String button) {
switch (button) {
case 'play':
return PressedButton.play;
case 'pause':
return PressedButton.pause;
case 'next':
return PressedButton.next;
case 'previous':
return PressedButton.previous;
case 'fast_forward':
return PressedButton.fastForward;
case 'rewind':
return PressedButton.rewind;
case 'stop':
return PressedButton.stop;
case 'record':
return PressedButton.record;
case 'channel_up':
return PressedButton.channelUp;
case 'channel_down':
return PressedButton.channelDown;
default:
throw Exception('Unknown button: $button');
}
}
}
class SMTCConfig {
final bool playEnabled;
final bool pauseEnabled;
final bool stopEnabled;
final bool nextEnabled;
final bool prevEnabled;
final bool fastForwardEnabled;
final bool rewindEnabled;
const SMTCConfig({
required this.playEnabled,
required this.pauseEnabled,
required this.stopEnabled,
required this.nextEnabled,
required this.prevEnabled,
required this.fastForwardEnabled,
required this.rewindEnabled,
});
}
enum RepeatMode {
none,
track,
list;
static RepeatMode fromString(String value) {
switch (value) {
case 'none':
return none;
case 'track':
return track;
case 'list':
return list;
default:
throw Exception('Unknown repeat mode: $value');
}
}
String get asString => toString().split('.').last;
}
class PlaybackTimeline {
final int startTimeMs;
final int endTimeMs;
final int positionMs;
final int? minSeekTimeMs;
final int? maxSeekTimeMs;
const PlaybackTimeline({
required this.startTimeMs,
required this.endTimeMs,
required this.positionMs,
this.minSeekTimeMs,
this.maxSeekTimeMs,
});
}
class SMTCWindows {
SMTCWindows({
SMTCConfig? config,
PlaybackTimeline? timeline,
MusicMetadata? metadata,
PlaybackStatus? status,
bool? shuffleEnabled,
RepeatMode? repeatMode,
bool? enabled,
});
SMTCConfig get config => throw UnimplementedError();
PlaybackTimeline get timeline => throw UnimplementedError();
MusicMetadata get metadata => throw UnimplementedError();
PlaybackStatus? get status => throw UnimplementedError();
Stream<PressedButton> get buttonPressStream => throw UnimplementedError();
Stream<bool> get shuffleChangeStream => throw UnimplementedError();
Stream<RepeatMode> get repeatModeChangeStream => throw UnimplementedError();
bool get isPlayEnabled => config.playEnabled;
bool get isPauseEnabled => config.pauseEnabled;
bool get isStopEnabled => config.stopEnabled;
bool get isNextEnabled => config.nextEnabled;
bool get isPrevEnabled => config.prevEnabled;
bool get isFastForwardEnabled => config.fastForwardEnabled;
bool get isRewindEnabled => config.rewindEnabled;
bool get isShuffleEnabled => throw UnimplementedError();
RepeatMode get repeatMode => throw UnimplementedError();
bool get enabled => throw UnimplementedError();
Duration? get startTime => Duration(milliseconds: timeline.startTimeMs);
Duration? get endTime => Duration(milliseconds: timeline.endTimeMs);
Duration? get position => Duration(milliseconds: timeline.positionMs);
Duration? get minSeekTime => timeline.minSeekTimeMs == null
? null
: Duration(milliseconds: timeline.minSeekTimeMs!);
Duration? get maxSeekTime => timeline.maxSeekTimeMs == null
? null
: Duration(milliseconds: timeline.maxSeekTimeMs!);
Future<void> updateConfig(SMTCConfig config) {
throw UnimplementedError();
}
Future<void> updateTimeline(PlaybackTimeline timeline) {
throw UnimplementedError();
}
Future<void> updateMetadata(MusicMetadata metadata) {
throw UnimplementedError();
}
Future<void> clearMetadata() {
throw UnimplementedError();
}
Future<void> dispose() async {
throw UnimplementedError();
}
Future<void> disableSmtc() {
throw UnimplementedError();
}
Future<void> enableSmtc() {
throw UnimplementedError();
}
Future<void> setPlaybackStatus(PlaybackStatus status) async {
throw UnimplementedError();
}
Future<void> setIsPlayEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setIsPauseEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setIsStopEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setIsNextEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setIsPrevEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setIsFastForwardEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setIsRewindEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setTimeline(PlaybackTimeline timeline) {
return updateTimeline(timeline);
}
Future<void> setTitle(String title) {
throw UnimplementedError();
}
Future<void> setArtist(String artist) {
throw UnimplementedError();
}
Future<void> setAlbum(String album) {
throw UnimplementedError();
}
Future<void> setAlbumArtist(String albumArtist) {
throw UnimplementedError();
}
Future<void> setThumbnail(String thumbnail) {
throw UnimplementedError();
}
Future<void> setPosition(Duration position) {
throw UnimplementedError();
}
Future<void> setStartTime(Duration startTime) {
throw UnimplementedError();
}
Future<void> setEndTime(Duration endTime) {
throw UnimplementedError();
}
Future<void> setMaxSeekTime(Duration maxSeekTime) {
throw UnimplementedError();
}
Future<void> setMinSeekTime(Duration minSeekTime) {
throw UnimplementedError();
}
Future<void> setShuffleEnabled(bool enabled) {
throw UnimplementedError();
}
Future<void> setRepeatMode(RepeatMode repeatMode) {
throw UnimplementedError();
}
}

View File

@ -18,7 +18,7 @@ class WindowsAudioService {
WindowsAudioService(this.ref, this.audioPlayerNotifier)
: smtc = SMTCWindows(enabled: false) {
smtc.setPlaybackStatus(PlaybackStatus.Stopped);
smtc.setPlaybackStatus(PlaybackStatus.stopped);
final buttonStream = smtc.buttonPressStream.listen((event) {
switch (event) {
case PressedButton.play:
@ -45,16 +45,16 @@ class WindowsAudioService {
audioPlayer.playerStateStream.listen((state) async {
switch (state) {
case AudioPlaybackState.playing:
await smtc.setPlaybackStatus(PlaybackStatus.Playing);
await smtc.setPlaybackStatus(PlaybackStatus.playing);
break;
case AudioPlaybackState.paused:
await smtc.setPlaybackStatus(PlaybackStatus.Paused);
await smtc.setPlaybackStatus(PlaybackStatus.paused);
break;
case AudioPlaybackState.stopped:
await smtc.setPlaybackStatus(PlaybackStatus.Stopped);
await smtc.setPlaybackStatus(PlaybackStatus.stopped);
break;
case AudioPlaybackState.completed:
await smtc.setPlaybackStatus(PlaybackStatus.Changing);
await smtc.setPlaybackStatus(PlaybackStatus.changing);
break;
default:
break;

View File

@ -14,7 +14,8 @@ PODS:
- FlutterMacOS
- file_selector_macos (0.0.1):
- FlutterMacOS
- flutter_discord_rpc (0.0.1)
- flutter_discord_rpc (0.0.1):
- FlutterMacOS
- flutter_inappwebview_macos (0.0.1):
- FlutterMacOS
- OrderedSet (~> 5.0)
@ -27,7 +28,8 @@ PODS:
- FlutterMacOS
- media_kit_native_event_loop (1.0.0):
- FlutterMacOS
- metadata_god (0.0.1)
- metadata_god (0.0.1):
- FlutterMacOS
- OrderedSet (5.0.0)
- package_info_plus (0.0.1):
- FlutterMacOS
@ -163,14 +165,14 @@ SPEC CHECKSUMS:
desktop_webview_window: 89bb3d691f4c80314a10be312f4cd35db93a9d5a
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
flutter_discord_rpc: 53b006f68ef620a99fe1b3ba7e83513f3ae95b4c
flutter_discord_rpc: 67a7c10ea24d9d3bf35d01af643f48fbcfa7c24f
flutter_inappwebview_macos: 9600c9df9fdb346aaa8933812009f8d94304203d
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
media_kit_libs_macos_audio: 3871782a4f3f84c77f04d7666c87800a781c24da
media_kit_native_event_loop: 7321675377cb9ae8596a29bddf3a3d2b5e8792c5
metadata_god: eceae399d0020475069a5cebc35943ce8562b5d7
metadata_god: 829f61208b44ac1173e7cd32ab740d8776be5435
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c

View File

@ -715,10 +715,9 @@ packages:
flutter_discord_rpc:
dependency: "direct main"
description:
name: flutter_discord_rpc
sha256: "290c0d91c8ef24c3acb84cb6fcc5c5fed652fe4871b59896c8d180d5eae5d647"
url: "https://pub.dev"
source: hosted
path: "../frb_plugins/packages/flutter_discord_rpc"
relative: true
source: path
version: "0.1.0+1"
flutter_displaymode:
dependency: "direct main"
@ -918,10 +917,10 @@ packages:
dependency: transitive
description:
name: flutter_rust_bridge
sha256: "02720226035257ad0b571c1256f43df3e1556a499f6bcb004849a0faaa0e87f0"
sha256: fac14d2dd67eeba29a20e5d99fac0d4d9fcd552cdf6bf4f8945f7679c6b07b1d
url: "https://pub.dev"
source: hosted
version: "1.82.6"
version: "2.1.0"
flutter_secure_storage:
dependency: "direct main"
description:
@ -1266,10 +1265,10 @@ packages:
dependency: "direct main"
description:
name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.18.1"
version: "0.19.0"
introduction_screen:
dependency: "direct main"
description:
@ -1322,26 +1321,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.1"
lints:
dependency: transitive
description:
@ -1474,18 +1473,17 @@ packages:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.12.0"
metadata_god:
dependency: "direct main"
description:
name: metadata_god
sha256: cf13931c39eba0b9443d16e8940afdabee125bf08945f18d4c0d02bcae2a3317
url: "https://pub.dev"
source: hosted
version: "0.5.2+1"
path: "../frb_plugins/packages/metadata_god"
relative: true
source: path
version: "0.5.3"
mime:
dependency: "direct main"
description:
@ -1766,14 +1764,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.3"
puppeteer:
dependency: transitive
description:
name: puppeteer
sha256: "6833edca01b1e9dcdd9a6e41bad84b706dfba4366d095c4edff64b00c02ac472"
url: "https://pub.dev"
source: hosted
version: "3.8.0"
quiver:
dependency: transitive
description:
@ -1935,14 +1925,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.4"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
url: "https://pub.dev"
source: hosted
version: "1.1.2"
shelf_web_socket:
dependency: "direct main"
description:
@ -1999,10 +1981,9 @@ packages:
smtc_windows:
dependency: "direct main"
description:
name: smtc_windows
sha256: "0fd64d0c6a0c8ea4ea7908d31195eadc8f6d45d5245159fc67259e9e8704100f"
url: "https://pub.dev"
source: hosted
path: "../frb_plugins/packages/smtc_windows"
relative: true
source: path
version: "0.1.3"
source_gen:
dependency: transitive
@ -2031,12 +2012,11 @@ packages:
spotify:
dependency: "direct main"
description:
path: "."
ref: "fix/explicit-to-json"
resolved-ref: c4b37c599413ac7bfd78993e416a56105c62b634
url: "https://github.com/KRTirtho/spotify-dart.git"
source: git
version: "0.13.6"
name: spotify
sha256: "705f09a457a893973451c15f4072670ac4783d67e42c35c080c55a48dee3a01f"
url: "https://pub.dev"
source: hosted
version: "0.13.7"
sprintf:
dependency: transitive
description:
@ -2186,10 +2166,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.0"
time:
dependency: transitive
description:
@ -2230,14 +2210,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.2"
tuple:
dependency: transitive
description:
name: tuple
sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151
url: "https://pub.dev"
source: hosted
version: "2.0.2"
type_plus:
dependency: transitive
description:
@ -2394,10 +2366,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "13.0.0"
version: "14.2.1"
watcher:
dependency: transitive
description:
@ -2495,5 +2467,5 @@ packages:
source: hosted
version: "2.2.1"
sdks:
dart: ">=3.3.0 <4.0.0"
dart: ">=3.4.0 <4.0.0"
flutter: ">=3.19.2"

View File

@ -65,7 +65,11 @@ dependencies:
logger: ^2.0.2
media_kit: ^1.1.10+1
media_kit_libs_audio: ^1.0.4
metadata_god: ^0.5.2+1
metadata_god:
git:
url: https://github.com/KRTirtho/frb_plugins.git
path: packages/metadata_god
ref: cargokit
mime: ^1.0.2
package_info_plus: ^6.0.0
palette_generator: ^0.3.3
@ -81,7 +85,11 @@ dependencies:
scroll_to_index: ^3.0.1
sidebarx: ^0.17.1
shared_preferences: ^2.2.3
smtc_windows: ^0.1.3
smtc_windows:
git:
url: https://github.com/KRTirtho/frb_plugins.git
path: packages/smtc_windows
ref: cargokit
stroke_text: ^0.0.2
system_theme: ^2.1.0
titlebar_buttons: ^1.0.0
@ -100,7 +108,11 @@ dependencies:
very_good_infinite_list: ^0.7.1
gap: ^3.0.1
sliver_tools: ^0.2.12
flutter_discord_rpc: ^0.1.0+1
flutter_discord_rpc:
git:
url: https://github.com/KRTirtho/frb_plugins.git
path: packages/flutter_discord_rpc
ref: cargokit
html_unescape: ^2.0.0
wikipedia_api: ^0.1.0
skeletonizer: ^1.1.1
@ -109,10 +121,7 @@ dependencies:
flutter_sharing_intent: ^1.1.0
flutter_broadcasts: ^0.4.0
freezed_annotation: ^2.4.1
spotify:
git:
url: https://github.com/KRTirtho/spotify-dart.git
ref: fix/explicit-to-json
spotify: ^0.13.7
bonsoir: ^5.1.9
shelf: ^1.4.1
shelf_router: ^1.1.4