mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
display albums from db
This commit is contained in:
parent
0c80dbdba5
commit
ee2a276f2f
@ -1,20 +1,35 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
|
|
||||||
|
import '../../sources/models.dart';
|
||||||
import '../hooks/use_paging_controller.dart';
|
import '../hooks/use_paging_controller.dart';
|
||||||
|
import '../state/database.dart';
|
||||||
|
import '../state/settings.dart';
|
||||||
import 'list_items.dart';
|
import 'list_items.dart';
|
||||||
|
|
||||||
class AlbumsGrid extends HookWidget {
|
const kPageSize = 30;
|
||||||
|
|
||||||
|
class AlbumsGrid extends HookConsumerWidget {
|
||||||
const AlbumsGrid({super.key});
|
const AlbumsGrid({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final controller = usePagingController<int, String>(
|
final db = ref.watch(databaseProvider);
|
||||||
|
final sourceId = ref.watch(sourceIdProvider);
|
||||||
|
|
||||||
|
final controller = usePagingController<int, Album>(
|
||||||
getNextPageKey: (state) =>
|
getNextPageKey: (state) =>
|
||||||
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
||||||
fetchPage: (pageKey) => List.generate(30, (_) => pageKey.toString()),
|
fetchPage: (pageKey) async {
|
||||||
|
final query = db.albums.select()
|
||||||
|
..where((f) => f.sourceId.equals(sourceId))
|
||||||
|
..limit(kPageSize, offset: (pageKey - 1) * kPageSize);
|
||||||
|
|
||||||
|
return await query.get();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return PagingListener(
|
return PagingListener(
|
||||||
@ -26,10 +41,11 @@ class AlbumsGrid extends HookWidget {
|
|||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
),
|
),
|
||||||
builderDelegate: PagedChildBuilderDelegate<String>(
|
builderDelegate: PagedChildBuilderDelegate<Album>(
|
||||||
itemBuilder: (context, item, index) => AlbumGridTile(
|
itemBuilder: (context, item, index) => AlbumGridTile(
|
||||||
onTap: () {
|
album: item,
|
||||||
context.push('/album');
|
onTap: () async {
|
||||||
|
context.push('/album/${item.id}');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../images/images.dart';
|
||||||
|
import '../../sources/models.dart';
|
||||||
import '../util/clip.dart';
|
import '../util/clip.dart';
|
||||||
|
|
||||||
class AlbumGridTile extends StatelessWidget {
|
class AlbumGridTile extends HookConsumerWidget {
|
||||||
const AlbumGridTile({
|
const AlbumGridTile({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.album,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final Album album;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return CardTheme(
|
return CardTheme(
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
@ -21,11 +26,7 @@ class AlbumGridTile extends StatelessWidget {
|
|||||||
margin: EdgeInsets.all(2),
|
margin: EdgeInsets.all(2),
|
||||||
child: ImageCard(
|
child: ImageCard(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: CachedNetworkImage(
|
child: CoverArtImage(coverArt: album.coverArt),
|
||||||
imageUrl: 'https://placehold.net/400x400.png',
|
|
||||||
placeholder: (context, url) => CircularProgressIndicator(),
|
|
||||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -71,7 +72,7 @@ class ImageCard extends StatelessWidget {
|
|||||||
child,
|
child,
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
type: MaterialType.transparency,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onLongPress: onLongPress,
|
onLongPress: onLongPress,
|
||||||
|
|||||||
@ -17,8 +17,9 @@ final router = GoRouter(
|
|||||||
builder: (context, state) => LibraryScreen(),
|
builder: (context, state) => LibraryScreen(),
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'album',
|
path: 'album/:id',
|
||||||
builder: (context, state) => AlbumScreen(),
|
builder: (context, state) =>
|
||||||
|
AlbumScreen(id: state.pathParameters['id']!),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'artist',
|
path: 'artist',
|
||||||
|
|||||||
@ -3,7 +3,12 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class AlbumScreen extends StatelessWidget {
|
class AlbumScreen extends StatelessWidget {
|
||||||
const AlbumScreen({super.key});
|
const AlbumScreen({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -12,7 +17,7 @@ class AlbumScreen extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text('Album!'),
|
Text('Album $id!'),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.push('/artist');
|
context.push('/artist');
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
import '../lists/albums_grid.dart';
|
import '../lists/albums_grid.dart';
|
||||||
|
import '../state/services.dart';
|
||||||
import '../util/custom_scroll_fix.dart';
|
import '../util/custom_scroll_fix.dart';
|
||||||
|
|
||||||
class LibraryScreen extends StatefulWidget {
|
class LibraryScreen extends StatefulWidget {
|
||||||
@ -126,13 +128,18 @@ class _LibraryScreenState extends State<LibraryScreen>
|
|||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
IconButton(
|
Row(
|
||||||
onPressed: () {
|
children: [
|
||||||
context.push('/settings');
|
SyncButton(),
|
||||||
},
|
IconButton(
|
||||||
icon: Icon(
|
onPressed: () {
|
||||||
Symbols.settings_rounded,
|
context.push('/settings');
|
||||||
),
|
},
|
||||||
|
icon: Icon(
|
||||||
|
Symbols.settings_rounded,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -209,3 +216,19 @@ class _NewWidgetState extends State<NewWidget>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SyncButton extends HookConsumerWidget {
|
||||||
|
const SyncButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final syncService = ref.watch(syncServiceProvider);
|
||||||
|
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(Symbols.sync_rounded),
|
||||||
|
onPressed: () {
|
||||||
|
syncService.sync();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
7
lib/app/state/database.dart
Normal file
7
lib/app/state/database.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../database/database.dart';
|
||||||
|
|
||||||
|
final databaseProvider = Provider<SubtracksDatabase>((ref) {
|
||||||
|
return SubtracksDatabase();
|
||||||
|
});
|
||||||
14
lib/app/state/services.dart
Normal file
14
lib/app/state/services.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../services/sync_services.dart';
|
||||||
|
import 'database.dart';
|
||||||
|
import 'settings.dart';
|
||||||
|
import 'source.dart';
|
||||||
|
|
||||||
|
final syncServiceProvider = Provider<SyncService>((ref) {
|
||||||
|
final db = ref.watch(databaseProvider);
|
||||||
|
final source = ref.watch(sourceProvider);
|
||||||
|
final sourceId = ref.watch(sourceIdProvider);
|
||||||
|
|
||||||
|
return SyncService(source: source, db: db, sourceId: sourceId);
|
||||||
|
});
|
||||||
5
lib/app/state/settings.dart
Normal file
5
lib/app/state/settings.dart
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
final sourceIdProvider = Provider<int>((ref) {
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
39
lib/app/state/source.dart
Normal file
39
lib/app/state/source.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
|
import '../../sources/music_source.dart';
|
||||||
|
import '../../sources/subsonic/client.dart';
|
||||||
|
import '../../sources/subsonic/source.dart';
|
||||||
|
import '../../util/http.dart';
|
||||||
|
import 'database.dart';
|
||||||
|
import 'settings.dart';
|
||||||
|
|
||||||
|
final _sourceProvider = FutureProvider<MusicSource>((ref) async {
|
||||||
|
final db = ref.watch(databaseProvider);
|
||||||
|
final sourceId = ref.watch(sourceIdProvider);
|
||||||
|
|
||||||
|
final query = db.sources.select().join([
|
||||||
|
leftOuterJoin(
|
||||||
|
db.subsonicSettings,
|
||||||
|
db.subsonicSettings.sourceId.equalsExp(db.sources.id),
|
||||||
|
),
|
||||||
|
])..where(db.sources.id.equals(sourceId));
|
||||||
|
|
||||||
|
final result = await query.getSingle();
|
||||||
|
final subsonicSettings = result.readTable(db.subsonicSettings);
|
||||||
|
|
||||||
|
return SubsonicSource(
|
||||||
|
SubsonicClient(
|
||||||
|
http: SubtracksHttpClient(),
|
||||||
|
address: subsonicSettings.address,
|
||||||
|
username: subsonicSettings.username,
|
||||||
|
password: subsonicSettings.password,
|
||||||
|
useTokenAuth: subsonicSettings.useTokenAuth,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
final sourceProvider = Provider<MusicSource>((ref) {
|
||||||
|
final source = ref.watch(_sourceProvider);
|
||||||
|
return source.requireValue;
|
||||||
|
});
|
||||||
@ -300,12 +300,12 @@ class SourcesCompanion extends UpdateCompanion<Source> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubsonicSourceOptions extends Table
|
class SubsonicSettings extends Table
|
||||||
with TableInfo<SubsonicSourceOptions, SubsonicSourceOption> {
|
with TableInfo<SubsonicSettings, SubsonicSetting> {
|
||||||
@override
|
@override
|
||||||
final GeneratedDatabase attachedDatabase;
|
final GeneratedDatabase attachedDatabase;
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
SubsonicSourceOptions(this.attachedDatabase, [this._alias]);
|
SubsonicSettings(this.attachedDatabase, [this._alias]);
|
||||||
static const VerificationMeta _sourceIdMeta = const VerificationMeta(
|
static const VerificationMeta _sourceIdMeta = const VerificationMeta(
|
||||||
'sourceId',
|
'sourceId',
|
||||||
);
|
);
|
||||||
@ -325,7 +325,7 @@ class SubsonicSourceOptions extends Table
|
|||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
$customConstraints: 'NOT NULL',
|
$customConstraints: 'NOT NULL',
|
||||||
).withConverter<Uri>(SubsonicSourceOptions.$converteraddress);
|
).withConverter<Uri>(SubsonicSettings.$converteraddress);
|
||||||
static const VerificationMeta _usernameMeta = const VerificationMeta(
|
static const VerificationMeta _usernameMeta = const VerificationMeta(
|
||||||
'username',
|
'username',
|
||||||
);
|
);
|
||||||
@ -372,10 +372,10 @@ class SubsonicSourceOptions extends Table
|
|||||||
String get aliasedName => _alias ?? actualTableName;
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
@override
|
@override
|
||||||
String get actualTableName => $name;
|
String get actualTableName => $name;
|
||||||
static const String $name = 'subsonic_source_options';
|
static const String $name = 'subsonic_settings';
|
||||||
@override
|
@override
|
||||||
VerificationContext validateIntegrity(
|
VerificationContext validateIntegrity(
|
||||||
Insertable<SubsonicSourceOption> instance, {
|
Insertable<SubsonicSetting> instance, {
|
||||||
bool isInserting = false,
|
bool isInserting = false,
|
||||||
}) {
|
}) {
|
||||||
final context = VerificationContext();
|
final context = VerificationContext();
|
||||||
@ -417,14 +417,14 @@ class SubsonicSourceOptions extends Table
|
|||||||
@override
|
@override
|
||||||
Set<GeneratedColumn> get $primaryKey => {sourceId};
|
Set<GeneratedColumn> get $primaryKey => {sourceId};
|
||||||
@override
|
@override
|
||||||
SubsonicSourceOption map(Map<String, dynamic> data, {String? tablePrefix}) {
|
SubsonicSetting map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
return SubsonicSourceOption(
|
return SubsonicSetting(
|
||||||
sourceId: attachedDatabase.typeMapping.read(
|
sourceId: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.int,
|
DriftSqlType.int,
|
||||||
data['${effectivePrefix}source_id'],
|
data['${effectivePrefix}source_id'],
|
||||||
)!,
|
)!,
|
||||||
address: SubsonicSourceOptions.$converteraddress.fromSql(
|
address: SubsonicSettings.$converteraddress.fromSql(
|
||||||
attachedDatabase.typeMapping.read(
|
attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}address'],
|
data['${effectivePrefix}address'],
|
||||||
@ -446,8 +446,8 @@ class SubsonicSourceOptions extends Table
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SubsonicSourceOptions createAlias(String alias) {
|
SubsonicSettings createAlias(String alias) {
|
||||||
return SubsonicSourceOptions(attachedDatabase, alias);
|
return SubsonicSettings(attachedDatabase, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeConverter<Uri, String> $converteraddress = const UriConverter();
|
static TypeConverter<Uri, String> $converteraddress = const UriConverter();
|
||||||
@ -459,14 +459,13 @@ class SubsonicSourceOptions extends Table
|
|||||||
bool get dontWriteConstraints => true;
|
bool get dontWriteConstraints => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubsonicSourceOption extends DataClass
|
class SubsonicSetting extends DataClass implements Insertable<SubsonicSetting> {
|
||||||
implements Insertable<SubsonicSourceOption> {
|
|
||||||
final int sourceId;
|
final int sourceId;
|
||||||
final Uri address;
|
final Uri address;
|
||||||
final String username;
|
final String username;
|
||||||
final String password;
|
final String password;
|
||||||
final bool useTokenAuth;
|
final bool useTokenAuth;
|
||||||
const SubsonicSourceOption({
|
const SubsonicSetting({
|
||||||
required this.sourceId,
|
required this.sourceId,
|
||||||
required this.address,
|
required this.address,
|
||||||
required this.username,
|
required this.username,
|
||||||
@ -479,7 +478,7 @@ class SubsonicSourceOption extends DataClass
|
|||||||
map['source_id'] = Variable<int>(sourceId);
|
map['source_id'] = Variable<int>(sourceId);
|
||||||
{
|
{
|
||||||
map['address'] = Variable<String>(
|
map['address'] = Variable<String>(
|
||||||
SubsonicSourceOptions.$converteraddress.toSql(address),
|
SubsonicSettings.$converteraddress.toSql(address),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
map['username'] = Variable<String>(username);
|
map['username'] = Variable<String>(username);
|
||||||
@ -488,8 +487,8 @@ class SubsonicSourceOption extends DataClass
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubsonicSourceOptionsCompanion toCompanion(bool nullToAbsent) {
|
SubsonicSettingsCompanion toCompanion(bool nullToAbsent) {
|
||||||
return SubsonicSourceOptionsCompanion(
|
return SubsonicSettingsCompanion(
|
||||||
sourceId: Value(sourceId),
|
sourceId: Value(sourceId),
|
||||||
address: Value(address),
|
address: Value(address),
|
||||||
username: Value(username),
|
username: Value(username),
|
||||||
@ -498,12 +497,12 @@ class SubsonicSourceOption extends DataClass
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory SubsonicSourceOption.fromJson(
|
factory SubsonicSetting.fromJson(
|
||||||
Map<String, dynamic> json, {
|
Map<String, dynamic> json, {
|
||||||
ValueSerializer? serializer,
|
ValueSerializer? serializer,
|
||||||
}) {
|
}) {
|
||||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
return SubsonicSourceOption(
|
return SubsonicSetting(
|
||||||
sourceId: serializer.fromJson<int>(json['source_id']),
|
sourceId: serializer.fromJson<int>(json['source_id']),
|
||||||
address: serializer.fromJson<Uri>(json['address']),
|
address: serializer.fromJson<Uri>(json['address']),
|
||||||
username: serializer.fromJson<String>(json['username']),
|
username: serializer.fromJson<String>(json['username']),
|
||||||
@ -523,21 +522,21 @@ class SubsonicSourceOption extends DataClass
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
SubsonicSourceOption copyWith({
|
SubsonicSetting copyWith({
|
||||||
int? sourceId,
|
int? sourceId,
|
||||||
Uri? address,
|
Uri? address,
|
||||||
String? username,
|
String? username,
|
||||||
String? password,
|
String? password,
|
||||||
bool? useTokenAuth,
|
bool? useTokenAuth,
|
||||||
}) => SubsonicSourceOption(
|
}) => SubsonicSetting(
|
||||||
sourceId: sourceId ?? this.sourceId,
|
sourceId: sourceId ?? this.sourceId,
|
||||||
address: address ?? this.address,
|
address: address ?? this.address,
|
||||||
username: username ?? this.username,
|
username: username ?? this.username,
|
||||||
password: password ?? this.password,
|
password: password ?? this.password,
|
||||||
useTokenAuth: useTokenAuth ?? this.useTokenAuth,
|
useTokenAuth: useTokenAuth ?? this.useTokenAuth,
|
||||||
);
|
);
|
||||||
SubsonicSourceOption copyWithCompanion(SubsonicSourceOptionsCompanion data) {
|
SubsonicSetting copyWithCompanion(SubsonicSettingsCompanion data) {
|
||||||
return SubsonicSourceOption(
|
return SubsonicSetting(
|
||||||
sourceId: data.sourceId.present ? data.sourceId.value : this.sourceId,
|
sourceId: data.sourceId.present ? data.sourceId.value : this.sourceId,
|
||||||
address: data.address.present ? data.address.value : this.address,
|
address: data.address.present ? data.address.value : this.address,
|
||||||
username: data.username.present ? data.username.value : this.username,
|
username: data.username.present ? data.username.value : this.username,
|
||||||
@ -550,7 +549,7 @@ class SubsonicSourceOption extends DataClass
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('SubsonicSourceOption(')
|
return (StringBuffer('SubsonicSetting(')
|
||||||
..write('sourceId: $sourceId, ')
|
..write('sourceId: $sourceId, ')
|
||||||
..write('address: $address, ')
|
..write('address: $address, ')
|
||||||
..write('username: $username, ')
|
..write('username: $username, ')
|
||||||
@ -566,7 +565,7 @@ class SubsonicSourceOption extends DataClass
|
|||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is SubsonicSourceOption &&
|
(other is SubsonicSetting &&
|
||||||
other.sourceId == this.sourceId &&
|
other.sourceId == this.sourceId &&
|
||||||
other.address == this.address &&
|
other.address == this.address &&
|
||||||
other.username == this.username &&
|
other.username == this.username &&
|
||||||
@ -574,21 +573,20 @@ class SubsonicSourceOption extends DataClass
|
|||||||
other.useTokenAuth == this.useTokenAuth);
|
other.useTokenAuth == this.useTokenAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubsonicSourceOptionsCompanion
|
class SubsonicSettingsCompanion extends UpdateCompanion<SubsonicSetting> {
|
||||||
extends UpdateCompanion<SubsonicSourceOption> {
|
|
||||||
final Value<int> sourceId;
|
final Value<int> sourceId;
|
||||||
final Value<Uri> address;
|
final Value<Uri> address;
|
||||||
final Value<String> username;
|
final Value<String> username;
|
||||||
final Value<String> password;
|
final Value<String> password;
|
||||||
final Value<bool> useTokenAuth;
|
final Value<bool> useTokenAuth;
|
||||||
const SubsonicSourceOptionsCompanion({
|
const SubsonicSettingsCompanion({
|
||||||
this.sourceId = const Value.absent(),
|
this.sourceId = const Value.absent(),
|
||||||
this.address = const Value.absent(),
|
this.address = const Value.absent(),
|
||||||
this.username = const Value.absent(),
|
this.username = const Value.absent(),
|
||||||
this.password = const Value.absent(),
|
this.password = const Value.absent(),
|
||||||
this.useTokenAuth = const Value.absent(),
|
this.useTokenAuth = const Value.absent(),
|
||||||
});
|
});
|
||||||
SubsonicSourceOptionsCompanion.insert({
|
SubsonicSettingsCompanion.insert({
|
||||||
this.sourceId = const Value.absent(),
|
this.sourceId = const Value.absent(),
|
||||||
required Uri address,
|
required Uri address,
|
||||||
required String username,
|
required String username,
|
||||||
@ -597,7 +595,7 @@ class SubsonicSourceOptionsCompanion
|
|||||||
}) : address = Value(address),
|
}) : address = Value(address),
|
||||||
username = Value(username),
|
username = Value(username),
|
||||||
password = Value(password);
|
password = Value(password);
|
||||||
static Insertable<SubsonicSourceOption> custom({
|
static Insertable<SubsonicSetting> custom({
|
||||||
Expression<int>? sourceId,
|
Expression<int>? sourceId,
|
||||||
Expression<String>? address,
|
Expression<String>? address,
|
||||||
Expression<String>? username,
|
Expression<String>? username,
|
||||||
@ -613,14 +611,14 @@ class SubsonicSourceOptionsCompanion
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SubsonicSourceOptionsCompanion copyWith({
|
SubsonicSettingsCompanion copyWith({
|
||||||
Value<int>? sourceId,
|
Value<int>? sourceId,
|
||||||
Value<Uri>? address,
|
Value<Uri>? address,
|
||||||
Value<String>? username,
|
Value<String>? username,
|
||||||
Value<String>? password,
|
Value<String>? password,
|
||||||
Value<bool>? useTokenAuth,
|
Value<bool>? useTokenAuth,
|
||||||
}) {
|
}) {
|
||||||
return SubsonicSourceOptionsCompanion(
|
return SubsonicSettingsCompanion(
|
||||||
sourceId: sourceId ?? this.sourceId,
|
sourceId: sourceId ?? this.sourceId,
|
||||||
address: address ?? this.address,
|
address: address ?? this.address,
|
||||||
username: username ?? this.username,
|
username: username ?? this.username,
|
||||||
@ -637,7 +635,7 @@ class SubsonicSourceOptionsCompanion
|
|||||||
}
|
}
|
||||||
if (address.present) {
|
if (address.present) {
|
||||||
map['address'] = Variable<String>(
|
map['address'] = Variable<String>(
|
||||||
SubsonicSourceOptions.$converteraddress.toSql(address.value),
|
SubsonicSettings.$converteraddress.toSql(address.value),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (username.present) {
|
if (username.present) {
|
||||||
@ -654,7 +652,7 @@ class SubsonicSourceOptionsCompanion
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('SubsonicSourceOptionsCompanion(')
|
return (StringBuffer('SubsonicSettingsCompanion(')
|
||||||
..write('sourceId: $sourceId, ')
|
..write('sourceId: $sourceId, ')
|
||||||
..write('address: $address, ')
|
..write('address: $address, ')
|
||||||
..write('username: $username, ')
|
..write('username: $username, ')
|
||||||
@ -2386,8 +2384,7 @@ abstract class _$SubtracksDatabase extends GeneratedDatabase {
|
|||||||
_$SubtracksDatabase(QueryExecutor e) : super(e);
|
_$SubtracksDatabase(QueryExecutor e) : super(e);
|
||||||
$SubtracksDatabaseManager get managers => $SubtracksDatabaseManager(this);
|
$SubtracksDatabaseManager get managers => $SubtracksDatabaseManager(this);
|
||||||
late final Sources sources = Sources(this);
|
late final Sources sources = Sources(this);
|
||||||
late final SubsonicSourceOptions subsonicSourceOptions =
|
late final SubsonicSettings subsonicSettings = SubsonicSettings(this);
|
||||||
SubsonicSourceOptions(this);
|
|
||||||
late final Artists artists = Artists(this);
|
late final Artists artists = Artists(this);
|
||||||
late final Index artistsSourceId = Index(
|
late final Index artistsSourceId = Index(
|
||||||
'artists_source_id',
|
'artists_source_id',
|
||||||
@ -2431,7 +2428,7 @@ abstract class _$SubtracksDatabase extends GeneratedDatabase {
|
|||||||
@override
|
@override
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities => [
|
List<DatabaseSchemaEntity> get allSchemaEntities => [
|
||||||
sources,
|
sources,
|
||||||
subsonicSourceOptions,
|
subsonicSettings,
|
||||||
artists,
|
artists,
|
||||||
artistsSourceId,
|
artistsSourceId,
|
||||||
albums,
|
albums,
|
||||||
@ -2453,7 +2450,7 @@ abstract class _$SubtracksDatabase extends GeneratedDatabase {
|
|||||||
'sources',
|
'sources',
|
||||||
limitUpdateKind: UpdateKind.delete,
|
limitUpdateKind: UpdateKind.delete,
|
||||||
),
|
),
|
||||||
result: [TableUpdate('subsonic_source_options', kind: UpdateKind.delete)],
|
result: [TableUpdate('subsonic_settings', kind: UpdateKind.delete)],
|
||||||
),
|
),
|
||||||
WritePropagation(
|
WritePropagation(
|
||||||
on: TableUpdateQuery.onTableName(
|
on: TableUpdateQuery.onTableName(
|
||||||
@ -2660,16 +2657,16 @@ typedef $SourcesProcessedTableManager =
|
|||||||
Source,
|
Source,
|
||||||
PrefetchHooks Function()
|
PrefetchHooks Function()
|
||||||
>;
|
>;
|
||||||
typedef $SubsonicSourceOptionsCreateCompanionBuilder =
|
typedef $SubsonicSettingsCreateCompanionBuilder =
|
||||||
SubsonicSourceOptionsCompanion Function({
|
SubsonicSettingsCompanion Function({
|
||||||
Value<int> sourceId,
|
Value<int> sourceId,
|
||||||
required Uri address,
|
required Uri address,
|
||||||
required String username,
|
required String username,
|
||||||
required String password,
|
required String password,
|
||||||
Value<bool> useTokenAuth,
|
Value<bool> useTokenAuth,
|
||||||
});
|
});
|
||||||
typedef $SubsonicSourceOptionsUpdateCompanionBuilder =
|
typedef $SubsonicSettingsUpdateCompanionBuilder =
|
||||||
SubsonicSourceOptionsCompanion Function({
|
SubsonicSettingsCompanion Function({
|
||||||
Value<int> sourceId,
|
Value<int> sourceId,
|
||||||
Value<Uri> address,
|
Value<Uri> address,
|
||||||
Value<String> username,
|
Value<String> username,
|
||||||
@ -2677,9 +2674,9 @@ typedef $SubsonicSourceOptionsUpdateCompanionBuilder =
|
|||||||
Value<bool> useTokenAuth,
|
Value<bool> useTokenAuth,
|
||||||
});
|
});
|
||||||
|
|
||||||
class $SubsonicSourceOptionsFilterComposer
|
class $SubsonicSettingsFilterComposer
|
||||||
extends Composer<_$SubtracksDatabase, SubsonicSourceOptions> {
|
extends Composer<_$SubtracksDatabase, SubsonicSettings> {
|
||||||
$SubsonicSourceOptionsFilterComposer({
|
$SubsonicSettingsFilterComposer({
|
||||||
required super.$db,
|
required super.$db,
|
||||||
required super.$table,
|
required super.$table,
|
||||||
super.joinBuilder,
|
super.joinBuilder,
|
||||||
@ -2713,9 +2710,9 @@ class $SubsonicSourceOptionsFilterComposer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $SubsonicSourceOptionsOrderingComposer
|
class $SubsonicSettingsOrderingComposer
|
||||||
extends Composer<_$SubtracksDatabase, SubsonicSourceOptions> {
|
extends Composer<_$SubtracksDatabase, SubsonicSettings> {
|
||||||
$SubsonicSourceOptionsOrderingComposer({
|
$SubsonicSettingsOrderingComposer({
|
||||||
required super.$db,
|
required super.$db,
|
||||||
required super.$table,
|
required super.$table,
|
||||||
super.joinBuilder,
|
super.joinBuilder,
|
||||||
@ -2748,9 +2745,9 @@ class $SubsonicSourceOptionsOrderingComposer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $SubsonicSourceOptionsAnnotationComposer
|
class $SubsonicSettingsAnnotationComposer
|
||||||
extends Composer<_$SubtracksDatabase, SubsonicSourceOptions> {
|
extends Composer<_$SubtracksDatabase, SubsonicSettings> {
|
||||||
$SubsonicSourceOptionsAnnotationComposer({
|
$SubsonicSettingsAnnotationComposer({
|
||||||
required super.$db,
|
required super.$db,
|
||||||
required super.$table,
|
required super.$table,
|
||||||
super.joinBuilder,
|
super.joinBuilder,
|
||||||
@ -2775,41 +2772,39 @@ class $SubsonicSourceOptionsAnnotationComposer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class $SubsonicSourceOptionsTableManager
|
class $SubsonicSettingsTableManager
|
||||||
extends
|
extends
|
||||||
RootTableManager<
|
RootTableManager<
|
||||||
_$SubtracksDatabase,
|
_$SubtracksDatabase,
|
||||||
SubsonicSourceOptions,
|
SubsonicSettings,
|
||||||
SubsonicSourceOption,
|
SubsonicSetting,
|
||||||
$SubsonicSourceOptionsFilterComposer,
|
$SubsonicSettingsFilterComposer,
|
||||||
$SubsonicSourceOptionsOrderingComposer,
|
$SubsonicSettingsOrderingComposer,
|
||||||
$SubsonicSourceOptionsAnnotationComposer,
|
$SubsonicSettingsAnnotationComposer,
|
||||||
$SubsonicSourceOptionsCreateCompanionBuilder,
|
$SubsonicSettingsCreateCompanionBuilder,
|
||||||
$SubsonicSourceOptionsUpdateCompanionBuilder,
|
$SubsonicSettingsUpdateCompanionBuilder,
|
||||||
(
|
(
|
||||||
SubsonicSourceOption,
|
SubsonicSetting,
|
||||||
BaseReferences<
|
BaseReferences<
|
||||||
_$SubtracksDatabase,
|
_$SubtracksDatabase,
|
||||||
SubsonicSourceOptions,
|
SubsonicSettings,
|
||||||
SubsonicSourceOption
|
SubsonicSetting
|
||||||
>,
|
>,
|
||||||
),
|
),
|
||||||
SubsonicSourceOption,
|
SubsonicSetting,
|
||||||
PrefetchHooks Function()
|
PrefetchHooks Function()
|
||||||
> {
|
> {
|
||||||
$SubsonicSourceOptionsTableManager(
|
$SubsonicSettingsTableManager(_$SubtracksDatabase db, SubsonicSettings table)
|
||||||
_$SubtracksDatabase db,
|
: super(
|
||||||
SubsonicSourceOptions table,
|
|
||||||
) : super(
|
|
||||||
TableManagerState(
|
TableManagerState(
|
||||||
db: db,
|
db: db,
|
||||||
table: table,
|
table: table,
|
||||||
createFilteringComposer: () =>
|
createFilteringComposer: () =>
|
||||||
$SubsonicSourceOptionsFilterComposer($db: db, $table: table),
|
$SubsonicSettingsFilterComposer($db: db, $table: table),
|
||||||
createOrderingComposer: () =>
|
createOrderingComposer: () =>
|
||||||
$SubsonicSourceOptionsOrderingComposer($db: db, $table: table),
|
$SubsonicSettingsOrderingComposer($db: db, $table: table),
|
||||||
createComputedFieldComposer: () =>
|
createComputedFieldComposer: () =>
|
||||||
$SubsonicSourceOptionsAnnotationComposer($db: db, $table: table),
|
$SubsonicSettingsAnnotationComposer($db: db, $table: table),
|
||||||
updateCompanionCallback:
|
updateCompanionCallback:
|
||||||
({
|
({
|
||||||
Value<int> sourceId = const Value.absent(),
|
Value<int> sourceId = const Value.absent(),
|
||||||
@ -2817,7 +2812,7 @@ class $SubsonicSourceOptionsTableManager
|
|||||||
Value<String> username = const Value.absent(),
|
Value<String> username = const Value.absent(),
|
||||||
Value<String> password = const Value.absent(),
|
Value<String> password = const Value.absent(),
|
||||||
Value<bool> useTokenAuth = const Value.absent(),
|
Value<bool> useTokenAuth = const Value.absent(),
|
||||||
}) => SubsonicSourceOptionsCompanion(
|
}) => SubsonicSettingsCompanion(
|
||||||
sourceId: sourceId,
|
sourceId: sourceId,
|
||||||
address: address,
|
address: address,
|
||||||
username: username,
|
username: username,
|
||||||
@ -2831,7 +2826,7 @@ class $SubsonicSourceOptionsTableManager
|
|||||||
required String username,
|
required String username,
|
||||||
required String password,
|
required String password,
|
||||||
Value<bool> useTokenAuth = const Value.absent(),
|
Value<bool> useTokenAuth = const Value.absent(),
|
||||||
}) => SubsonicSourceOptionsCompanion.insert(
|
}) => SubsonicSettingsCompanion.insert(
|
||||||
sourceId: sourceId,
|
sourceId: sourceId,
|
||||||
address: address,
|
address: address,
|
||||||
username: username,
|
username: username,
|
||||||
@ -2846,25 +2841,21 @@ class $SubsonicSourceOptionsTableManager
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef $SubsonicSourceOptionsProcessedTableManager =
|
typedef $SubsonicSettingsProcessedTableManager =
|
||||||
ProcessedTableManager<
|
ProcessedTableManager<
|
||||||
_$SubtracksDatabase,
|
_$SubtracksDatabase,
|
||||||
SubsonicSourceOptions,
|
SubsonicSettings,
|
||||||
SubsonicSourceOption,
|
SubsonicSetting,
|
||||||
$SubsonicSourceOptionsFilterComposer,
|
$SubsonicSettingsFilterComposer,
|
||||||
$SubsonicSourceOptionsOrderingComposer,
|
$SubsonicSettingsOrderingComposer,
|
||||||
$SubsonicSourceOptionsAnnotationComposer,
|
$SubsonicSettingsAnnotationComposer,
|
||||||
$SubsonicSourceOptionsCreateCompanionBuilder,
|
$SubsonicSettingsCreateCompanionBuilder,
|
||||||
$SubsonicSourceOptionsUpdateCompanionBuilder,
|
$SubsonicSettingsUpdateCompanionBuilder,
|
||||||
(
|
(
|
||||||
SubsonicSourceOption,
|
SubsonicSetting,
|
||||||
BaseReferences<
|
BaseReferences<_$SubtracksDatabase, SubsonicSettings, SubsonicSetting>,
|
||||||
_$SubtracksDatabase,
|
|
||||||
SubsonicSourceOptions,
|
|
||||||
SubsonicSourceOption
|
|
||||||
>,
|
|
||||||
),
|
),
|
||||||
SubsonicSourceOption,
|
SubsonicSetting,
|
||||||
PrefetchHooks Function()
|
PrefetchHooks Function()
|
||||||
>;
|
>;
|
||||||
typedef $ArtistsCreateCompanionBuilder =
|
typedef $ArtistsCreateCompanionBuilder =
|
||||||
@ -4137,8 +4128,8 @@ class $SubtracksDatabaseManager {
|
|||||||
final _$SubtracksDatabase _db;
|
final _$SubtracksDatabase _db;
|
||||||
$SubtracksDatabaseManager(this._db);
|
$SubtracksDatabaseManager(this._db);
|
||||||
$SourcesTableManager get sources => $SourcesTableManager(_db, _db.sources);
|
$SourcesTableManager get sources => $SourcesTableManager(_db, _db.sources);
|
||||||
$SubsonicSourceOptionsTableManager get subsonicSourceOptions =>
|
$SubsonicSettingsTableManager get subsonicSettings =>
|
||||||
$SubsonicSourceOptionsTableManager(_db, _db.subsonicSourceOptions);
|
$SubsonicSettingsTableManager(_db, _db.subsonicSettings);
|
||||||
$ArtistsTableManager get artists => $ArtistsTableManager(_db, _db.artists);
|
$ArtistsTableManager get artists => $ArtistsTableManager(_db, _db.artists);
|
||||||
$AlbumsTableManager get albums => $AlbumsTableManager(_db, _db.albums);
|
$AlbumsTableManager get albums => $AlbumsTableManager(_db, _db.albums);
|
||||||
$PlaylistsTableManager get playlists =>
|
$PlaylistsTableManager get playlists =>
|
||||||
|
|||||||
@ -51,7 +51,7 @@ CREATE TABLE sources(
|
|||||||
created_at DATETIME NOT NULL DEFAULT (strftime('%s', CURRENT_TIMESTAMP))
|
created_at DATETIME NOT NULL DEFAULT (strftime('%s', CURRENT_TIMESTAMP))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE subsonic_source_options(
|
CREATE TABLE subsonic_settings(
|
||||||
source_id INT NOT NULL PRIMARY KEY,
|
source_id INT NOT NULL PRIMARY KEY,
|
||||||
address TEXT NOT NULL MAPPED BY `const UriConverter()`,
|
address TEXT NOT NULL MAPPED BY `const UriConverter()`,
|
||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
|
|||||||
52
lib/images/images.dart
Normal file
52
lib/images/images.dart
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:octo_image/octo_image.dart';
|
||||||
|
|
||||||
|
import '../app/state/source.dart';
|
||||||
|
|
||||||
|
class CoverArtImage extends HookConsumerWidget {
|
||||||
|
const CoverArtImage({
|
||||||
|
super.key,
|
||||||
|
this.coverArt,
|
||||||
|
this.thumbnail = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? coverArt;
|
||||||
|
final bool thumbnail;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final source = ref.watch(sourceProvider);
|
||||||
|
|
||||||
|
final imageProviderKeys = [source, coverArt, thumbnail];
|
||||||
|
final buildImageProvider = useCallback(
|
||||||
|
() => CachedNetworkImageProvider(
|
||||||
|
coverArt != null
|
||||||
|
? source.coverArtUri(coverArt!, thumbnail: thumbnail).toString()
|
||||||
|
: 'https://placehold.net/400x400.png',
|
||||||
|
),
|
||||||
|
imageProviderKeys,
|
||||||
|
);
|
||||||
|
|
||||||
|
final imageProvider = useState(buildImageProvider());
|
||||||
|
useEffect(
|
||||||
|
() {
|
||||||
|
imageProvider.value = buildImageProvider();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
imageProviderKeys,
|
||||||
|
);
|
||||||
|
|
||||||
|
return OctoImage(
|
||||||
|
image: imageProvider.value,
|
||||||
|
placeholderBuilder: (context) => Icon(Symbols.album_rounded),
|
||||||
|
errorBuilder: (context, error, trace) => Icon(Icons.error),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
fadeOutDuration: Duration(milliseconds: 100),
|
||||||
|
fadeInDuration: Duration(milliseconds: 200),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,34 @@
|
|||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
import 'app/router.dart';
|
import 'app/router.dart';
|
||||||
|
import 'database/database.dart';
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
final db = SubtracksDatabase();
|
||||||
|
await db
|
||||||
|
.into(db.sources)
|
||||||
|
.insertOnConflictUpdate(
|
||||||
|
SourcesCompanion.insert(
|
||||||
|
id: Value(1),
|
||||||
|
name: 'test navidrome',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await db
|
||||||
|
.into(db.subsonicSettings)
|
||||||
|
.insertOnConflictUpdate(
|
||||||
|
SubsonicSettingsCompanion.insert(
|
||||||
|
sourceId: Value(1),
|
||||||
|
address: Uri.parse('http://10.0.2.2:4533'),
|
||||||
|
username: 'admin',
|
||||||
|
password: 'password',
|
||||||
|
useTokenAuth: Value(true),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
void main() {
|
|
||||||
runApp(const MainApp());
|
runApp(const MainApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,11 +37,13 @@ class MainApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp.router(
|
return ProviderScope(
|
||||||
themeMode: ThemeMode.dark,
|
child: MaterialApp.router(
|
||||||
darkTheme: ThemeData.dark(useMaterial3: true),
|
themeMode: ThemeMode.dark,
|
||||||
debugShowCheckedModeBanner: false,
|
darkTheme: ThemeData.dark(useMaterial3: true),
|
||||||
routerConfig: router,
|
debugShowCheckedModeBanner: false,
|
||||||
|
routerConfig: router,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,5 +12,5 @@ abstract class MusicSource {
|
|||||||
Uri streamUri(String songId);
|
Uri streamUri(String songId);
|
||||||
Uri downloadUri(String songId);
|
Uri downloadUri(String songId);
|
||||||
|
|
||||||
Uri coverArtUri(String coverArtId, {bool thumbnail = true});
|
Uri coverArtUri(String coverArt, {bool thumbnail = false});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -151,8 +151,8 @@ class SubsonicSource implements MusicSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Uri coverArtUri(String id, {bool thumbnail = true}) {
|
Uri coverArtUri(String coverArt, {bool thumbnail = false}) {
|
||||||
final opts = {'id': id};
|
final opts = {'id': coverArt};
|
||||||
if (thumbnail) {
|
if (thumbnail) {
|
||||||
opts['size'] = 256.toString();
|
opts['size'] = 256.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
6
lib/util/http.dart
Normal file
6
lib/util/http.dart
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:http/http.dart';
|
||||||
|
|
||||||
|
class SubtracksHttpClient extends BaseClient {
|
||||||
|
@override
|
||||||
|
Future<StreamedResponse> send(BaseRequest request) => request.send();
|
||||||
|
}
|
||||||
@ -625,7 +625,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
octo_image:
|
octo_image:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: octo_image
|
name: octo_image
|
||||||
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
|
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
|
||||||
|
|||||||
@ -24,6 +24,7 @@ dependencies:
|
|||||||
infinite_scroll_pagination: ^5.1.1
|
infinite_scroll_pagination: ^5.1.1
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
material_symbols_icons: ^4.2874.0
|
material_symbols_icons: ^4.2874.0
|
||||||
|
octo_image: ^2.1.0
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
pool: ^1.5.2
|
pool: ^1.5.2
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user