chore: wip implemented rust side

This commit is contained in:
Kingkor Roy Tirtho 2025-11-28 17:50:14 +06:00
parent 67cf9805a5
commit 57553cdb2e
80 changed files with 29856 additions and 362 deletions

View File

@ -30,5 +30,8 @@
"README.md": "LICENSE,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,CONTRIBUTION.md,CHANGELOG.md,PRIVACY_POLICY.md",
"*.dart": "${capture}.g.dart,${capture}.freezed.dart"
},
"dart.flutterSdkPath": ".fvm/versions/3.35.2"
"dart.flutterSdkPath": ".fvm/versions/3.35.2",
"rust-analyzer.files.exclude": [
"rust/src/frb_generated.rs"
]
}

View File

@ -1,3 +1,7 @@
rust_input: crate::api
rust_root: rust/
dart_output: lib/src/rust
web: false
enable_lifetime: true
rust_preamble: |
use tokio::sync::mpsc::Sender;

View File

@ -60,7 +60,7 @@ import 'package:spotube/pages/track/track.dart' as _i35;
/// [_i1.AboutSpotubePage]
class AboutSpotubeRoute extends _i41.PageRouteInfo<void> {
const AboutSpotubeRoute({List<_i41.PageRouteInfo>? children})
: super(AboutSpotubeRoute.name, initialChildren: children);
: super(AboutSpotubeRoute.name, initialChildren: children);
static const String name = 'AboutSpotubeRoute';
@ -81,11 +81,11 @@ class AlbumRoute extends _i41.PageRouteInfo<AlbumRouteArgs> {
required _i43.SpotubeSimpleAlbumObject album,
List<_i41.PageRouteInfo>? children,
}) : super(
AlbumRoute.name,
args: AlbumRouteArgs(key: key, id: id, album: album),
rawPathParams: {'id': id},
initialChildren: children,
);
AlbumRoute.name,
args: AlbumRouteArgs(key: key, id: id, album: album),
rawPathParams: {'id': id},
initialChildren: children,
);
static const String name = 'AlbumRoute';
@ -121,11 +121,11 @@ class ArtistRoute extends _i41.PageRouteInfo<ArtistRouteArgs> {
_i42.Key? key,
List<_i41.PageRouteInfo>? children,
}) : super(
ArtistRoute.name,
args: ArtistRouteArgs(artistId: artistId, key: key),
rawPathParams: {'id': artistId},
initialChildren: children,
);
ArtistRoute.name,
args: ArtistRouteArgs(artistId: artistId, key: key),
rawPathParams: {'id': artistId},
initialChildren: children,
);
static const String name = 'ArtistRoute';
@ -158,7 +158,7 @@ class ArtistRouteArgs {
/// [_i4.BlackListPage]
class BlackListRoute extends _i41.PageRouteInfo<void> {
const BlackListRoute({List<_i41.PageRouteInfo>? children})
: super(BlackListRoute.name, initialChildren: children);
: super(BlackListRoute.name, initialChildren: children);
static const String name = 'BlackListRoute';
@ -174,7 +174,7 @@ class BlackListRoute extends _i41.PageRouteInfo<void> {
/// [_i5.ConnectControlPage]
class ConnectControlRoute extends _i41.PageRouteInfo<void> {
const ConnectControlRoute({List<_i41.PageRouteInfo>? children})
: super(ConnectControlRoute.name, initialChildren: children);
: super(ConnectControlRoute.name, initialChildren: children);
static const String name = 'ConnectControlRoute';
@ -190,7 +190,7 @@ class ConnectControlRoute extends _i41.PageRouteInfo<void> {
/// [_i6.ConnectPage]
class ConnectRoute extends _i41.PageRouteInfo<void> {
const ConnectRoute({List<_i41.PageRouteInfo>? children})
: super(ConnectRoute.name, initialChildren: children);
: super(ConnectRoute.name, initialChildren: children);
static const String name = 'ConnectRoute';
@ -206,7 +206,7 @@ class ConnectRoute extends _i41.PageRouteInfo<void> {
/// [_i7.GettingStartedPage]
class GettingStartedRoute extends _i41.PageRouteInfo<void> {
const GettingStartedRoute({List<_i41.PageRouteInfo>? children})
: super(GettingStartedRoute.name, initialChildren: children);
: super(GettingStartedRoute.name, initialChildren: children);
static const String name = 'GettingStartedRoute';
@ -228,15 +228,15 @@ class HomeBrowseSectionItemsRoute
required _i43.SpotubeBrowseSectionObject<Object> section,
List<_i41.PageRouteInfo>? children,
}) : super(
HomeBrowseSectionItemsRoute.name,
args: HomeBrowseSectionItemsRouteArgs(
key: key,
sectionId: sectionId,
section: section,
),
rawPathParams: {'sectionId': sectionId},
initialChildren: children,
);
HomeBrowseSectionItemsRoute.name,
args: HomeBrowseSectionItemsRouteArgs(
key: key,
sectionId: sectionId,
section: section,
),
rawPathParams: {'sectionId': sectionId},
initialChildren: children,
);
static const String name = 'HomeBrowseSectionItemsRoute';
@ -276,7 +276,7 @@ class HomeBrowseSectionItemsRouteArgs {
/// [_i9.HomePage]
class HomeRoute extends _i41.PageRouteInfo<void> {
const HomeRoute({List<_i41.PageRouteInfo>? children})
: super(HomeRoute.name, initialChildren: children);
: super(HomeRoute.name, initialChildren: children);
static const String name = 'HomeRoute';
@ -292,7 +292,7 @@ class HomeRoute extends _i41.PageRouteInfo<void> {
/// [_i10.LastFMLoginPage]
class LastFMLoginRoute extends _i41.PageRouteInfo<void> {
const LastFMLoginRoute({List<_i41.PageRouteInfo>? children})
: super(LastFMLoginRoute.name, initialChildren: children);
: super(LastFMLoginRoute.name, initialChildren: children);
static const String name = 'LastFMLoginRoute';
@ -308,7 +308,7 @@ class LastFMLoginRoute extends _i41.PageRouteInfo<void> {
/// [_i11.LibraryPage]
class LibraryRoute extends _i41.PageRouteInfo<void> {
const LibraryRoute({List<_i41.PageRouteInfo>? children})
: super(LibraryRoute.name, initialChildren: children);
: super(LibraryRoute.name, initialChildren: children);
static const String name = 'LibraryRoute';
@ -328,10 +328,10 @@ class LikedPlaylistRoute extends _i41.PageRouteInfo<LikedPlaylistRouteArgs> {
required _i43.SpotubeSimplePlaylistObject playlist,
List<_i41.PageRouteInfo>? children,
}) : super(
LikedPlaylistRoute.name,
args: LikedPlaylistRouteArgs(key: key, playlist: playlist),
initialChildren: children,
);
LikedPlaylistRoute.name,
args: LikedPlaylistRouteArgs(key: key, playlist: playlist),
initialChildren: children,
);
static const String name = 'LikedPlaylistRoute';
@ -367,15 +367,15 @@ class LocalLibraryRoute extends _i41.PageRouteInfo<LocalLibraryRouteArgs> {
bool isCache = false,
List<_i41.PageRouteInfo>? children,
}) : super(
LocalLibraryRoute.name,
args: LocalLibraryRouteArgs(
location: location,
key: key,
isDownloads: isDownloads,
isCache: isCache,
),
initialChildren: children,
);
LocalLibraryRoute.name,
args: LocalLibraryRouteArgs(
location: location,
key: key,
isDownloads: isDownloads,
isCache: isCache,
),
initialChildren: children,
);
static const String name = 'LocalLibraryRoute';
@ -419,7 +419,7 @@ class LocalLibraryRouteArgs {
/// [_i14.LogsPage]
class LogsRoute extends _i41.PageRouteInfo<void> {
const LogsRoute({List<_i41.PageRouteInfo>? children})
: super(LogsRoute.name, initialChildren: children);
: super(LogsRoute.name, initialChildren: children);
static const String name = 'LogsRoute';
@ -435,7 +435,7 @@ class LogsRoute extends _i41.PageRouteInfo<void> {
/// [_i15.LyricsPage]
class LyricsRoute extends _i41.PageRouteInfo<void> {
const LyricsRoute({List<_i41.PageRouteInfo>? children})
: super(LyricsRoute.name, initialChildren: children);
: super(LyricsRoute.name, initialChildren: children);
static const String name = 'LyricsRoute';
@ -455,10 +455,10 @@ class MiniLyricsRoute extends _i41.PageRouteInfo<MiniLyricsRouteArgs> {
required _i44.Size prevSize,
List<_i41.PageRouteInfo>? children,
}) : super(
MiniLyricsRoute.name,
args: MiniLyricsRouteArgs(key: key, prevSize: prevSize),
initialChildren: children,
);
MiniLyricsRoute.name,
args: MiniLyricsRouteArgs(key: key, prevSize: prevSize),
initialChildren: children,
);
static const String name = 'MiniLyricsRoute';
@ -488,7 +488,7 @@ class MiniLyricsRouteArgs {
/// [_i17.PlayerLyricsPage]
class PlayerLyricsRoute extends _i41.PageRouteInfo<void> {
const PlayerLyricsRoute({List<_i41.PageRouteInfo>? children})
: super(PlayerLyricsRoute.name, initialChildren: children);
: super(PlayerLyricsRoute.name, initialChildren: children);
static const String name = 'PlayerLyricsRoute';
@ -504,7 +504,7 @@ class PlayerLyricsRoute extends _i41.PageRouteInfo<void> {
/// [_i18.PlayerQueuePage]
class PlayerQueueRoute extends _i41.PageRouteInfo<void> {
const PlayerQueueRoute({List<_i41.PageRouteInfo>? children})
: super(PlayerQueueRoute.name, initialChildren: children);
: super(PlayerQueueRoute.name, initialChildren: children);
static const String name = 'PlayerQueueRoute';
@ -520,7 +520,7 @@ class PlayerQueueRoute extends _i41.PageRouteInfo<void> {
/// [_i19.PlayerTrackSourcesPage]
class PlayerTrackSourcesRoute extends _i41.PageRouteInfo<void> {
const PlayerTrackSourcesRoute({List<_i41.PageRouteInfo>? children})
: super(PlayerTrackSourcesRoute.name, initialChildren: children);
: super(PlayerTrackSourcesRoute.name, initialChildren: children);
static const String name = 'PlayerTrackSourcesRoute';
@ -541,11 +541,11 @@ class PlaylistRoute extends _i41.PageRouteInfo<PlaylistRouteArgs> {
required _i43.SpotubeSimplePlaylistObject playlist,
List<_i41.PageRouteInfo>? children,
}) : super(
PlaylistRoute.name,
args: PlaylistRouteArgs(key: key, id: id, playlist: playlist),
rawPathParams: {'id': id},
initialChildren: children,
);
PlaylistRoute.name,
args: PlaylistRouteArgs(key: key, id: id, playlist: playlist),
rawPathParams: {'id': id},
initialChildren: children,
);
static const String name = 'PlaylistRoute';
@ -581,7 +581,7 @@ class PlaylistRouteArgs {
/// [_i21.ProfilePage]
class ProfileRoute extends _i41.PageRouteInfo<void> {
const ProfileRoute({List<_i41.PageRouteInfo>? children})
: super(ProfileRoute.name, initialChildren: children);
: super(ProfileRoute.name, initialChildren: children);
static const String name = 'ProfileRoute';
@ -597,7 +597,7 @@ class ProfileRoute extends _i41.PageRouteInfo<void> {
/// [_i22.RootAppPage]
class RootAppRoute extends _i41.PageRouteInfo<void> {
const RootAppRoute({List<_i41.PageRouteInfo>? children})
: super(RootAppRoute.name, initialChildren: children);
: super(RootAppRoute.name, initialChildren: children);
static const String name = 'RootAppRoute';
@ -613,7 +613,7 @@ class RootAppRoute extends _i41.PageRouteInfo<void> {
/// [_i23.SearchPage]
class SearchRoute extends _i41.PageRouteInfo<void> {
const SearchRoute({List<_i41.PageRouteInfo>? children})
: super(SearchRoute.name, initialChildren: children);
: super(SearchRoute.name, initialChildren: children);
static const String name = 'SearchRoute';
@ -635,14 +635,14 @@ class SettingsMetadataProviderFormRoute
required List<_i43.MetadataFormFieldObject> fields,
List<_i41.PageRouteInfo>? children,
}) : super(
SettingsMetadataProviderFormRoute.name,
args: SettingsMetadataProviderFormRouteArgs(
key: key,
title: title,
fields: fields,
),
initialChildren: children,
);
SettingsMetadataProviderFormRoute.name,
args: SettingsMetadataProviderFormRouteArgs(
key: key,
title: title,
fields: fields,
),
initialChildren: children,
);
static const String name = 'SettingsMetadataProviderFormRoute';
@ -682,7 +682,7 @@ class SettingsMetadataProviderFormRouteArgs {
/// [_i25.SettingsMetadataProviderPage]
class SettingsMetadataProviderRoute extends _i41.PageRouteInfo<void> {
const SettingsMetadataProviderRoute({List<_i41.PageRouteInfo>? children})
: super(SettingsMetadataProviderRoute.name, initialChildren: children);
: super(SettingsMetadataProviderRoute.name, initialChildren: children);
static const String name = 'SettingsMetadataProviderRoute';
@ -698,7 +698,7 @@ class SettingsMetadataProviderRoute extends _i41.PageRouteInfo<void> {
/// [_i26.SettingsPage]
class SettingsRoute extends _i41.PageRouteInfo<void> {
const SettingsRoute({List<_i41.PageRouteInfo>? children})
: super(SettingsRoute.name, initialChildren: children);
: super(SettingsRoute.name, initialChildren: children);
static const String name = 'SettingsRoute';
@ -714,7 +714,7 @@ class SettingsRoute extends _i41.PageRouteInfo<void> {
/// [_i27.SettingsScrobblingPage]
class SettingsScrobblingRoute extends _i41.PageRouteInfo<void> {
const SettingsScrobblingRoute({List<_i41.PageRouteInfo>? children})
: super(SettingsScrobblingRoute.name, initialChildren: children);
: super(SettingsScrobblingRoute.name, initialChildren: children);
static const String name = 'SettingsScrobblingRoute';
@ -730,7 +730,7 @@ class SettingsScrobblingRoute extends _i41.PageRouteInfo<void> {
/// [_i28.StatsAlbumsPage]
class StatsAlbumsRoute extends _i41.PageRouteInfo<void> {
const StatsAlbumsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsAlbumsRoute.name, initialChildren: children);
: super(StatsAlbumsRoute.name, initialChildren: children);
static const String name = 'StatsAlbumsRoute';
@ -746,7 +746,7 @@ class StatsAlbumsRoute extends _i41.PageRouteInfo<void> {
/// [_i29.StatsArtistsPage]
class StatsArtistsRoute extends _i41.PageRouteInfo<void> {
const StatsArtistsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsArtistsRoute.name, initialChildren: children);
: super(StatsArtistsRoute.name, initialChildren: children);
static const String name = 'StatsArtistsRoute';
@ -762,7 +762,7 @@ class StatsArtistsRoute extends _i41.PageRouteInfo<void> {
/// [_i30.StatsMinutesPage]
class StatsMinutesRoute extends _i41.PageRouteInfo<void> {
const StatsMinutesRoute({List<_i41.PageRouteInfo>? children})
: super(StatsMinutesRoute.name, initialChildren: children);
: super(StatsMinutesRoute.name, initialChildren: children);
static const String name = 'StatsMinutesRoute';
@ -778,7 +778,7 @@ class StatsMinutesRoute extends _i41.PageRouteInfo<void> {
/// [_i31.StatsPage]
class StatsRoute extends _i41.PageRouteInfo<void> {
const StatsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsRoute.name, initialChildren: children);
: super(StatsRoute.name, initialChildren: children);
static const String name = 'StatsRoute';
@ -794,7 +794,7 @@ class StatsRoute extends _i41.PageRouteInfo<void> {
/// [_i32.StatsPlaylistsPage]
class StatsPlaylistsRoute extends _i41.PageRouteInfo<void> {
const StatsPlaylistsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsPlaylistsRoute.name, initialChildren: children);
: super(StatsPlaylistsRoute.name, initialChildren: children);
static const String name = 'StatsPlaylistsRoute';
@ -810,7 +810,7 @@ class StatsPlaylistsRoute extends _i41.PageRouteInfo<void> {
/// [_i33.StatsStreamFeesPage]
class StatsStreamFeesRoute extends _i41.PageRouteInfo<void> {
const StatsStreamFeesRoute({List<_i41.PageRouteInfo>? children})
: super(StatsStreamFeesRoute.name, initialChildren: children);
: super(StatsStreamFeesRoute.name, initialChildren: children);
static const String name = 'StatsStreamFeesRoute';
@ -826,7 +826,7 @@ class StatsStreamFeesRoute extends _i41.PageRouteInfo<void> {
/// [_i34.StatsStreamsPage]
class StatsStreamsRoute extends _i41.PageRouteInfo<void> {
const StatsStreamsRoute({List<_i41.PageRouteInfo>? children})
: super(StatsStreamsRoute.name, initialChildren: children);
: super(StatsStreamsRoute.name, initialChildren: children);
static const String name = 'StatsStreamsRoute';
@ -846,11 +846,11 @@ class TrackRoute extends _i41.PageRouteInfo<TrackRouteArgs> {
required String trackId,
List<_i41.PageRouteInfo>? children,
}) : super(
TrackRoute.name,
args: TrackRouteArgs(key: key, trackId: trackId),
rawPathParams: {'id': trackId},
initialChildren: children,
);
TrackRoute.name,
args: TrackRouteArgs(key: key, trackId: trackId),
rawPathParams: {'id': trackId},
initialChildren: children,
);
static const String name = 'TrackRoute';
@ -883,7 +883,7 @@ class TrackRouteArgs {
/// [_i36.UserAlbumsPage]
class UserAlbumsRoute extends _i41.PageRouteInfo<void> {
const UserAlbumsRoute({List<_i41.PageRouteInfo>? children})
: super(UserAlbumsRoute.name, initialChildren: children);
: super(UserAlbumsRoute.name, initialChildren: children);
static const String name = 'UserAlbumsRoute';
@ -899,7 +899,7 @@ class UserAlbumsRoute extends _i41.PageRouteInfo<void> {
/// [_i37.UserArtistsPage]
class UserArtistsRoute extends _i41.PageRouteInfo<void> {
const UserArtistsRoute({List<_i41.PageRouteInfo>? children})
: super(UserArtistsRoute.name, initialChildren: children);
: super(UserArtistsRoute.name, initialChildren: children);
static const String name = 'UserArtistsRoute';
@ -915,7 +915,7 @@ class UserArtistsRoute extends _i41.PageRouteInfo<void> {
/// [_i38.UserDownloadsPage]
class UserDownloadsRoute extends _i41.PageRouteInfo<void> {
const UserDownloadsRoute({List<_i41.PageRouteInfo>? children})
: super(UserDownloadsRoute.name, initialChildren: children);
: super(UserDownloadsRoute.name, initialChildren: children);
static const String name = 'UserDownloadsRoute';
@ -931,7 +931,7 @@ class UserDownloadsRoute extends _i41.PageRouteInfo<void> {
/// [_i39.UserLocalLibraryPage]
class UserLocalLibraryRoute extends _i41.PageRouteInfo<void> {
const UserLocalLibraryRoute({List<_i41.PageRouteInfo>? children})
: super(UserLocalLibraryRoute.name, initialChildren: children);
: super(UserLocalLibraryRoute.name, initialChildren: children);
static const String name = 'UserLocalLibraryRoute';
@ -947,7 +947,7 @@ class UserLocalLibraryRoute extends _i41.PageRouteInfo<void> {
/// [_i40.UserPlaylistsPage]
class UserPlaylistsRoute extends _i41.PageRouteInfo<void> {
const UserPlaylistsRoute({List<_i41.PageRouteInfo>? children})
: super(UserPlaylistsRoute.name, initialChildren: children);
: super(UserPlaylistsRoute.name, initialChildren: children);
static const String name = 'UserPlaylistsRoute';

View File

@ -45,6 +45,9 @@ import 'package:spotube/services/kv_store/encrypted_kv_store.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/wm_tools/wm_tools.dart';
import 'package:spotube/src/rust/api/plugin/models/core.dart';
import 'package:spotube/src/rust/api/plugin/plugin.dart';
import 'package:spotube/src/rust/frb_generated.dart';
import 'package:spotube/utils/migrations/sandbox.dart';
import 'package:spotube/utils/platform.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
@ -55,6 +58,23 @@ import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:yt_dlp_dart/yt_dlp_dart.dart';
import 'package:flutter_new_pipe_extractor/flutter_new_pipe_extractor.dart';
const pluginJS = """
class CoreEndpoint {
async checkUpdate() {
console.log('Core checkUpdate');
}
support() {
return 'Metadata';
}
}
class TestingPlugin {
constructor() {
this.core = new CoreEndpoint();
}
}
""";
Future<void> main(List<String> rawArgs) async {
if (rawArgs.contains("web_view_title_bar")) {
WidgetsFlutterBinding.ensureInitialized();
@ -94,6 +114,26 @@ Future<void> main(List<String> rawArgs) async {
await KVStoreService.initialize();
await RustLib.init();
final plugin = SpotubePlugin();
final sender = SpotubePlugin.newContext(
pluginScript: pluginJS,
pluginConfig: const PluginConfiguration(
entryPoint: "TestingPlugin",
abilities: [PluginAbility.metadata],
apis: [],
author: "KRTirtho",
description: "Testing Plugin",
name: "Testing Plugin",
pluginApiVersion: "2.0.0",
repository: null,
version: "0.1.0",
),
);
await plugin.dispose(tx: sender);
if (kIsDesktop) {
await windowManager.setPreventClose(true);
await YtDlp.instance

View File

@ -0,0 +1,12 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `AlbumCommands`, `ArtistCommands`, `AudioSourceCommands`, `AuthCommands`, `BrowseCommands`, `CoreCommands`, `PlaylistCommands`, `SearchCommands`, `TrackCommands`, `UserCommands`
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<PluginCommand>>
abstract class PluginCommand implements RustOpaqueInterface {}

View File

@ -0,0 +1,58 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'artist.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'album.freezed.dart';
part 'album.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`
enum SpotubeAlbumType {
album,
single,
compilation,
;
}
@freezed
sealed class SpotubeFullAlbumObject with _$SpotubeFullAlbumObject {
const factory SpotubeFullAlbumObject({
required String typeName,
required String id,
required String name,
required List<SpotubeSimpleArtistObject> artists,
required List<SpotubeImageObject> images,
required String releaseDate,
required String externalUri,
required int totalTracks,
required SpotubeAlbumType albumType,
String? recordLabel,
List<String>? genres,
}) = _SpotubeFullAlbumObject;
factory SpotubeFullAlbumObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeFullAlbumObjectFromJson(json);
}
@freezed
sealed class SpotubeSimpleAlbumObject with _$SpotubeSimpleAlbumObject {
const factory SpotubeSimpleAlbumObject({
required String typeName,
required String id,
required String name,
required String externalUri,
required List<SpotubeSimpleArtistObject> artists,
required List<SpotubeImageObject> images,
required SpotubeAlbumType albumType,
String? releaseDate,
}) = _SpotubeSimpleAlbumObject;
factory SpotubeSimpleAlbumObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeSimpleAlbumObjectFromJson(json);
}

View File

@ -0,0 +1,742 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'album.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotubeFullAlbumObject _$SpotubeFullAlbumObjectFromJson(
Map<String, dynamic> json) {
return _SpotubeFullAlbumObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeFullAlbumObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
List<SpotubeSimpleArtistObject> get artists =>
throw _privateConstructorUsedError;
List<SpotubeImageObject> get images => throw _privateConstructorUsedError;
String get releaseDate => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
int get totalTracks => throw _privateConstructorUsedError;
SpotubeAlbumType get albumType => throw _privateConstructorUsedError;
String? get recordLabel => throw _privateConstructorUsedError;
List<String>? get genres => throw _privateConstructorUsedError;
/// Serializes this SpotubeFullAlbumObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeFullAlbumObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeFullAlbumObjectCopyWith<SpotubeFullAlbumObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeFullAlbumObjectCopyWith<$Res> {
factory $SpotubeFullAlbumObjectCopyWith(SpotubeFullAlbumObject value,
$Res Function(SpotubeFullAlbumObject) then) =
_$SpotubeFullAlbumObjectCopyWithImpl<$Res, SpotubeFullAlbumObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
List<SpotubeSimpleArtistObject> artists,
List<SpotubeImageObject> images,
String releaseDate,
String externalUri,
int totalTracks,
SpotubeAlbumType albumType,
String? recordLabel,
List<String>? genres});
}
/// @nodoc
class _$SpotubeFullAlbumObjectCopyWithImpl<$Res,
$Val extends SpotubeFullAlbumObject>
implements $SpotubeFullAlbumObjectCopyWith<$Res> {
_$SpotubeFullAlbumObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeFullAlbumObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? artists = null,
Object? images = null,
Object? releaseDate = null,
Object? externalUri = null,
Object? totalTracks = null,
Object? albumType = null,
Object? recordLabel = freezed,
Object? genres = freezed,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
artists: null == artists
? _value.artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleArtistObject>,
images: null == images
? _value.images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
releaseDate: null == releaseDate
? _value.releaseDate
: releaseDate // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
totalTracks: null == totalTracks
? _value.totalTracks
: totalTracks // ignore: cast_nullable_to_non_nullable
as int,
albumType: null == albumType
? _value.albumType
: albumType // ignore: cast_nullable_to_non_nullable
as SpotubeAlbumType,
recordLabel: freezed == recordLabel
? _value.recordLabel
: recordLabel // ignore: cast_nullable_to_non_nullable
as String?,
genres: freezed == genres
? _value.genres
: genres // ignore: cast_nullable_to_non_nullable
as List<String>?,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpotubeFullAlbumObjectImplCopyWith<$Res>
implements $SpotubeFullAlbumObjectCopyWith<$Res> {
factory _$$SpotubeFullAlbumObjectImplCopyWith(
_$SpotubeFullAlbumObjectImpl value,
$Res Function(_$SpotubeFullAlbumObjectImpl) then) =
__$$SpotubeFullAlbumObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
List<SpotubeSimpleArtistObject> artists,
List<SpotubeImageObject> images,
String releaseDate,
String externalUri,
int totalTracks,
SpotubeAlbumType albumType,
String? recordLabel,
List<String>? genres});
}
/// @nodoc
class __$$SpotubeFullAlbumObjectImplCopyWithImpl<$Res>
extends _$SpotubeFullAlbumObjectCopyWithImpl<$Res,
_$SpotubeFullAlbumObjectImpl>
implements _$$SpotubeFullAlbumObjectImplCopyWith<$Res> {
__$$SpotubeFullAlbumObjectImplCopyWithImpl(
_$SpotubeFullAlbumObjectImpl _value,
$Res Function(_$SpotubeFullAlbumObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeFullAlbumObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? artists = null,
Object? images = null,
Object? releaseDate = null,
Object? externalUri = null,
Object? totalTracks = null,
Object? albumType = null,
Object? recordLabel = freezed,
Object? genres = freezed,
}) {
return _then(_$SpotubeFullAlbumObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
artists: null == artists
? _value._artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleArtistObject>,
images: null == images
? _value._images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
releaseDate: null == releaseDate
? _value.releaseDate
: releaseDate // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
totalTracks: null == totalTracks
? _value.totalTracks
: totalTracks // ignore: cast_nullable_to_non_nullable
as int,
albumType: null == albumType
? _value.albumType
: albumType // ignore: cast_nullable_to_non_nullable
as SpotubeAlbumType,
recordLabel: freezed == recordLabel
? _value.recordLabel
: recordLabel // ignore: cast_nullable_to_non_nullable
as String?,
genres: freezed == genres
? _value._genres
: genres // ignore: cast_nullable_to_non_nullable
as List<String>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeFullAlbumObjectImpl implements _SpotubeFullAlbumObject {
const _$SpotubeFullAlbumObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required final List<SpotubeSimpleArtistObject> artists,
required final List<SpotubeImageObject> images,
required this.releaseDate,
required this.externalUri,
required this.totalTracks,
required this.albumType,
this.recordLabel,
final List<String>? genres})
: _artists = artists,
_images = images,
_genres = genres;
factory _$SpotubeFullAlbumObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeFullAlbumObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
final List<SpotubeSimpleArtistObject> _artists;
@override
List<SpotubeSimpleArtistObject> get artists {
if (_artists is EqualUnmodifiableListView) return _artists;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_artists);
}
final List<SpotubeImageObject> _images;
@override
List<SpotubeImageObject> get images {
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_images);
}
@override
final String releaseDate;
@override
final String externalUri;
@override
final int totalTracks;
@override
final SpotubeAlbumType albumType;
@override
final String? recordLabel;
final List<String>? _genres;
@override
List<String>? get genres {
final value = _genres;
if (value == null) return null;
if (_genres is EqualUnmodifiableListView) return _genres;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
String toString() {
return 'SpotubeFullAlbumObject(typeName: $typeName, id: $id, name: $name, artists: $artists, images: $images, releaseDate: $releaseDate, externalUri: $externalUri, totalTracks: $totalTracks, albumType: $albumType, recordLabel: $recordLabel, genres: $genres)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeFullAlbumObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
const DeepCollectionEquality().equals(other._artists, _artists) &&
const DeepCollectionEquality().equals(other._images, _images) &&
(identical(other.releaseDate, releaseDate) ||
other.releaseDate == releaseDate) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri) &&
(identical(other.totalTracks, totalTracks) ||
other.totalTracks == totalTracks) &&
(identical(other.albumType, albumType) ||
other.albumType == albumType) &&
(identical(other.recordLabel, recordLabel) ||
other.recordLabel == recordLabel) &&
const DeepCollectionEquality().equals(other._genres, _genres));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
typeName,
id,
name,
const DeepCollectionEquality().hash(_artists),
const DeepCollectionEquality().hash(_images),
releaseDate,
externalUri,
totalTracks,
albumType,
recordLabel,
const DeepCollectionEquality().hash(_genres));
/// Create a copy of SpotubeFullAlbumObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeFullAlbumObjectImplCopyWith<_$SpotubeFullAlbumObjectImpl>
get copyWith => __$$SpotubeFullAlbumObjectImplCopyWithImpl<
_$SpotubeFullAlbumObjectImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeFullAlbumObjectImplToJson(
this,
);
}
}
abstract class _SpotubeFullAlbumObject implements SpotubeFullAlbumObject {
const factory _SpotubeFullAlbumObject(
{required final String typeName,
required final String id,
required final String name,
required final List<SpotubeSimpleArtistObject> artists,
required final List<SpotubeImageObject> images,
required final String releaseDate,
required final String externalUri,
required final int totalTracks,
required final SpotubeAlbumType albumType,
final String? recordLabel,
final List<String>? genres}) = _$SpotubeFullAlbumObjectImpl;
factory _SpotubeFullAlbumObject.fromJson(Map<String, dynamic> json) =
_$SpotubeFullAlbumObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
List<SpotubeSimpleArtistObject> get artists;
@override
List<SpotubeImageObject> get images;
@override
String get releaseDate;
@override
String get externalUri;
@override
int get totalTracks;
@override
SpotubeAlbumType get albumType;
@override
String? get recordLabel;
@override
List<String>? get genres;
/// Create a copy of SpotubeFullAlbumObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeFullAlbumObjectImplCopyWith<_$SpotubeFullAlbumObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}
SpotubeSimpleAlbumObject _$SpotubeSimpleAlbumObjectFromJson(
Map<String, dynamic> json) {
return _SpotubeSimpleAlbumObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeSimpleAlbumObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
List<SpotubeSimpleArtistObject> get artists =>
throw _privateConstructorUsedError;
List<SpotubeImageObject> get images => throw _privateConstructorUsedError;
SpotubeAlbumType get albumType => throw _privateConstructorUsedError;
String? get releaseDate => throw _privateConstructorUsedError;
/// Serializes this SpotubeSimpleAlbumObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeSimpleAlbumObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeSimpleAlbumObjectCopyWith<SpotubeSimpleAlbumObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeSimpleAlbumObjectCopyWith<$Res> {
factory $SpotubeSimpleAlbumObjectCopyWith(SpotubeSimpleAlbumObject value,
$Res Function(SpotubeSimpleAlbumObject) then) =
_$SpotubeSimpleAlbumObjectCopyWithImpl<$Res, SpotubeSimpleAlbumObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeSimpleArtistObject> artists,
List<SpotubeImageObject> images,
SpotubeAlbumType albumType,
String? releaseDate});
}
/// @nodoc
class _$SpotubeSimpleAlbumObjectCopyWithImpl<$Res,
$Val extends SpotubeSimpleAlbumObject>
implements $SpotubeSimpleAlbumObjectCopyWith<$Res> {
_$SpotubeSimpleAlbumObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeSimpleAlbumObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? artists = null,
Object? images = null,
Object? albumType = null,
Object? releaseDate = freezed,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
artists: null == artists
? _value.artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleArtistObject>,
images: null == images
? _value.images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
albumType: null == albumType
? _value.albumType
: albumType // ignore: cast_nullable_to_non_nullable
as SpotubeAlbumType,
releaseDate: freezed == releaseDate
? _value.releaseDate
: releaseDate // ignore: cast_nullable_to_non_nullable
as String?,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpotubeSimpleAlbumObjectImplCopyWith<$Res>
implements $SpotubeSimpleAlbumObjectCopyWith<$Res> {
factory _$$SpotubeSimpleAlbumObjectImplCopyWith(
_$SpotubeSimpleAlbumObjectImpl value,
$Res Function(_$SpotubeSimpleAlbumObjectImpl) then) =
__$$SpotubeSimpleAlbumObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeSimpleArtistObject> artists,
List<SpotubeImageObject> images,
SpotubeAlbumType albumType,
String? releaseDate});
}
/// @nodoc
class __$$SpotubeSimpleAlbumObjectImplCopyWithImpl<$Res>
extends _$SpotubeSimpleAlbumObjectCopyWithImpl<$Res,
_$SpotubeSimpleAlbumObjectImpl>
implements _$$SpotubeSimpleAlbumObjectImplCopyWith<$Res> {
__$$SpotubeSimpleAlbumObjectImplCopyWithImpl(
_$SpotubeSimpleAlbumObjectImpl _value,
$Res Function(_$SpotubeSimpleAlbumObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeSimpleAlbumObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? artists = null,
Object? images = null,
Object? albumType = null,
Object? releaseDate = freezed,
}) {
return _then(_$SpotubeSimpleAlbumObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
artists: null == artists
? _value._artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleArtistObject>,
images: null == images
? _value._images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
albumType: null == albumType
? _value.albumType
: albumType // ignore: cast_nullable_to_non_nullable
as SpotubeAlbumType,
releaseDate: freezed == releaseDate
? _value.releaseDate
: releaseDate // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeSimpleAlbumObjectImpl implements _SpotubeSimpleAlbumObject {
const _$SpotubeSimpleAlbumObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required this.externalUri,
required final List<SpotubeSimpleArtistObject> artists,
required final List<SpotubeImageObject> images,
required this.albumType,
this.releaseDate})
: _artists = artists,
_images = images;
factory _$SpotubeSimpleAlbumObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeSimpleAlbumObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
@override
final String externalUri;
final List<SpotubeSimpleArtistObject> _artists;
@override
List<SpotubeSimpleArtistObject> get artists {
if (_artists is EqualUnmodifiableListView) return _artists;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_artists);
}
final List<SpotubeImageObject> _images;
@override
List<SpotubeImageObject> get images {
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_images);
}
@override
final SpotubeAlbumType albumType;
@override
final String? releaseDate;
@override
String toString() {
return 'SpotubeSimpleAlbumObject(typeName: $typeName, id: $id, name: $name, externalUri: $externalUri, artists: $artists, images: $images, albumType: $albumType, releaseDate: $releaseDate)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeSimpleAlbumObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri) &&
const DeepCollectionEquality().equals(other._artists, _artists) &&
const DeepCollectionEquality().equals(other._images, _images) &&
(identical(other.albumType, albumType) ||
other.albumType == albumType) &&
(identical(other.releaseDate, releaseDate) ||
other.releaseDate == releaseDate));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
typeName,
id,
name,
externalUri,
const DeepCollectionEquality().hash(_artists),
const DeepCollectionEquality().hash(_images),
albumType,
releaseDate);
/// Create a copy of SpotubeSimpleAlbumObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeSimpleAlbumObjectImplCopyWith<_$SpotubeSimpleAlbumObjectImpl>
get copyWith => __$$SpotubeSimpleAlbumObjectImplCopyWithImpl<
_$SpotubeSimpleAlbumObjectImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeSimpleAlbumObjectImplToJson(
this,
);
}
}
abstract class _SpotubeSimpleAlbumObject implements SpotubeSimpleAlbumObject {
const factory _SpotubeSimpleAlbumObject(
{required final String typeName,
required final String id,
required final String name,
required final String externalUri,
required final List<SpotubeSimpleArtistObject> artists,
required final List<SpotubeImageObject> images,
required final SpotubeAlbumType albumType,
final String? releaseDate}) = _$SpotubeSimpleAlbumObjectImpl;
factory _SpotubeSimpleAlbumObject.fromJson(Map<String, dynamic> json) =
_$SpotubeSimpleAlbumObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
String get externalUri;
@override
List<SpotubeSimpleArtistObject> get artists;
@override
List<SpotubeImageObject> get images;
@override
SpotubeAlbumType get albumType;
@override
String? get releaseDate;
/// Create a copy of SpotubeSimpleAlbumObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeSimpleAlbumObjectImplCopyWith<_$SpotubeSimpleAlbumObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,83 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'album.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeFullAlbumObjectImpl _$$SpotubeFullAlbumObjectImplFromJson(Map json) =>
_$SpotubeFullAlbumObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
artists: (json['artists'] as List<dynamic>)
.map((e) => SpotubeSimpleArtistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
images: (json['images'] as List<dynamic>)
.map((e) =>
SpotubeImageObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
releaseDate: json['releaseDate'] as String,
externalUri: json['externalUri'] as String,
totalTracks: (json['totalTracks'] as num).toInt(),
albumType: $enumDecode(_$SpotubeAlbumTypeEnumMap, json['albumType']),
recordLabel: json['recordLabel'] as String?,
genres:
(json['genres'] as List<dynamic>?)?.map((e) => e as String).toList(),
);
Map<String, dynamic> _$$SpotubeFullAlbumObjectImplToJson(
_$SpotubeFullAlbumObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'artists': instance.artists.map((e) => e.toJson()).toList(),
'images': instance.images.map((e) => e.toJson()).toList(),
'releaseDate': instance.releaseDate,
'externalUri': instance.externalUri,
'totalTracks': instance.totalTracks,
'albumType': _$SpotubeAlbumTypeEnumMap[instance.albumType]!,
'recordLabel': instance.recordLabel,
'genres': instance.genres,
};
const _$SpotubeAlbumTypeEnumMap = {
SpotubeAlbumType.album: 'album',
SpotubeAlbumType.single: 'single',
SpotubeAlbumType.compilation: 'compilation',
};
_$SpotubeSimpleAlbumObjectImpl _$$SpotubeSimpleAlbumObjectImplFromJson(
Map json) =>
_$SpotubeSimpleAlbumObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
externalUri: json['externalUri'] as String,
artists: (json['artists'] as List<dynamic>)
.map((e) => SpotubeSimpleArtistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
images: (json['images'] as List<dynamic>)
.map((e) =>
SpotubeImageObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
albumType: $enumDecode(_$SpotubeAlbumTypeEnumMap, json['albumType']),
releaseDate: json['releaseDate'] as String?,
);
Map<String, dynamic> _$$SpotubeSimpleAlbumObjectImplToJson(
_$SpotubeSimpleAlbumObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'externalUri': instance.externalUri,
'artists': instance.artists.map((e) => e.toJson()).toList(),
'images': instance.images.map((e) => e.toJson()).toList(),
'albumType': _$SpotubeAlbumTypeEnumMap[instance.albumType]!,
'releaseDate': instance.releaseDate,
};

View File

@ -0,0 +1,43 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'artist.freezed.dart';
part 'artist.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `fmt`, `fmt`
@freezed
sealed class SpotubeFullArtistObject with _$SpotubeFullArtistObject {
const factory SpotubeFullArtistObject({
required String typeName,
required String id,
required String name,
required String externalUri,
required List<SpotubeImageObject> images,
List<String>? genres,
int? followers,
}) = _SpotubeFullArtistObject;
factory SpotubeFullArtistObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeFullArtistObjectFromJson(json);
}
@freezed
sealed class SpotubeSimpleArtistObject with _$SpotubeSimpleArtistObject {
const factory SpotubeSimpleArtistObject({
required String typeName,
required String id,
required String name,
required String externalUri,
List<SpotubeImageObject>? images,
}) = _SpotubeSimpleArtistObject;
factory SpotubeSimpleArtistObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeSimpleArtistObjectFromJson(json);
}

View File

@ -0,0 +1,572 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'artist.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotubeFullArtistObject _$SpotubeFullArtistObjectFromJson(
Map<String, dynamic> json) {
return _SpotubeFullArtistObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeFullArtistObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
List<SpotubeImageObject> get images => throw _privateConstructorUsedError;
List<String>? get genres => throw _privateConstructorUsedError;
int? get followers => throw _privateConstructorUsedError;
/// Serializes this SpotubeFullArtistObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeFullArtistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeFullArtistObjectCopyWith<SpotubeFullArtistObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeFullArtistObjectCopyWith<$Res> {
factory $SpotubeFullArtistObjectCopyWith(SpotubeFullArtistObject value,
$Res Function(SpotubeFullArtistObject) then) =
_$SpotubeFullArtistObjectCopyWithImpl<$Res, SpotubeFullArtistObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeImageObject> images,
List<String>? genres,
int? followers});
}
/// @nodoc
class _$SpotubeFullArtistObjectCopyWithImpl<$Res,
$Val extends SpotubeFullArtistObject>
implements $SpotubeFullArtistObjectCopyWith<$Res> {
_$SpotubeFullArtistObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeFullArtistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? images = null,
Object? genres = freezed,
Object? followers = freezed,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
images: null == images
? _value.images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
genres: freezed == genres
? _value.genres
: genres // ignore: cast_nullable_to_non_nullable
as List<String>?,
followers: freezed == followers
? _value.followers
: followers // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpotubeFullArtistObjectImplCopyWith<$Res>
implements $SpotubeFullArtistObjectCopyWith<$Res> {
factory _$$SpotubeFullArtistObjectImplCopyWith(
_$SpotubeFullArtistObjectImpl value,
$Res Function(_$SpotubeFullArtistObjectImpl) then) =
__$$SpotubeFullArtistObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeImageObject> images,
List<String>? genres,
int? followers});
}
/// @nodoc
class __$$SpotubeFullArtistObjectImplCopyWithImpl<$Res>
extends _$SpotubeFullArtistObjectCopyWithImpl<$Res,
_$SpotubeFullArtistObjectImpl>
implements _$$SpotubeFullArtistObjectImplCopyWith<$Res> {
__$$SpotubeFullArtistObjectImplCopyWithImpl(
_$SpotubeFullArtistObjectImpl _value,
$Res Function(_$SpotubeFullArtistObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeFullArtistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? images = null,
Object? genres = freezed,
Object? followers = freezed,
}) {
return _then(_$SpotubeFullArtistObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
images: null == images
? _value._images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
genres: freezed == genres
? _value._genres
: genres // ignore: cast_nullable_to_non_nullable
as List<String>?,
followers: freezed == followers
? _value.followers
: followers // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeFullArtistObjectImpl implements _SpotubeFullArtistObject {
const _$SpotubeFullArtistObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required this.externalUri,
required final List<SpotubeImageObject> images,
final List<String>? genres,
this.followers})
: _images = images,
_genres = genres;
factory _$SpotubeFullArtistObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeFullArtistObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
@override
final String externalUri;
final List<SpotubeImageObject> _images;
@override
List<SpotubeImageObject> get images {
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_images);
}
final List<String>? _genres;
@override
List<String>? get genres {
final value = _genres;
if (value == null) return null;
if (_genres is EqualUnmodifiableListView) return _genres;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
final int? followers;
@override
String toString() {
return 'SpotubeFullArtistObject(typeName: $typeName, id: $id, name: $name, externalUri: $externalUri, images: $images, genres: $genres, followers: $followers)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeFullArtistObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri) &&
const DeepCollectionEquality().equals(other._images, _images) &&
const DeepCollectionEquality().equals(other._genres, _genres) &&
(identical(other.followers, followers) ||
other.followers == followers));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
typeName,
id,
name,
externalUri,
const DeepCollectionEquality().hash(_images),
const DeepCollectionEquality().hash(_genres),
followers);
/// Create a copy of SpotubeFullArtistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeFullArtistObjectImplCopyWith<_$SpotubeFullArtistObjectImpl>
get copyWith => __$$SpotubeFullArtistObjectImplCopyWithImpl<
_$SpotubeFullArtistObjectImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeFullArtistObjectImplToJson(
this,
);
}
}
abstract class _SpotubeFullArtistObject implements SpotubeFullArtistObject {
const factory _SpotubeFullArtistObject(
{required final String typeName,
required final String id,
required final String name,
required final String externalUri,
required final List<SpotubeImageObject> images,
final List<String>? genres,
final int? followers}) = _$SpotubeFullArtistObjectImpl;
factory _SpotubeFullArtistObject.fromJson(Map<String, dynamic> json) =
_$SpotubeFullArtistObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
String get externalUri;
@override
List<SpotubeImageObject> get images;
@override
List<String>? get genres;
@override
int? get followers;
/// Create a copy of SpotubeFullArtistObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeFullArtistObjectImplCopyWith<_$SpotubeFullArtistObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}
SpotubeSimpleArtistObject _$SpotubeSimpleArtistObjectFromJson(
Map<String, dynamic> json) {
return _SpotubeSimpleArtistObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeSimpleArtistObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
List<SpotubeImageObject>? get images => throw _privateConstructorUsedError;
/// Serializes this SpotubeSimpleArtistObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeSimpleArtistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeSimpleArtistObjectCopyWith<SpotubeSimpleArtistObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeSimpleArtistObjectCopyWith<$Res> {
factory $SpotubeSimpleArtistObjectCopyWith(SpotubeSimpleArtistObject value,
$Res Function(SpotubeSimpleArtistObject) then) =
_$SpotubeSimpleArtistObjectCopyWithImpl<$Res, SpotubeSimpleArtistObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeImageObject>? images});
}
/// @nodoc
class _$SpotubeSimpleArtistObjectCopyWithImpl<$Res,
$Val extends SpotubeSimpleArtistObject>
implements $SpotubeSimpleArtistObjectCopyWith<$Res> {
_$SpotubeSimpleArtistObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeSimpleArtistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? images = freezed,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
images: freezed == images
? _value.images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>?,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpotubeSimpleArtistObjectImplCopyWith<$Res>
implements $SpotubeSimpleArtistObjectCopyWith<$Res> {
factory _$$SpotubeSimpleArtistObjectImplCopyWith(
_$SpotubeSimpleArtistObjectImpl value,
$Res Function(_$SpotubeSimpleArtistObjectImpl) then) =
__$$SpotubeSimpleArtistObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeImageObject>? images});
}
/// @nodoc
class __$$SpotubeSimpleArtistObjectImplCopyWithImpl<$Res>
extends _$SpotubeSimpleArtistObjectCopyWithImpl<$Res,
_$SpotubeSimpleArtistObjectImpl>
implements _$$SpotubeSimpleArtistObjectImplCopyWith<$Res> {
__$$SpotubeSimpleArtistObjectImplCopyWithImpl(
_$SpotubeSimpleArtistObjectImpl _value,
$Res Function(_$SpotubeSimpleArtistObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeSimpleArtistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? images = freezed,
}) {
return _then(_$SpotubeSimpleArtistObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
images: freezed == images
? _value._images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeSimpleArtistObjectImpl implements _SpotubeSimpleArtistObject {
const _$SpotubeSimpleArtistObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required this.externalUri,
final List<SpotubeImageObject>? images})
: _images = images;
factory _$SpotubeSimpleArtistObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeSimpleArtistObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
@override
final String externalUri;
final List<SpotubeImageObject>? _images;
@override
List<SpotubeImageObject>? get images {
final value = _images;
if (value == null) return null;
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override
String toString() {
return 'SpotubeSimpleArtistObject(typeName: $typeName, id: $id, name: $name, externalUri: $externalUri, images: $images)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeSimpleArtistObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri) &&
const DeepCollectionEquality().equals(other._images, _images));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, typeName, id, name, externalUri,
const DeepCollectionEquality().hash(_images));
/// Create a copy of SpotubeSimpleArtistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeSimpleArtistObjectImplCopyWith<_$SpotubeSimpleArtistObjectImpl>
get copyWith => __$$SpotubeSimpleArtistObjectImplCopyWithImpl<
_$SpotubeSimpleArtistObjectImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeSimpleArtistObjectImplToJson(
this,
);
}
}
abstract class _SpotubeSimpleArtistObject implements SpotubeSimpleArtistObject {
const factory _SpotubeSimpleArtistObject(
{required final String typeName,
required final String id,
required final String name,
required final String externalUri,
final List<SpotubeImageObject>? images}) =
_$SpotubeSimpleArtistObjectImpl;
factory _SpotubeSimpleArtistObject.fromJson(Map<String, dynamic> json) =
_$SpotubeSimpleArtistObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
String get externalUri;
@override
List<SpotubeImageObject>? get images;
/// Create a copy of SpotubeSimpleArtistObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeSimpleArtistObjectImplCopyWith<_$SpotubeSimpleArtistObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,58 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'artist.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeFullArtistObjectImpl _$$SpotubeFullArtistObjectImplFromJson(
Map json) =>
_$SpotubeFullArtistObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
externalUri: json['externalUri'] as String,
images: (json['images'] as List<dynamic>)
.map((e) =>
SpotubeImageObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
genres:
(json['genres'] as List<dynamic>?)?.map((e) => e as String).toList(),
followers: (json['followers'] as num?)?.toInt(),
);
Map<String, dynamic> _$$SpotubeFullArtistObjectImplToJson(
_$SpotubeFullArtistObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'externalUri': instance.externalUri,
'images': instance.images.map((e) => e.toJson()).toList(),
'genres': instance.genres,
'followers': instance.followers,
};
_$SpotubeSimpleArtistObjectImpl _$$SpotubeSimpleArtistObjectImplFromJson(
Map json) =>
_$SpotubeSimpleArtistObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
externalUri: json['externalUri'] as String,
images: (json['images'] as List<dynamic>?)
?.map((e) =>
SpotubeImageObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
);
Map<String, dynamic> _$$SpotubeSimpleArtistObjectImplToJson(
_$SpotubeSimpleArtistObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'externalUri': instance.externalUri,
'images': instance.images?.map((e) => e.toJson()).toList(),
};

View File

@ -0,0 +1,114 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'audio_source.freezed.dart';
part 'audio_source.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`
@freezed
sealed class SpotubeAudioLosslessContainerQuality
with _$SpotubeAudioLosslessContainerQuality {
const SpotubeAudioLosslessContainerQuality._();
const factory SpotubeAudioLosslessContainerQuality({
required int bitDepth,
required int sampleRate,
}) = _SpotubeAudioLosslessContainerQuality;
String toStringFmt() => RustLib.instance.api
.crateApiPluginModelsAudioSourceSpotubeAudioLosslessContainerQualityToStringFmt(
that: this,
);
factory SpotubeAudioLosslessContainerQuality.fromJson(
Map<String, dynamic> json) =>
_$SpotubeAudioLosslessContainerQualityFromJson(json);
}
@freezed
sealed class SpotubeAudioLossyContainerQuality
with _$SpotubeAudioLossyContainerQuality {
const SpotubeAudioLossyContainerQuality._();
const factory SpotubeAudioLossyContainerQuality({
required int bitrate,
}) = _SpotubeAudioLossyContainerQuality;
Future<String> toStringFmt() => RustLib.instance.api
.crateApiPluginModelsAudioSourceSpotubeAudioLossyContainerQualityToStringFmt(
that: this,
);
factory SpotubeAudioLossyContainerQuality.fromJson(
Map<String, dynamic> json) =>
_$SpotubeAudioLossyContainerQualityFromJson(json);
}
@freezed
sealed class SpotubeAudioSourceContainerPreset
with _$SpotubeAudioSourceContainerPreset {
const SpotubeAudioSourceContainerPreset._();
const factory SpotubeAudioSourceContainerPreset.lossy({
required SpotubeMediaCompressionType compressionType,
required String name,
required List<SpotubeAudioLossyContainerQuality> qualities,
}) = SpotubeAudioSourceContainerPreset_Lossy;
const factory SpotubeAudioSourceContainerPreset.lossless({
required SpotubeMediaCompressionType compressionType,
required String name,
required List<SpotubeAudioLosslessContainerQuality> qualities,
}) = SpotubeAudioSourceContainerPreset_Lossless;
factory SpotubeAudioSourceContainerPreset.fromJson(
Map<String, dynamic> json) =>
_$SpotubeAudioSourceContainerPresetFromJson(json);
String fileExtension() => RustLib.instance.api
.crateApiPluginModelsAudioSourceSpotubeAudioSourceContainerPresetFileExtension(
that: this,
);
}
@freezed
sealed class SpotubeAudioSourceMatchObject
with _$SpotubeAudioSourceMatchObject {
const factory SpotubeAudioSourceMatchObject({
required String typeName,
required String id,
required String title,
required List<String> artists,
required BigInt duration,
String? thumbnail,
required String externalUri,
}) = _SpotubeAudioSourceMatchObject;
factory SpotubeAudioSourceMatchObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeAudioSourceMatchObjectFromJson(json);
}
@freezed
sealed class SpotubeAudioSourceStreamObject
with _$SpotubeAudioSourceStreamObject {
const factory SpotubeAudioSourceStreamObject({
required String typeName,
required String url,
required String container,
required SpotubeMediaCompressionType compressionType,
String? codec,
double? bitrate,
int? bitDepth,
double? sampleRate,
}) = _SpotubeAudioSourceStreamObject;
factory SpotubeAudioSourceStreamObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeAudioSourceStreamObjectFromJson(json);
}
enum SpotubeMediaCompressionType {
lossy,
lossless,
;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,138 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'audio_source.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeAudioLosslessContainerQualityImpl
_$$SpotubeAudioLosslessContainerQualityImplFromJson(Map json) =>
_$SpotubeAudioLosslessContainerQualityImpl(
bitDepth: (json['bitDepth'] as num).toInt(),
sampleRate: (json['sampleRate'] as num).toInt(),
);
Map<String, dynamic> _$$SpotubeAudioLosslessContainerQualityImplToJson(
_$SpotubeAudioLosslessContainerQualityImpl instance) =>
<String, dynamic>{
'bitDepth': instance.bitDepth,
'sampleRate': instance.sampleRate,
};
_$SpotubeAudioLossyContainerQualityImpl
_$$SpotubeAudioLossyContainerQualityImplFromJson(Map json) =>
_$SpotubeAudioLossyContainerQualityImpl(
bitrate: (json['bitrate'] as num).toInt(),
);
Map<String, dynamic> _$$SpotubeAudioLossyContainerQualityImplToJson(
_$SpotubeAudioLossyContainerQualityImpl instance) =>
<String, dynamic>{
'bitrate': instance.bitrate,
};
_$SpotubeAudioSourceContainerPreset_LossyImpl
_$$SpotubeAudioSourceContainerPreset_LossyImplFromJson(Map json) =>
_$SpotubeAudioSourceContainerPreset_LossyImpl(
compressionType: $enumDecode(
_$SpotubeMediaCompressionTypeEnumMap, json['compressionType']),
name: json['name'] as String,
qualities: (json['qualities'] as List<dynamic>)
.map((e) => SpotubeAudioLossyContainerQuality.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$SpotubeAudioSourceContainerPreset_LossyImplToJson(
_$SpotubeAudioSourceContainerPreset_LossyImpl instance) =>
<String, dynamic>{
'compressionType':
_$SpotubeMediaCompressionTypeEnumMap[instance.compressionType]!,
'name': instance.name,
'qualities': instance.qualities.map((e) => e.toJson()).toList(),
'runtimeType': instance.$type,
};
const _$SpotubeMediaCompressionTypeEnumMap = {
SpotubeMediaCompressionType.lossy: 'lossy',
SpotubeMediaCompressionType.lossless: 'lossless',
};
_$SpotubeAudioSourceContainerPreset_LosslessImpl
_$$SpotubeAudioSourceContainerPreset_LosslessImplFromJson(Map json) =>
_$SpotubeAudioSourceContainerPreset_LosslessImpl(
compressionType: $enumDecode(
_$SpotubeMediaCompressionTypeEnumMap, json['compressionType']),
name: json['name'] as String,
qualities: (json['qualities'] as List<dynamic>)
.map((e) => SpotubeAudioLosslessContainerQuality.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$SpotubeAudioSourceContainerPreset_LosslessImplToJson(
_$SpotubeAudioSourceContainerPreset_LosslessImpl instance) =>
<String, dynamic>{
'compressionType':
_$SpotubeMediaCompressionTypeEnumMap[instance.compressionType]!,
'name': instance.name,
'qualities': instance.qualities.map((e) => e.toJson()).toList(),
'runtimeType': instance.$type,
};
_$SpotubeAudioSourceMatchObjectImpl
_$$SpotubeAudioSourceMatchObjectImplFromJson(Map json) =>
_$SpotubeAudioSourceMatchObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
title: json['title'] as String,
artists: (json['artists'] as List<dynamic>)
.map((e) => e as String)
.toList(),
duration: BigInt.parse(json['duration'] as String),
thumbnail: json['thumbnail'] as String?,
externalUri: json['externalUri'] as String,
);
Map<String, dynamic> _$$SpotubeAudioSourceMatchObjectImplToJson(
_$SpotubeAudioSourceMatchObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'title': instance.title,
'artists': instance.artists,
'duration': instance.duration.toString(),
'thumbnail': instance.thumbnail,
'externalUri': instance.externalUri,
};
_$SpotubeAudioSourceStreamObjectImpl
_$$SpotubeAudioSourceStreamObjectImplFromJson(Map json) =>
_$SpotubeAudioSourceStreamObjectImpl(
typeName: json['typeName'] as String,
url: json['url'] as String,
container: json['container'] as String,
compressionType: $enumDecode(
_$SpotubeMediaCompressionTypeEnumMap, json['compressionType']),
codec: json['codec'] as String?,
bitrate: (json['bitrate'] as num?)?.toDouble(),
bitDepth: (json['bitDepth'] as num?)?.toInt(),
sampleRate: (json['sampleRate'] as num?)?.toDouble(),
);
Map<String, dynamic> _$$SpotubeAudioSourceStreamObjectImplToJson(
_$SpotubeAudioSourceStreamObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'url': instance.url,
'container': instance.container,
'compressionType':
_$SpotubeMediaCompressionTypeEnumMap[instance.compressionType]!,
'codec': instance.codec,
'bitrate': instance.bitrate,
'bitDepth': instance.bitDepth,
'sampleRate': instance.sampleRate,
};

View File

@ -0,0 +1,57 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'album.dart';
import 'artist.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
import 'playlist.dart';
import 'track.dart';
import 'user.dart';
part 'browse.freezed.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `fmt`, `fmt`
@freezed
sealed class SpotubeBrowseSectionObject with _$SpotubeBrowseSectionObject {
const factory SpotubeBrowseSectionObject({
required String typeName,
required String id,
required String title,
required String externalUri,
required bool browseMore,
required List<SpotubeBrowseSectionResponseObjectItem> items,
}) = _SpotubeBrowseSectionObject;
}
@freezed
sealed class SpotubeBrowseSectionResponseObjectItem
with _$SpotubeBrowseSectionResponseObjectItem {
const SpotubeBrowseSectionResponseObjectItem._();
const factory SpotubeBrowseSectionResponseObjectItem.track(
SpotubeTrackObject field0,
) = SpotubeBrowseSectionResponseObjectItem_Track;
const factory SpotubeBrowseSectionResponseObjectItem.playlistFull(
SpotubeFullPlaylistObject field0,
) = SpotubeBrowseSectionResponseObjectItem_PlaylistFull;
const factory SpotubeBrowseSectionResponseObjectItem.playlistSimple(
SpotubeSimplePlaylistObject field0,
) = SpotubeBrowseSectionResponseObjectItem_PlaylistSimple;
const factory SpotubeBrowseSectionResponseObjectItem.albumSimple(
SpotubeSimpleAlbumObject field0,
) = SpotubeBrowseSectionResponseObjectItem_AlbumSimple;
const factory SpotubeBrowseSectionResponseObjectItem.albumFull(
SpotubeFullAlbumObject field0,
) = SpotubeBrowseSectionResponseObjectItem_AlbumFull;
const factory SpotubeBrowseSectionResponseObjectItem.artistFull(
SpotubeFullArtistObject field0,
) = SpotubeBrowseSectionResponseObjectItem_ArtistFull;
const factory SpotubeBrowseSectionResponseObjectItem.artistSimple(
SpotubeSimpleArtistObject field0,
) = SpotubeBrowseSectionResponseObjectItem_ArtistSimple;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,190 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `cmp`, `cmp`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `partial_cmp`, `partial_cmp`
enum PluginAbility {
authentication,
scrobbling,
metadata,
audioSource,
;
}
enum PluginApi {
webview,
localstorage,
timezone,
;
}
class PluginConfiguration {
final String name;
final String description;
final String version;
final String author;
final String entryPoint;
final String pluginApiVersion;
final List<PluginApi> apis;
final List<PluginAbility> abilities;
final String? repository;
const PluginConfiguration({
required this.name,
required this.description,
required this.version,
required this.author,
required this.entryPoint,
required this.pluginApiVersion,
required this.apis,
required this.abilities,
this.repository,
});
Future<String> slug() =>
RustLib.instance.api.crateApiPluginModelsCorePluginConfigurationSlug(
that: this,
);
@override
int get hashCode =>
name.hashCode ^
description.hashCode ^
version.hashCode ^
author.hashCode ^
entryPoint.hashCode ^
pluginApiVersion.hashCode ^
apis.hashCode ^
abilities.hashCode ^
repository.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginConfiguration &&
runtimeType == other.runtimeType &&
name == other.name &&
description == other.description &&
version == other.version &&
author == other.author &&
entryPoint == other.entryPoint &&
pluginApiVersion == other.pluginApiVersion &&
apis == other.apis &&
abilities == other.abilities &&
repository == other.repository;
}
class PluginUpdateAvailable {
final String downloadUrl;
final String version;
final String? changelog;
const PluginUpdateAvailable({
required this.downloadUrl,
required this.version,
this.changelog,
});
@override
int get hashCode =>
downloadUrl.hashCode ^ version.hashCode ^ changelog.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginUpdateAvailable &&
runtimeType == other.runtimeType &&
downloadUrl == other.downloadUrl &&
version == other.version &&
changelog == other.changelog;
}
class ScrobbleAlbum {
final String id;
final String name;
const ScrobbleAlbum({
required this.id,
required this.name,
});
@override
int get hashCode => id.hashCode ^ name.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ScrobbleAlbum &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name;
}
class ScrobbleArtist {
final String id;
final String name;
const ScrobbleArtist({
required this.id,
required this.name,
});
@override
int get hashCode => id.hashCode ^ name.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ScrobbleArtist &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name;
}
class ScrobbleDetails {
final String id;
final String title;
final List<ScrobbleArtist> artists;
final ScrobbleAlbum album;
final PlatformInt64? timestamp;
final int? durationMs;
final String? isrc;
const ScrobbleDetails({
required this.id,
required this.title,
required this.artists,
required this.album,
this.timestamp,
this.durationMs,
this.isrc,
});
@override
int get hashCode =>
id.hashCode ^
title.hashCode ^
artists.hashCode ^
album.hashCode ^
timestamp.hashCode ^
durationMs.hashCode ^
isrc.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ScrobbleDetails &&
runtimeType == other.runtimeType &&
id == other.id &&
title == other.title &&
artists == other.artists &&
album == other.album &&
timestamp == other.timestamp &&
durationMs == other.durationMs &&
isrc == other.isrc;
}

View File

@ -0,0 +1,25 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'image.freezed.dart';
part 'image.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
@freezed
sealed class SpotubeImageObject with _$SpotubeImageObject {
const factory SpotubeImageObject({
required String typeName,
required String url,
int? width,
int? height,
}) = _SpotubeImageObject;
factory SpotubeImageObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeImageObjectFromJson(json);
}

View File

@ -0,0 +1,219 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'image.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotubeImageObject _$SpotubeImageObjectFromJson(Map<String, dynamic> json) {
return _SpotubeImageObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeImageObject {
String get typeName => throw _privateConstructorUsedError;
String get url => throw _privateConstructorUsedError;
int? get width => throw _privateConstructorUsedError;
int? get height => throw _privateConstructorUsedError;
/// Serializes this SpotubeImageObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeImageObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeImageObjectCopyWith<SpotubeImageObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeImageObjectCopyWith<$Res> {
factory $SpotubeImageObjectCopyWith(
SpotubeImageObject value, $Res Function(SpotubeImageObject) then) =
_$SpotubeImageObjectCopyWithImpl<$Res, SpotubeImageObject>;
@useResult
$Res call({String typeName, String url, int? width, int? height});
}
/// @nodoc
class _$SpotubeImageObjectCopyWithImpl<$Res, $Val extends SpotubeImageObject>
implements $SpotubeImageObjectCopyWith<$Res> {
_$SpotubeImageObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeImageObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? url = null,
Object? width = freezed,
Object? height = freezed,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
url: null == url
? _value.url
: url // ignore: cast_nullable_to_non_nullable
as String,
width: freezed == width
? _value.width
: width // ignore: cast_nullable_to_non_nullable
as int?,
height: freezed == height
? _value.height
: height // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpotubeImageObjectImplCopyWith<$Res>
implements $SpotubeImageObjectCopyWith<$Res> {
factory _$$SpotubeImageObjectImplCopyWith(_$SpotubeImageObjectImpl value,
$Res Function(_$SpotubeImageObjectImpl) then) =
__$$SpotubeImageObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String typeName, String url, int? width, int? height});
}
/// @nodoc
class __$$SpotubeImageObjectImplCopyWithImpl<$Res>
extends _$SpotubeImageObjectCopyWithImpl<$Res, _$SpotubeImageObjectImpl>
implements _$$SpotubeImageObjectImplCopyWith<$Res> {
__$$SpotubeImageObjectImplCopyWithImpl(_$SpotubeImageObjectImpl _value,
$Res Function(_$SpotubeImageObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeImageObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? url = null,
Object? width = freezed,
Object? height = freezed,
}) {
return _then(_$SpotubeImageObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
url: null == url
? _value.url
: url // ignore: cast_nullable_to_non_nullable
as String,
width: freezed == width
? _value.width
: width // ignore: cast_nullable_to_non_nullable
as int?,
height: freezed == height
? _value.height
: height // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeImageObjectImpl implements _SpotubeImageObject {
const _$SpotubeImageObjectImpl(
{required this.typeName, required this.url, this.width, this.height});
factory _$SpotubeImageObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeImageObjectImplFromJson(json);
@override
final String typeName;
@override
final String url;
@override
final int? width;
@override
final int? height;
@override
String toString() {
return 'SpotubeImageObject(typeName: $typeName, url: $url, width: $width, height: $height)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeImageObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.url, url) || other.url == url) &&
(identical(other.width, width) || other.width == width) &&
(identical(other.height, height) || other.height == height));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, typeName, url, width, height);
/// Create a copy of SpotubeImageObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeImageObjectImplCopyWith<_$SpotubeImageObjectImpl> get copyWith =>
__$$SpotubeImageObjectImplCopyWithImpl<_$SpotubeImageObjectImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeImageObjectImplToJson(
this,
);
}
}
abstract class _SpotubeImageObject implements SpotubeImageObject {
const factory _SpotubeImageObject(
{required final String typeName,
required final String url,
final int? width,
final int? height}) = _$SpotubeImageObjectImpl;
factory _SpotubeImageObject.fromJson(Map<String, dynamic> json) =
_$SpotubeImageObjectImpl.fromJson;
@override
String get typeName;
@override
String get url;
@override
int? get width;
@override
int? get height;
/// Create a copy of SpotubeImageObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeImageObjectImplCopyWith<_$SpotubeImageObjectImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'image.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeImageObjectImpl _$$SpotubeImageObjectImplFromJson(Map json) =>
_$SpotubeImageObjectImpl(
typeName: json['typeName'] as String,
url: json['url'] as String,
width: (json['width'] as num?)?.toInt(),
height: (json['height'] as num?)?.toInt(),
);
Map<String, dynamic> _$$SpotubeImageObjectImplToJson(
_$SpotubeImageObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'url': instance.url,
'width': instance.width,
'height': instance.height,
};

View File

@ -0,0 +1,61 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'album.dart';
import 'artist.dart';
import 'browse.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
import 'playlist.dart';
import 'track.dart';
import 'user.dart';
part 'pagination.freezed.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `fmt`, `fmt`
@freezed
sealed class SpotubePaginationResponseObject
with _$SpotubePaginationResponseObject {
const factory SpotubePaginationResponseObject({
required int limit,
int? nextOffset,
required int total,
required bool hasMore,
required List<SpotubePaginationResponseObjectItem> items,
}) = _SpotubePaginationResponseObject;
}
@freezed
sealed class SpotubePaginationResponseObjectItem
with _$SpotubePaginationResponseObjectItem {
const SpotubePaginationResponseObjectItem._();
const factory SpotubePaginationResponseObjectItem.track(
SpotubeTrackObject field0,
) = SpotubePaginationResponseObjectItem_Track;
const factory SpotubePaginationResponseObjectItem.playlistFull(
SpotubeFullPlaylistObject field0,
) = SpotubePaginationResponseObjectItem_PlaylistFull;
const factory SpotubePaginationResponseObjectItem.playlistSimple(
SpotubeSimplePlaylistObject field0,
) = SpotubePaginationResponseObjectItem_PlaylistSimple;
const factory SpotubePaginationResponseObjectItem.albumSimple(
SpotubeSimpleAlbumObject field0,
) = SpotubePaginationResponseObjectItem_AlbumSimple;
const factory SpotubePaginationResponseObjectItem.albumFull(
SpotubeFullAlbumObject field0,
) = SpotubePaginationResponseObjectItem_AlbumFull;
const factory SpotubePaginationResponseObjectItem.artistFull(
SpotubeFullArtistObject field0,
) = SpotubePaginationResponseObjectItem_ArtistFull;
const factory SpotubePaginationResponseObjectItem.artistSimple(
SpotubeSimpleArtistObject field0,
) = SpotubePaginationResponseObjectItem_ArtistSimple;
const factory SpotubePaginationResponseObjectItem.browseSection(
SpotubeBrowseSectionObject field0,
) = SpotubePaginationResponseObjectItem_BrowseSection;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
import 'user.dart';
part 'playlist.freezed.dart';
part 'playlist.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `clone`, `fmt`, `fmt`
@freezed
sealed class SpotubeFullPlaylistObject with _$SpotubeFullPlaylistObject {
const factory SpotubeFullPlaylistObject({
required String typeName,
required String id,
required String name,
required String description,
required String externalUri,
required SpotubeUserObject owner,
required List<SpotubeImageObject> images,
required List<SpotubeUserObject> collaborators,
required bool collaborative,
required bool public,
}) = _SpotubeFullPlaylistObject;
factory SpotubeFullPlaylistObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeFullPlaylistObjectFromJson(json);
}
@freezed
sealed class SpotubeSimplePlaylistObject with _$SpotubeSimplePlaylistObject {
const factory SpotubeSimplePlaylistObject({
required String typeName,
required String id,
required String name,
required String description,
required String externalUri,
required SpotubeUserObject owner,
required List<SpotubeImageObject> images,
}) = _SpotubeSimplePlaylistObject;
factory SpotubeSimplePlaylistObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeSimplePlaylistObjectFromJson(json);
}

View File

@ -0,0 +1,710 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'playlist.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotubeFullPlaylistObject _$SpotubeFullPlaylistObjectFromJson(
Map<String, dynamic> json) {
return _SpotubeFullPlaylistObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeFullPlaylistObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get description => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
SpotubeUserObject get owner => throw _privateConstructorUsedError;
List<SpotubeImageObject> get images => throw _privateConstructorUsedError;
List<SpotubeUserObject> get collaborators =>
throw _privateConstructorUsedError;
bool get collaborative => throw _privateConstructorUsedError;
bool get public => throw _privateConstructorUsedError;
/// Serializes this SpotubeFullPlaylistObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeFullPlaylistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeFullPlaylistObjectCopyWith<SpotubeFullPlaylistObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeFullPlaylistObjectCopyWith<$Res> {
factory $SpotubeFullPlaylistObjectCopyWith(SpotubeFullPlaylistObject value,
$Res Function(SpotubeFullPlaylistObject) then) =
_$SpotubeFullPlaylistObjectCopyWithImpl<$Res, SpotubeFullPlaylistObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
String description,
String externalUri,
SpotubeUserObject owner,
List<SpotubeImageObject> images,
List<SpotubeUserObject> collaborators,
bool collaborative,
bool public});
$SpotubeUserObjectCopyWith<$Res> get owner;
}
/// @nodoc
class _$SpotubeFullPlaylistObjectCopyWithImpl<$Res,
$Val extends SpotubeFullPlaylistObject>
implements $SpotubeFullPlaylistObjectCopyWith<$Res> {
_$SpotubeFullPlaylistObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeFullPlaylistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? description = null,
Object? externalUri = null,
Object? owner = null,
Object? images = null,
Object? collaborators = null,
Object? collaborative = null,
Object? public = null,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
owner: null == owner
? _value.owner
: owner // ignore: cast_nullable_to_non_nullable
as SpotubeUserObject,
images: null == images
? _value.images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
collaborators: null == collaborators
? _value.collaborators
: collaborators // ignore: cast_nullable_to_non_nullable
as List<SpotubeUserObject>,
collaborative: null == collaborative
? _value.collaborative
: collaborative // ignore: cast_nullable_to_non_nullable
as bool,
public: null == public
? _value.public
: public // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
/// Create a copy of SpotubeFullPlaylistObject
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SpotubeUserObjectCopyWith<$Res> get owner {
return $SpotubeUserObjectCopyWith<$Res>(_value.owner, (value) {
return _then(_value.copyWith(owner: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$SpotubeFullPlaylistObjectImplCopyWith<$Res>
implements $SpotubeFullPlaylistObjectCopyWith<$Res> {
factory _$$SpotubeFullPlaylistObjectImplCopyWith(
_$SpotubeFullPlaylistObjectImpl value,
$Res Function(_$SpotubeFullPlaylistObjectImpl) then) =
__$$SpotubeFullPlaylistObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
String description,
String externalUri,
SpotubeUserObject owner,
List<SpotubeImageObject> images,
List<SpotubeUserObject> collaborators,
bool collaborative,
bool public});
@override
$SpotubeUserObjectCopyWith<$Res> get owner;
}
/// @nodoc
class __$$SpotubeFullPlaylistObjectImplCopyWithImpl<$Res>
extends _$SpotubeFullPlaylistObjectCopyWithImpl<$Res,
_$SpotubeFullPlaylistObjectImpl>
implements _$$SpotubeFullPlaylistObjectImplCopyWith<$Res> {
__$$SpotubeFullPlaylistObjectImplCopyWithImpl(
_$SpotubeFullPlaylistObjectImpl _value,
$Res Function(_$SpotubeFullPlaylistObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeFullPlaylistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? description = null,
Object? externalUri = null,
Object? owner = null,
Object? images = null,
Object? collaborators = null,
Object? collaborative = null,
Object? public = null,
}) {
return _then(_$SpotubeFullPlaylistObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
owner: null == owner
? _value.owner
: owner // ignore: cast_nullable_to_non_nullable
as SpotubeUserObject,
images: null == images
? _value._images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
collaborators: null == collaborators
? _value._collaborators
: collaborators // ignore: cast_nullable_to_non_nullable
as List<SpotubeUserObject>,
collaborative: null == collaborative
? _value.collaborative
: collaborative // ignore: cast_nullable_to_non_nullable
as bool,
public: null == public
? _value.public
: public // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeFullPlaylistObjectImpl implements _SpotubeFullPlaylistObject {
const _$SpotubeFullPlaylistObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required this.description,
required this.externalUri,
required this.owner,
required final List<SpotubeImageObject> images,
required final List<SpotubeUserObject> collaborators,
required this.collaborative,
required this.public})
: _images = images,
_collaborators = collaborators;
factory _$SpotubeFullPlaylistObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeFullPlaylistObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
@override
final String description;
@override
final String externalUri;
@override
final SpotubeUserObject owner;
final List<SpotubeImageObject> _images;
@override
List<SpotubeImageObject> get images {
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_images);
}
final List<SpotubeUserObject> _collaborators;
@override
List<SpotubeUserObject> get collaborators {
if (_collaborators is EqualUnmodifiableListView) return _collaborators;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_collaborators);
}
@override
final bool collaborative;
@override
final bool public;
@override
String toString() {
return 'SpotubeFullPlaylistObject(typeName: $typeName, id: $id, name: $name, description: $description, externalUri: $externalUri, owner: $owner, images: $images, collaborators: $collaborators, collaborative: $collaborative, public: $public)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeFullPlaylistObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri) &&
(identical(other.owner, owner) || other.owner == owner) &&
const DeepCollectionEquality().equals(other._images, _images) &&
const DeepCollectionEquality()
.equals(other._collaborators, _collaborators) &&
(identical(other.collaborative, collaborative) ||
other.collaborative == collaborative) &&
(identical(other.public, public) || other.public == public));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
typeName,
id,
name,
description,
externalUri,
owner,
const DeepCollectionEquality().hash(_images),
const DeepCollectionEquality().hash(_collaborators),
collaborative,
public);
/// Create a copy of SpotubeFullPlaylistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeFullPlaylistObjectImplCopyWith<_$SpotubeFullPlaylistObjectImpl>
get copyWith => __$$SpotubeFullPlaylistObjectImplCopyWithImpl<
_$SpotubeFullPlaylistObjectImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeFullPlaylistObjectImplToJson(
this,
);
}
}
abstract class _SpotubeFullPlaylistObject implements SpotubeFullPlaylistObject {
const factory _SpotubeFullPlaylistObject(
{required final String typeName,
required final String id,
required final String name,
required final String description,
required final String externalUri,
required final SpotubeUserObject owner,
required final List<SpotubeImageObject> images,
required final List<SpotubeUserObject> collaborators,
required final bool collaborative,
required final bool public}) = _$SpotubeFullPlaylistObjectImpl;
factory _SpotubeFullPlaylistObject.fromJson(Map<String, dynamic> json) =
_$SpotubeFullPlaylistObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
String get description;
@override
String get externalUri;
@override
SpotubeUserObject get owner;
@override
List<SpotubeImageObject> get images;
@override
List<SpotubeUserObject> get collaborators;
@override
bool get collaborative;
@override
bool get public;
/// Create a copy of SpotubeFullPlaylistObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeFullPlaylistObjectImplCopyWith<_$SpotubeFullPlaylistObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}
SpotubeSimplePlaylistObject _$SpotubeSimplePlaylistObjectFromJson(
Map<String, dynamic> json) {
return _SpotubeSimplePlaylistObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeSimplePlaylistObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get description => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
SpotubeUserObject get owner => throw _privateConstructorUsedError;
List<SpotubeImageObject> get images => throw _privateConstructorUsedError;
/// Serializes this SpotubeSimplePlaylistObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeSimplePlaylistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeSimplePlaylistObjectCopyWith<SpotubeSimplePlaylistObject>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeSimplePlaylistObjectCopyWith<$Res> {
factory $SpotubeSimplePlaylistObjectCopyWith(
SpotubeSimplePlaylistObject value,
$Res Function(SpotubeSimplePlaylistObject) then) =
_$SpotubeSimplePlaylistObjectCopyWithImpl<$Res,
SpotubeSimplePlaylistObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
String description,
String externalUri,
SpotubeUserObject owner,
List<SpotubeImageObject> images});
$SpotubeUserObjectCopyWith<$Res> get owner;
}
/// @nodoc
class _$SpotubeSimplePlaylistObjectCopyWithImpl<$Res,
$Val extends SpotubeSimplePlaylistObject>
implements $SpotubeSimplePlaylistObjectCopyWith<$Res> {
_$SpotubeSimplePlaylistObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeSimplePlaylistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? description = null,
Object? externalUri = null,
Object? owner = null,
Object? images = null,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
owner: null == owner
? _value.owner
: owner // ignore: cast_nullable_to_non_nullable
as SpotubeUserObject,
images: null == images
? _value.images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
) as $Val);
}
/// Create a copy of SpotubeSimplePlaylistObject
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SpotubeUserObjectCopyWith<$Res> get owner {
return $SpotubeUserObjectCopyWith<$Res>(_value.owner, (value) {
return _then(_value.copyWith(owner: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$SpotubeSimplePlaylistObjectImplCopyWith<$Res>
implements $SpotubeSimplePlaylistObjectCopyWith<$Res> {
factory _$$SpotubeSimplePlaylistObjectImplCopyWith(
_$SpotubeSimplePlaylistObjectImpl value,
$Res Function(_$SpotubeSimplePlaylistObjectImpl) then) =
__$$SpotubeSimplePlaylistObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
String description,
String externalUri,
SpotubeUserObject owner,
List<SpotubeImageObject> images});
@override
$SpotubeUserObjectCopyWith<$Res> get owner;
}
/// @nodoc
class __$$SpotubeSimplePlaylistObjectImplCopyWithImpl<$Res>
extends _$SpotubeSimplePlaylistObjectCopyWithImpl<$Res,
_$SpotubeSimplePlaylistObjectImpl>
implements _$$SpotubeSimplePlaylistObjectImplCopyWith<$Res> {
__$$SpotubeSimplePlaylistObjectImplCopyWithImpl(
_$SpotubeSimplePlaylistObjectImpl _value,
$Res Function(_$SpotubeSimplePlaylistObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeSimplePlaylistObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? description = null,
Object? externalUri = null,
Object? owner = null,
Object? images = null,
}) {
return _then(_$SpotubeSimplePlaylistObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
owner: null == owner
? _value.owner
: owner // ignore: cast_nullable_to_non_nullable
as SpotubeUserObject,
images: null == images
? _value._images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeSimplePlaylistObjectImpl
implements _SpotubeSimplePlaylistObject {
const _$SpotubeSimplePlaylistObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required this.description,
required this.externalUri,
required this.owner,
required final List<SpotubeImageObject> images})
: _images = images;
factory _$SpotubeSimplePlaylistObjectImpl.fromJson(
Map<String, dynamic> json) =>
_$$SpotubeSimplePlaylistObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
@override
final String description;
@override
final String externalUri;
@override
final SpotubeUserObject owner;
final List<SpotubeImageObject> _images;
@override
List<SpotubeImageObject> get images {
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_images);
}
@override
String toString() {
return 'SpotubeSimplePlaylistObject(typeName: $typeName, id: $id, name: $name, description: $description, externalUri: $externalUri, owner: $owner, images: $images)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeSimplePlaylistObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri) &&
(identical(other.owner, owner) || other.owner == owner) &&
const DeepCollectionEquality().equals(other._images, _images));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, typeName, id, name, description,
externalUri, owner, const DeepCollectionEquality().hash(_images));
/// Create a copy of SpotubeSimplePlaylistObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeSimplePlaylistObjectImplCopyWith<_$SpotubeSimplePlaylistObjectImpl>
get copyWith => __$$SpotubeSimplePlaylistObjectImplCopyWithImpl<
_$SpotubeSimplePlaylistObjectImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeSimplePlaylistObjectImplToJson(
this,
);
}
}
abstract class _SpotubeSimplePlaylistObject
implements SpotubeSimplePlaylistObject {
const factory _SpotubeSimplePlaylistObject(
{required final String typeName,
required final String id,
required final String name,
required final String description,
required final String externalUri,
required final SpotubeUserObject owner,
required final List<SpotubeImageObject> images}) =
_$SpotubeSimplePlaylistObjectImpl;
factory _SpotubeSimplePlaylistObject.fromJson(Map<String, dynamic> json) =
_$SpotubeSimplePlaylistObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
String get description;
@override
String get externalUri;
@override
SpotubeUserObject get owner;
@override
List<SpotubeImageObject> get images;
/// Create a copy of SpotubeSimplePlaylistObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeSimplePlaylistObjectImplCopyWith<_$SpotubeSimplePlaylistObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,72 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'playlist.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeFullPlaylistObjectImpl _$$SpotubeFullPlaylistObjectImplFromJson(
Map json) =>
_$SpotubeFullPlaylistObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
description: json['description'] as String,
externalUri: json['externalUri'] as String,
owner: SpotubeUserObject.fromJson(
Map<String, dynamic>.from(json['owner'] as Map)),
images: (json['images'] as List<dynamic>)
.map((e) =>
SpotubeImageObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
collaborators: (json['collaborators'] as List<dynamic>)
.map((e) =>
SpotubeUserObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
collaborative: json['collaborative'] as bool,
public: json['public'] as bool,
);
Map<String, dynamic> _$$SpotubeFullPlaylistObjectImplToJson(
_$SpotubeFullPlaylistObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'description': instance.description,
'externalUri': instance.externalUri,
'owner': instance.owner.toJson(),
'images': instance.images.map((e) => e.toJson()).toList(),
'collaborators': instance.collaborators.map((e) => e.toJson()).toList(),
'collaborative': instance.collaborative,
'public': instance.public,
};
_$SpotubeSimplePlaylistObjectImpl _$$SpotubeSimplePlaylistObjectImplFromJson(
Map json) =>
_$SpotubeSimplePlaylistObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
description: json['description'] as String,
externalUri: json['externalUri'] as String,
owner: SpotubeUserObject.fromJson(
Map<String, dynamic>.from(json['owner'] as Map)),
images: (json['images'] as List<dynamic>)
.map((e) =>
SpotubeImageObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
);
Map<String, dynamic> _$$SpotubeSimplePlaylistObjectImplToJson(
_$SpotubeSimplePlaylistObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'description': instance.description,
'externalUri': instance.externalUri,
'owner': instance.owner.toJson(),
'images': instance.images.map((e) => e.toJson()).toList(),
};

View File

@ -0,0 +1,32 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'album.dart';
import 'artist.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
import 'playlist.dart';
import 'track.dart';
import 'user.dart';
part 'search.freezed.dart';
part 'search.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
@freezed
sealed class SpotubeSearchResponseObject with _$SpotubeSearchResponseObject {
const factory SpotubeSearchResponseObject({
required String typeName,
required List<SpotubeSimpleAlbumObject> albums,
required List<SpotubeFullArtistObject> artists,
required List<SpotubeSimplePlaylistObject> playlists,
required List<SpotubeTrackObject> tracks,
}) = _SpotubeSearchResponseObject;
factory SpotubeSearchResponseObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeSearchResponseObjectFromJson(json);
}

View File

@ -0,0 +1,298 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'search.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotubeSearchResponseObject _$SpotubeSearchResponseObjectFromJson(
Map<String, dynamic> json) {
return _SpotubeSearchResponseObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeSearchResponseObject {
String get typeName => throw _privateConstructorUsedError;
List<SpotubeSimpleAlbumObject> get albums =>
throw _privateConstructorUsedError;
List<SpotubeFullArtistObject> get artists =>
throw _privateConstructorUsedError;
List<SpotubeSimplePlaylistObject> get playlists =>
throw _privateConstructorUsedError;
List<SpotubeTrackObject> get tracks => throw _privateConstructorUsedError;
/// Serializes this SpotubeSearchResponseObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeSearchResponseObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeSearchResponseObjectCopyWith<SpotubeSearchResponseObject>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeSearchResponseObjectCopyWith<$Res> {
factory $SpotubeSearchResponseObjectCopyWith(
SpotubeSearchResponseObject value,
$Res Function(SpotubeSearchResponseObject) then) =
_$SpotubeSearchResponseObjectCopyWithImpl<$Res,
SpotubeSearchResponseObject>;
@useResult
$Res call(
{String typeName,
List<SpotubeSimpleAlbumObject> albums,
List<SpotubeFullArtistObject> artists,
List<SpotubeSimplePlaylistObject> playlists,
List<SpotubeTrackObject> tracks});
}
/// @nodoc
class _$SpotubeSearchResponseObjectCopyWithImpl<$Res,
$Val extends SpotubeSearchResponseObject>
implements $SpotubeSearchResponseObjectCopyWith<$Res> {
_$SpotubeSearchResponseObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeSearchResponseObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? albums = null,
Object? artists = null,
Object? playlists = null,
Object? tracks = null,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
albums: null == albums
? _value.albums
: albums // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleAlbumObject>,
artists: null == artists
? _value.artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeFullArtistObject>,
playlists: null == playlists
? _value.playlists
: playlists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimplePlaylistObject>,
tracks: null == tracks
? _value.tracks
: tracks // ignore: cast_nullable_to_non_nullable
as List<SpotubeTrackObject>,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpotubeSearchResponseObjectImplCopyWith<$Res>
implements $SpotubeSearchResponseObjectCopyWith<$Res> {
factory _$$SpotubeSearchResponseObjectImplCopyWith(
_$SpotubeSearchResponseObjectImpl value,
$Res Function(_$SpotubeSearchResponseObjectImpl) then) =
__$$SpotubeSearchResponseObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
List<SpotubeSimpleAlbumObject> albums,
List<SpotubeFullArtistObject> artists,
List<SpotubeSimplePlaylistObject> playlists,
List<SpotubeTrackObject> tracks});
}
/// @nodoc
class __$$SpotubeSearchResponseObjectImplCopyWithImpl<$Res>
extends _$SpotubeSearchResponseObjectCopyWithImpl<$Res,
_$SpotubeSearchResponseObjectImpl>
implements _$$SpotubeSearchResponseObjectImplCopyWith<$Res> {
__$$SpotubeSearchResponseObjectImplCopyWithImpl(
_$SpotubeSearchResponseObjectImpl _value,
$Res Function(_$SpotubeSearchResponseObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeSearchResponseObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? albums = null,
Object? artists = null,
Object? playlists = null,
Object? tracks = null,
}) {
return _then(_$SpotubeSearchResponseObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
albums: null == albums
? _value._albums
: albums // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleAlbumObject>,
artists: null == artists
? _value._artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeFullArtistObject>,
playlists: null == playlists
? _value._playlists
: playlists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimplePlaylistObject>,
tracks: null == tracks
? _value._tracks
: tracks // ignore: cast_nullable_to_non_nullable
as List<SpotubeTrackObject>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeSearchResponseObjectImpl
implements _SpotubeSearchResponseObject {
const _$SpotubeSearchResponseObjectImpl(
{required this.typeName,
required final List<SpotubeSimpleAlbumObject> albums,
required final List<SpotubeFullArtistObject> artists,
required final List<SpotubeSimplePlaylistObject> playlists,
required final List<SpotubeTrackObject> tracks})
: _albums = albums,
_artists = artists,
_playlists = playlists,
_tracks = tracks;
factory _$SpotubeSearchResponseObjectImpl.fromJson(
Map<String, dynamic> json) =>
_$$SpotubeSearchResponseObjectImplFromJson(json);
@override
final String typeName;
final List<SpotubeSimpleAlbumObject> _albums;
@override
List<SpotubeSimpleAlbumObject> get albums {
if (_albums is EqualUnmodifiableListView) return _albums;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_albums);
}
final List<SpotubeFullArtistObject> _artists;
@override
List<SpotubeFullArtistObject> get artists {
if (_artists is EqualUnmodifiableListView) return _artists;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_artists);
}
final List<SpotubeSimplePlaylistObject> _playlists;
@override
List<SpotubeSimplePlaylistObject> get playlists {
if (_playlists is EqualUnmodifiableListView) return _playlists;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_playlists);
}
final List<SpotubeTrackObject> _tracks;
@override
List<SpotubeTrackObject> get tracks {
if (_tracks is EqualUnmodifiableListView) return _tracks;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_tracks);
}
@override
String toString() {
return 'SpotubeSearchResponseObject(typeName: $typeName, albums: $albums, artists: $artists, playlists: $playlists, tracks: $tracks)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeSearchResponseObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
const DeepCollectionEquality().equals(other._albums, _albums) &&
const DeepCollectionEquality().equals(other._artists, _artists) &&
const DeepCollectionEquality()
.equals(other._playlists, _playlists) &&
const DeepCollectionEquality().equals(other._tracks, _tracks));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
typeName,
const DeepCollectionEquality().hash(_albums),
const DeepCollectionEquality().hash(_artists),
const DeepCollectionEquality().hash(_playlists),
const DeepCollectionEquality().hash(_tracks));
/// Create a copy of SpotubeSearchResponseObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeSearchResponseObjectImplCopyWith<_$SpotubeSearchResponseObjectImpl>
get copyWith => __$$SpotubeSearchResponseObjectImplCopyWithImpl<
_$SpotubeSearchResponseObjectImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeSearchResponseObjectImplToJson(
this,
);
}
}
abstract class _SpotubeSearchResponseObject
implements SpotubeSearchResponseObject {
const factory _SpotubeSearchResponseObject(
{required final String typeName,
required final List<SpotubeSimpleAlbumObject> albums,
required final List<SpotubeFullArtistObject> artists,
required final List<SpotubeSimplePlaylistObject> playlists,
required final List<SpotubeTrackObject> tracks}) =
_$SpotubeSearchResponseObjectImpl;
factory _SpotubeSearchResponseObject.fromJson(Map<String, dynamic> json) =
_$SpotubeSearchResponseObjectImpl.fromJson;
@override
String get typeName;
@override
List<SpotubeSimpleAlbumObject> get albums;
@override
List<SpotubeFullArtistObject> get artists;
@override
List<SpotubeSimplePlaylistObject> get playlists;
@override
List<SpotubeTrackObject> get tracks;
/// Create a copy of SpotubeSearchResponseObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeSearchResponseObjectImplCopyWith<_$SpotubeSearchResponseObjectImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,39 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'search.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeSearchResponseObjectImpl _$$SpotubeSearchResponseObjectImplFromJson(
Map json) =>
_$SpotubeSearchResponseObjectImpl(
typeName: json['typeName'] as String,
albums: (json['albums'] as List<dynamic>)
.map((e) => SpotubeSimpleAlbumObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
artists: (json['artists'] as List<dynamic>)
.map((e) => SpotubeFullArtistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
playlists: (json['playlists'] as List<dynamic>)
.map((e) => SpotubeSimplePlaylistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
tracks: (json['tracks'] as List<dynamic>)
.map((e) =>
SpotubeTrackObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
);
Map<String, dynamic> _$$SpotubeSearchResponseObjectImplToJson(
_$SpotubeSearchResponseObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'albums': instance.albums.map((e) => e.toJson()).toList(),
'artists': instance.artists.map((e) => e.toJson()).toList(),
'playlists': instance.playlists.map((e) => e.toJson()).toList(),
'tracks': instance.tracks.map((e) => e.toJson()).toList(),
};

View File

@ -0,0 +1,33 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'album.dart';
import 'artist.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'track.freezed.dart';
part 'track.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
@freezed
sealed class SpotubeTrackObject with _$SpotubeTrackObject {
const factory SpotubeTrackObject({
required String typeName,
required String id,
required String name,
required String externalUri,
required List<SpotubeSimpleArtistObject> artists,
required SpotubeSimpleAlbumObject album,
required BigInt durationMs,
required String isrc,
required bool explicit,
}) = _SpotubeTrackObject;
factory SpotubeTrackObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeTrackObjectFromJson(json);
}

View File

@ -0,0 +1,366 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'track.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotubeTrackObject _$SpotubeTrackObjectFromJson(Map<String, dynamic> json) {
return _SpotubeTrackObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeTrackObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
List<SpotubeSimpleArtistObject> get artists =>
throw _privateConstructorUsedError;
SpotubeSimpleAlbumObject get album => throw _privateConstructorUsedError;
BigInt get durationMs => throw _privateConstructorUsedError;
String get isrc => throw _privateConstructorUsedError;
bool get explicit => throw _privateConstructorUsedError;
/// Serializes this SpotubeTrackObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeTrackObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeTrackObjectCopyWith<SpotubeTrackObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeTrackObjectCopyWith<$Res> {
factory $SpotubeTrackObjectCopyWith(
SpotubeTrackObject value, $Res Function(SpotubeTrackObject) then) =
_$SpotubeTrackObjectCopyWithImpl<$Res, SpotubeTrackObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeSimpleArtistObject> artists,
SpotubeSimpleAlbumObject album,
BigInt durationMs,
String isrc,
bool explicit});
$SpotubeSimpleAlbumObjectCopyWith<$Res> get album;
}
/// @nodoc
class _$SpotubeTrackObjectCopyWithImpl<$Res, $Val extends SpotubeTrackObject>
implements $SpotubeTrackObjectCopyWith<$Res> {
_$SpotubeTrackObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeTrackObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? artists = null,
Object? album = null,
Object? durationMs = null,
Object? isrc = null,
Object? explicit = null,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
artists: null == artists
? _value.artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleArtistObject>,
album: null == album
? _value.album
: album // ignore: cast_nullable_to_non_nullable
as SpotubeSimpleAlbumObject,
durationMs: null == durationMs
? _value.durationMs
: durationMs // ignore: cast_nullable_to_non_nullable
as BigInt,
isrc: null == isrc
? _value.isrc
: isrc // ignore: cast_nullable_to_non_nullable
as String,
explicit: null == explicit
? _value.explicit
: explicit // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
/// Create a copy of SpotubeTrackObject
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SpotubeSimpleAlbumObjectCopyWith<$Res> get album {
return $SpotubeSimpleAlbumObjectCopyWith<$Res>(_value.album, (value) {
return _then(_value.copyWith(album: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$SpotubeTrackObjectImplCopyWith<$Res>
implements $SpotubeTrackObjectCopyWith<$Res> {
factory _$$SpotubeTrackObjectImplCopyWith(_$SpotubeTrackObjectImpl value,
$Res Function(_$SpotubeTrackObjectImpl) then) =
__$$SpotubeTrackObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
String externalUri,
List<SpotubeSimpleArtistObject> artists,
SpotubeSimpleAlbumObject album,
BigInt durationMs,
String isrc,
bool explicit});
@override
$SpotubeSimpleAlbumObjectCopyWith<$Res> get album;
}
/// @nodoc
class __$$SpotubeTrackObjectImplCopyWithImpl<$Res>
extends _$SpotubeTrackObjectCopyWithImpl<$Res, _$SpotubeTrackObjectImpl>
implements _$$SpotubeTrackObjectImplCopyWith<$Res> {
__$$SpotubeTrackObjectImplCopyWithImpl(_$SpotubeTrackObjectImpl _value,
$Res Function(_$SpotubeTrackObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeTrackObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? externalUri = null,
Object? artists = null,
Object? album = null,
Object? durationMs = null,
Object? isrc = null,
Object? explicit = null,
}) {
return _then(_$SpotubeTrackObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
artists: null == artists
? _value._artists
: artists // ignore: cast_nullable_to_non_nullable
as List<SpotubeSimpleArtistObject>,
album: null == album
? _value.album
: album // ignore: cast_nullable_to_non_nullable
as SpotubeSimpleAlbumObject,
durationMs: null == durationMs
? _value.durationMs
: durationMs // ignore: cast_nullable_to_non_nullable
as BigInt,
isrc: null == isrc
? _value.isrc
: isrc // ignore: cast_nullable_to_non_nullable
as String,
explicit: null == explicit
? _value.explicit
: explicit // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeTrackObjectImpl implements _SpotubeTrackObject {
const _$SpotubeTrackObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required this.externalUri,
required final List<SpotubeSimpleArtistObject> artists,
required this.album,
required this.durationMs,
required this.isrc,
required this.explicit})
: _artists = artists;
factory _$SpotubeTrackObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeTrackObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
@override
final String externalUri;
final List<SpotubeSimpleArtistObject> _artists;
@override
List<SpotubeSimpleArtistObject> get artists {
if (_artists is EqualUnmodifiableListView) return _artists;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_artists);
}
@override
final SpotubeSimpleAlbumObject album;
@override
final BigInt durationMs;
@override
final String isrc;
@override
final bool explicit;
@override
String toString() {
return 'SpotubeTrackObject(typeName: $typeName, id: $id, name: $name, externalUri: $externalUri, artists: $artists, album: $album, durationMs: $durationMs, isrc: $isrc, explicit: $explicit)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeTrackObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri) &&
const DeepCollectionEquality().equals(other._artists, _artists) &&
(identical(other.album, album) || other.album == album) &&
(identical(other.durationMs, durationMs) ||
other.durationMs == durationMs) &&
(identical(other.isrc, isrc) || other.isrc == isrc) &&
(identical(other.explicit, explicit) ||
other.explicit == explicit));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
typeName,
id,
name,
externalUri,
const DeepCollectionEquality().hash(_artists),
album,
durationMs,
isrc,
explicit);
/// Create a copy of SpotubeTrackObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeTrackObjectImplCopyWith<_$SpotubeTrackObjectImpl> get copyWith =>
__$$SpotubeTrackObjectImplCopyWithImpl<_$SpotubeTrackObjectImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeTrackObjectImplToJson(
this,
);
}
}
abstract class _SpotubeTrackObject implements SpotubeTrackObject {
const factory _SpotubeTrackObject(
{required final String typeName,
required final String id,
required final String name,
required final String externalUri,
required final List<SpotubeSimpleArtistObject> artists,
required final SpotubeSimpleAlbumObject album,
required final BigInt durationMs,
required final String isrc,
required final bool explicit}) = _$SpotubeTrackObjectImpl;
factory _SpotubeTrackObject.fromJson(Map<String, dynamic> json) =
_$SpotubeTrackObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
String get externalUri;
@override
List<SpotubeSimpleArtistObject> get artists;
@override
SpotubeSimpleAlbumObject get album;
@override
BigInt get durationMs;
@override
String get isrc;
@override
bool get explicit;
/// Create a copy of SpotubeTrackObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeTrackObjectImplCopyWith<_$SpotubeTrackObjectImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,38 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'track.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeTrackObjectImpl _$$SpotubeTrackObjectImplFromJson(Map json) =>
_$SpotubeTrackObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
externalUri: json['externalUri'] as String,
artists: (json['artists'] as List<dynamic>)
.map((e) => SpotubeSimpleArtistObject.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList(),
album: SpotubeSimpleAlbumObject.fromJson(
Map<String, dynamic>.from(json['album'] as Map)),
durationMs: BigInt.parse(json['durationMs'] as String),
isrc: json['isrc'] as String,
explicit: json['explicit'] as bool,
);
Map<String, dynamic> _$$SpotubeTrackObjectImplToJson(
_$SpotubeTrackObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'externalUri': instance.externalUri,
'artists': instance.artists.map((e) => e.toJson()).toList(),
'album': instance.album.toJson(),
'durationMs': instance.durationMs.toString(),
'isrc': instance.isrc,
'explicit': instance.explicit,
};

View File

@ -0,0 +1,27 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../../frb_generated.dart';
import 'image.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'package:freezed_annotation/freezed_annotation.dart' hide protected;
part 'user.freezed.dart';
part 'user.g.dart';
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `fmt`
@freezed
sealed class SpotubeUserObject with _$SpotubeUserObject {
const factory SpotubeUserObject({
required String typeName,
required String id,
required String name,
required List<SpotubeImageObject> images,
required String externalUri,
}) = _SpotubeUserObject;
factory SpotubeUserObject.fromJson(Map<String, dynamic> json) =>
_$SpotubeUserObjectFromJson(json);
}

View File

@ -0,0 +1,259 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'user.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
SpotubeUserObject _$SpotubeUserObjectFromJson(Map<String, dynamic> json) {
return _SpotubeUserObject.fromJson(json);
}
/// @nodoc
mixin _$SpotubeUserObject {
String get typeName => throw _privateConstructorUsedError;
String get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
List<SpotubeImageObject> get images => throw _privateConstructorUsedError;
String get externalUri => throw _privateConstructorUsedError;
/// Serializes this SpotubeUserObject to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SpotubeUserObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SpotubeUserObjectCopyWith<SpotubeUserObject> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SpotubeUserObjectCopyWith<$Res> {
factory $SpotubeUserObjectCopyWith(
SpotubeUserObject value, $Res Function(SpotubeUserObject) then) =
_$SpotubeUserObjectCopyWithImpl<$Res, SpotubeUserObject>;
@useResult
$Res call(
{String typeName,
String id,
String name,
List<SpotubeImageObject> images,
String externalUri});
}
/// @nodoc
class _$SpotubeUserObjectCopyWithImpl<$Res, $Val extends SpotubeUserObject>
implements $SpotubeUserObjectCopyWith<$Res> {
_$SpotubeUserObjectCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SpotubeUserObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? images = null,
Object? externalUri = null,
}) {
return _then(_value.copyWith(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
images: null == images
? _value.images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$SpotubeUserObjectImplCopyWith<$Res>
implements $SpotubeUserObjectCopyWith<$Res> {
factory _$$SpotubeUserObjectImplCopyWith(_$SpotubeUserObjectImpl value,
$Res Function(_$SpotubeUserObjectImpl) then) =
__$$SpotubeUserObjectImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String typeName,
String id,
String name,
List<SpotubeImageObject> images,
String externalUri});
}
/// @nodoc
class __$$SpotubeUserObjectImplCopyWithImpl<$Res>
extends _$SpotubeUserObjectCopyWithImpl<$Res, _$SpotubeUserObjectImpl>
implements _$$SpotubeUserObjectImplCopyWith<$Res> {
__$$SpotubeUserObjectImplCopyWithImpl(_$SpotubeUserObjectImpl _value,
$Res Function(_$SpotubeUserObjectImpl) _then)
: super(_value, _then);
/// Create a copy of SpotubeUserObject
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? typeName = null,
Object? id = null,
Object? name = null,
Object? images = null,
Object? externalUri = null,
}) {
return _then(_$SpotubeUserObjectImpl(
typeName: null == typeName
? _value.typeName
: typeName // ignore: cast_nullable_to_non_nullable
as String,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
images: null == images
? _value._images
: images // ignore: cast_nullable_to_non_nullable
as List<SpotubeImageObject>,
externalUri: null == externalUri
? _value.externalUri
: externalUri // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SpotubeUserObjectImpl implements _SpotubeUserObject {
const _$SpotubeUserObjectImpl(
{required this.typeName,
required this.id,
required this.name,
required final List<SpotubeImageObject> images,
required this.externalUri})
: _images = images;
factory _$SpotubeUserObjectImpl.fromJson(Map<String, dynamic> json) =>
_$$SpotubeUserObjectImplFromJson(json);
@override
final String typeName;
@override
final String id;
@override
final String name;
final List<SpotubeImageObject> _images;
@override
List<SpotubeImageObject> get images {
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_images);
}
@override
final String externalUri;
@override
String toString() {
return 'SpotubeUserObject(typeName: $typeName, id: $id, name: $name, images: $images, externalUri: $externalUri)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SpotubeUserObjectImpl &&
(identical(other.typeName, typeName) ||
other.typeName == typeName) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
const DeepCollectionEquality().equals(other._images, _images) &&
(identical(other.externalUri, externalUri) ||
other.externalUri == externalUri));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, typeName, id, name,
const DeepCollectionEquality().hash(_images), externalUri);
/// Create a copy of SpotubeUserObject
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SpotubeUserObjectImplCopyWith<_$SpotubeUserObjectImpl> get copyWith =>
__$$SpotubeUserObjectImplCopyWithImpl<_$SpotubeUserObjectImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SpotubeUserObjectImplToJson(
this,
);
}
}
abstract class _SpotubeUserObject implements SpotubeUserObject {
const factory _SpotubeUserObject(
{required final String typeName,
required final String id,
required final String name,
required final List<SpotubeImageObject> images,
required final String externalUri}) = _$SpotubeUserObjectImpl;
factory _SpotubeUserObject.fromJson(Map<String, dynamic> json) =
_$SpotubeUserObjectImpl.fromJson;
@override
String get typeName;
@override
String get id;
@override
String get name;
@override
List<SpotubeImageObject> get images;
@override
String get externalUri;
/// Create a copy of SpotubeUserObject
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SpotubeUserObjectImplCopyWith<_$SpotubeUserObjectImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,29 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$SpotubeUserObjectImpl _$$SpotubeUserObjectImplFromJson(Map json) =>
_$SpotubeUserObjectImpl(
typeName: json['typeName'] as String,
id: json['id'] as String,
name: json['name'] as String,
images: (json['images'] as List<dynamic>)
.map((e) =>
SpotubeImageObject.fromJson(Map<String, dynamic>.from(e as Map)))
.toList(),
externalUri: json['externalUri'] as String,
);
Map<String, dynamic> _$$SpotubeUserObjectImplToJson(
_$SpotubeUserObjectImpl instance) =>
<String, dynamic>{
'typeName': instance.typeName,
'id': instance.id,
'name': instance.name,
'images': instance.images.map((e) => e.toJson()).toList(),
'externalUri': instance.externalUri,
};

View File

@ -0,0 +1,90 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../frb_generated.dart';
import 'models/core.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'senders.dart';
// These functions are ignored because they are not marked as `pub`: `js_executor_thread`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `fmt`
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `create_context`
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<OpaqueSender>>
abstract class OpaqueSender implements RustOpaqueInterface {
SenderPluginCommand get sender;
set sender(SenderPluginCommand sender);
}
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Sender < PluginCommand >>>
abstract class SenderPluginCommand implements RustOpaqueInterface {}
class SpotubePlugin {
final PluginArtistSender artist;
final PluginAlbumSender album;
final PluginAudioSourceSender audioSource;
final PluginAuthSender auth;
final PluginBrowseSender browse;
final PluginCoreSender core;
final PluginPlaylistSender playlist;
final PluginSearchSender search;
final PluginTrackSender track;
final PluginUserSender user;
const SpotubePlugin.raw({
required this.artist,
required this.album,
required this.audioSource,
required this.auth,
required this.browse,
required this.core,
required this.playlist,
required this.search,
required this.track,
required this.user,
});
Future<void> dispose({required OpaqueSender tx}) => RustLib.instance.api
.crateApiPluginPluginSpotubePluginDispose(that: this, tx: tx);
factory SpotubePlugin() =>
RustLib.instance.api.crateApiPluginPluginSpotubePluginNew();
static OpaqueSender newContext(
{required String pluginScript,
required PluginConfiguration pluginConfig}) =>
RustLib.instance.api.crateApiPluginPluginSpotubePluginNewContext(
pluginScript: pluginScript, pluginConfig: pluginConfig);
@override
int get hashCode =>
artist.hashCode ^
album.hashCode ^
audioSource.hashCode ^
auth.hashCode ^
browse.hashCode ^
core.hashCode ^
playlist.hashCode ^
search.hashCode ^
track.hashCode ^
user.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SpotubePlugin &&
runtimeType == other.runtimeType &&
artist == other.artist &&
album == other.album &&
audioSource == other.audioSource &&
auth == other.auth &&
browse == other.browse &&
core == other.core &&
playlist == other.playlist &&
search == other.search &&
track == other.track &&
user == other.user;
}

View File

@ -0,0 +1,444 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../../frb_generated.dart';
import 'models/album.dart';
import 'models/artist.dart';
import 'models/audio_source.dart';
import 'models/browse.dart';
import 'models/core.dart';
import 'models/image.dart';
import 'models/pagination.dart';
import 'models/playlist.dart';
import 'models/search.dart';
import 'models/track.dart';
import 'models/user.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
import 'plugin.dart';
// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`, `new`
class PluginAlbumSender {
const PluginAlbumSender();
Future<SpotubeFullAlbumObject> getAlbum(
{required OpaqueSender mpscTx, required String id}) =>
RustLib.instance.api.crateApiPluginSendersPluginAlbumSenderGetAlbum(
that: this, mpscTx: mpscTx, id: id);
Future<SpotubePaginationResponseObject> releases(
{required OpaqueSender mpscTx, int? offset, int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginAlbumSenderReleases(
that: this, mpscTx: mpscTx, offset: offset, limit: limit);
Future<void> save(
{required OpaqueSender mpscTx, required List<String> ids}) =>
RustLib.instance.api.crateApiPluginSendersPluginAlbumSenderSave(
that: this, mpscTx: mpscTx, ids: ids);
Future<SpotubePaginationResponseObject> tracks(
{required OpaqueSender mpscTx,
required String id,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginAlbumSenderTracks(
that: this, mpscTx: mpscTx, id: id, offset: offset, limit: limit);
Future<void> unsave(
{required OpaqueSender mpscTx, required List<String> ids}) =>
RustLib.instance.api.crateApiPluginSendersPluginAlbumSenderUnsave(
that: this, mpscTx: mpscTx, ids: ids);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginAlbumSender && runtimeType == other.runtimeType;
}
class PluginArtistSender {
const PluginArtistSender();
Future<SpotubePaginationResponseObject> albums(
{required OpaqueSender mpscTx,
required String id,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginArtistSenderAlbums(
that: this, mpscTx: mpscTx, id: id, offset: offset, limit: limit);
Future<SpotubeFullArtistObject> getArtist(
{required OpaqueSender mpscTx, required String id}) =>
RustLib.instance.api.crateApiPluginSendersPluginArtistSenderGetArtist(
that: this, mpscTx: mpscTx, id: id);
Future<SpotubePaginationResponseObject> related(
{required OpaqueSender mpscTx,
required String id,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginArtistSenderRelated(
that: this, mpscTx: mpscTx, id: id, offset: offset, limit: limit);
Future<void> save(
{required OpaqueSender mpscTx, required List<String> ids}) =>
RustLib.instance.api.crateApiPluginSendersPluginArtistSenderSave(
that: this, mpscTx: mpscTx, ids: ids);
Future<SpotubePaginationResponseObject> topTracks(
{required OpaqueSender mpscTx,
required String id,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginArtistSenderTopTracks(
that: this, mpscTx: mpscTx, id: id, offset: offset, limit: limit);
Future<void> unsave(
{required OpaqueSender mpscTx, required List<String> ids}) =>
RustLib.instance.api.crateApiPluginSendersPluginArtistSenderUnsave(
that: this, mpscTx: mpscTx, ids: ids);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginArtistSender && runtimeType == other.runtimeType;
}
class PluginAudioSourceSender {
const PluginAudioSourceSender();
Future<List<SpotubeAudioSourceMatchObject>> matches(
{required OpaqueSender mpscTx, required SpotubeTrackObject track}) =>
RustLib.instance.api.crateApiPluginSendersPluginAudioSourceSenderMatches(
that: this, mpscTx: mpscTx, track: track);
Future<List<SpotubeAudioSourceStreamObject>> streams(
{required OpaqueSender mpscTx,
required SpotubeAudioSourceMatchObject matched}) =>
RustLib.instance.api.crateApiPluginSendersPluginAudioSourceSenderStreams(
that: this, mpscTx: mpscTx, matched: matched);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginAudioSourceSender && runtimeType == other.runtimeType;
}
class PluginAuthSender {
const PluginAuthSender();
Future<void> authenticate({required OpaqueSender mpscTx}) =>
RustLib.instance.api.crateApiPluginSendersPluginAuthSenderAuthenticate(
that: this, mpscTx: mpscTx);
Future<bool> isAuthenticated({required OpaqueSender mpscTx}) =>
RustLib.instance.api.crateApiPluginSendersPluginAuthSenderIsAuthenticated(
that: this, mpscTx: mpscTx);
Future<void> logout({required OpaqueSender mpscTx}) => RustLib.instance.api
.crateApiPluginSendersPluginAuthSenderLogout(that: this, mpscTx: mpscTx);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginAuthSender && runtimeType == other.runtimeType;
}
class PluginBrowseSender {
const PluginBrowseSender();
Future<SpotubePaginationResponseObject> sectionItems(
{required OpaqueSender mpscTx,
required String id,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginBrowseSenderSectionItems(
that: this, mpscTx: mpscTx, id: id, offset: offset, limit: limit);
Future<SpotubePaginationResponseObject> sections(
{required OpaqueSender mpscTx, int? offset, int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginBrowseSenderSections(
that: this, mpscTx: mpscTx, offset: offset, limit: limit);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginBrowseSender && runtimeType == other.runtimeType;
}
class PluginCoreSender {
const PluginCoreSender();
Future<PluginUpdateAvailable?> checkUpdate(
{required OpaqueSender mpscTx,
required PluginConfiguration pluginConfig}) =>
RustLib.instance.api.crateApiPluginSendersPluginCoreSenderCheckUpdate(
that: this, mpscTx: mpscTx, pluginConfig: pluginConfig);
Future<void> scrobble(
{required OpaqueSender mpscTx, required ScrobbleDetails details}) =>
RustLib.instance.api.crateApiPluginSendersPluginCoreSenderScrobble(
that: this, mpscTx: mpscTx, details: details);
Future<String> support({required OpaqueSender mpscTx}) => RustLib.instance.api
.crateApiPluginSendersPluginCoreSenderSupport(that: this, mpscTx: mpscTx);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginCoreSender && runtimeType == other.runtimeType;
}
class PluginPlaylistSender {
const PluginPlaylistSender();
Future<void> addTracks(
{required OpaqueSender mpscTx,
required String playlistId,
required List<String> trackIds,
int? position}) =>
RustLib.instance.api.crateApiPluginSendersPluginPlaylistSenderAddTracks(
that: this,
mpscTx: mpscTx,
playlistId: playlistId,
trackIds: trackIds,
position: position);
Future<SpotubeFullPlaylistObject?> createPlaylist(
{required OpaqueSender mpscTx,
required String userId,
required String name,
String? description,
bool? public,
bool? collaborative}) =>
RustLib.instance.api
.crateApiPluginSendersPluginPlaylistSenderCreatePlaylist(
that: this,
mpscTx: mpscTx,
userId: userId,
name: name,
description: description,
public: public,
collaborative: collaborative);
Future<void> deletePlaylist(
{required OpaqueSender mpscTx, required String playlistId}) =>
RustLib.instance.api
.crateApiPluginSendersPluginPlaylistSenderDeletePlaylist(
that: this, mpscTx: mpscTx, playlistId: playlistId);
Future<SpotubeFullPlaylistObject> getPlaylist(
{required OpaqueSender mpscTx, required String id}) =>
RustLib.instance.api.crateApiPluginSendersPluginPlaylistSenderGetPlaylist(
that: this, mpscTx: mpscTx, id: id);
Future<void> removeTracks(
{required OpaqueSender mpscTx,
required String playlistId,
required List<String> trackIds}) =>
RustLib.instance.api
.crateApiPluginSendersPluginPlaylistSenderRemoveTracks(
that: this,
mpscTx: mpscTx,
playlistId: playlistId,
trackIds: trackIds);
Future<void> save(
{required OpaqueSender mpscTx, required String playlistId}) =>
RustLib.instance.api.crateApiPluginSendersPluginPlaylistSenderSave(
that: this, mpscTx: mpscTx, playlistId: playlistId);
Future<SpotubePaginationResponseObject> tracks(
{required OpaqueSender mpscTx,
required String id,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginPlaylistSenderTracks(
that: this, mpscTx: mpscTx, id: id, offset: offset, limit: limit);
Future<void> unsave(
{required OpaqueSender mpscTx, required String playlistId}) =>
RustLib.instance.api.crateApiPluginSendersPluginPlaylistSenderUnsave(
that: this, mpscTx: mpscTx, playlistId: playlistId);
Future<void> updatePlaylist(
{required OpaqueSender mpscTx,
required String playlistId,
String? name,
String? description,
bool? public,
bool? collaborative}) =>
RustLib.instance.api
.crateApiPluginSendersPluginPlaylistSenderUpdatePlaylist(
that: this,
mpscTx: mpscTx,
playlistId: playlistId,
name: name,
description: description,
public: public,
collaborative: collaborative);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginPlaylistSender && runtimeType == other.runtimeType;
}
class PluginSearchSender {
const PluginSearchSender();
Future<SpotubePaginationResponseObject> albums(
{required OpaqueSender mpscTx,
required String query,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginSearchSenderAlbums(
that: this,
mpscTx: mpscTx,
query: query,
offset: offset,
limit: limit);
Future<SpotubeSearchResponseObject> all(
{required OpaqueSender mpscTx, required String query}) =>
RustLib.instance.api.crateApiPluginSendersPluginSearchSenderAll(
that: this, mpscTx: mpscTx, query: query);
Future<SpotubePaginationResponseObject> artists(
{required OpaqueSender mpscTx,
required String query,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginSearchSenderArtists(
that: this,
mpscTx: mpscTx,
query: query,
offset: offset,
limit: limit);
Future<List<String>> chips({required OpaqueSender mpscTx}) => RustLib
.instance.api
.crateApiPluginSendersPluginSearchSenderChips(that: this, mpscTx: mpscTx);
Future<SpotubePaginationResponseObject> playlists(
{required OpaqueSender mpscTx,
required String query,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginSearchSenderPlaylists(
that: this,
mpscTx: mpscTx,
query: query,
offset: offset,
limit: limit);
Future<SpotubePaginationResponseObject> tracks(
{required OpaqueSender mpscTx,
required String query,
int? offset,
int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginSearchSenderTracks(
that: this,
mpscTx: mpscTx,
query: query,
offset: offset,
limit: limit);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginSearchSender && runtimeType == other.runtimeType;
}
class PluginTrackSender {
const PluginTrackSender();
Future<SpotubeTrackObject> getTrack(
{required OpaqueSender mpscTx, required String id}) =>
RustLib.instance.api.crateApiPluginSendersPluginTrackSenderGetTrack(
that: this, mpscTx: mpscTx, id: id);
Future<List<SpotubeTrackObject>> radio(
{required OpaqueSender mpscTx, required String id}) =>
RustLib.instance.api.crateApiPluginSendersPluginTrackSenderRadio(
that: this, mpscTx: mpscTx, id: id);
Future<void> save(
{required OpaqueSender mpscTx, required List<String> ids}) =>
RustLib.instance.api.crateApiPluginSendersPluginTrackSenderSave(
that: this, mpscTx: mpscTx, ids: ids);
Future<void> unsave(
{required OpaqueSender mpscTx, required List<String> ids}) =>
RustLib.instance.api.crateApiPluginSendersPluginTrackSenderUnsave(
that: this, mpscTx: mpscTx, ids: ids);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginTrackSender && runtimeType == other.runtimeType;
}
class PluginUserSender {
const PluginUserSender();
Future<SpotubeUserObject> me({required OpaqueSender mpscTx}) =>
RustLib.instance.api
.crateApiPluginSendersPluginUserSenderMe(that: this, mpscTx: mpscTx);
Future<SpotubePaginationResponseObject> savedAlbums(
{required OpaqueSender mpscTx, int? offset, int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginUserSenderSavedAlbums(
that: this, mpscTx: mpscTx, offset: offset, limit: limit);
Future<SpotubePaginationResponseObject> savedArtists(
{required OpaqueSender mpscTx, int? offset, int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginUserSenderSavedArtists(
that: this, mpscTx: mpscTx, offset: offset, limit: limit);
Future<SpotubePaginationResponseObject> savedPlaylists(
{required OpaqueSender mpscTx, int? offset, int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginUserSenderSavedPlaylists(
that: this, mpscTx: mpscTx, offset: offset, limit: limit);
Future<SpotubePaginationResponseObject> savedTracks(
{required OpaqueSender mpscTx, int? offset, int? limit}) =>
RustLib.instance.api.crateApiPluginSendersPluginUserSenderSavedTracks(
that: this, mpscTx: mpscTx, offset: offset, limit: limit);
@override
int get hashCode => 0;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is PluginUserSender && runtimeType == other.runtimeType;
}

View File

@ -1,10 +0,0 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
import '../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
String greet({required String name}) =>
RustLib.instance.api.crateApiSimpleGreet(name: name);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +0,0 @@
// This file is automatically generated, so please do not edit it.
// @generated by `flutter_rust_bridge`@ 2.11.1.
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
// Static analysis wrongly picks the IO variant, thus ignore this
// ignore_for_file: argument_type_not_assignable
import 'api/simple.dart';
import 'dart:async';
import 'dart:convert';
import 'frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart';
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
RustLibApiImplPlatform({
required super.handler,
required super.wire,
required super.generalizedFrbRustBinding,
required super.portManager,
});
@protected
String dco_decode_String(dynamic raw);
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@protected
int dco_decode_u_8(dynamic raw);
@protected
void dco_decode_unit(dynamic raw);
@protected
String sse_decode_String(SseDeserializer deserializer);
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@protected
int sse_decode_u_8(SseDeserializer deserializer);
@protected
void sse_decode_unit(SseDeserializer deserializer);
@protected
int sse_decode_i_32(SseDeserializer deserializer);
@protected
bool sse_decode_bool(SseDeserializer deserializer);
@protected
void sse_encode_String(String self, SseSerializer serializer);
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);
@protected
void sse_encode_u_8(int self, SseSerializer serializer);
@protected
void sse_encode_unit(void self, SseSerializer serializer);
@protected
void sse_encode_i_32(int self, SseSerializer serializer);
@protected
void sse_encode_bool(bool self, SseSerializer serializer);
}
// Section: wire_class
class RustLibWire implements BaseWire {
RustLibWire.fromExternalLibrary(ExternalLibrary lib);
}
@JS('wasm_bindgen')
external RustLibWasmModule get wasmModule;
@JS()
@anonymous
extension type RustLibWasmModule._(JSObject _) implements JSObject {}

View File

@ -1392,7 +1392,7 @@ packages:
source: hosted
version: "0.6.7"
json_annotation:
dependency: transitive
dependency: "direct main"
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"

View File

@ -156,6 +156,7 @@ dependencies:
rust_lib_spotube:
path: rust_builder
flutter_rust_bridge: 2.11.1
json_annotation: ^4.9.0
dev_dependencies:
build_runner: ^2.4.13

2505
rust/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,23 @@ edition = "2021"
[lib]
crate-type = ["cdylib", "staticlib"]
[[bin]]
name = "spotube"
path = "src/main.rs"
[dependencies]
flutter_rust_bridge = "=2.11.1"
boa_engine = "0.21.0"
boa_runtime = "0.21.0"
boa_gc = "0.21.0"
anyhow = "1"
reqwest = { version = "0.12.x" }
http = { version = "1.3.1" }
serde_json = "1"
serde = { version = "1.0.228", features = ["derive"] }
futures = "0.3.x"
tokio = { version = "1.48.0", features = ["full"] }
heck = "0.5.0"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] }

View File

@ -1 +1,7 @@
pub mod simple;
pub mod plugin;
#[flutter_rust_bridge::frb(init)]
pub fn init_app() {
// Default utilities - feel free to customize
flutter_rust_bridge::setup_default_user_utils();
}

View File

@ -0,0 +1,271 @@
use flutter_rust_bridge::frb;
use crate::api::plugin::models::album::SpotubeFullAlbumObject;
use crate::api::plugin::models::artist::SpotubeFullArtistObject;
use crate::api::plugin::models::audio_source::{
SpotubeAudioSourceMatchObject, SpotubeAudioSourceStreamObject,
};
use crate::api::plugin::models::core::{
PluginConfiguration, PluginUpdateAvailable, ScrobbleDetails,
};
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
use crate::api::plugin::models::playlist::SpotubeFullPlaylistObject;
use crate::api::plugin::models::search::SpotubeSearchResponseObject;
use crate::api::plugin::models::track::SpotubeTrackObject;
use crate::api::plugin::models::user::SpotubeUserObject;
use tokio::sync::oneshot;
pub enum ArtistCommands {
GetArtist {
id: String,
response_tx: oneshot::Sender<anyhow::Result<SpotubeFullArtistObject>>,
},
TopTracks {
id: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Albums {
id: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Related {
id: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Save {
ids: Vec<String>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
Unsave {
ids: Vec<String>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
}
pub enum AlbumCommands {
GetAlbum {
id: String,
response_tx: oneshot::Sender<anyhow::Result<SpotubeFullAlbumObject>>,
},
Tracks {
id: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Releases {
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Save {
ids: Vec<String>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
Unsave {
ids: Vec<String>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
}
pub enum AudioSourceCommands {
Matches {
track: SpotubeTrackObject,
response_tx: oneshot::Sender<anyhow::Result<Vec<SpotubeAudioSourceMatchObject>>>,
},
Streams {
matched: SpotubeAudioSourceMatchObject,
response_tx: oneshot::Sender<anyhow::Result<Vec<SpotubeAudioSourceStreamObject>>>,
},
}
pub enum AuthCommands {
Authenticate {
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
Logout {
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
IsAuthenticated {
response_tx: oneshot::Sender<anyhow::Result<bool>>,
},
}
pub enum BrowseCommands {
Sections {
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
SectionItems {
id: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
}
pub enum CoreCommands {
CheckUpdate {
plugin_config: PluginConfiguration,
response_tx: oneshot::Sender<anyhow::Result<Option<PluginUpdateAvailable>>>,
},
Support {
response_tx: oneshot::Sender<anyhow::Result<String>>,
},
Scrobble {
details: ScrobbleDetails,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
}
pub enum PlaylistCommands {
GetPlaylist {
id: String,
response_tx: oneshot::Sender<anyhow::Result<SpotubeFullPlaylistObject>>,
},
Tracks {
id: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
CreatePlaylist {
user_id: String,
name: String,
description: Option<String>,
public: Option<bool>,
collaborative: Option<bool>,
response_tx: oneshot::Sender<anyhow::Result<Option<SpotubeFullPlaylistObject>>>,
},
UpdatePlaylist {
playlist_id: String,
name: Option<String>,
description: Option<String>,
public: Option<bool>,
collaborative: Option<bool>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
DeletePlaylist {
playlist_id: String,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
AddTracks {
playlist_id: String,
track_ids: Vec<String>,
position: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
RemoveTracks {
playlist_id: String,
track_ids: Vec<String>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
Save {
playlist_id: String,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
Unsave {
playlist_id: String,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
}
pub enum SearchCommands {
Chips {
response_tx: oneshot::Sender<anyhow::Result<Vec<String>>>,
},
All {
query: String,
response_tx: oneshot::Sender<anyhow::Result<SpotubeSearchResponseObject>>,
},
Tracks {
query: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Albums {
query: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Artists {
query: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
Playlists {
query: String,
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
}
pub enum TrackCommands {
GetTrack {
id: String,
response_tx: oneshot::Sender<anyhow::Result<SpotubeTrackObject>>,
},
Save {
ids: Vec<String>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
Unsave {
ids: Vec<String>,
response_tx: oneshot::Sender<anyhow::Result<()>>,
},
Radio {
id: String,
response_tx: oneshot::Sender<anyhow::Result<Vec<SpotubeTrackObject>>>,
},
}
pub enum UserCommands {
Me {
response_tx: oneshot::Sender<anyhow::Result<SpotubeUserObject>>,
},
SavedTracks {
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
SavedAlbums {
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
SavedArtists {
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
SavedPlaylists {
offset: Option<u32>,
limit: Option<u32>,
response_tx: oneshot::Sender<anyhow::Result<SpotubePaginationResponseObject>>,
},
}
#[frb(unignore)]
pub enum PluginCommand {
Artist(ArtistCommands),
Album(AlbumCommands),
AudioSource(AudioSourceCommands),
Browse(BrowseCommands),
Track(TrackCommands),
User(UserCommands),
Playlist(PlaylistCommands),
Search(SearchCommands),
Core(CoreCommands),
Auth(AuthCommands),
Shutdown,
}

View File

@ -0,0 +1,401 @@
use crate::api::plugin::commands::{
AlbumCommands, ArtistCommands, AudioSourceCommands, AuthCommands, BrowseCommands, CoreCommands,
PlaylistCommands, SearchCommands, TrackCommands, UserCommands,
};
use crate::internal::album::PluginAlbumEndpoint;
use crate::internal::artist::PluginArtistEndpoint;
use crate::internal::audio_source::PluginAudioSourceEndpoint;
use crate::internal::auth::PluginAuthEndpoint;
use crate::internal::browse::PluginBrowseEndpoint;
use crate::internal::core::PluginCoreEndpoint;
use crate::internal::playlist::PluginPlaylistEndpoint;
use crate::internal::search::PluginSearchEndpoint;
use crate::internal::track::PluginTrackEndpoint;
use crate::internal::user::PluginUserEndpoint;
use boa_engine::Context;
use flutter_rust_bridge::frb;
use std::fmt::Debug;
use tokio::sync::oneshot;
fn send_response<T>(tx: oneshot::Sender<T>, response: T) -> anyhow::Result<()>
where
T: Debug,
{
tx.send(response)
.map_err(|e| anyhow::anyhow!("Failed to send response: {:?}", e))
}
#[frb(ignore)]
pub async fn execute_artists(command: ArtistCommands, context: &mut Context) -> anyhow::Result<()> {
let mut artist = PluginArtistEndpoint::new(context);
match command {
ArtistCommands::GetArtist { id, response_tx } => {
let artist = artist.get_artist(id).await;
send_response(response_tx, artist)
}
ArtistCommands::TopTracks {
id,
offset,
limit,
response_tx,
} => {
let tracks = artist.top_tracks(id, offset, limit).await;
send_response(response_tx, tracks)
}
ArtistCommands::Albums {
id,
offset,
limit,
response_tx,
} => {
let albums = artist.albums(id, offset, limit).await;
send_response(response_tx, albums)
}
ArtistCommands::Related {
id,
offset,
limit,
response_tx,
} => {
let artists = artist.related(id, offset, limit).await;
send_response(response_tx, artists)
}
ArtistCommands::Save { ids, response_tx } => {
let res = artist.save(ids).await;
send_response(response_tx, res)
}
ArtistCommands::Unsave { ids, response_tx } => {
let res = artist.unsave(ids).await;
send_response(response_tx, res)
}
}
}
#[frb(ignore)]
pub async fn execute_albums(command: AlbumCommands, context: &mut Context) -> anyhow::Result<()> {
let mut album = PluginAlbumEndpoint::new(context);
match command {
AlbumCommands::GetAlbum { id, response_tx } => {
let album = album.get_album(id).await;
send_response(response_tx, album)
}
AlbumCommands::Tracks {
id,
offset,
limit,
response_tx,
} => {
let tracks = album.tracks(id, offset, limit).await;
send_response(response_tx, tracks)
}
AlbumCommands::Releases {
offset,
limit,
response_tx,
} => {
let releases = album.releases(offset, limit).await;
send_response(response_tx, releases)
}
AlbumCommands::Save { ids, response_tx } => {
let res = album.save(ids).await;
send_response(response_tx, res)
}
AlbumCommands::Unsave { ids, response_tx } => {
let res = album.unsave(ids).await;
send_response(response_tx, res)
}
}
}
#[frb(ignore)]
pub async fn execute_audio_source(
command: AudioSourceCommands,
context: &mut Context,
) -> anyhow::Result<()> {
let mut audio_source = PluginAudioSourceEndpoint::new(context);
match command {
AudioSourceCommands::Matches { track, response_tx } => {
let audio_source = audio_source.matches(track).await;
send_response(response_tx, audio_source)
}
AudioSourceCommands::Streams {
matched,
response_tx,
} => {
let audio_source = audio_source.streams(matched).await;
send_response(response_tx, audio_source)
}
}
}
#[frb(ignore)]
pub async fn execute_auth(command: AuthCommands, context: &mut Context) -> anyhow::Result<()> {
let mut auth = PluginAuthEndpoint::new(context);
match command {
AuthCommands::Authenticate { response_tx } => {
let res = auth.authenticate().await;
send_response(response_tx, res)
}
AuthCommands::IsAuthenticated { response_tx } => {
let res = auth.is_authenticated();
send_response(response_tx, res)
}
AuthCommands::Logout { response_tx } => {
let res = auth.logout().await;
send_response(response_tx, res)
}
}
}
#[frb(ignore)]
pub async fn execute_browse(command: BrowseCommands, context: &mut Context) -> anyhow::Result<()> {
let mut browse = PluginBrowseEndpoint::new(context);
match command {
BrowseCommands::Sections {
offset,
limit,
response_tx,
} => {
let sections = browse.sections(offset, limit).await;
send_response(response_tx, sections)
}
BrowseCommands::SectionItems {
id,
offset,
limit,
response_tx,
} => {
let items = browse.section_items(id, offset, limit).await;
send_response(response_tx, items)
}
}
}
#[frb(ignore)]
pub async fn execute_core(command: CoreCommands, context: &mut Context) -> anyhow::Result<()> {
let mut core = PluginCoreEndpoint::new(context);
match command {
CoreCommands::CheckUpdate {
response_tx,
plugin_config,
} => {
let res = core.check_update(plugin_config).await;
send_response(response_tx, res)
}
CoreCommands::Scrobble {
details,
response_tx,
} => {
let res = core.scrobble(details).await;
send_response(response_tx, res)
}
CoreCommands::Support { response_tx } => {
let res = core.support();
send_response(response_tx, res)
}
}
}
#[frb(ignore)]
pub async fn execute_playlist(
command: PlaylistCommands,
context: &mut Context,
) -> anyhow::Result<()> {
let mut playlist = PluginPlaylistEndpoint::new(context);
match command {
PlaylistCommands::GetPlaylist { id, response_tx } => {
let playlist = playlist.get_playlist(id).await;
send_response(response_tx, playlist)
}
PlaylistCommands::Tracks {
id,
offset,
limit,
response_tx,
} => {
let tracks = playlist.tracks(id, offset, limit).await;
send_response(response_tx, tracks)
}
PlaylistCommands::CreatePlaylist {
user_id,
name,
description,
public,
collaborative,
response_tx,
} => {
let playlist = playlist
.create(user_id, name, description, public, collaborative)
.await;
send_response(response_tx, playlist)
}
PlaylistCommands::UpdatePlaylist {
playlist_id,
name,
description,
public,
collaborative,
response_tx,
} => {
let res = playlist
.update(playlist_id, name, description, public, collaborative)
.await;
send_response(response_tx, res)
}
PlaylistCommands::DeletePlaylist {
playlist_id,
response_tx,
} => {
let res = playlist.delete_playlist(playlist_id).await;
send_response(response_tx, res)
}
PlaylistCommands::AddTracks {
playlist_id,
track_ids,
position,
response_tx,
} => {
let res = playlist.add_tracks(playlist_id, track_ids, position).await;
send_response(response_tx, res)
}
PlaylistCommands::RemoveTracks {
playlist_id,
track_ids,
response_tx,
} => {
let res = playlist.remove_tracks(playlist_id, track_ids).await;
send_response(response_tx, res)
}
PlaylistCommands::Save {
playlist_id,
response_tx,
} => {
let res = playlist.save(playlist_id).await;
send_response(response_tx, res)
}
PlaylistCommands::Unsave {
playlist_id,
response_tx,
} => {
let res = playlist.unsave(playlist_id).await;
send_response(response_tx, res)
}
}
}
#[frb(ignore)]
pub async fn execute_search(command: SearchCommands, context: &mut Context) -> anyhow::Result<()> {
let mut search = PluginSearchEndpoint::new(context);
match command {
SearchCommands::Chips { response_tx } => {
let chips = search.chips();
send_response(response_tx, chips)
}
SearchCommands::All { query, response_tx } => {
let all = search.all(query).await;
send_response(response_tx, all)
}
SearchCommands::Tracks {
query,
offset,
limit,
response_tx,
} => {
let tracks = search.tracks(query, offset, limit).await;
send_response(response_tx, tracks)
}
SearchCommands::Albums {
query,
offset,
limit,
response_tx,
} => {
let albums = search.albums(query, offset, limit).await;
send_response(response_tx, albums)
}
SearchCommands::Artists {
query,
offset,
limit,
response_tx,
} => {
let artists = search.artists(query, offset, limit).await;
send_response(response_tx, artists)
}
SearchCommands::Playlists {
query,
offset,
limit,
response_tx,
} => {
let playlists = search.playlists(query, offset, limit).await;
send_response(response_tx, playlists)
}
}
}
#[frb(ignore)]
pub async fn execute_track(command: TrackCommands, context: &mut Context) -> anyhow::Result<()> {
let mut track = PluginTrackEndpoint::new(context);
match command {
TrackCommands::GetTrack { id, response_tx } => {
let res = track.get_track(id).await;
send_response(response_tx, res)
}
TrackCommands::Save { ids, response_tx } => {
let res = track.save(ids).await;
send_response(response_tx, res)
}
TrackCommands::Unsave { ids, response_tx } => {
let res = track.unsave(ids).await;
send_response(response_tx, res)
}
TrackCommands::Radio { id, response_tx } => {
let res = track.radio(id).await;
send_response(response_tx, res)
}
}
}
#[frb(ignore)]
pub async fn execute_user(command: UserCommands, context: &mut Context) -> anyhow::Result<()> {
let mut user = PluginUserEndpoint::new(context);
match command {
UserCommands::Me { response_tx } => {
let me = user.me().await;
send_response(response_tx, me)
}
UserCommands::SavedTracks {
offset,
limit,
response_tx,
} => {
let tracks = user.saved_tracks(offset, limit).await;
send_response(response_tx, tracks)
}
UserCommands::SavedAlbums {
offset,
limit,
response_tx,
} => {
let albums = user.saved_albums(offset, limit).await;
send_response(response_tx, albums)
}
UserCommands::SavedArtists {
offset,
limit,
response_tx,
} => {
let artists = user.saved_artists(offset, limit).await;
send_response(response_tx, artists)
}
UserCommands::SavedPlaylists {
offset,
limit,
response_tx,
} => {
let playlists = user.saved_playlists(offset, limit).await;
send_response(response_tx, playlists)
}
}
}

View File

@ -0,0 +1,5 @@
pub mod commands;
pub mod plugin;
pub mod executors;
pub mod senders;
pub mod models;

View File

@ -0,0 +1,44 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
use crate::api::plugin::models::artist::SpotubeSimpleArtistObject;
use crate::api::plugin::models::image::SpotubeImageObject;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum SpotubeAlbumType {
Album,
Single,
Compilation,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeSimpleAlbumObject {
pub type_name: String,
pub id: String,
pub name: String,
pub external_uri: String,
pub artists: Vec<SpotubeSimpleArtistObject>,
#[serde(default)]
pub images: Vec<SpotubeImageObject>,
pub album_type: SpotubeAlbumType,
pub release_date: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeFullAlbumObject {
pub type_name: String,
pub id: String,
pub name: String,
pub artists: Vec<SpotubeSimpleArtistObject>,
#[serde(default)]
pub images: Vec<SpotubeImageObject>,
pub release_date: String,
pub external_uri: String,
pub total_tracks: i32,
pub album_type: SpotubeAlbumType,
pub record_label: Option<String>,
pub genres: Option<Vec<String>>,
}

View File

@ -0,0 +1,28 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
use crate::api::plugin::models::image::SpotubeImageObject;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeSimpleArtistObject {
pub type_name: String,
pub id: String,
pub name: String,
pub external_uri: String,
pub images: Option<Vec<SpotubeImageObject>>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeFullArtistObject {
pub type_name: String,
pub id: String,
pub name: String,
pub external_uri: String,
#[serde(default)]
pub images: Vec<SpotubeImageObject>,
pub genres: Option<Vec<String>>,
pub followers: Option<i32>,
}

View File

@ -0,0 +1,111 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[frb(dart_metadata=("freezed"),json_serializable)]
#[serde(rename_all = "lowercase")]
pub enum SpotubeMediaCompressionType {
Lossy,
Lossless,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeAudioLossyContainerQuality {
pub bitrate: i32,
}
impl SpotubeAudioLossyContainerQuality {
pub fn to_string_fmt(&self) -> String {
let kbps = self.bitrate as f64 / 1000.0;
if kbps.fract() == 0.0 {
format!("{}kbps", kbps as i32)
} else {
format!("{:.1}kbps", kbps)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeAudioLosslessContainerQuality {
pub bit_depth: i32,
pub sample_rate: i32,
}
impl SpotubeAudioLosslessContainerQuality {
#[frb(sync)]
pub fn to_string_fmt(&self) -> String {
let khz = self.sample_rate as f64 / 1000.0;
if khz.fract() == 0.0 {
format!("{}bit • {}kHz", self.bit_depth, khz as i32)
} else {
format!("{}bit • {:.1}kHz", self.bit_depth, khz)
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[frb(dart_metadata=("freezed"),json_serializable)]
#[serde(tag = "type", content = "data")]
pub enum SpotubeAudioSourceContainerPreset {
#[serde(rename = "lossy")]
Lossy {
#[serde(rename = "type")]
compression_type: SpotubeMediaCompressionType,
name: String,
qualities: Vec<SpotubeAudioLossyContainerQuality>,
},
#[serde(rename = "lossless")]
Lossless {
#[serde(rename = "type")]
compression_type: SpotubeMediaCompressionType,
name: String,
qualities: Vec<SpotubeAudioLosslessContainerQuality>,
},
}
impl SpotubeAudioSourceContainerPreset {
#[frb(sync)]
pub fn file_extension(&self) -> String {
let name = match self {
Self::Lossy { name, .. } => name,
Self::Lossless { name, .. } => name,
};
match name.as_str() {
"mp4" => "m4a".into(),
"webm" => "weba".into(),
other => other.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeAudioSourceMatchObject {
pub type_name: String,
pub id: String,
pub title: String,
pub artists: Vec<String>,
pub duration: u64, // Duration in ms
pub thumbnail: Option<String>,
pub external_uri: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeAudioSourceStreamObject {
pub type_name: String,
pub url: String,
pub container: String,
#[serde(rename = "type")]
pub compression_type: SpotubeMediaCompressionType,
pub codec: Option<String>,
pub bitrate: Option<f64>,
pub bit_depth: Option<i32>,
pub sample_rate: Option<f64>,
}

View File

@ -0,0 +1,37 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
use crate::api::plugin::models::album::{SpotubeFullAlbumObject, SpotubeSimpleAlbumObject};
use crate::api::plugin::models::artist::{SpotubeFullArtistObject, SpotubeSimpleArtistObject};
use crate::api::plugin::models::playlist::{SpotubeFullPlaylistObject, SpotubeSimplePlaylistObject};
use crate::api::plugin::models::track::SpotubeTrackObject;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"))]
pub struct SpotubeBrowseSectionObject {
pub type_name: String,
pub id: String,
pub title: String,
pub external_uri: String,
pub browse_more: bool,
pub items: Vec<SpotubeBrowseSectionResponseObjectItem>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase", tag = "type_name")]
pub enum SpotubeBrowseSectionResponseObjectItem {
#[serde(rename = "track")]
Track(SpotubeTrackObject),
#[serde(rename = "playlist_full")]
PlaylistFull(SpotubeFullPlaylistObject),
#[serde(rename = "playlist_simple")]
PlaylistSimple(SpotubeSimplePlaylistObject),
#[serde(rename = "album_simple")]
AlbumSimple(SpotubeSimpleAlbumObject),
#[serde(rename = "album_full")]
AlbumFull(SpotubeFullAlbumObject),
#[serde(rename = "artist_full")]
ArtistFull(SpotubeFullArtistObject),
#[serde(rename = "artist_simple")]
ArtistSimple(SpotubeSimpleArtistObject),
}

View File

@ -0,0 +1,78 @@
use heck::ToKebabCase;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum PluginApi {
Webview,
Localstorage,
Timezone,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum PluginAbility {
#[serde(rename = "authentication")]
Authentication,
#[serde(rename = "scrobbling")]
Scrobbling,
#[serde(rename = "metadata")]
Metadata,
#[serde(rename = "audio-source")]
AudioSource,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PluginConfiguration {
pub name: String,
pub description: String,
pub version: String,
pub author: String,
pub entry_point: String,
pub plugin_api_version: String,
#[serde(default)]
pub apis: Vec<PluginApi>,
#[serde(default)]
pub abilities: Vec<PluginAbility>,
pub repository: Option<String>,
}
impl PluginConfiguration {
pub fn slug(&self) -> String {
self.name.to_kebab_case()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PluginUpdateAvailable {
pub download_url: String,
pub version: String,
pub changelog: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ScrobbleArtist {
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ScrobbleAlbum {
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ScrobbleDetails {
pub id: String,
pub title: String,
pub artists: Vec<ScrobbleArtist>,
pub album: ScrobbleAlbum,
pub timestamp: Option<i64>,
pub duration_ms: Option<u32>,
pub isrc: Option<String>,
}

View File

@ -0,0 +1,11 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeImageObject {
pub type_name: String,
pub url: String,
pub width: Option<i32>,
pub height: Option<i32>,
}

View File

@ -0,0 +1,11 @@
pub mod audio_source;
pub mod album;
pub mod artist;
pub mod browse;
pub mod image;
pub mod search;
pub mod playlist;
pub mod track;
pub mod user;
pub mod pagination;
pub mod core;

View File

@ -0,0 +1,39 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
use crate::api::plugin::models::album::{SpotubeFullAlbumObject, SpotubeSimpleAlbumObject};
use crate::api::plugin::models::artist::{SpotubeFullArtistObject, SpotubeSimpleArtistObject};
use crate::api::plugin::models::browse::SpotubeBrowseSectionObject;
use crate::api::plugin::models::playlist::{SpotubeFullPlaylistObject, SpotubeSimplePlaylistObject};
use crate::api::plugin::models::track::SpotubeTrackObject;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"))]
pub struct SpotubePaginationResponseObject {
pub limit: i32,
pub next_offset: Option<i32>,
pub total: i32,
pub has_more: bool,
pub items: Vec<SpotubePaginationResponseObjectItem>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase", tag = "type_name")]
pub enum SpotubePaginationResponseObjectItem {
#[serde(rename = "track")]
Track(SpotubeTrackObject),
#[serde(rename = "playlist_full")]
PlaylistFull(SpotubeFullPlaylistObject),
#[serde(rename = "playlist_simple")]
PlaylistSimple(SpotubeSimplePlaylistObject),
#[serde(rename = "album_simple")]
AlbumSimple(SpotubeSimpleAlbumObject),
#[serde(rename = "album_full")]
AlbumFull(SpotubeFullAlbumObject),
#[serde(rename = "artist_full")]
ArtistFull(SpotubeFullArtistObject),
#[serde(rename = "artist_simple")]
ArtistSimple(SpotubeSimpleArtistObject),
#[serde(rename = "browse_section")]
BrowseSection(SpotubeBrowseSectionObject),
}

View File

@ -0,0 +1,38 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
use crate::api::plugin::models::image::SpotubeImageObject;
use crate::api::plugin::models::user::SpotubeUserObject;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeSimplePlaylistObject {
pub type_name: String,
pub id: String,
pub name: String,
pub description: String,
pub external_uri: String,
pub owner: SpotubeUserObject,
#[serde(default)]
pub images: Vec<SpotubeImageObject>, // @Default([])
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeFullPlaylistObject {
pub type_name: String,
pub id: String,
pub name: String,
pub description: String,
pub external_uri: String,
pub owner: SpotubeUserObject,
#[serde(default)]
pub images: Vec<SpotubeImageObject>, // @Default([])
#[serde(default)]
pub collaborators: Vec<SpotubeUserObject>, // @Default([])
#[serde(default)]
pub collaborative: bool, // @Default(false)
#[serde(default)]
pub public: bool, // @Default(false)
}

View File

@ -0,0 +1,17 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
use crate::api::plugin::models::album::SpotubeSimpleAlbumObject;
use crate::api::plugin::models::artist::SpotubeFullArtistObject;
use crate::api::plugin::models::playlist::SpotubeSimplePlaylistObject;
use crate::api::plugin::models::track::SpotubeTrackObject;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeSearchResponseObject {
pub type_name: String,
pub albums: Vec<SpotubeSimpleAlbumObject>,
pub artists: Vec<SpotubeFullArtistObject>,
pub playlists: Vec<SpotubeSimplePlaylistObject>,
pub tracks: Vec<SpotubeTrackObject>,
}

View File

@ -0,0 +1,20 @@
use flutter_rust_bridge::frb;
use crate::api::plugin::models::album::SpotubeSimpleAlbumObject;
use crate::api::plugin::models::artist::SpotubeSimpleArtistObject;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeTrackObject {
pub type_name: String,
pub id: String,
pub name: String,
pub external_uri: String,
#[serde(default)]
pub artists: Vec<SpotubeSimpleArtistObject>,
pub album: SpotubeSimpleAlbumObject,
pub duration_ms: u64, // Duration in ms
pub isrc: String,
pub explicit: bool,
}

View File

@ -0,0 +1,15 @@
use flutter_rust_bridge::frb;
use serde::{Deserialize, Serialize};
use crate::api::plugin::models::image::SpotubeImageObject;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
#[frb(dart_metadata=("freezed"),json_serializable)]
pub struct SpotubeUserObject {
pub type_name: String,
pub id: String,
pub name: String,
#[serde(default)]
pub images: Vec<SpotubeImageObject>, // @Default([])
pub external_uri: String,
}

View File

@ -0,0 +1,137 @@
use crate::api::plugin::commands::PluginCommand;
use crate::api::plugin::executors::{
execute_albums, execute_artists, execute_audio_source, execute_auth, execute_browse,
execute_core, execute_playlist, execute_search, execute_track, execute_user,
};
use crate::api::plugin::models::core::PluginConfiguration;
use crate::api::plugin::senders::{
PluginAlbumSender, PluginArtistSender, PluginAudioSourceSender, PluginAuthSender,
PluginBrowseSender, PluginCoreSender, PluginPlaylistSender, PluginSearchSender,
PluginTrackSender, PluginUserSender,
};
use crate::internal::apis::fetcher::ReqwestFetcher;
use anyhow::anyhow;
use boa_engine::{Context, Source};
use boa_runtime::{fetch, interval, microtask, text, Console, DefaultLogger};
use flutter_rust_bridge::frb;
use std::thread;
use tokio::runtime::Runtime;
use tokio::sync::mpsc;
use tokio::sync::mpsc::Sender;
#[derive(Debug, Clone)]
#[frb(opaque)]
pub struct OpaqueSender {
pub sender: Sender<PluginCommand>,
}
#[frb(ignore)]
async fn js_executor_thread(
plugin_script: String,
plugin_config: PluginConfiguration,
mut rx: mpsc::Receiver<PluginCommand>,
) -> anyhow::Result<()> {
let mut context = create_context()?;
let injection = format!(
"const pluginInstance = new {}();",
plugin_config.entry_point
);
let script = format!("{}\n{}", plugin_script, injection);
context
.eval(Source::from_bytes(script.as_bytes()))
.map_err(|e| anyhow!("{}", e))?;
while let Some(command) = rx.blocking_recv() {
match command {
PluginCommand::Artist(commands) => execute_artists(commands, &mut context).await?,
PluginCommand::Album(commands) => execute_albums(commands, &mut context).await?,
PluginCommand::AudioSource(commands) => {
execute_audio_source(commands, &mut context).await?
}
PluginCommand::Auth(commands) => execute_auth(commands, &mut context).await?,
PluginCommand::Browse(commands) => execute_browse(commands, &mut context).await?,
PluginCommand::Core(commands) => execute_core(commands, &mut context).await?,
PluginCommand::Playlist(commands) => execute_playlist(commands, &mut context).await?,
PluginCommand::Search(commands) => execute_search(commands, &mut context).await?,
PluginCommand::Track(commands) => execute_track(commands, &mut context).await?,
PluginCommand::User(commands) => execute_user(commands, &mut context).await?,
PluginCommand::Shutdown => {
println!("JS Executor thread shutting down.");
// This command doesn't send a response; break the loop instead.
return Ok(());
}
};
}
Ok(())
}
#[frb(ignore)]
pub fn create_context() -> anyhow::Result<Context> {
let mut context = Context::default();
Console::register_with_logger(DefaultLogger, &mut context).map_err(|e| anyhow!("{}", e))?;
fetch::register(ReqwestFetcher::new(), None, &mut context).map_err(|e| anyhow!("{}", e))?;
interval::register(&mut context).map_err(|e| anyhow!("{}", e))?;
microtask::register(None, &mut context).map_err(|e| anyhow!("{}", e))?;
text::register(None, &mut context).map_err(|e| anyhow!("{}", e))?;
interval::register(&mut context).map_err(|e| anyhow!("{}", e))?;
microtask::register(None, &mut context).map_err(|e| anyhow!("{}", e))?;
Ok(context)
}
pub struct SpotubePlugin {
pub artist: PluginArtistSender,
pub album: PluginAlbumSender,
pub audio_source: PluginAudioSourceSender,
pub auth: PluginAuthSender,
pub browse: PluginBrowseSender,
pub core: PluginCoreSender,
pub playlist: PluginPlaylistSender,
pub search: PluginSearchSender,
pub track: PluginTrackSender,
pub user: PluginUserSender,
}
impl SpotubePlugin {
#[frb(sync)]
pub fn new() -> Self {
Self {
artist: PluginArtistSender::new(),
album: PluginAlbumSender::new(),
audio_source: PluginAudioSourceSender::new(),
auth: PluginAuthSender::new(),
browse: PluginBrowseSender::new(),
core: PluginCoreSender::new(),
playlist: PluginPlaylistSender::new(),
search: PluginSearchSender::new(),
track: PluginTrackSender::new(),
user: PluginUserSender::new(),
}
}
#[frb(sync)]
pub fn new_context(
plugin_script: String,
plugin_config: PluginConfiguration,
) -> anyhow::Result<OpaqueSender> {
let (command_tx, command_rx) = mpsc::channel(32);
let _thread_handle = thread::spawn(|| {
let rt = Runtime::new().unwrap();
rt.block_on(async {
if let Err(e) = js_executor_thread(plugin_script, plugin_config, command_rx).await {
eprintln!("JS Executor thread encountered a fatal error: {:?}", e);
}
});
});
Ok(OpaqueSender { sender: command_tx })
}
pub async fn dispose(&self, tx: OpaqueSender) -> anyhow::Result<()> {
tx.sender.send(PluginCommand::Shutdown).await?;
Ok(())
}
}

View File

@ -0,0 +1,886 @@
use crate::api::plugin::commands::{
AlbumCommands, ArtistCommands, AudioSourceCommands, AuthCommands, BrowseCommands, CoreCommands,
PlaylistCommands, PluginCommand, SearchCommands, TrackCommands, UserCommands,
};
use crate::api::plugin::models::album::SpotubeFullAlbumObject;
use crate::api::plugin::models::artist::SpotubeFullArtistObject;
use crate::api::plugin::models::audio_source::{
SpotubeAudioSourceMatchObject, SpotubeAudioSourceStreamObject,
};
use crate::api::plugin::models::core::{
PluginConfiguration, PluginUpdateAvailable, ScrobbleDetails,
};
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
use crate::api::plugin::models::playlist::SpotubeFullPlaylistObject;
use crate::api::plugin::models::search::SpotubeSearchResponseObject;
use crate::api::plugin::models::track::SpotubeTrackObject;
use crate::api::plugin::models::user::SpotubeUserObject;
use crate::api::plugin::plugin::OpaqueSender;
use anyhow::anyhow;
use flutter_rust_bridge::frb;
use tokio::sync::oneshot;
pub struct PluginArtistSender {}
impl PluginArtistSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn get_artist(
&self,
mpsc_tx: OpaqueSender,
id: String,
) -> anyhow::Result<SpotubeFullArtistObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Artist(ArtistCommands::GetArtist {
id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn top_tracks(
&self,
mpsc_tx: OpaqueSender,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Artist(ArtistCommands::TopTracks {
id,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn albums(
&self,
mpsc_tx: OpaqueSender,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Artist(ArtistCommands::Albums {
id,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn related(
&self,
mpsc_tx: OpaqueSender,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Artist(ArtistCommands::Related {
id,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn save(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Artist(ArtistCommands::Save {
ids,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn unsave(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Artist(ArtistCommands::Unsave {
ids,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginAlbumSender {}
impl PluginAlbumSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn get_album(
&self,
mpsc_tx: OpaqueSender,
id: String,
) -> anyhow::Result<SpotubeFullAlbumObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Album(AlbumCommands::GetAlbum {
id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn tracks(
&self,
mpsc_tx: OpaqueSender,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Album(AlbumCommands::Tracks {
id,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn releases(
&self,
mpsc_tx: OpaqueSender,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Album(AlbumCommands::Releases {
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn save(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Album(AlbumCommands::Save {
ids,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn unsave(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Album(AlbumCommands::Unsave {
ids,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginAudioSourceSender {}
impl PluginAudioSourceSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn matches(
&self,
mpsc_tx: OpaqueSender,
track: SpotubeTrackObject,
) -> anyhow::Result<Vec<SpotubeAudioSourceMatchObject>> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::AudioSource(AudioSourceCommands::Matches {
track,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn streams(
&self,
mpsc_tx: OpaqueSender,
matched: SpotubeAudioSourceMatchObject,
) -> anyhow::Result<Vec<SpotubeAudioSourceStreamObject>> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::AudioSource(AudioSourceCommands::Streams {
matched,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginAuthSender {}
impl PluginAuthSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn authenticate(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Auth(AuthCommands::Authenticate {
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn logout(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Auth(AuthCommands::Logout {
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn is_authenticated(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<bool> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Auth(AuthCommands::IsAuthenticated {
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginBrowseSender {}
impl PluginBrowseSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn sections(
&self,
mpsc_tx: OpaqueSender,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Browse(BrowseCommands::Sections {
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn section_items(
&self,
mpsc_tx: OpaqueSender,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Browse(BrowseCommands::SectionItems {
id,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginCoreSender {}
impl PluginCoreSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn check_update(
&self,
mpsc_tx: OpaqueSender,
plugin_config: PluginConfiguration,
) -> anyhow::Result<Option<PluginUpdateAvailable>> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Core(CoreCommands::CheckUpdate {
plugin_config,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn support(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<String> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Core(CoreCommands::Support {
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn scrobble(
&self,
mpsc_tx: OpaqueSender,
details: ScrobbleDetails,
) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Core(CoreCommands::Scrobble {
details,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginPlaylistSender {}
impl PluginPlaylistSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn get_playlist(
&self,
mpsc_tx: OpaqueSender,
id: String,
) -> anyhow::Result<SpotubeFullPlaylistObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::GetPlaylist {
id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn tracks(
&self,
mpsc_tx: OpaqueSender,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::Tracks {
id,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn create_playlist(
&self,
mpsc_tx: OpaqueSender,
user_id: String,
name: String,
description: Option<String>,
public: Option<bool>,
collaborative: Option<bool>,
) -> anyhow::Result<Option<SpotubeFullPlaylistObject>> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::CreatePlaylist {
user_id,
name,
description,
public,
collaborative,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn update_playlist(
&self,
mpsc_tx: OpaqueSender,
playlist_id: String,
name: Option<String>,
description: Option<String>,
public: Option<bool>,
collaborative: Option<bool>,
) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::UpdatePlaylist {
playlist_id,
name,
description,
public,
collaborative,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn delete_playlist(
&self,
mpsc_tx: OpaqueSender,
playlist_id: String,
) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::DeletePlaylist {
playlist_id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn add_tracks(
&self,
mpsc_tx: OpaqueSender,
playlist_id: String,
track_ids: Vec<String>,
position: Option<u32>,
) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::AddTracks {
playlist_id,
track_ids,
position,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn remove_tracks(
&self,
mpsc_tx: OpaqueSender,
playlist_id: String,
track_ids: Vec<String>,
) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::RemoveTracks {
playlist_id,
track_ids,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn save(&self, mpsc_tx: OpaqueSender, playlist_id: String) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::Save {
playlist_id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn unsave(&self, mpsc_tx: OpaqueSender, playlist_id: String) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Playlist(PlaylistCommands::Unsave {
playlist_id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginSearchSender {}
impl PluginSearchSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn chips(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<Vec<String>> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Search(SearchCommands::Chips {
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn all(
&self,
mpsc_tx: OpaqueSender,
query: String,
) -> anyhow::Result<SpotubeSearchResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Search(SearchCommands::All {
query,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn tracks(
&self,
mpsc_tx: OpaqueSender,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Search(SearchCommands::Tracks {
query,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn albums(
&self,
mpsc_tx: OpaqueSender,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Search(SearchCommands::Albums {
query,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn artists(
&self,
mpsc_tx: OpaqueSender,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Search(SearchCommands::Artists {
query,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn playlists(
&self,
mpsc_tx: OpaqueSender,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Search(SearchCommands::Playlists {
query,
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginTrackSender {}
impl PluginTrackSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn get_track(
&self,
mpsc_tx: OpaqueSender,
id: String,
) -> anyhow::Result<SpotubeTrackObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Track(TrackCommands::GetTrack {
id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn save(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Track(TrackCommands::Save {
ids,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn unsave(&self, mpsc_tx: OpaqueSender, ids: Vec<String>) -> anyhow::Result<()> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Track(TrackCommands::Unsave {
ids,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn radio(
&self,
mpsc_tx: OpaqueSender,
id: String,
) -> anyhow::Result<Vec<SpotubeTrackObject>> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::Track(TrackCommands::Radio {
id,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}
pub struct PluginUserSender {}
impl PluginUserSender {
#[frb(ignore)]
pub fn new() -> Self {
Self {}
}
pub async fn me(&self, mpsc_tx: OpaqueSender) -> anyhow::Result<SpotubeUserObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::User(UserCommands::Me { response_tx: tx }))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn saved_tracks(
&self,
mpsc_tx: OpaqueSender,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::User(UserCommands::SavedTracks {
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn saved_albums(
&self,
mpsc_tx: OpaqueSender,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::User(UserCommands::SavedAlbums {
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn saved_artists(
&self,
mpsc_tx: OpaqueSender,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::User(UserCommands::SavedArtists {
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
pub async fn saved_playlists(
&self,
mpsc_tx: OpaqueSender,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let (tx, rx) = oneshot::channel();
mpsc_tx
.sender
.send(PluginCommand::User(UserCommands::SavedPlaylists {
offset,
limit,
response_tx: tx,
}))
.await?;
rx.await.map_err(|e| anyhow!("{e}")).and_then(|o| o)
}
}

View File

@ -1,10 +0,0 @@
#[flutter_rust_bridge::frb(sync)] // Synchronous mode for simplicity of the demo
pub fn greet(name: String) -> String {
format!("Hello, {name}!")
}
#[flutter_rust_bridge::frb(init)]
pub fn init_app() {
// Default utilities - feel free to customize
flutter_rust_bridge::setup_default_user_utils();
}

File diff suppressed because it is too large Load Diff

148
rust/src/internal/album.rs Normal file
View File

@ -0,0 +1,148 @@
use crate::api::plugin::models::album::SpotubeFullAlbumObject;
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginAlbumEndpoint<'a>(&'a mut Context);
impl<'a> PluginAlbumEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginAlbumEndpoint<'a> {
PluginAlbumEndpoint(context)
}
fn album_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("album"), self.0)
.or_else(|e| Err(anyhow!("album not found: \n{}", e)))
}
pub async fn get_album(&mut self, id: String) -> anyhow::Result<SpotubeFullAlbumObject> {
let album_val = self.album_obj()?;
let album_object = album_val.as_object().ok_or(anyhow!("Not an object"))?;
let get_album_fn = album_object
.get(js_string!("getAlbum"), self.0)
.map_err(|e| anyhow!("JS error while accessing getAlbum: {}", e))?
.as_function()
.ok_or(anyhow!("getAlbum is not a function"))?;
let args = [JsValue::from(js_string!(id))];
let res_json =
utils::js_call_to_json(get_album_fn.call(&album_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn tracks(
&mut self,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let album_val = self.album_obj()?;
let album_object = album_val.as_object().ok_or(anyhow!("Not an object"))?;
let tracks_fn = album_object
.get(js_string!("tracks"), self.0)
.map_err(|e| anyhow!("JS error while accessing tracks: {}", e))?
.as_function()
.ok_or(anyhow!("tracks is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(id)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json = utils::js_call_to_json(tracks_fn.call(&album_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn releases(
&mut self,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let album_val = self.album_obj()?;
let album_object = album_val.as_object().ok_or(anyhow!("Not an object"))?;
let releases_fn = album_object
.get(js_string!("releases"), self.0)
.map_err(|e| anyhow!("JS error while accessing releases: {}", e))?
.as_function()
.ok_or(anyhow!("releases is not a function"))?;
let args: [JsValue; 2] = [
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json =
utils::js_call_to_json(releases_fn.call(&album_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn save(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
let album_val = self.album_obj()?;
let album_object = album_val.as_object().ok_or(anyhow!("Not an object"))?;
let save_fn = album_object
.get(js_string!("save"), self.0)
.map_err(|e| anyhow!("JS error while accessing save: {}", e))?
.as_function()
.ok_or(anyhow!("save is not a function"))?;
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
let args = [ids_val.into()];
utils::js_call_to_void(save_fn.call(&album_val, &args, self.0), self.0)?;
Ok(())
}
pub async fn unsave(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
let album_val = self.album_obj()?;
let album_object = album_val.as_object().ok_or(anyhow!("Not an object"))?;
let unsave_fn = album_object
.get(js_string!("unsave"), self.0)
.map_err(|e| anyhow!("JS error while accessing save: {}", e))?
.as_function()
.ok_or(anyhow!("save is not a function"))?;
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
let args = [ids_val.into()];
utils::js_call_to_void(unsave_fn.call(&album_val, &args, self.0), self.0)?;
Ok(())
}
}

View File

@ -0,0 +1,59 @@
use std::{cell::RefCell, rc::Rc};
use boa_engine::{Context, Finalize, JsData, JsResult, Trace};
use boa_runtime::fetch::{request::JsRequest, response::JsResponse, Fetcher};
#[derive(Default, Debug, Clone, Trace, Finalize, JsData)]
pub struct ReqwestFetcher {
#[unsafe_ignore_trace]
client: reqwest::Client,
}
impl ReqwestFetcher {
pub fn new() -> Self {
ReqwestFetcher {
client: reqwest::Client::new(),
}
}
}
impl Fetcher for ReqwestFetcher {
async fn fetch(
self: Rc<Self>,
request: JsRequest,
_context: &RefCell<&mut Context>,
) -> JsResult<JsResponse> {
use boa_engine::{JsError, JsString};
let request = request.into_inner();
let method = request.method().clone();
let url = request.uri().to_string();
let req = self
.client
.request(method, &url)
.headers(request.headers().clone());
let req = req
.body(request.body().clone())
.build()
.map_err(JsError::from_rust)?;
let resp = self.client.execute(req).await.map_err(JsError::from_rust)?;
let status = resp.status();
let headers = resp.headers().clone();
let bytes = resp.bytes().await.map_err(JsError::from_rust)?;
let mut builder = http::Response::builder().status(status.as_u16());
for k in headers.keys() {
for v in headers.get_all(k) {
builder = builder.header(k.as_str(), v);
}
}
builder
.body(bytes.to_vec())
.map_err(JsError::from_rust)
.map(|request| JsResponse::basic(JsString::from(url), request))
}
}

View File

@ -0,0 +1 @@
pub mod fetcher;

180
rust/src/internal/artist.rs Normal file
View File

@ -0,0 +1,180 @@
use crate::api::plugin::models::artist::SpotubeFullArtistObject;
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginArtistEndpoint<'a>(&'a mut Context);
impl<'a> PluginArtistEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginArtistEndpoint<'a> {
PluginArtistEndpoint(context)
}
fn artist_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
return plugin_instance
.get(js_string!("artist"), self.0)
.or_else(|e| Err(anyhow!("artist not found: \n{}", e)));
}
pub async fn get_artist(&mut self, id: String) -> anyhow::Result<SpotubeFullArtistObject> {
let artist_val = self.artist_obj()?;
let artist_object = artist_val.as_object().ok_or(anyhow!("Not an object"))?;
let get_artist_fn = artist_object
.get(js_string!("getArtist"), self.0)
.map_err(|e| anyhow!("JS error while accessing getArtist: {}", e))?
.as_function()
.ok_or(anyhow!("getArtist is not a function"))?;
let args = [JsValue::from(js_string!(id))];
let res_json =
utils::js_call_to_json(get_artist_fn.call(&artist_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn top_tracks(
&mut self,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let artist_val = self.artist_obj()?;
let artist_object = artist_val.as_object().ok_or(anyhow!("Not an object"))?;
let top_tracks_fn = artist_object
.get(js_string!("topTracks"), self.0)
.map_err(|e| anyhow!("JS error while accessing getArtist: {}", e))?
.as_function()
.ok_or(anyhow!("getArtist is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(id)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json =
utils::js_call_to_json(top_tracks_fn.call(&artist_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn albums(
&mut self,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let artist_val = self.artist_obj()?;
let artist_object = artist_val.as_object().ok_or(anyhow!("Not an object"))?;
let albums_fn = artist_object
.get(js_string!("albums"), self.0)
.map_err(|e| anyhow!("JS error while accessing albums: {}", e))?
.as_function()
.ok_or(anyhow!("albums is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(id)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json = utils::js_call_to_json(albums_fn.call(&artist_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn related(
&mut self,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let artist_val = self.artist_obj()?;
let artist_object = artist_val.as_object().ok_or(anyhow!("Not an object"))?;
let related_fn = artist_object
.get(js_string!("related"), self.0)
.map_err(|e| anyhow!("JS error while accessing related: {}", e))?
.as_function()
.ok_or(anyhow!("related is not a function"))?;
let args = [
JsValue::from(js_string!(id)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json = utils::js_call_to_json(related_fn.call(&artist_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn save(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
let artist_val = self.artist_obj()?;
let artist_object = artist_val.as_object().ok_or(anyhow!("Not an object"))?;
let save_fn = artist_object
.get(js_string!("save"), self.0)
.map_err(|e| anyhow!("JS error while accessing save: {}", e))?
.as_function()
.ok_or(anyhow!("save is not a function"))?;
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
let args = [ids_val.into()];
utils::js_call_to_void(save_fn.call(&artist_val, &args, self.0), self.0)?;
Ok(())
}
pub async fn unsave(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
let artist_val = self.artist_obj()?;
let artist_object = artist_val.as_object().ok_or(anyhow!("Not an object"))?;
let unsave_fn = artist_object
.get(js_string!("unsave"), self.0)
.map_err(|e| anyhow!("JS error while accessing save: {}", e))?
.as_function()
.ok_or(anyhow!("save is not a function"))?;
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
let args = [ids_val.into()];
utils::js_call_to_void(unsave_fn.call(&artist_val, &args, self.0), self.0)?;
Ok(())
}
}

View File

@ -0,0 +1,81 @@
use crate::api::plugin::models::audio_source::{
SpotubeAudioSourceMatchObject, SpotubeAudioSourceStreamObject,
};
use crate::api::plugin::models::track::SpotubeTrackObject;
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginAudioSourceEndpoint<'a>(&'a mut Context);
impl<'a> PluginAudioSourceEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginAudioSourceEndpoint<'a> {
PluginAudioSourceEndpoint(context)
}
fn audio_source_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("audioSource"), self.0)
.or_else(|e| Err(anyhow!("artist not found: \n{}", e)))
}
pub async fn matches(
&mut self,
track: SpotubeTrackObject,
) -> anyhow::Result<Vec<SpotubeAudioSourceMatchObject>> {
let audio_source_val = self.audio_source_obj()?;
let audio_source_object = audio_source_val
.as_object()
.ok_or(anyhow!("Not an object"))?;
let matches_fn = audio_source_object
.get(js_string!("matches"), self.0)
.map_err(|e| anyhow!("JS error while accessing matches: {}", e))?
.as_function()
.ok_or(anyhow!("matches is not a function"))?;
let value = serde_json::to_value(track)?;
let track_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
let args = [track_val];
let res =
utils::js_call_to_json(matches_fn.call(&audio_source_val, &args, self.0), self.0)?;
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
}
pub async fn streams(
&mut self,
matched: SpotubeAudioSourceMatchObject,
) -> anyhow::Result<Vec<SpotubeAudioSourceStreamObject>> {
let audio_source_val = self.audio_source_obj()?;
let audio_source_object = audio_source_val
.as_object()
.ok_or(anyhow!("Not an object"))?;
let matches_fn = audio_source_object
.get(js_string!("streams"), self.0)
.map_err(|e| anyhow!("JS error while accessing matches: {}", e))?
.as_function()
.ok_or(anyhow!("matches is not a function"))?;
let value = serde_json::to_value(matched)?;
let matched_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
let args = [matched_val];
let res =
utils::js_call_to_json(matches_fn.call(&audio_source_val, &args, self.0), self.0)?;
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
}
}

74
rust/src/internal/auth.rs Normal file
View File

@ -0,0 +1,74 @@
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginAuthEndpoint<'a>(&'a mut Context);
impl<'a> PluginAuthEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginAuthEndpoint<'a> {
PluginAuthEndpoint(context)
}
fn auth_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
return plugin_instance
.get(js_string!("auth"), self.0)
.or_else(|e| Err(anyhow!("auth not found:\n{}", e)));
}
pub async fn authenticate(&mut self) -> anyhow::Result<()> {
let auth_val = self.auth_obj()?;
let auth_object = auth_val.as_object().ok_or(anyhow!("Not an object"))?;
let authenticate_fn = auth_object
.get(js_string!("authenticate"), self.0)
.map_err(|e| anyhow!("JS error while accessing authenticate: {}", e))?
.as_function()
.ok_or(anyhow!("authenticate is not a function"))?;
let args = [];
utils::js_call_to_void(authenticate_fn.call(&auth_val, &args, self.0), self.0)
}
pub fn is_authenticated(&mut self) -> anyhow::Result<bool> {
let auth_val = self.auth_obj()?;
let auth_object = auth_val.as_object().ok_or(anyhow!("Not an object"))?;
let authenticate_fn = auth_object
.get(js_string!("is_authenticated"), self.0)
.map_err(|e| anyhow!("JS error while accessing authenticate: {}", e))?
.as_function()
.ok_or(anyhow!("authenticate is not a function"))?;
authenticate_fn
.call(&auth_val, &[], self.0)
.map_err(|e| anyhow!("{}", e))?
.as_boolean()
.ok_or(anyhow!("Not a boolean"))
}
pub async fn logout(&mut self) -> anyhow::Result<()> {
let auth_val = self.auth_obj()?;
let auth_object = auth_val.as_object().ok_or(anyhow!("Not an object"))?;
let logout_fn = auth_object
.get(js_string!("logout"), self.0)
.map_err(|e| anyhow!("JS error while accessing authenticate: {}", e))?
.as_function()
.ok_or(anyhow!("authenticate is not a function"))?;
let args = [];
utils::js_call_to_void(logout_fn.call(&auth_val, &args, self.0), self.0)
}
}

View File

@ -0,0 +1,91 @@
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
#[derive(Debug)]
pub struct PluginBrowseEndpoint<'a>(&'a mut Context);
impl<'a> PluginBrowseEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginBrowseEndpoint<'a> {
PluginBrowseEndpoint(context)
}
fn browse_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("browse"), self.0)
.or_else(|e| Err(anyhow!("browse not found:\n{}", e)))
}
pub async fn sections(
&mut self,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let browse_val = self.browse_obj()?;
let browse_object = browse_val.as_object().ok_or(anyhow!("Not an object"))?;
let sections_fn = browse_object
.get(js_string!("sections"), self.0)
.map_err(|e| anyhow!("JS error while accessing sections: {}", e))?
.as_function()
.ok_or(anyhow!("sections is not a function"))?;
let args = [
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res = utils::js_call_to_json(sections_fn.call(&browse_val, &args, self.0), self.0)?;
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
}
pub async fn section_items(
&mut self,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let browse_val = self.browse_obj()?;
let browse_object = browse_val.as_object().ok_or(anyhow!("Not an object"))?;
let section_items_fn = browse_object
.get(js_string!("sectionItems"), self.0)
.map_err(|e| anyhow!("JS error while accessing sectionItems: {}", e))?
.as_function()
.ok_or(anyhow!("sectionItems is not a function"))?;
let args = [
JsValue::from(js_string!(id)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res =
utils::js_call_to_json(section_items_fn.call(&browse_val, &args, self.0), self.0)?;
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
}
}

86
rust/src/internal/core.rs Normal file
View File

@ -0,0 +1,86 @@
use crate::api::plugin::models::core::{PluginConfiguration, PluginUpdateAvailable, ScrobbleDetails};
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginCoreEndpoint<'a>(&'a mut Context);
impl<'a> PluginCoreEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginCoreEndpoint<'a> {
PluginCoreEndpoint(context)
}
fn core_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("core"), self.0)
.or_else(|e| Err(anyhow!("core not found:\n{}", e)))
}
pub async fn check_update(
&mut self,
plugin_config: PluginConfiguration,
) -> anyhow::Result<Option<PluginUpdateAvailable>> {
let core_val = self.core_obj()?;
let core_object = core_val.as_object().ok_or(anyhow!("Not an object"))?;
let check_update_fn = core_object
.get(js_string!("checkUpdate"), self.0)
.map_err(|e| anyhow!("JS error while accessing checkUpdate: {}", e))?
.as_function()
.ok_or(anyhow!("checkUpdate is not a function"))?;
let value = serde_json::to_value(plugin_config)?;
let config_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
let args = [config_val];
let res = utils::js_call_to_json(check_update_fn.call(&core_val, &args, self.0), self.0)?;
if res.is_null() {
Ok(None)
} else {
serde_json::from_value(res).map_err(|e| anyhow!("{}", e))
}
}
pub fn support(&mut self) -> anyhow::Result<String> {
let core_val = self.core_obj()?;
let core_object = core_val.as_object().ok_or(anyhow!("Not an object"))?;
let support_val = core_object
.get(js_string!("support"), self.0)
.map_err(|e| anyhow!("JS error while accessing support: {}", e))?;
support_val
.as_string()
.ok_or(anyhow!("support is not a string"))?
.to_std_string()
.map_err(|e| anyhow!("{}", e))
}
pub async fn scrobble(&mut self, details: ScrobbleDetails) -> anyhow::Result<()> {
let core_val = self.core_obj()?;
let core_object = core_val.as_object().ok_or(anyhow!("Not an object"))?;
let scrobble_fn = core_object
.get(js_string!("scrobble"), self.0)
.map_err(|e| anyhow!("JS error while accessing scrobble: {}", e))?
.as_function()
.ok_or(anyhow!("scrobble is not a function"))?;
let value = serde_json::to_value(details)?;
let details_val = utils::json_value_to_js(&value, self.0).map_err(|e| anyhow!("{}", e))?;
let args = [details_val];
utils::js_call_to_void(scrobble_fn.call(&core_val, &args, self.0), self.0)
}
}

13
rust/src/internal/mod.rs Normal file
View File

@ -0,0 +1,13 @@
pub mod album;
pub mod apis;
pub mod artist;
pub mod audio_source;
pub mod browse;
pub mod core;
pub mod playlist;
pub mod search;
pub mod track;
pub mod user;
pub mod auth;
mod utils;
// Export Context

View File

@ -0,0 +1,268 @@
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
use crate::api::plugin::models::playlist::SpotubeFullPlaylistObject;
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginPlaylistEndpoint<'a>(&'a mut Context);
impl<'a> PluginPlaylistEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginPlaylistEndpoint<'a> {
PluginPlaylistEndpoint(context)
}
fn playlist_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("playlist"), self.0)
.or_else(|e| Err(anyhow!("playlist not found: \n{}", e)))
}
pub async fn get_playlist(&mut self, id: String) -> anyhow::Result<SpotubeFullPlaylistObject> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let get_playlist_fn = playlist_object
.get(js_string!("getPlaylist"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("getPlaylist is not a function"))?;
let args = [JsValue::from(js_string!(id))];
let res_json =
utils::js_call_to_json(get_playlist_fn.call(&playlist_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn tracks(
&mut self,
id: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let tracks_fn = playlist_object
.get(js_string!("tracks"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("tracks is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(id)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json =
utils::js_call_to_json(tracks_fn.call(&playlist_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn create(
&mut self,
user_id: String,
name: String,
description: Option<String>,
public: Option<bool>,
collaborative: Option<bool>,
) -> anyhow::Result<Option<SpotubeFullPlaylistObject>> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let create_fn = playlist_object
.get(js_string!("create"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("create is not a function"))?;
let args = [
JsValue::from(js_string!(user_id)),
JsValue::from(js_string!(name)),
match description {
Some(o) => JsValue::from(js_string!(o)),
None => JsValue::undefined(),
},
match public {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match collaborative {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json =
utils::js_call_to_json(create_fn.call(&playlist_val, &args, self.0), self.0)?;
if res_json.is_null() {
Ok(None)
} else {
serde_json::from_value(res_json)
.map(Some)
.map_err(|e| anyhow!("{}", e))
}
}
pub async fn update(
&mut self,
playlist_id: String,
name: Option<String>,
description: Option<String>,
public: Option<bool>,
collaborative: Option<bool>,
) -> anyhow::Result<()> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let update_fn = playlist_object
.get(js_string!("update"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("update is not a function"))?;
let args = [
JsValue::from(js_string!(playlist_id)),
match name {
Some(o) => JsValue::from(js_string!(o)),
None => JsValue::undefined(),
},
match description {
Some(o) => JsValue::from(js_string!(o)),
None => JsValue::undefined(),
},
match public {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match collaborative {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
utils::js_call_to_void(update_fn.call(&playlist_val, &args, self.0), self.0)
}
pub async fn add_tracks(
&mut self,
playlist_id: String,
track_ids: Vec<String>,
position: Option<u32>,
) -> anyhow::Result<()> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let add_tracks_fn = playlist_object
.get(js_string!("addTracks"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("addTracks is not a function"))?;
let args = [
JsValue::from(js_string!(playlist_id)),
utils::vec_string_to_js_array(track_ids, self.0)?,
match position {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
utils::js_call_to_void(add_tracks_fn.call(&playlist_val, &args, self.0), self.0)
}
pub async fn remove_tracks(
&mut self,
playlist_id: String,
track_ids: Vec<String>,
) -> anyhow::Result<()> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let remove_tracks_fn = playlist_object
.get(js_string!("removeTracks"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("removeTracks is not a function"))?;
let args = [
JsValue::from(js_string!(playlist_id)),
utils::vec_string_to_js_array(track_ids, self.0)?,
];
utils::js_call_to_void(remove_tracks_fn.call(&playlist_val, &args, self.0), self.0)
}
pub async fn save(&mut self, playlist_id: String) -> anyhow::Result<()> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let save_fn = playlist_object
.get(js_string!("save"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("save is not a function"))?;
let args = [JsValue::from(js_string!(playlist_id))];
utils::js_call_to_void(save_fn.call(&playlist_val, &args, self.0), self.0)
}
pub async fn unsave(&mut self, playlist_id: String) -> anyhow::Result<()> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let unsave_fn = playlist_object
.get(js_string!("unsave"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("unsave is not a function"))?;
let args = [JsValue::from(js_string!(playlist_id))];
utils::js_call_to_void(unsave_fn.call(&playlist_val, &args, self.0), self.0)
}
pub async fn delete_playlist(&mut self, playlist_id: String) -> anyhow::Result<()> {
let playlist_val = self.playlist_obj()?;
let playlist_object = playlist_val.as_object().ok_or(anyhow!("Not an object"))?;
let delete_playlist_fn = playlist_object
.get(js_string!("deletePlaylist"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("deletePlaylist is not a function"))?;
let args = [JsValue::from(js_string!(playlist_id))];
utils::js_call_to_void(
delete_playlist_fn.call(&playlist_val, &args, self.0),
self.0,
)
}
}

201
rust/src/internal/search.rs Normal file
View File

@ -0,0 +1,201 @@
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
use crate::api::plugin::models::search::SpotubeSearchResponseObject;
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::object::builtins::JsArray;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginSearchEndpoint<'a>(&'a mut Context);
impl<'a> PluginSearchEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginSearchEndpoint<'a> {
PluginSearchEndpoint(context)
}
fn search_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("search"), self.0)
.or_else(|e| Err(anyhow!("search not found: \n{}", e)))
}
pub fn chips(&mut self) -> anyhow::Result<Vec<String>> {
let search_val = self.search_obj()?;
let search_object = search_val.as_object().ok_or(anyhow!("Not an object"))?;
let chips_val = search_object
.get(js_string!("chips"), self.0)
.map_err(|e| anyhow!("{}", e))?;
let chips_obj = chips_val.as_object().ok_or(anyhow!("Not an object"))?;
if !chips_obj.is_array() {
return Err(anyhow!("chips is not an array"));
}
let chips_array = JsArray::from_object(chips_obj.clone()).map_err(|e| anyhow!("{}", e))?;
let length = chips_array.length(self.0).map_err(|e| anyhow!("{}", e))?;
let mut result = Vec::new();
for i in 0..length {
let item = chips_array.get(i, self.0).map_err(|e| anyhow!("{}", e))?;
if let Some(s) = item.as_string() {
result.push(s.to_std_string().map_err(|e| anyhow!("{}", e))?);
}
}
Ok(result)
}
pub async fn all(&mut self, query: String) -> anyhow::Result<SpotubeSearchResponseObject> {
let search_val = self.search_obj()?;
let search_object = search_val.as_object().ok_or(anyhow!("Not an object"))?;
let all_fn = search_object
.get(js_string!("all"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("all is not a function"))?;
let args = [JsValue::from(js_string!(query))];
let res_json = utils::js_call_to_json(all_fn.call(&search_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn albums(
&mut self,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let search_val = self.search_obj()?;
let search_object = search_val.as_object().ok_or(anyhow!("Not an object"))?;
let albums_fn = search_object
.get(js_string!("albums"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("albums is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(query)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json = utils::js_call_to_json(albums_fn.call(&search_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn artists(
&mut self,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let search_val = self.search_obj()?;
let search_object = search_val.as_object().ok_or(anyhow!("Not an object"))?;
let artists_fn = search_object
.get(js_string!("artists"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("artists is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(query)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json = utils::js_call_to_json(artists_fn.call(&search_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn playlists(
&mut self,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let search_val = self.search_obj()?;
let search_object = search_val.as_object().ok_or(anyhow!("Not an object"))?;
let playlists_fn = search_object
.get(js_string!("playlists"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("playlists is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(query)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json =
utils::js_call_to_json(playlists_fn.call(&search_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn tracks(
&mut self,
query: String,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let search_val = self.search_obj()?;
let search_object = search_val.as_object().ok_or(anyhow!("Not an object"))?;
let tracks_fn = search_object
.get(js_string!("tracks"), self.0)
.map_err(|e| anyhow!("{}", e))?
.as_function()
.ok_or(anyhow!("tracks is not a function"))?;
let args: [JsValue; 3] = [
JsValue::from(js_string!(query)),
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json = utils::js_call_to_json(tracks_fn.call(&search_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
}

100
rust/src/internal/track.rs Normal file
View File

@ -0,0 +1,100 @@
use crate::api::plugin::models::track::SpotubeTrackObject;
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
#[derive(Debug)]
pub struct PluginTrackEndpoint<'a>(&'a mut Context);
impl<'a> PluginTrackEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginTrackEndpoint<'a> {
PluginTrackEndpoint(context)
}
fn track_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("track"), self.0)
.or_else(|e| Err(anyhow!("track not found: \n{}", e)))
}
pub async fn get_track(&mut self, id: String) -> anyhow::Result<SpotubeTrackObject> {
let track_val = self.track_obj()?;
let track_object = track_val.as_object().ok_or(anyhow!("Not an object"))?;
let get_track_fn = track_object
.get(js_string!("getTrack"), self.0)
.map_err(|e| anyhow!("JS error while accessing getTrack: {}", e))?
.as_function()
.ok_or(anyhow!("getTrack is not a function"))?;
let args = [JsValue::from(js_string!(id))];
let res_json =
utils::js_call_to_json(get_track_fn.call(&track_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn save(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
let track_val = self.track_obj()?;
let track_object = track_val.as_object().ok_or(anyhow!("Not an object"))?;
let save_fn = track_object
.get(js_string!("save"), self.0)
.map_err(|e| anyhow!("JS error while accessing save: {}", e))?
.as_function()
.ok_or(anyhow!("save is not a function"))?;
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
let args = [ids_val.into()];
utils::js_call_to_void(save_fn.call(&track_val, &args, self.0), self.0)?;
Ok(())
}
pub async fn unsave(&mut self, ids: Vec<String>) -> anyhow::Result<()> {
let track_val = self.track_obj()?;
let track_object = track_val.as_object().ok_or(anyhow!("Not an object"))?;
let unsave_fn = track_object
.get(js_string!("unsave"), self.0)
.map_err(|e| anyhow!("JS error while accessing save: {}", e))?
.as_function()
.ok_or(anyhow!("save is not a function"))?;
let ids_val = utils::vec_string_to_js_array(ids, self.0)?;
let args = [ids_val.into()];
utils::js_call_to_void(unsave_fn.call(&track_val, &args, self.0), self.0)?;
Ok(())
}
pub async fn radio(&mut self, id: String) -> anyhow::Result<Vec<SpotubeTrackObject>> {
let track_val = self.track_obj()?;
let track_object = track_val.as_object().ok_or(anyhow!("Not an object"))?;
let get_track_fn = track_object
.get(js_string!("radio"), self.0)
.map_err(|e| anyhow!("JS error while accessing radio: {}", e))?
.as_function()
.ok_or(anyhow!("radio is not a function"))?;
let args = [JsValue::from(js_string!(id))];
let res_json =
utils::js_call_to_json(get_track_fn.call(&track_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
}

107
rust/src/internal/user.rs Normal file
View File

@ -0,0 +1,107 @@
use crate::api::plugin::models::pagination::SpotubePaginationResponseObject;
use crate::internal::utils;
use anyhow::anyhow;
use boa_engine::{js_string, Context, JsValue};
use flutter_rust_bridge::frb;
use crate::api::plugin::models::user::SpotubeUserObject;
#[derive(Debug)]
pub struct PluginUserEndpoint<'a>(&'a mut Context);
impl<'a> PluginUserEndpoint<'a> {
#[frb(ignore)]
pub fn new(context: &'a mut Context) -> PluginUserEndpoint<'a> {
PluginUserEndpoint(context)
}
fn user_obj(&mut self) -> anyhow::Result<JsValue> {
let global = self.0.global_object();
let plugin_instance = global
.get(js_string!("pluginInstance"), self.0)
.map_err(|e| anyhow!("{}", e))
.and_then(|a| a.as_object().ok_or(anyhow!("Not an object")))?;
plugin_instance
.get(js_string!("user"), self.0)
.or_else(|e| Err(anyhow!("user not found: \n{}", e)))
}
pub async fn me(&mut self) -> anyhow::Result<SpotubeUserObject> {
let user_val = self.user_obj()?;
let user_object = user_val.as_object().ok_or(anyhow!("Not an object"))?;
let me_fn = user_object
.get(js_string!("me"), self.0)
.map_err(|e| anyhow!("JS error while accessing me: {}", e))?
.as_function()
.ok_or(anyhow!("me is not a function"))?;
let res_json = utils::js_call_to_json(me_fn.call(&user_val, &[], self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
async fn get_saved(
&mut self,
method: &str,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
let user_val = self.user_obj()?;
let user_object = user_val.as_object().ok_or(anyhow!("Not an object"))?;
let saved_fn = user_object
.get(js_string!(method), self.0)
.map_err(|e| anyhow!("JS error while accessing {}: {}", method, e))?
.as_function()
.ok_or(anyhow!("{} is not a function", method))?;
let args: [JsValue; 2] = [
match offset {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
match limit {
Some(o) => JsValue::from(o),
None => JsValue::undefined(),
},
];
let res_json = utils::js_call_to_json(saved_fn.call(&user_val, &args, self.0), self.0)?;
serde_json::from_value(res_json).map_err(|e| anyhow!("{}", e))
}
pub async fn saved_playlists(
&mut self,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
self.get_saved("savedPlaylists", offset, limit).await
}
pub async fn saved_tracks(
&mut self,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
self.get_saved("savedTracks", offset, limit).await
}
pub async fn saved_albums(
&mut self,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
self.get_saved("savedAlbums", offset, limit).await
}
pub async fn saved_artists(
&mut self,
offset: Option<u32>,
limit: Option<u32>,
) -> anyhow::Result<SpotubePaginationResponseObject> {
self.get_saved("savedArtists", offset, limit).await
}
}

166
rust/src/internal/utils.rs Normal file
View File

@ -0,0 +1,166 @@
use anyhow::anyhow;
use boa_engine::property::PropertyKey;
use boa_engine::{
object::builtins::JsArray, Context, JsObject, JsResult, JsString, JsValue,
};
use serde_json::{Map, Value};
pub fn vec_string_to_js_array(
rust_vec: Vec<String>,
context: &mut Context,
) -> anyhow::Result<JsValue> {
let builder = JsArray::new(context);
for (index, rust_string) in rust_vec.into_iter().enumerate() {
let js_string_value = JsString::from(rust_string);
builder
.set(index as u32, js_string_value, true, context)
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
}
Ok(builder.into())
}
#[allow(dead_code)]
pub fn js_call_to_string(
result: JsResult<JsValue>,
context: &mut Context,
) -> anyhow::Result<String> {
let res = result
.map_err(|e| anyhow!("{}", e))
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
.await_blocking(context)
.map_err(|e| anyhow!("{}", e))?
.as_string()
.ok_or(anyhow!("No response string returned"))?
.to_std_string()
.map_err(|e| anyhow!("{}", e))?;
Ok(res)
}
pub fn js_call_to_json(result: JsResult<JsValue>, context: &mut Context) -> anyhow::Result<Value> {
let res = result
.map_err(|e| anyhow!("{}", e))
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
.await_blocking(context)
.map_err(|e| anyhow!("{}", e))?;
let ls = js_value_to_json(&res, context)?;
Ok(ls)
}
pub fn js_call_to_void(result: JsResult<JsValue>, context: &mut Context) -> anyhow::Result<()> {
result
.map_err(|e| anyhow!("{}", e))
.and_then(|f| f.as_promise().ok_or(anyhow!("Not a promise")))?
.await_blocking(context)
.map_err(|e| anyhow!("{}", e))?;
Ok(())
}
/// Convert a `serde_json::Value` into a Boa `JsValue`
pub fn json_value_to_js(value: &Value, ctx: &mut Context) -> JsResult<JsValue> {
match value {
Value::Null => Ok(JsValue::null()),
Value::Bool(b) => Ok(JsValue::from(*b)),
Value::Number(n) => {
if let Some(i) = n.as_i64() {
Ok(JsValue::new(i))
} else if let Some(f) = n.as_f64() {
Ok(JsValue::new(f))
} else {
Ok(JsValue::null()) // fallback (rare)
}
}
Value::String(s) => Ok(JsValue::from(JsString::from(s.as_str()))),
Value::Array(arr) => {
let js_arr = JsArray::new(ctx);
for (idx, item) in arr.iter().enumerate() {
let js_val = json_value_to_js(item, ctx)?;
js_arr.set(idx, js_val, false, ctx)?;
}
Ok(JsValue::from(js_arr))
}
Value::Object(obj) => {
let js_obj = JsObject::with_null_proto();
for (key, val) in obj {
let js_val = json_value_to_js(val, ctx)?;
js_obj.set(JsString::from(key.as_str()), js_val, true, ctx)?;
}
Ok(JsValue::from(js_obj))
}
}
}
/// Convert a Boa `JsValue` into a `serde_json::Value`
pub fn js_value_to_json(value: &JsValue, ctx: &mut Context) -> anyhow::Result<Value> {
if value.is_null() || value.is_undefined() {
return Ok(Value::Null);
}
if let Some(b) = value.as_boolean() {
return Ok(Value::Bool(b));
}
if let Some(n) = value.as_number() {
let num = serde_json::Number::from_f64(n).ok_or(anyhow!("Invalid number"))?;
return Ok(Value::Number(num));
}
if let Some(s) = value.as_string() {
let s = s.to_std_string().map_err(|e| anyhow!("{}", e))?;
return Ok(Value::String(s));
}
if value.is_bigint() {
// BigInts are NOT JSON-compatible → store as string
return Ok(Value::String(value.display().to_string()));
}
if value.is_object() {
let obj = value.as_object().ok_or(anyhow!("Not an object"))?;
// Array?
if obj.is_array() {
let obj = JsArray::from_object(obj).map_err(|e| anyhow!("{}", e))?;
let length = obj.length(ctx).map_err(|e| anyhow!("{}", e))?;
let mut json_arr = Vec::<Value>::with_capacity(length as usize);
for i in 0..length {
let item = obj.get(i, ctx).unwrap_or(JsValue::null());
let item_json = js_value_to_json(&item, ctx)?;
json_arr.push(item_json);
}
return Ok(Value::Array(json_arr));
}
// Regular Object
let mut map = Map::<String, Value>::new();
for key in obj.own_property_keys(ctx).map_err(|e| anyhow!("{}", e))? {
let key_val: Option<String> = match key.clone() {
PropertyKey::String(s) => Some(s.to_std_string().map_err(|e| anyhow!("{}", e))?),
PropertyKey::Index(i) => Some(serde_json::Number::from(i.get()).to_string()),
_ => None,
};
let v_js = obj.get(key, ctx).unwrap_or(JsValue::null());
let v_json = js_value_to_json(&v_js, ctx)?;
if let Some(key_val) = key_val {
map.insert(key_val, v_json);
}
}
return Ok(Value::Object(map));
}
// Fallback for unsupported JS types: functions, symbols, etc.
Ok(Value::Null)
}

View File

@ -1,2 +1,3 @@
pub mod api;
mod frb_generated;
mod internal;

48
rust/src/main.rs Normal file
View File

@ -0,0 +1,48 @@
pub mod api;
pub mod internal;
pub mod frb_generated;
use api::plugin::models::core::{PluginAbility, PluginConfiguration};
use api::plugin::plugin::SpotubePlugin;
const PLUGIN_JS: &str = "\
class Core {
async checkUpdate() {
console.log('Core checkUpdate');
}
support() {
return 'Metadata';
}
}
class TestingPlugin {
constructor() {
this.core = new Core();
}
}
";
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let sp_plugin = SpotubePlugin::new();
let sender = SpotubePlugin::new_context(
PLUGIN_JS.to_string(),
PluginConfiguration {
entry_point: "TestingPlugin".to_string(),
abilities: vec![PluginAbility::Metadata],
apis: vec![],
author: "KRTirtho".to_string(),
description: "Testing Plugin".to_string(),
name: "Testing Plugin".to_string(),
plugin_api_version: "2.0.0".to_string(),
repository: None,
version: "0.1.0".to_string(),
}
)?;
let result = sp_plugin.core.support(sender.clone()).await?;
println!("Result: {:?}", result);
sp_plugin.dispose(sender.clone()).await?;
Ok(())
}