Compare commits

...

7 Commits

Author SHA1 Message Date
Gustavo Moreno
fb362b9cfc
Merge 3ff0f6dd27 into 308c417ed3 2025-08-27 23:54:07 -04:00
Kingkor Roy Tirtho
308c417ed3 cd: use macos 14 instead 15 2025-08-25 19:27:18 +06:00
Kingkor Roy Tirtho
0823629fb5 chore: upgrade dependencies 2025-08-23 10:07:41 +06:00
Kingkor Roy Tirtho
7037145519 feat: add ErrorBox and NoDefaultMetadataPlugin components
- Implemented ErrorBox for displaying error messages with retry functionality and log viewing.
- Created NoDefaultMetadataPlugin to inform users about missing default metadata providers and provide navigation to manage them.
2025-08-19 22:34:37 +06:00
Kingkor Roy Tirtho
08d1c98674 refactor: move scrobbler to their own place 2025-08-19 20:22:48 +06:00
Kingkor Roy Tirtho
c0dcc87eb2 chore: upgrade hetu_spotube_plugin 2025-08-19 19:07:37 +06:00
Gustavo Moreno
3ff0f6dd27 bug #2354: Fix icon overflow in Local Folder Cache page
- Wrapped  inside  to prevent layout breaking

Fixes #2354
2025-02-27 13:31:44 +01:00
40 changed files with 942 additions and 413 deletions

View File

@ -56,7 +56,7 @@ jobs:
files: | files: |
dist/Spotube-windows-x86_64.nupkg dist/Spotube-windows-x86_64.nupkg
dist/Spotube-windows-x86_64-setup.exe dist/Spotube-windows-x86_64-setup.exe
- os: macos-latest - os: macos-14
platform: ios platform: ios
arch: all arch: all
files: | files: |

View File

@ -0,0 +1,96 @@
-------------------------------
UBUNTU FONT LICENCE Version 1.0
-------------------------------
PREAMBLE
This licence allows the licensed fonts to be used, studied, modified and
redistributed freely. The fonts, including any derivative works, can be
bundled, embedded, and redistributed provided the terms of this licence
are met. The fonts and derivatives, however, cannot be released under
any other licence. The requirement for fonts to remain under this
licence does not require any document created using the fonts or their
derivatives to be published under this licence, as long as the primary
purpose of the document is not to be a vehicle for the distribution of
the fonts.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this licence and clearly marked as such. This may
include source files, build scripts and documentation.
"Original Version" refers to the collection of Font Software components
as received under this licence.
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to
a new environment.
"Copyright Holder(s)" refers to all individuals and companies who have a
copyright ownership of the Font Software.
"Substantially Changed" refers to Modified Versions which can be easily
identified as dissimilar to the Font Software by users of the Font
Software comparing the Original Version with the Modified Version.
To "Propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification and with or without charging
a redistribution fee), making available to the public, and in some
countries other activities as well.
PERMISSION & CONDITIONS
This licence does not grant any rights under trademark law and all such
rights are reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of the Font Software, to propagate the Font Software, subject to
the below conditions:
1) Each copy of the Font Software must contain the above copyright
notice and this licence. These can be included either as stand-alone
text files, human-readable headers or in the appropriate machine-
readable metadata fields within text or binary files as long as those
fields can be easily viewed by the user.
2) The font name complies with the following:
(a) The Original Version must retain its name, unmodified.
(b) Modified Versions which are Substantially Changed must be renamed to
avoid use of the name of the Original Version or similar names entirely.
(c) Modified Versions which are not Substantially Changed must be
renamed to both (i) retain the name of the Original Version and (ii) add
additional naming elements to distinguish the Modified Version from the
Original Version. The name of such Modified Versions must be the name of
the Original Version, with "derivative X" where X represents the name of
the new work, appended to that name.
3) The name(s) of the Copyright Holder(s) and any contributor to the
Font Software shall not be used to promote, endorse or advertise any
Modified Version, except (i) as required by this licence, (ii) to
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
their explicit written permission.
4) The Font Software, modified or unmodified, in part or in whole, must
be distributed entirely under this licence, and must not be distributed
under any other licence. The requirement for fonts to remain under this
licence does not affect any document created using the Font Software,
except any version of the Font Software extracted from a document
created using the Font Software may only be distributed under this
licence.
TERMINATION
This licence becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -108,6 +108,10 @@ class AppRouter extends RootStackRouter {
path: "settings/about", path: "settings/about",
page: AboutSpotubeRoute.page, page: AboutSpotubeRoute.page,
), ),
AutoRoute(
path: "settings/scrobbling",
page: SettingsScrobblingRoute.page,
),
AutoRoute( AutoRoute(
path: "album/:id", path: "album/:id",
page: AlbumRoute.page, page: AlbumRoute.page,

View File

@ -8,10 +8,10 @@
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:auto_route/auto_route.dart' as _i40; import 'package:auto_route/auto_route.dart' as _i41;
import 'package:flutter/material.dart' as _i41; import 'package:flutter/material.dart' as _i42;
import 'package:shadcn_flutter/shadcn_flutter.dart' as _i43; import 'package:shadcn_flutter/shadcn_flutter.dart' as _i44;
import 'package:spotube/models/metadata/metadata.dart' as _i42; import 'package:spotube/models/metadata/metadata.dart' as _i43;
import 'package:spotube/pages/album/album.dart' as _i2; import 'package:spotube/pages/album/album.dart' as _i2;
import 'package:spotube/pages/artist/artist.dart' as _i3; import 'package:spotube/pages/artist/artist.dart' as _i3;
import 'package:spotube/pages/connect/connect.dart' as _i6; import 'package:spotube/pages/connect/connect.dart' as _i6;
@ -21,14 +21,14 @@ import 'package:spotube/pages/home/home.dart' as _i9;
import 'package:spotube/pages/home/sections/section_items.dart' as _i8; import 'package:spotube/pages/home/sections/section_items.dart' as _i8;
import 'package:spotube/pages/lastfm_login/lastfm_login.dart' as _i10; import 'package:spotube/pages/lastfm_login/lastfm_login.dart' as _i10;
import 'package:spotube/pages/library/library.dart' as _i11; import 'package:spotube/pages/library/library.dart' as _i11;
import 'package:spotube/pages/library/user_albums.dart' as _i35; import 'package:spotube/pages/library/user_albums.dart' as _i36;
import 'package:spotube/pages/library/user_artists.dart' as _i36; import 'package:spotube/pages/library/user_artists.dart' as _i37;
import 'package:spotube/pages/library/user_downloads.dart' as _i37; import 'package:spotube/pages/library/user_downloads.dart' as _i38;
import 'package:spotube/pages/library/user_local_tracks/local_folder.dart' import 'package:spotube/pages/library/user_local_tracks/local_folder.dart'
as _i13; as _i13;
import 'package:spotube/pages/library/user_local_tracks/user_local_tracks.dart' import 'package:spotube/pages/library/user_local_tracks/user_local_tracks.dart'
as _i38; as _i39;
import 'package:spotube/pages/library/user_playlists.dart' as _i39; import 'package:spotube/pages/library/user_playlists.dart' as _i40;
import 'package:spotube/pages/lyrics/lyrics.dart' as _i15; import 'package:spotube/pages/lyrics/lyrics.dart' as _i15;
import 'package:spotube/pages/lyrics/mini_lyrics.dart' as _i16; import 'package:spotube/pages/lyrics/mini_lyrics.dart' as _i16;
import 'package:spotube/pages/player/lyrics.dart' as _i17; import 'package:spotube/pages/player/lyrics.dart' as _i17;
@ -44,20 +44,21 @@ import 'package:spotube/pages/settings/blacklist.dart' as _i4;
import 'package:spotube/pages/settings/logs.dart' as _i14; import 'package:spotube/pages/settings/logs.dart' as _i14;
import 'package:spotube/pages/settings/metadata/metadata_form.dart' as _i24; import 'package:spotube/pages/settings/metadata/metadata_form.dart' as _i24;
import 'package:spotube/pages/settings/metadata_plugins.dart' as _i25; import 'package:spotube/pages/settings/metadata_plugins.dart' as _i25;
import 'package:spotube/pages/settings/scrobbling/scrobbling.dart' as _i27;
import 'package:spotube/pages/settings/settings.dart' as _i26; import 'package:spotube/pages/settings/settings.dart' as _i26;
import 'package:spotube/pages/stats/albums/albums.dart' as _i27; import 'package:spotube/pages/stats/albums/albums.dart' as _i28;
import 'package:spotube/pages/stats/artists/artists.dart' as _i28; import 'package:spotube/pages/stats/artists/artists.dart' as _i29;
import 'package:spotube/pages/stats/fees/fees.dart' as _i32; import 'package:spotube/pages/stats/fees/fees.dart' as _i33;
import 'package:spotube/pages/stats/minutes/minutes.dart' as _i29; import 'package:spotube/pages/stats/minutes/minutes.dart' as _i30;
import 'package:spotube/pages/stats/playlists/playlists.dart' as _i31; import 'package:spotube/pages/stats/playlists/playlists.dart' as _i32;
import 'package:spotube/pages/stats/stats.dart' as _i30; import 'package:spotube/pages/stats/stats.dart' as _i31;
import 'package:spotube/pages/stats/streams/streams.dart' as _i33; import 'package:spotube/pages/stats/streams/streams.dart' as _i34;
import 'package:spotube/pages/track/track.dart' as _i34; import 'package:spotube/pages/track/track.dart' as _i35;
/// generated route for /// generated route for
/// [_i1.AboutSpotubePage] /// [_i1.AboutSpotubePage]
class AboutSpotubeRoute extends _i40.PageRouteInfo<void> { class AboutSpotubeRoute extends _i41.PageRouteInfo<void> {
const AboutSpotubeRoute({List<_i40.PageRouteInfo>? children}) const AboutSpotubeRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
AboutSpotubeRoute.name, AboutSpotubeRoute.name,
initialChildren: children, initialChildren: children,
@ -65,7 +66,7 @@ class AboutSpotubeRoute extends _i40.PageRouteInfo<void> {
static const String name = 'AboutSpotubeRoute'; static const String name = 'AboutSpotubeRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i1.AboutSpotubePage(); return const _i1.AboutSpotubePage();
@ -75,12 +76,12 @@ class AboutSpotubeRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i2.AlbumPage] /// [_i2.AlbumPage]
class AlbumRoute extends _i40.PageRouteInfo<AlbumRouteArgs> { class AlbumRoute extends _i41.PageRouteInfo<AlbumRouteArgs> {
AlbumRoute({ AlbumRoute({
_i41.Key? key, _i42.Key? key,
required String id, required String id,
required _i42.SpotubeSimpleAlbumObject album, required _i43.SpotubeSimpleAlbumObject album,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
AlbumRoute.name, AlbumRoute.name,
args: AlbumRouteArgs( args: AlbumRouteArgs(
@ -94,7 +95,7 @@ class AlbumRoute extends _i40.PageRouteInfo<AlbumRouteArgs> {
static const String name = 'AlbumRoute'; static const String name = 'AlbumRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final args = data.argsAs<AlbumRouteArgs>(); final args = data.argsAs<AlbumRouteArgs>();
@ -114,11 +115,11 @@ class AlbumRouteArgs {
required this.album, required this.album,
}); });
final _i41.Key? key; final _i42.Key? key;
final String id; final String id;
final _i42.SpotubeSimpleAlbumObject album; final _i43.SpotubeSimpleAlbumObject album;
@override @override
String toString() { String toString() {
@ -128,11 +129,11 @@ class AlbumRouteArgs {
/// generated route for /// generated route for
/// [_i3.ArtistPage] /// [_i3.ArtistPage]
class ArtistRoute extends _i40.PageRouteInfo<ArtistRouteArgs> { class ArtistRoute extends _i41.PageRouteInfo<ArtistRouteArgs> {
ArtistRoute({ ArtistRoute({
required String artistId, required String artistId,
_i41.Key? key, _i42.Key? key,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
ArtistRoute.name, ArtistRoute.name,
args: ArtistRouteArgs( args: ArtistRouteArgs(
@ -145,7 +146,7 @@ class ArtistRoute extends _i40.PageRouteInfo<ArtistRouteArgs> {
static const String name = 'ArtistRoute'; static const String name = 'ArtistRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final pathParams = data.inheritedPathParams; final pathParams = data.inheritedPathParams;
@ -167,7 +168,7 @@ class ArtistRouteArgs {
final String artistId; final String artistId;
final _i41.Key? key; final _i42.Key? key;
@override @override
String toString() { String toString() {
@ -177,8 +178,8 @@ class ArtistRouteArgs {
/// generated route for /// generated route for
/// [_i4.BlackListPage] /// [_i4.BlackListPage]
class BlackListRoute extends _i40.PageRouteInfo<void> { class BlackListRoute extends _i41.PageRouteInfo<void> {
const BlackListRoute({List<_i40.PageRouteInfo>? children}) const BlackListRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
BlackListRoute.name, BlackListRoute.name,
initialChildren: children, initialChildren: children,
@ -186,7 +187,7 @@ class BlackListRoute extends _i40.PageRouteInfo<void> {
static const String name = 'BlackListRoute'; static const String name = 'BlackListRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i4.BlackListPage(); return const _i4.BlackListPage();
@ -196,8 +197,8 @@ class BlackListRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i5.ConnectControlPage] /// [_i5.ConnectControlPage]
class ConnectControlRoute extends _i40.PageRouteInfo<void> { class ConnectControlRoute extends _i41.PageRouteInfo<void> {
const ConnectControlRoute({List<_i40.PageRouteInfo>? children}) const ConnectControlRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
ConnectControlRoute.name, ConnectControlRoute.name,
initialChildren: children, initialChildren: children,
@ -205,7 +206,7 @@ class ConnectControlRoute extends _i40.PageRouteInfo<void> {
static const String name = 'ConnectControlRoute'; static const String name = 'ConnectControlRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i5.ConnectControlPage(); return const _i5.ConnectControlPage();
@ -215,8 +216,8 @@ class ConnectControlRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i6.ConnectPage] /// [_i6.ConnectPage]
class ConnectRoute extends _i40.PageRouteInfo<void> { class ConnectRoute extends _i41.PageRouteInfo<void> {
const ConnectRoute({List<_i40.PageRouteInfo>? children}) const ConnectRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
ConnectRoute.name, ConnectRoute.name,
initialChildren: children, initialChildren: children,
@ -224,7 +225,7 @@ class ConnectRoute extends _i40.PageRouteInfo<void> {
static const String name = 'ConnectRoute'; static const String name = 'ConnectRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i6.ConnectPage(); return const _i6.ConnectPage();
@ -234,8 +235,8 @@ class ConnectRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i7.GettingStartedPage] /// [_i7.GettingStartedPage]
class GettingStartedRoute extends _i40.PageRouteInfo<void> { class GettingStartedRoute extends _i41.PageRouteInfo<void> {
const GettingStartedRoute({List<_i40.PageRouteInfo>? children}) const GettingStartedRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
GettingStartedRoute.name, GettingStartedRoute.name,
initialChildren: children, initialChildren: children,
@ -243,7 +244,7 @@ class GettingStartedRoute extends _i40.PageRouteInfo<void> {
static const String name = 'GettingStartedRoute'; static const String name = 'GettingStartedRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i7.GettingStartedPage(); return const _i7.GettingStartedPage();
@ -254,12 +255,12 @@ class GettingStartedRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i8.HomeBrowseSectionItemsPage] /// [_i8.HomeBrowseSectionItemsPage]
class HomeBrowseSectionItemsRoute class HomeBrowseSectionItemsRoute
extends _i40.PageRouteInfo<HomeBrowseSectionItemsRouteArgs> { extends _i41.PageRouteInfo<HomeBrowseSectionItemsRouteArgs> {
HomeBrowseSectionItemsRoute({ HomeBrowseSectionItemsRoute({
_i43.Key? key, _i44.Key? key,
required String sectionId, required String sectionId,
required _i42.SpotubeBrowseSectionObject<Object> section, required _i43.SpotubeBrowseSectionObject<Object> section,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
HomeBrowseSectionItemsRoute.name, HomeBrowseSectionItemsRoute.name,
args: HomeBrowseSectionItemsRouteArgs( args: HomeBrowseSectionItemsRouteArgs(
@ -273,7 +274,7 @@ class HomeBrowseSectionItemsRoute
static const String name = 'HomeBrowseSectionItemsRoute'; static const String name = 'HomeBrowseSectionItemsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final args = data.argsAs<HomeBrowseSectionItemsRouteArgs>(); final args = data.argsAs<HomeBrowseSectionItemsRouteArgs>();
@ -293,11 +294,11 @@ class HomeBrowseSectionItemsRouteArgs {
required this.section, required this.section,
}); });
final _i43.Key? key; final _i44.Key? key;
final String sectionId; final String sectionId;
final _i42.SpotubeBrowseSectionObject<Object> section; final _i43.SpotubeBrowseSectionObject<Object> section;
@override @override
String toString() { String toString() {
@ -307,8 +308,8 @@ class HomeBrowseSectionItemsRouteArgs {
/// generated route for /// generated route for
/// [_i9.HomePage] /// [_i9.HomePage]
class HomeRoute extends _i40.PageRouteInfo<void> { class HomeRoute extends _i41.PageRouteInfo<void> {
const HomeRoute({List<_i40.PageRouteInfo>? children}) const HomeRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
HomeRoute.name, HomeRoute.name,
initialChildren: children, initialChildren: children,
@ -316,7 +317,7 @@ class HomeRoute extends _i40.PageRouteInfo<void> {
static const String name = 'HomeRoute'; static const String name = 'HomeRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i9.HomePage(); return const _i9.HomePage();
@ -326,8 +327,8 @@ class HomeRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i10.LastFMLoginPage] /// [_i10.LastFMLoginPage]
class LastFMLoginRoute extends _i40.PageRouteInfo<void> { class LastFMLoginRoute extends _i41.PageRouteInfo<void> {
const LastFMLoginRoute({List<_i40.PageRouteInfo>? children}) const LastFMLoginRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
LastFMLoginRoute.name, LastFMLoginRoute.name,
initialChildren: children, initialChildren: children,
@ -335,7 +336,7 @@ class LastFMLoginRoute extends _i40.PageRouteInfo<void> {
static const String name = 'LastFMLoginRoute'; static const String name = 'LastFMLoginRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i10.LastFMLoginPage(); return const _i10.LastFMLoginPage();
@ -345,8 +346,8 @@ class LastFMLoginRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i11.LibraryPage] /// [_i11.LibraryPage]
class LibraryRoute extends _i40.PageRouteInfo<void> { class LibraryRoute extends _i41.PageRouteInfo<void> {
const LibraryRoute({List<_i40.PageRouteInfo>? children}) const LibraryRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
LibraryRoute.name, LibraryRoute.name,
initialChildren: children, initialChildren: children,
@ -354,7 +355,7 @@ class LibraryRoute extends _i40.PageRouteInfo<void> {
static const String name = 'LibraryRoute'; static const String name = 'LibraryRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i11.LibraryPage(); return const _i11.LibraryPage();
@ -364,11 +365,11 @@ class LibraryRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i12.LikedPlaylistPage] /// [_i12.LikedPlaylistPage]
class LikedPlaylistRoute extends _i40.PageRouteInfo<LikedPlaylistRouteArgs> { class LikedPlaylistRoute extends _i41.PageRouteInfo<LikedPlaylistRouteArgs> {
LikedPlaylistRoute({ LikedPlaylistRoute({
_i41.Key? key, _i42.Key? key,
required _i42.SpotubeSimplePlaylistObject playlist, required _i43.SpotubeSimplePlaylistObject playlist,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
LikedPlaylistRoute.name, LikedPlaylistRoute.name,
args: LikedPlaylistRouteArgs( args: LikedPlaylistRouteArgs(
@ -380,7 +381,7 @@ class LikedPlaylistRoute extends _i40.PageRouteInfo<LikedPlaylistRouteArgs> {
static const String name = 'LikedPlaylistRoute'; static const String name = 'LikedPlaylistRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final args = data.argsAs<LikedPlaylistRouteArgs>(); final args = data.argsAs<LikedPlaylistRouteArgs>();
@ -398,9 +399,9 @@ class LikedPlaylistRouteArgs {
required this.playlist, required this.playlist,
}); });
final _i41.Key? key; final _i42.Key? key;
final _i42.SpotubeSimplePlaylistObject playlist; final _i43.SpotubeSimplePlaylistObject playlist;
@override @override
String toString() { String toString() {
@ -410,13 +411,13 @@ class LikedPlaylistRouteArgs {
/// generated route for /// generated route for
/// [_i13.LocalLibraryPage] /// [_i13.LocalLibraryPage]
class LocalLibraryRoute extends _i40.PageRouteInfo<LocalLibraryRouteArgs> { class LocalLibraryRoute extends _i41.PageRouteInfo<LocalLibraryRouteArgs> {
LocalLibraryRoute({ LocalLibraryRoute({
required String location, required String location,
_i41.Key? key, _i42.Key? key,
bool isDownloads = false, bool isDownloads = false,
bool isCache = false, bool isCache = false,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
LocalLibraryRoute.name, LocalLibraryRoute.name,
args: LocalLibraryRouteArgs( args: LocalLibraryRouteArgs(
@ -430,7 +431,7 @@ class LocalLibraryRoute extends _i40.PageRouteInfo<LocalLibraryRouteArgs> {
static const String name = 'LocalLibraryRoute'; static const String name = 'LocalLibraryRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final args = data.argsAs<LocalLibraryRouteArgs>(); final args = data.argsAs<LocalLibraryRouteArgs>();
@ -454,7 +455,7 @@ class LocalLibraryRouteArgs {
final String location; final String location;
final _i41.Key? key; final _i42.Key? key;
final bool isDownloads; final bool isDownloads;
@ -468,8 +469,8 @@ class LocalLibraryRouteArgs {
/// generated route for /// generated route for
/// [_i14.LogsPage] /// [_i14.LogsPage]
class LogsRoute extends _i40.PageRouteInfo<void> { class LogsRoute extends _i41.PageRouteInfo<void> {
const LogsRoute({List<_i40.PageRouteInfo>? children}) const LogsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
LogsRoute.name, LogsRoute.name,
initialChildren: children, initialChildren: children,
@ -477,7 +478,7 @@ class LogsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'LogsRoute'; static const String name = 'LogsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i14.LogsPage(); return const _i14.LogsPage();
@ -487,8 +488,8 @@ class LogsRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i15.LyricsPage] /// [_i15.LyricsPage]
class LyricsRoute extends _i40.PageRouteInfo<void> { class LyricsRoute extends _i41.PageRouteInfo<void> {
const LyricsRoute({List<_i40.PageRouteInfo>? children}) const LyricsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
LyricsRoute.name, LyricsRoute.name,
initialChildren: children, initialChildren: children,
@ -496,7 +497,7 @@ class LyricsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'LyricsRoute'; static const String name = 'LyricsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i15.LyricsPage(); return const _i15.LyricsPage();
@ -506,11 +507,11 @@ class LyricsRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i16.MiniLyricsPage] /// [_i16.MiniLyricsPage]
class MiniLyricsRoute extends _i40.PageRouteInfo<MiniLyricsRouteArgs> { class MiniLyricsRoute extends _i41.PageRouteInfo<MiniLyricsRouteArgs> {
MiniLyricsRoute({ MiniLyricsRoute({
_i43.Key? key, _i44.Key? key,
required _i43.Size prevSize, required _i44.Size prevSize,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
MiniLyricsRoute.name, MiniLyricsRoute.name,
args: MiniLyricsRouteArgs( args: MiniLyricsRouteArgs(
@ -522,7 +523,7 @@ class MiniLyricsRoute extends _i40.PageRouteInfo<MiniLyricsRouteArgs> {
static const String name = 'MiniLyricsRoute'; static const String name = 'MiniLyricsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final args = data.argsAs<MiniLyricsRouteArgs>(); final args = data.argsAs<MiniLyricsRouteArgs>();
@ -540,9 +541,9 @@ class MiniLyricsRouteArgs {
required this.prevSize, required this.prevSize,
}); });
final _i43.Key? key; final _i44.Key? key;
final _i43.Size prevSize; final _i44.Size prevSize;
@override @override
String toString() { String toString() {
@ -552,8 +553,8 @@ class MiniLyricsRouteArgs {
/// generated route for /// generated route for
/// [_i17.PlayerLyricsPage] /// [_i17.PlayerLyricsPage]
class PlayerLyricsRoute extends _i40.PageRouteInfo<void> { class PlayerLyricsRoute extends _i41.PageRouteInfo<void> {
const PlayerLyricsRoute({List<_i40.PageRouteInfo>? children}) const PlayerLyricsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
PlayerLyricsRoute.name, PlayerLyricsRoute.name,
initialChildren: children, initialChildren: children,
@ -561,7 +562,7 @@ class PlayerLyricsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'PlayerLyricsRoute'; static const String name = 'PlayerLyricsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i17.PlayerLyricsPage(); return const _i17.PlayerLyricsPage();
@ -571,8 +572,8 @@ class PlayerLyricsRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i18.PlayerQueuePage] /// [_i18.PlayerQueuePage]
class PlayerQueueRoute extends _i40.PageRouteInfo<void> { class PlayerQueueRoute extends _i41.PageRouteInfo<void> {
const PlayerQueueRoute({List<_i40.PageRouteInfo>? children}) const PlayerQueueRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
PlayerQueueRoute.name, PlayerQueueRoute.name,
initialChildren: children, initialChildren: children,
@ -580,7 +581,7 @@ class PlayerQueueRoute extends _i40.PageRouteInfo<void> {
static const String name = 'PlayerQueueRoute'; static const String name = 'PlayerQueueRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i18.PlayerQueuePage(); return const _i18.PlayerQueuePage();
@ -590,8 +591,8 @@ class PlayerQueueRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i19.PlayerTrackSourcesPage] /// [_i19.PlayerTrackSourcesPage]
class PlayerTrackSourcesRoute extends _i40.PageRouteInfo<void> { class PlayerTrackSourcesRoute extends _i41.PageRouteInfo<void> {
const PlayerTrackSourcesRoute({List<_i40.PageRouteInfo>? children}) const PlayerTrackSourcesRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
PlayerTrackSourcesRoute.name, PlayerTrackSourcesRoute.name,
initialChildren: children, initialChildren: children,
@ -599,7 +600,7 @@ class PlayerTrackSourcesRoute extends _i40.PageRouteInfo<void> {
static const String name = 'PlayerTrackSourcesRoute'; static const String name = 'PlayerTrackSourcesRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i19.PlayerTrackSourcesPage(); return const _i19.PlayerTrackSourcesPage();
@ -609,12 +610,12 @@ class PlayerTrackSourcesRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i20.PlaylistPage] /// [_i20.PlaylistPage]
class PlaylistRoute extends _i40.PageRouteInfo<PlaylistRouteArgs> { class PlaylistRoute extends _i41.PageRouteInfo<PlaylistRouteArgs> {
PlaylistRoute({ PlaylistRoute({
_i41.Key? key, _i42.Key? key,
required String id, required String id,
required _i42.SpotubeSimplePlaylistObject playlist, required _i43.SpotubeSimplePlaylistObject playlist,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
PlaylistRoute.name, PlaylistRoute.name,
args: PlaylistRouteArgs( args: PlaylistRouteArgs(
@ -628,7 +629,7 @@ class PlaylistRoute extends _i40.PageRouteInfo<PlaylistRouteArgs> {
static const String name = 'PlaylistRoute'; static const String name = 'PlaylistRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final args = data.argsAs<PlaylistRouteArgs>(); final args = data.argsAs<PlaylistRouteArgs>();
@ -648,11 +649,11 @@ class PlaylistRouteArgs {
required this.playlist, required this.playlist,
}); });
final _i41.Key? key; final _i42.Key? key;
final String id; final String id;
final _i42.SpotubeSimplePlaylistObject playlist; final _i43.SpotubeSimplePlaylistObject playlist;
@override @override
String toString() { String toString() {
@ -662,8 +663,8 @@ class PlaylistRouteArgs {
/// generated route for /// generated route for
/// [_i21.ProfilePage] /// [_i21.ProfilePage]
class ProfileRoute extends _i40.PageRouteInfo<void> { class ProfileRoute extends _i41.PageRouteInfo<void> {
const ProfileRoute({List<_i40.PageRouteInfo>? children}) const ProfileRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
ProfileRoute.name, ProfileRoute.name,
initialChildren: children, initialChildren: children,
@ -671,7 +672,7 @@ class ProfileRoute extends _i40.PageRouteInfo<void> {
static const String name = 'ProfileRoute'; static const String name = 'ProfileRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i21.ProfilePage(); return const _i21.ProfilePage();
@ -681,8 +682,8 @@ class ProfileRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i22.RootAppPage] /// [_i22.RootAppPage]
class RootAppRoute extends _i40.PageRouteInfo<void> { class RootAppRoute extends _i41.PageRouteInfo<void> {
const RootAppRoute({List<_i40.PageRouteInfo>? children}) const RootAppRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
RootAppRoute.name, RootAppRoute.name,
initialChildren: children, initialChildren: children,
@ -690,7 +691,7 @@ class RootAppRoute extends _i40.PageRouteInfo<void> {
static const String name = 'RootAppRoute'; static const String name = 'RootAppRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i22.RootAppPage(); return const _i22.RootAppPage();
@ -700,8 +701,8 @@ class RootAppRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i23.SearchPage] /// [_i23.SearchPage]
class SearchRoute extends _i40.PageRouteInfo<void> { class SearchRoute extends _i41.PageRouteInfo<void> {
const SearchRoute({List<_i40.PageRouteInfo>? children}) const SearchRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
SearchRoute.name, SearchRoute.name,
initialChildren: children, initialChildren: children,
@ -709,7 +710,7 @@ class SearchRoute extends _i40.PageRouteInfo<void> {
static const String name = 'SearchRoute'; static const String name = 'SearchRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i23.SearchPage(); return const _i23.SearchPage();
@ -720,12 +721,12 @@ class SearchRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i24.SettingsMetadataProviderFormPage] /// [_i24.SettingsMetadataProviderFormPage]
class SettingsMetadataProviderFormRoute class SettingsMetadataProviderFormRoute
extends _i40.PageRouteInfo<SettingsMetadataProviderFormRouteArgs> { extends _i41.PageRouteInfo<SettingsMetadataProviderFormRouteArgs> {
SettingsMetadataProviderFormRoute({ SettingsMetadataProviderFormRoute({
_i43.Key? key, _i44.Key? key,
required String title, required String title,
required List<_i42.MetadataFormFieldObject> fields, required List<_i43.MetadataFormFieldObject> fields,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
SettingsMetadataProviderFormRoute.name, SettingsMetadataProviderFormRoute.name,
args: SettingsMetadataProviderFormRouteArgs( args: SettingsMetadataProviderFormRouteArgs(
@ -738,7 +739,7 @@ class SettingsMetadataProviderFormRoute
static const String name = 'SettingsMetadataProviderFormRoute'; static const String name = 'SettingsMetadataProviderFormRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final args = data.argsAs<SettingsMetadataProviderFormRouteArgs>(); final args = data.argsAs<SettingsMetadataProviderFormRouteArgs>();
@ -758,11 +759,11 @@ class SettingsMetadataProviderFormRouteArgs {
required this.fields, required this.fields,
}); });
final _i43.Key? key; final _i44.Key? key;
final String title; final String title;
final List<_i42.MetadataFormFieldObject> fields; final List<_i43.MetadataFormFieldObject> fields;
@override @override
String toString() { String toString() {
@ -772,8 +773,8 @@ class SettingsMetadataProviderFormRouteArgs {
/// generated route for /// generated route for
/// [_i25.SettingsMetadataProviderPage] /// [_i25.SettingsMetadataProviderPage]
class SettingsMetadataProviderRoute extends _i40.PageRouteInfo<void> { class SettingsMetadataProviderRoute extends _i41.PageRouteInfo<void> {
const SettingsMetadataProviderRoute({List<_i40.PageRouteInfo>? children}) const SettingsMetadataProviderRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
SettingsMetadataProviderRoute.name, SettingsMetadataProviderRoute.name,
initialChildren: children, initialChildren: children,
@ -781,7 +782,7 @@ class SettingsMetadataProviderRoute extends _i40.PageRouteInfo<void> {
static const String name = 'SettingsMetadataProviderRoute'; static const String name = 'SettingsMetadataProviderRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i25.SettingsMetadataProviderPage(); return const _i25.SettingsMetadataProviderPage();
@ -791,8 +792,8 @@ class SettingsMetadataProviderRoute extends _i40.PageRouteInfo<void> {
/// generated route for /// generated route for
/// [_i26.SettingsPage] /// [_i26.SettingsPage]
class SettingsRoute extends _i40.PageRouteInfo<void> { class SettingsRoute extends _i41.PageRouteInfo<void> {
const SettingsRoute({List<_i40.PageRouteInfo>? children}) const SettingsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
SettingsRoute.name, SettingsRoute.name,
initialChildren: children, initialChildren: children,
@ -800,7 +801,7 @@ class SettingsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'SettingsRoute'; static const String name = 'SettingsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i26.SettingsPage(); return const _i26.SettingsPage();
@ -809,9 +810,28 @@ class SettingsRoute extends _i40.PageRouteInfo<void> {
} }
/// generated route for /// generated route for
/// [_i27.StatsAlbumsPage] /// [_i27.SettingsScrobblingPage]
class StatsAlbumsRoute extends _i40.PageRouteInfo<void> { class SettingsScrobblingRoute extends _i41.PageRouteInfo<void> {
const StatsAlbumsRoute({List<_i40.PageRouteInfo>? children}) const SettingsScrobblingRoute({List<_i41.PageRouteInfo>? children})
: super(
SettingsScrobblingRoute.name,
initialChildren: children,
);
static const String name = 'SettingsScrobblingRoute';
static _i41.PageInfo page = _i41.PageInfo(
name,
builder: (data) {
return const _i27.SettingsScrobblingPage();
},
);
}
/// generated route for
/// [_i28.StatsAlbumsPage]
class StatsAlbumsRoute extends _i41.PageRouteInfo<void> {
const StatsAlbumsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
StatsAlbumsRoute.name, StatsAlbumsRoute.name,
initialChildren: children, initialChildren: children,
@ -819,18 +839,18 @@ class StatsAlbumsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'StatsAlbumsRoute'; static const String name = 'StatsAlbumsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i27.StatsAlbumsPage(); return const _i28.StatsAlbumsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i28.StatsArtistsPage] /// [_i29.StatsArtistsPage]
class StatsArtistsRoute extends _i40.PageRouteInfo<void> { class StatsArtistsRoute extends _i41.PageRouteInfo<void> {
const StatsArtistsRoute({List<_i40.PageRouteInfo>? children}) const StatsArtistsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
StatsArtistsRoute.name, StatsArtistsRoute.name,
initialChildren: children, initialChildren: children,
@ -838,18 +858,18 @@ class StatsArtistsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'StatsArtistsRoute'; static const String name = 'StatsArtistsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i28.StatsArtistsPage(); return const _i29.StatsArtistsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i29.StatsMinutesPage] /// [_i30.StatsMinutesPage]
class StatsMinutesRoute extends _i40.PageRouteInfo<void> { class StatsMinutesRoute extends _i41.PageRouteInfo<void> {
const StatsMinutesRoute({List<_i40.PageRouteInfo>? children}) const StatsMinutesRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
StatsMinutesRoute.name, StatsMinutesRoute.name,
initialChildren: children, initialChildren: children,
@ -857,18 +877,18 @@ class StatsMinutesRoute extends _i40.PageRouteInfo<void> {
static const String name = 'StatsMinutesRoute'; static const String name = 'StatsMinutesRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i29.StatsMinutesPage(); return const _i30.StatsMinutesPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i30.StatsPage] /// [_i31.StatsPage]
class StatsRoute extends _i40.PageRouteInfo<void> { class StatsRoute extends _i41.PageRouteInfo<void> {
const StatsRoute({List<_i40.PageRouteInfo>? children}) const StatsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
StatsRoute.name, StatsRoute.name,
initialChildren: children, initialChildren: children,
@ -876,18 +896,18 @@ class StatsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'StatsRoute'; static const String name = 'StatsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i30.StatsPage(); return const _i31.StatsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i31.StatsPlaylistsPage] /// [_i32.StatsPlaylistsPage]
class StatsPlaylistsRoute extends _i40.PageRouteInfo<void> { class StatsPlaylistsRoute extends _i41.PageRouteInfo<void> {
const StatsPlaylistsRoute({List<_i40.PageRouteInfo>? children}) const StatsPlaylistsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
StatsPlaylistsRoute.name, StatsPlaylistsRoute.name,
initialChildren: children, initialChildren: children,
@ -895,18 +915,18 @@ class StatsPlaylistsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'StatsPlaylistsRoute'; static const String name = 'StatsPlaylistsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i31.StatsPlaylistsPage(); return const _i32.StatsPlaylistsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i32.StatsStreamFeesPage] /// [_i33.StatsStreamFeesPage]
class StatsStreamFeesRoute extends _i40.PageRouteInfo<void> { class StatsStreamFeesRoute extends _i41.PageRouteInfo<void> {
const StatsStreamFeesRoute({List<_i40.PageRouteInfo>? children}) const StatsStreamFeesRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
StatsStreamFeesRoute.name, StatsStreamFeesRoute.name,
initialChildren: children, initialChildren: children,
@ -914,18 +934,18 @@ class StatsStreamFeesRoute extends _i40.PageRouteInfo<void> {
static const String name = 'StatsStreamFeesRoute'; static const String name = 'StatsStreamFeesRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i32.StatsStreamFeesPage(); return const _i33.StatsStreamFeesPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i33.StatsStreamsPage] /// [_i34.StatsStreamsPage]
class StatsStreamsRoute extends _i40.PageRouteInfo<void> { class StatsStreamsRoute extends _i41.PageRouteInfo<void> {
const StatsStreamsRoute({List<_i40.PageRouteInfo>? children}) const StatsStreamsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
StatsStreamsRoute.name, StatsStreamsRoute.name,
initialChildren: children, initialChildren: children,
@ -933,21 +953,21 @@ class StatsStreamsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'StatsStreamsRoute'; static const String name = 'StatsStreamsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i33.StatsStreamsPage(); return const _i34.StatsStreamsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i34.TrackPage] /// [_i35.TrackPage]
class TrackRoute extends _i40.PageRouteInfo<TrackRouteArgs> { class TrackRoute extends _i41.PageRouteInfo<TrackRouteArgs> {
TrackRoute({ TrackRoute({
_i43.Key? key, _i44.Key? key,
required String trackId, required String trackId,
List<_i40.PageRouteInfo>? children, List<_i41.PageRouteInfo>? children,
}) : super( }) : super(
TrackRoute.name, TrackRoute.name,
args: TrackRouteArgs( args: TrackRouteArgs(
@ -960,13 +980,13 @@ class TrackRoute extends _i40.PageRouteInfo<TrackRouteArgs> {
static const String name = 'TrackRoute'; static const String name = 'TrackRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
final pathParams = data.inheritedPathParams; final pathParams = data.inheritedPathParams;
final args = data.argsAs<TrackRouteArgs>( final args = data.argsAs<TrackRouteArgs>(
orElse: () => TrackRouteArgs(trackId: pathParams.getString('id'))); orElse: () => TrackRouteArgs(trackId: pathParams.getString('id')));
return _i34.TrackPage( return _i35.TrackPage(
key: args.key, key: args.key,
trackId: args.trackId, trackId: args.trackId,
); );
@ -980,7 +1000,7 @@ class TrackRouteArgs {
required this.trackId, required this.trackId,
}); });
final _i43.Key? key; final _i44.Key? key;
final String trackId; final String trackId;
@ -991,9 +1011,9 @@ class TrackRouteArgs {
} }
/// generated route for /// generated route for
/// [_i35.UserAlbumsPage] /// [_i36.UserAlbumsPage]
class UserAlbumsRoute extends _i40.PageRouteInfo<void> { class UserAlbumsRoute extends _i41.PageRouteInfo<void> {
const UserAlbumsRoute({List<_i40.PageRouteInfo>? children}) const UserAlbumsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
UserAlbumsRoute.name, UserAlbumsRoute.name,
initialChildren: children, initialChildren: children,
@ -1001,18 +1021,18 @@ class UserAlbumsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'UserAlbumsRoute'; static const String name = 'UserAlbumsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i35.UserAlbumsPage(); return const _i36.UserAlbumsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i36.UserArtistsPage] /// [_i37.UserArtistsPage]
class UserArtistsRoute extends _i40.PageRouteInfo<void> { class UserArtistsRoute extends _i41.PageRouteInfo<void> {
const UserArtistsRoute({List<_i40.PageRouteInfo>? children}) const UserArtistsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
UserArtistsRoute.name, UserArtistsRoute.name,
initialChildren: children, initialChildren: children,
@ -1020,18 +1040,18 @@ class UserArtistsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'UserArtistsRoute'; static const String name = 'UserArtistsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i36.UserArtistsPage(); return const _i37.UserArtistsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i37.UserDownloadsPage] /// [_i38.UserDownloadsPage]
class UserDownloadsRoute extends _i40.PageRouteInfo<void> { class UserDownloadsRoute extends _i41.PageRouteInfo<void> {
const UserDownloadsRoute({List<_i40.PageRouteInfo>? children}) const UserDownloadsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
UserDownloadsRoute.name, UserDownloadsRoute.name,
initialChildren: children, initialChildren: children,
@ -1039,18 +1059,18 @@ class UserDownloadsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'UserDownloadsRoute'; static const String name = 'UserDownloadsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i37.UserDownloadsPage(); return const _i38.UserDownloadsPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i38.UserLocalLibraryPage] /// [_i39.UserLocalLibraryPage]
class UserLocalLibraryRoute extends _i40.PageRouteInfo<void> { class UserLocalLibraryRoute extends _i41.PageRouteInfo<void> {
const UserLocalLibraryRoute({List<_i40.PageRouteInfo>? children}) const UserLocalLibraryRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
UserLocalLibraryRoute.name, UserLocalLibraryRoute.name,
initialChildren: children, initialChildren: children,
@ -1058,18 +1078,18 @@ class UserLocalLibraryRoute extends _i40.PageRouteInfo<void> {
static const String name = 'UserLocalLibraryRoute'; static const String name = 'UserLocalLibraryRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i38.UserLocalLibraryPage(); return const _i39.UserLocalLibraryPage();
}, },
); );
} }
/// generated route for /// generated route for
/// [_i39.UserPlaylistsPage] /// [_i40.UserPlaylistsPage]
class UserPlaylistsRoute extends _i40.PageRouteInfo<void> { class UserPlaylistsRoute extends _i41.PageRouteInfo<void> {
const UserPlaylistsRoute({List<_i40.PageRouteInfo>? children}) const UserPlaylistsRoute({List<_i41.PageRouteInfo>? children})
: super( : super(
UserPlaylistsRoute.name, UserPlaylistsRoute.name,
initialChildren: children, initialChildren: children,
@ -1077,10 +1097,10 @@ class UserPlaylistsRoute extends _i40.PageRouteInfo<void> {
static const String name = 'UserPlaylistsRoute'; static const String name = 'UserPlaylistsRoute';
static _i40.PageInfo page = _i40.PageInfo( static _i41.PageInfo page = _i41.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i39.UserPlaylistsPage(); return const _i40.UserPlaylistsPage();
}, },
); );
} }

View File

@ -0,0 +1,137 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/spotube_icons.dart';
class ErrorBox extends StatelessWidget {
final Object error;
final VoidCallback? onRetry;
const ErrorBox({
super.key,
required this.error,
this.onRetry,
});
@override
Widget build(BuildContext context) {
// Make a monospace error log view. Make sure it's only 4 lines
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
spacing: 12,
children: [
const Basic(
leading: Icon(SpotubeIcons.error),
contentSpacing: 8,
title: Text("An error occurred"),
),
Card(
padding: const EdgeInsets.all(8.0),
filled: true,
fillColor: context.theme.colorScheme.muted,
child: Text(
error.toString(),
style: TextStyle(
// Use monospace
fontFamily: 'Ubuntu Mono',
color: context.theme.colorScheme.mutedForeground,
fontSize: 14,
),
maxLines: 6,
overflow: TextOverflow.ellipsis,
),
),
// Show a dialog with full log and a retry button as well
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Button.text(
leading: const Icon(SpotubeIcons.logs),
onPressed: () {
showDialog(
context: context,
builder: (context) {
return ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 480,
maxHeight:
MediaQuery.of(context).size.height * 0.8,
),
child: AlertDialog(
padding: const EdgeInsets.all(12),
title: Row(
spacing: 8,
children: [
const Icon(SpotubeIcons.logs),
const Text("Logs"),
const Spacer(),
IconButton.ghost(
icon: const Icon(SpotubeIcons.close),
onPressed: () => context.maybePop(),
)
],
),
actions: [
HookBuilder(builder: (context) {
final copied = useState(false);
return Button.ghost(
leading: copied.value
? const Icon(SpotubeIcons.done)
: const Icon(SpotubeIcons.clipboard),
child: const Text("Copy to clipboard"),
onPressed: () {
Clipboard.setData(
ClipboardData(text: error.toString()),
);
copied.value = true;
},
);
})
],
content: SingleChildScrollView(
child: Card(
padding: const EdgeInsets.all(8.0),
filled: true,
fillColor: context.theme.colorScheme.muted,
child: SelectableText(
error.toString(),
style: TextStyle(
// Use monospace
fontFamily: 'Ubuntu Mono',
color: context
.theme.colorScheme.mutedForeground,
fontSize: 16,
),
),
),
),
),
);
},
);
},
child: const Text("View logs"),
),
if (onRetry != null)
Button.text(
leading: const Icon(SpotubeIcons.refresh),
onPressed: onRetry,
child: const Text("Retry"),
),
],
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,41 @@
import 'package:auto_route/auto_route.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter_undraw/flutter_undraw.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/collections/spotube_icons.dart';
class NoDefaultMetadataPlugin extends StatelessWidget {
const NoDefaultMetadataPlugin({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 10,
children: [
Undraw(
height: 200 * context.theme.scaling,
illustration: UndrawIllustration.stars,
color: context.theme.colorScheme.primary,
),
AutoSizeText(
"You've no default metadata provider set",
style: context.theme.typography.h4,
maxLines: 1,
),
Button.primary(
leading: const Icon(SpotubeIcons.extensions),
child: const Text("Manage metadata providers"),
onPressed: () {
context.pushRoute(const SettingsMetadataProviderRoute());
},
),
],
),
);
}
}

View File

@ -14,7 +14,7 @@ class TextFormBuilderField extends StatelessWidget {
// final AlignmentGeometry? placeholderAlignment; // final AlignmentGeometry? placeholderAlignment;
// final AlignmentGeometry? leadingAlignment; // final AlignmentGeometry? leadingAlignment;
// final AlignmentGeometry? trailingAlignment; // final AlignmentGeometry? trailingAlignment;
final bool border; final Border? border;
final List<InputFeature> features; final List<InputFeature> features;
final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? padding;
final ValueChanged<String>? onSubmitted; final ValueChanged<String>? onSubmitted;
@ -61,7 +61,7 @@ class TextFormBuilderField extends StatelessWidget {
this.minLines, this.minLines,
this.filled = false, this.filled = false,
this.placeholder, this.placeholder,
this.border = true, this.border,
this.padding, this.padding,
this.onSubmitted, this.onSubmitted,
this.onEditingComplete, this.onEditingComplete,

View File

@ -217,7 +217,7 @@ class Spotube extends HookConsumerWidget {
iconTheme: const IconThemeProperties(), iconTheme: const IconThemeProperties(),
colorScheme: colorScheme:
colorSchemeMap[accentMaterialColor.name]?.call(ThemeMode.light) ?? colorSchemeMap[accentMaterialColor.name]?.call(ThemeMode.light) ??
ColorSchemes.lightOrange(), LegacyColorSchemes.lightOrange(),
surfaceOpacity: .8, surfaceOpacity: .8,
surfaceBlur: 10, surfaceBlur: 10,
), ),
@ -226,7 +226,7 @@ class Spotube extends HookConsumerWidget {
iconTheme: const IconThemeProperties(), iconTheme: const IconThemeProperties(),
colorScheme: colorScheme:
colorSchemeMap[accentMaterialColor.name]?.call(ThemeMode.dark) ?? colorSchemeMap[accentMaterialColor.name]?.call(ThemeMode.dark) ??
ColorSchemes.darkOrange(), LegacyColorSchemes.darkOrange(),
surfaceOpacity: .8, surfaceOpacity: .8,
surfaceBlur: 10, surfaceBlur: 10,
), ),

View File

@ -2,10 +2,13 @@ import 'package:auto_route/auto_route.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/routes.gr.dart'; import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/fallbacks/no_default_metadata_plugin.dart';
import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart'; import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/metadata_plugin/browse/sections.dart'; import 'package:spotube/provider/metadata_plugin/browse/sections.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart'; import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:spotube/services/metadata/errors/exceptions.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart';
import 'package:flutter_undraw/flutter_undraw.dart'; import 'package:flutter_undraw/flutter_undraw.dart';
@ -44,6 +47,29 @@ class HomePageBrowseSection extends HookConsumerWidget {
); );
} }
if (browseSections.error
case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin,
message: _,
)) {
return const SliverFillRemaining(
child: Center(child: NoDefaultMetadataPlugin()),
);
}
if (browseSections.hasError) {
return SliverFillRemaining(
child: Center(
child: ErrorBox(
error: browseSections.error!,
onRetry: () {
ref.invalidate(metadataPluginBrowseSectionsProvider);
},
),
),
);
}
return SliverInfiniteList( return SliverInfiniteList(
hasReachedMax: browseSections.asData?.value.hasMore == false, hasReachedMax: browseSections.asData?.value.hasMore == false,
isLoading: !browseSections.isLoading && browseSections.isLoadingNextPage, isLoading: !browseSections.isLoading && browseSections.isLoadingNextPage,

View File

@ -80,6 +80,9 @@ class Sidebar extends HookConsumerWidget {
), ),
for (final tile in sidebarTileList) for (final tile in sidebarTileList)
NavigationButton( NavigationButton(
style: router.currentPath.startsWith(tile.pathPrefix)
? const ButtonStyle.secondary()
: null,
label: mediaQuery.lgAndUp ? Text(tile.title) : null, label: mediaQuery.lgAndUp ? Text(tile.title) : null,
child: Tooltip( child: Tooltip(
tooltip: TooltipContainer(child: Text(tile.title)).call, tooltip: TooltipContainer(child: Text(tile.title)).call,
@ -94,6 +97,9 @@ class Sidebar extends HookConsumerWidget {
NavigationLabel(child: Text(context.l10n.library)), NavigationLabel(child: Text(context.l10n.library)),
for (final tile in sidebarLibraryTileList) for (final tile in sidebarLibraryTileList)
NavigationButton( NavigationButton(
style: router.currentPath.startsWith(tile.pathPrefix)
? const ButtonStyle.secondary()
: null,
label: mediaQuery.lgAndUp ? Text(tile.title) : null, label: mediaQuery.lgAndUp ? Text(tile.title) : null,
onPressed: () { onPressed: () {
context.navigateTo(tile.route); context.navigateTo(tile.route);

View File

@ -41,18 +41,18 @@ final Set<SpotubeColor> colorsMap = {
}; };
final colorSchemeMap = { final colorSchemeMap = {
"slate": ColorSchemes.slate, "slate": LegacyColorSchemes.slate,
"gray": ColorSchemes.gray, "gray": LegacyColorSchemes.gray,
"zinc": ColorSchemes.zinc, "zinc": LegacyColorSchemes.zinc,
"neutral": ColorSchemes.neutral, "neutral": LegacyColorSchemes.neutral,
"stone": ColorSchemes.stone, "stone": LegacyColorSchemes.stone,
"red": ColorSchemes.red, "red": LegacyColorSchemes.red,
"orange": ColorSchemes.orange, "orange": LegacyColorSchemes.orange,
"yellow": ColorSchemes.yellow, "yellow": LegacyColorSchemes.yellow,
"green": ColorSchemes.green, "green": LegacyColorSchemes.green,
"blue": ColorSchemes.blue, "blue": LegacyColorSchemes.blue,
"violet": ColorSchemes.violet, "violet": LegacyColorSchemes.violet,
"rose": ColorSchemes.rose, "rose": LegacyColorSchemes.rose,
}; };
class ColorSchemePickerDialog extends HookConsumerWidget { class ColorSchemePickerDialog extends HookConsumerWidget {

View File

@ -104,15 +104,15 @@ class GettingStartedScreenSupportSection extends HookConsumerWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
Button.secondary( Button.primary(
leading: const Icon(SpotubeIcons.anonymous), leading: const Icon(SpotubeIcons.extensions),
onPressed: () async { onPressed: () async {
await KVStoreService.setDoneGettingStarted(true); await KVStoreService.setDoneGettingStarted(true);
if (context.mounted) { if (context.mounted) {
context.navigateTo(const HomeRoute()); context.pushRoute(const SettingsMetadataProviderRoute());
} }
}, },
child: Text(context.l10n.browse_anonymously), child: const Text("Install a Metadata Provider"),
), ),
], ],
), ),

View File

@ -9,6 +9,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/fallbacks/no_default_metadata_plugin.dart';
import 'package:spotube/components/playbutton_view/playbutton_view.dart'; import 'package:spotube/components/playbutton_view/playbutton_view.dart';
import 'package:spotube/modules/album/album_card.dart'; import 'package:spotube/modules/album/album_card.dart';
import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
@ -17,6 +19,7 @@ import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/metadata_plugin/core/auth.dart'; import 'package:spotube/provider/metadata_plugin/core/auth.dart';
import 'package:spotube/provider/metadata_plugin/library/albums.dart'; import 'package:spotube/provider/metadata_plugin/library/albums.dart';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:spotube/services/metadata/errors/exceptions.dart';
@RoutePage() @RoutePage()
class UserAlbumsPage extends HookConsumerWidget { class UserAlbumsPage extends HookConsumerWidget {
@ -50,10 +53,27 @@ class UserAlbumsPage extends HookConsumerWidget {
[]; [];
}, [albumsQuery.asData?.value, searchText.value]); }, [albumsQuery.asData?.value, searchText.value]);
if (albumsQuery.error
case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin,
message: _,
)) {
return const Center(child: NoDefaultMetadataPlugin());
}
if (authenticated.asData?.value != true) { if (authenticated.asData?.value != true) {
return const AnonymousFallback(); return const AnonymousFallback();
} }
if (albumsQuery.hasError) {
return ErrorBox(
error: albumsQuery.error!,
onRetry: () {
ref.invalidate(metadataPluginSavedAlbumsProvider);
},
);
}
return SafeArea( return SafeArea(
bottom: false, bottom: false,
child: Scaffold( child: Scaffold(

View File

@ -12,6 +12,8 @@ import 'package:spotube/collections/fake.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/fallbacks/anonymous_fallback.dart'; import 'package:spotube/components/fallbacks/anonymous_fallback.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/fallbacks/no_default_metadata_plugin.dart';
import 'package:spotube/modules/artist/artist_card.dart'; import 'package:spotube/modules/artist/artist_card.dart';
import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/components/waypoint.dart'; import 'package:spotube/components/waypoint.dart';
@ -20,6 +22,7 @@ import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/metadata_plugin/core/auth.dart'; import 'package:spotube/provider/metadata_plugin/core/auth.dart';
import 'package:spotube/provider/metadata_plugin/library/artists.dart'; import 'package:spotube/provider/metadata_plugin/library/artists.dart';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:spotube/services/metadata/errors/exceptions.dart';
@RoutePage() @RoutePage()
class UserArtistsPage extends HookConsumerWidget { class UserArtistsPage extends HookConsumerWidget {
@ -55,10 +58,27 @@ class UserArtistsPage extends HookConsumerWidget {
final controller = useScrollController(); final controller = useScrollController();
if (artistQuery.error
case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin,
message: _,
)) {
return const Center(child: NoDefaultMetadataPlugin());
}
if (authenticated.asData?.value != true) { if (authenticated.asData?.value != true) {
return const AnonymousFallback(); return const AnonymousFallback();
} }
if (artistQuery.hasError) {
return ErrorBox(
error: artistQuery.error!,
onRetry: () {
ref.invalidate(metadataPluginSavedArtistsProvider);
},
);
}
return SafeArea( return SafeArea(
bottom: false, bottom: false,
child: Scaffold( child: Scaffold(

View File

@ -136,8 +136,10 @@ class LocalLibraryPage extends HookConsumerWidget {
icon: Column( icon: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon(SpotubeIcons.delete), const Expanded(child: Icon(SpotubeIcons.delete)),
Text(context.l10n.clear_cache) Text(
context.l10n.clear_cache,
)
], ],
).xSmall().iconSmall(), ).xSmall().iconSmall(),
onPressed: () async { onPressed: () async {
@ -178,7 +180,7 @@ class LocalLibraryPage extends HookConsumerWidget {
icon: Column( icon: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon(SpotubeIcons.export), const Expanded(child: Icon(SpotubeIcons.export)),
Text( Text(
context.l10n.export, context.l10n.export,
) )

View File

@ -8,6 +8,8 @@ import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/fallbacks/no_default_metadata_plugin.dart';
import 'package:spotube/components/playbutton_view/playbutton_view.dart'; import 'package:spotube/components/playbutton_view/playbutton_view.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/modules/playlist/playlist_create_dialog.dart'; import 'package:spotube/modules/playlist/playlist_create_dialog.dart';
@ -19,6 +21,7 @@ import 'package:spotube/provider/metadata_plugin/core/auth.dart';
import 'package:spotube/provider/metadata_plugin/library/playlists.dart'; import 'package:spotube/provider/metadata_plugin/library/playlists.dart';
import 'package:spotube/provider/metadata_plugin/core/user.dart'; import 'package:spotube/provider/metadata_plugin/core/user.dart';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:spotube/services/metadata/errors/exceptions.dart';
@RoutePage() @RoutePage()
class UserPlaylistsPage extends HookConsumerWidget { class UserPlaylistsPage extends HookConsumerWidget {
@ -78,10 +81,27 @@ class UserPlaylistsPage extends HookConsumerWidget {
final controller = useScrollController(); final controller = useScrollController();
if (playlistsQuery.error
case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin,
message: _,
)) {
return const Center(child: NoDefaultMetadataPlugin());
}
if (authenticated.asData?.value != true) { if (authenticated.asData?.value != true) {
return const AnonymousFallback(); return const AnonymousFallback();
} }
if (playlistsQuery.hasError) {
return ErrorBox(
error: playlistsQuery.error!,
onRetry: () {
ref.invalidate(metadataPluginSavedPlaylistsProvider);
},
);
}
return material.RefreshIndicator.adaptive( return material.RefreshIndicator.adaptive(
onRefresh: () async { onRefresh: () async {
ref.invalidate(metadataPluginSavedPlaylistsProvider); ref.invalidate(metadataPluginSavedPlaylistsProvider);

View File

@ -7,7 +7,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/routes.gr.dart'; import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/fallbacks/anonymous_fallback.dart'; import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/fallbacks/no_default_metadata_plugin.dart';
import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/string.dart'; import 'package:spotube/extensions/string.dart';
@ -17,10 +18,10 @@ import 'package:spotube/pages/search/tabs/all.dart';
import 'package:spotube/pages/search/tabs/artists.dart'; import 'package:spotube/pages/search/tabs/artists.dart';
import 'package:spotube/pages/search/tabs/playlists.dart'; import 'package:spotube/pages/search/tabs/playlists.dart';
import 'package:spotube/pages/search/tabs/tracks.dart'; import 'package:spotube/pages/search/tabs/tracks.dart';
import 'package:spotube/provider/metadata_plugin/core/auth.dart';
import 'package:spotube/provider/metadata_plugin/search/all.dart'; import 'package:spotube/provider/metadata_plugin/search/all.dart';
import 'package:spotube/services/kv_store/kv_store.dart'; import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:spotube/services/metadata/errors/exceptions.dart';
final searchTermStateProvider = StateProvider<String>((ref) { final searchTermStateProvider = StateProvider<String>((ref) {
return ""; return "";
@ -37,8 +38,6 @@ class SearchPage extends HookConsumerWidget {
final controller = useShadcnTextEditingController(); final controller = useShadcnTextEditingController();
final focusNode = useFocusNode(); final focusNode = useFocusNode();
final authenticated = ref.watch(metadataPluginAuthenticatedProvider);
final searchTerm = ref.watch(searchTermStateProvider); final searchTerm = ref.watch(searchTermStateProvider);
final searchChipSnapshot = ref.watch(metadataPluginSearchChipsProvider); final searchChipSnapshot = ref.watch(metadataPluginSearchChipsProvider);
final selectedChip = useState<String?>( final selectedChip = useState<String?>(
@ -83,9 +82,25 @@ class SearchPage extends HookConsumerWidget {
if (kTitlebarVisible) if (kTitlebarVisible)
const TitleBar(automaticallyImplyLeading: false, height: 30) const TitleBar(automaticallyImplyLeading: false, height: 30)
], ],
child: authenticated.asData?.value != true child: Builder(builder: (context) {
? const AnonymousFallback() if (searchChipSnapshot.error
: Column( case MetadataPluginException(
errorCode: MetadataPluginErrorCode.noDefaultPlugin,
message: _
)) {
return const NoDefaultMetadataPlugin();
}
if (searchChipSnapshot.hasError) {
return ErrorBox(
error: searchChipSnapshot.error!,
onRetry: () {
ref.invalidate(metadataPluginSearchChipsProvider);
},
);
}
return Column(
children: [ children: [
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -144,22 +159,21 @@ class SearchPage extends HookConsumerWidget {
), ),
InputFeature.trailing( InputFeature.trailing(
AnimatedCrossFade( AnimatedCrossFade(
duration: const Duration( duration:
milliseconds: 300), const Duration(milliseconds: 300),
crossFadeState: controller crossFadeState:
.text.isNotEmpty controller.text.isNotEmpty
? CrossFadeState.showFirst ? CrossFadeState.showFirst
: CrossFadeState.showSecond, : CrossFadeState.showSecond,
firstChild: IconButton.ghost( firstChild: IconButton.ghost(
size: ButtonSize.small, size: ButtonSize.small,
icon: const Icon( icon:
SpotubeIcons.close), const Icon(SpotubeIcons.close),
onPressed: () { onPressed: () {
controller.clear(); controller.clear();
}, },
), ),
secondChild: secondChild: const SizedBox.square(
const SizedBox.square(
dimension: 28), dimension: 28),
), ),
) )
@ -223,7 +237,8 @@ class SearchPage extends HookConsumerWidget {
), ),
), ),
], ],
), );
}),
), ),
), ),
); );

View File

@ -2,6 +2,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/playbutton_view/playbutton_view.dart'; import 'package:spotube/components/playbutton_view/playbutton_view.dart';
import 'package:spotube/modules/album/album_card.dart'; import 'package:spotube/modules/album/album_card.dart';
import 'package:spotube/modules/search/loading.dart'; import 'package:spotube/modules/search/loading.dart';
@ -23,6 +24,15 @@ class SearchPageAlbumsTab extends HookConsumerWidget {
final searchAlbums = final searchAlbums =
searchAlbumsSnapshot.asData?.value.items ?? [FakeData.albumSimple]; searchAlbumsSnapshot.asData?.value.items ?? [FakeData.albumSimple];
if (searchAlbumsSnapshot.hasError) {
return ErrorBox(
error: searchAlbumsSnapshot.error!,
onRetry: () {
ref.invalidate(metadataPluginSearchAlbumsProvider(searchTerm));
},
);
}
return SearchPlaceholder( return SearchPlaceholder(
snapshot: searchAlbumsSnapshot, snapshot: searchAlbumsSnapshot,
child: Padding( child: Padding(

View File

@ -1,5 +1,6 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
import 'package:spotube/modules/search/loading.dart'; import 'package:spotube/modules/search/loading.dart';
import 'package:spotube/pages/search/search.dart'; import 'package:spotube/pages/search/search.dart';
@ -19,6 +20,15 @@ class SearchPageAllTab extends HookConsumerWidget {
final searchSnapshot = final searchSnapshot =
ref.watch(metadataPluginSearchAllProvider(searchTerm)); ref.watch(metadataPluginSearchAllProvider(searchTerm));
if (searchSnapshot.hasError) {
return ErrorBox(
error: searchSnapshot.error!,
onRetry: () {
ref.invalidate(metadataPluginSearchAllProvider(searchTerm));
},
);
}
return SearchPlaceholder( return SearchPlaceholder(
snapshot: searchSnapshot, snapshot: searchSnapshot,
child: InterScrollbar( child: InterScrollbar(

View File

@ -5,6 +5,7 @@ import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:skeletonizer/skeletonizer.dart'; import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/waypoint.dart'; import 'package:spotube/components/waypoint.dart';
import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
@ -27,6 +28,15 @@ class SearchPageArtistsTab extends HookConsumerWidget {
ref.read(metadataPluginSearchArtistsProvider(searchTerm).notifier); ref.read(metadataPluginSearchArtistsProvider(searchTerm).notifier);
final searchArtists = searchArtistsSnapshot.asData?.value.items ?? []; final searchArtists = searchArtistsSnapshot.asData?.value.items ?? [];
if (searchArtistsSnapshot.hasError) {
return ErrorBox(
error: searchArtistsSnapshot.error!,
onRetry: () {
ref.invalidate(metadataPluginSearchArtistsProvider(searchTerm));
},
);
}
return SearchPlaceholder( return SearchPlaceholder(
snapshot: searchArtistsSnapshot, snapshot: searchArtistsSnapshot,
child: AnimatedSwitcher( child: AnimatedSwitcher(

View File

@ -2,6 +2,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/playbutton_view/playbutton_view.dart'; import 'package:spotube/components/playbutton_view/playbutton_view.dart';
import 'package:spotube/modules/playlist/playlist_card.dart'; import 'package:spotube/modules/playlist/playlist_card.dart';
import 'package:spotube/modules/search/loading.dart'; import 'package:spotube/modules/search/loading.dart';
@ -23,6 +24,15 @@ class SearchPagePlaylistsTab extends HookConsumerWidget {
final searchPlaylists = searchPlaylistsSnapshot.asData?.value.items ?? final searchPlaylists = searchPlaylistsSnapshot.asData?.value.items ??
[FakeData.playlistSimple]; [FakeData.playlistSimple];
if (searchPlaylistsSnapshot.hasError) {
return ErrorBox(
error: searchPlaylistsSnapshot.error!,
onRetry: () {
ref.invalidate(metadataPluginSearchPlaylistsProvider(searchTerm));
},
);
}
return SearchPlaceholder( return SearchPlaceholder(
snapshot: searchPlaylistsSnapshot, snapshot: searchPlaylistsSnapshot,
child: Padding( child: Padding(

View File

@ -4,6 +4,7 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart'; import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/dialogs/prompt_dialog.dart'; import 'package:spotube/components/dialogs/prompt_dialog.dart';
import 'package:spotube/components/dialogs/select_device_dialog.dart'; import 'package:spotube/components/dialogs/select_device_dialog.dart';
import 'package:spotube/components/fallbacks/error_box.dart';
import 'package:spotube/components/track_tile/track_tile.dart'; import 'package:spotube/components/track_tile/track_tile.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/connect/connect.dart'; import 'package:spotube/models/connect/connect.dart';
@ -31,6 +32,15 @@ class SearchPageTracksTab extends HookConsumerWidget {
final playlist = ref.watch(audioPlayerProvider); final playlist = ref.watch(audioPlayerProvider);
final playlistNotifier = ref.watch(audioPlayerProvider.notifier); final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
if (searchTracksSnapshot.hasError) {
return ErrorBox(
error: searchTracksSnapshot.error!,
onRetry: () {
ref.invalidate(metadataPluginSearchTracksProvider(searchTerm));
},
);
}
return SearchPlaceholder( return SearchPlaceholder(
snapshot: searchTracksSnapshot, snapshot: searchTracksSnapshot,
child: InfiniteList( child: InfiniteList(

View File

@ -0,0 +1,67 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'
show ListTile, ListTileTheme, ListTileThemeData, Material, MaterialType;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/routes.gr.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/extensions/context.dart';
@RoutePage()
class SettingsScrobblingPage extends HookConsumerWidget {
static const name = "settings_scrobbling";
const SettingsScrobblingPage({super.key});
@override
Widget build(BuildContext context, ref) {
return Material(
type: MaterialType.transparency,
child: ListTileTheme(
data: ListTileThemeData(
contentPadding: EdgeInsets.zero,
minVerticalPadding: 0,
shape: RoundedRectangleBorder(
borderRadius: context.theme.borderRadiusLg,
side: BorderSide(
color: context.theme.colorScheme.border,
width: .5,
),
),
textColor: context.theme.colorScheme.foreground,
iconColor: context.theme.colorScheme.foreground,
selectedColor: context.theme.colorScheme.accent,
subtitleTextStyle: context.theme.typography.xSmall,
),
child: SafeArea(
bottom: false,
child: Scaffold(
headers: const [TitleBar(title: Text("Scrobbling"))],
child: ListView(
padding: const EdgeInsets.all(8),
children: [
Card(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListTile(
leading: const Icon(SpotubeIcons.lastFm, color: Colors.red),
title: Text(context.l10n.login_with_lastfm),
subtitle: Text(context.l10n.scrobble_to_lastfm),
trailing: Button.secondary(
leading: const Icon(SpotubeIcons.lastFm),
onPressed: () {
context.navigateTo(const LastFMLoginRoute());
},
child: Text(context.l10n.connect),
),
),
),
],
),
),
),
),
);
}
}

View File

@ -31,16 +31,12 @@ class SettingsAccountSection extends HookConsumerWidget {
), ),
if (scrobbler.asData?.value == null) if (scrobbler.asData?.value == null)
ListTile( ListTile(
leading: const Icon(SpotubeIcons.lastFm), leading: const Icon(SpotubeIcons.music),
title: Text(context.l10n.login_with_lastfm), title: const Text("Audio scrobblers"),
subtitle: Text(context.l10n.scrobble_to_lastfm), onTap: () {
trailing: Button.secondary( context.pushRoute(const SettingsScrobblingRoute());
leading: const Icon(SpotubeIcons.lastFm),
onPressed: () {
context.navigateTo(const LastFMLoginRoute());
}, },
child: Text(context.l10n.connect), trailing: const Icon(SpotubeIcons.angleRight),
),
) )
else else
ListTile( ListTile(

View File

@ -2,7 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart'; import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
final metadataPluginAlbumProvider = final metadataPluginAlbumProvider =
FutureProvider.autoDispose.family<SpotubeFullAlbumObject, String>( FutureProvider.autoDispose.family<SpotubeFullAlbumObject, String>(
@ -12,9 +12,7 @@ final metadataPluginAlbumProvider =
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"No metadata plugin is not set",
);
} }
return metadataPlugin.album.getAlbum(id); return metadataPlugin.album.getAlbum(id);

View File

@ -2,7 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart'; import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
final metadataPluginArtistProvider = final metadataPluginArtistProvider =
FutureProvider.autoDispose.family<SpotubeFullArtistObject, String>( FutureProvider.autoDispose.family<SpotubeFullArtistObject, String>(
@ -12,9 +12,7 @@ final metadataPluginArtistProvider =
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"No metadata plugin is not set",
);
} }
return metadataPlugin.artist.getArtist(artistId); return metadataPlugin.artist.getArtist(artistId);

View File

@ -4,7 +4,7 @@ import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/provider/metadata_plugin/tracks/playlist.dart'; import 'package:spotube/provider/metadata_plugin/tracks/playlist.dart';
import 'package:spotube/provider/metadata_plugin/utils/paginated.dart'; import 'package:spotube/provider/metadata_plugin/utils/paginated.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
class MetadataPluginSavedPlaylistsNotifier class MetadataPluginSavedPlaylistsNotifier
extends PaginatedAsyncNotifier<SpotubeSimplePlaylistObject> { extends PaginatedAsyncNotifier<SpotubeSimplePlaylistObject> {
@ -111,9 +111,7 @@ final metadataPluginIsSavedPlaylistProvider =
final plugin = await ref.watch(metadataPluginProvider.future); final plugin = await ref.watch(metadataPluginProvider.future);
if (plugin == null) { if (plugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"Failed to get metadata plugin",
);
} }
final follows = await plugin.user.isSavedPlaylist(id); final follows = await plugin.user.isSavedPlaylist(id);

View File

@ -4,7 +4,7 @@ import 'package:spotube/provider/metadata_plugin/library/playlists.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/provider/metadata_plugin/core/user.dart'; import 'package:spotube/provider/metadata_plugin/core/user.dart';
import 'package:spotube/provider/metadata_plugin/utils/common.dart'; import 'package:spotube/provider/metadata_plugin/utils/common.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
import 'package:spotube/services/metadata/metadata.dart'; import 'package:spotube/services/metadata/metadata.dart';
class MetadataPluginPlaylistNotifier class MetadataPluginPlaylistNotifier
@ -13,9 +13,7 @@ class MetadataPluginPlaylistNotifier
final metadataPlugin = await ref.read(metadataPluginProvider.future); final metadataPlugin = await ref.read(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"Metadata plugin is not set",
);
} }
return metadataPlugin; return metadataPlugin;

View File

@ -1,7 +1,7 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
final metadataPluginSearchAllProvider = final metadataPluginSearchAllProvider =
FutureProvider.autoDispose.family<SpotubeSearchResponseObject, String>( FutureProvider.autoDispose.family<SpotubeSearchResponseObject, String>(
@ -9,9 +9,7 @@ final metadataPluginSearchAllProvider =
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"No default metadata plugin found",
);
} }
return metadataPlugin.search.all(query); return metadataPlugin.search.all(query);
@ -22,9 +20,7 @@ final metadataPluginSearchChipsProvider = FutureProvider((ref) async {
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"No default metadata plugin found",
);
} }
return metadataPlugin.search.chips; return metadataPlugin.search.chips;
}); });

View File

@ -1,15 +1,14 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
final metadataPluginTrackProvider = final metadataPluginTrackProvider =
FutureProvider.family<SpotubeFullTrackObject, String>((ref, trackId) async { FutureProvider.family<SpotubeFullTrackObject, String>((ref, trackId) async {
final metadataPlugin = await ref.watch(metadataPluginProvider.future); final metadataPlugin = await ref.watch(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"No metadata plugin is set as default.");
} }
return metadataPlugin.track.getTrack(trackId); return metadataPlugin.track.getTrack(trackId);

View File

@ -6,7 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/models/metadata/metadata.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
import 'package:spotube/services/metadata/metadata.dart'; import 'package:spotube/services/metadata/metadata.dart';
extension PaginationExtension<T> on AsyncValue<T> { extension PaginationExtension<T> on AsyncValue<T> {
@ -20,8 +20,7 @@ mixin MetadataPluginMixin<K>
final plugin = await ref.read(metadataPluginProvider.future); final plugin = await ref.read(metadataPluginProvider.future);
if (plugin == null) { if (plugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"Metadata plugin is not set");
} }
return plugin; return plugin;

View File

@ -20,7 +20,7 @@ import 'package:spotube/provider/metadata_plugin/core/auth.dart';
import 'package:spotube/provider/metadata_plugin/library/playlists.dart'; import 'package:spotube/provider/metadata_plugin/library/playlists.dart';
import 'package:spotube/provider/metadata_plugin/library/tracks.dart'; import 'package:spotube/provider/metadata_plugin/library/tracks.dart';
import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart';
import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/errors/exceptions.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
enum TrackOptionValue { enum TrackOptionValue {
@ -97,9 +97,7 @@ class TrackOptionsActions {
final metadataPlugin = await ref.read(metadataPluginProvider.future); final metadataPlugin = await ref.read(metadataPluginProvider.future);
if (metadataPlugin == null) { if (metadataPlugin == null) {
throw MetadataPluginException.noDefaultPlugin( throw MetadataPluginException.noDefaultPlugin();
"No default metadata plugin set",
);
} }
final tracks = await metadataPlugin.track.radio(track.id); final tracks = await metadataPlugin.track.radio(track.id);

View File

@ -1,12 +0,0 @@
class MetadataPluginException implements Exception {
final String exceptionType;
final String message;
MetadataPluginException.noDefaultPlugin(this.message)
: exceptionType = "NoDefault";
@override
String toString() {
return "${exceptionType}MetadataPluginException: $message";
}
}

View File

@ -9,6 +9,7 @@ enum MetadataPluginErrorCode {
pluginDownloadFailed, pluginDownloadFailed,
duplicatePlugin, duplicatePlugin,
pluginByteCodeFileNotFound, pluginByteCodeFileNotFound,
noDefaultPlugin,
} }
class MetadataPluginException implements Exception { class MetadataPluginException implements Exception {
@ -67,6 +68,11 @@ class MetadataPluginException implements Exception {
'Plugin byte code file, plugin.out not found. Please ensure the plugin is correctly packaged.', 'Plugin byte code file, plugin.out not found. Please ensure the plugin is correctly packaged.',
errorCode: MetadataPluginErrorCode.pluginByteCodeFileNotFound, errorCode: MetadataPluginErrorCode.pluginByteCodeFileNotFound,
); );
MetadataPluginException.noDefaultPlugin()
: this._(
'No default metadata plugin is set. Please set a default plugin in the settings.',
errorCode: MetadataPluginErrorCode.noDefaultPlugin,
);
@override @override
String toString() => 'MetadataPluginException: $message'; String toString() => 'MetadataPluginException: $message';

View File

@ -515,8 +515,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "packages/desktop_webview_window" path: "packages/desktop_webview_window"
ref: "feat/cookies" ref: HEAD
resolved-ref: f20e433d4a948515b35089d40069f7dd9bced9e4 resolved-ref: f261ff20e310d05713249b21c199a9fe17a3de6f
url: "https://github.com/KRTirtho/flutter-plugins.git" url: "https://github.com/KRTirtho/flutter-plugins.git"
source: git source: git
version: "0.2.4" version: "0.2.4"
@ -762,6 +762,15 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
fk_user_agent:
dependency: transitive
description:
path: "."
ref: master
resolved-ref: "922f9f9eafd8b501da83dca67d56b2887fa8f916"
url: "https://github.com/TiffApps/fk_user_agent.git"
source: git
version: "2.1.1"
fluentui_system_icons: fluentui_system_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1217,7 +1226,7 @@ packages:
description: description:
path: "." path: "."
ref: main ref: main
resolved-ref: c4895250ee45a59c88770f97abebc9e9bbb62259 resolved-ref: "52cd25a12c1af6a8819963d222026539e8537586"
url: "https://github.com/KRTirtho/hetu_spotube_plugin.git" url: "https://github.com/KRTirtho/hetu_spotube_plugin.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
@ -1226,7 +1235,7 @@ packages:
description: description:
path: "." path: "."
ref: main ref: main
resolved-ref: "7e9032c054c547f7900c9c9fe4b76e29c8ac1cd1" resolved-ref: "577ad115dce0514afc53e2b3ab7b96bcd88d3be3"
url: "https://github.com/hetu-community/hetu_std.git" url: "https://github.com/hetu-community/hetu_std.git"
source: git source: git
version: "1.0.0" version: "1.0.0"
@ -2011,6 +2020,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.2" version: "3.2.2"
random_user_agents:
dependency: transitive
description:
name: random_user_agents
sha256: "19facde509a2482dababb454faf2aceff797a6ae08e80f91268c0c8a7420f03b"
url: "https://pub.dev"
source: hosted
version: "1.0.15"
recase: recase:
dependency: transitive dependency: transitive
description: description:
@ -2104,10 +2121,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: shadcn_flutter name: shadcn_flutter
sha256: "979a86e203eb1fb139e8b5c84b49b17b28808804cbff189b43052d56ba6854b5" sha256: "4a4dff36252101f344c77fd659d791c52c808cfe3ab68deea4ea949d942d9e5b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.37" version: "0.0.39"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -27,7 +27,6 @@ dependencies:
desktop_webview_window: desktop_webview_window:
git: git:
path: packages/desktop_webview_window path: packages/desktop_webview_window
ref: feat/cookies
url: https://github.com/KRTirtho/flutter-plugins.git url: https://github.com/KRTirtho/flutter-plugins.git
device_info_plus: ^11.1.1 device_info_plus: ^11.1.1
dio: ^5.4.3+1 dio: ^5.4.3+1
@ -102,7 +101,7 @@ dependencies:
ref: dart-3-support ref: dart-3-support
url: https://github.com/KRTirtho/scrobblenaut.git url: https://github.com/KRTirtho/scrobblenaut.git
scroll_to_index: ^3.0.1 scroll_to_index: ^3.0.1
shadcn_flutter: ^0.0.37 shadcn_flutter: ^0.0.39
shared_preferences: ^2.2.3 shared_preferences: ^2.2.3
shelf: ^1.4.1 shelf: ^1.4.1
shelf_router: ^1.1.4 shelf_router: ^1.1.4
@ -217,6 +216,7 @@ flutter:
- packages/flutter_undraw/assets/undraw/empty.svg - packages/flutter_undraw/assets/undraw/empty.svg
- packages/flutter_undraw/assets/undraw/no_data.svg - packages/flutter_undraw/assets/undraw/no_data.svg
- packages/flutter_undraw/assets/undraw/process.svg - packages/flutter_undraw/assets/undraw/process.svg
- packages/flutter_undraw/assets/undraw/stars.svg
# hetu script bytecode # hetu script bytecode
- packages/hetu_std/assets/bytecode/std.out - packages/hetu_std/assets/bytecode/std.out
- packages/hetu_otp_util/assets/bytecode/otp_util.out - packages/hetu_otp_util/assets/bytecode/otp_util.out
@ -233,6 +233,20 @@ flutter:
- asset: assets/fonts/Cookie-Regular.ttf - asset: assets/fonts/Cookie-Regular.ttf
style: normal style: normal
weight: 500 weight: 500
- family: Ubuntu Mono
fonts:
- asset: assets/fonts/Ubuntu_Mono/UbuntuMono-Regular.ttf
style: normal
weight: 400
- asset: assets/fonts/Ubuntu_Mono/UbuntuMono-Bold.ttf
style: normal
weight: 700
- asset: assets/fonts/Ubuntu_Mono/UbuntuMono-Italic.ttf
style: italic
weight: 400
- asset: assets/fonts/Ubuntu_Mono/UbuntuMono-BoldItalic.ttf
style: italic
weight: 700
flutter_gen: flutter_gen:
output: lib/collections output: lib/collections