mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-12-08 00:17:29 +00:00
Compare commits
5 Commits
65fe1282fb
...
a370ef55e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a370ef55e4 | ||
|
|
d3edf07ac9 | ||
|
|
8fc319d980 | ||
|
|
e986baa0aa | ||
|
|
3ff0f6dd27 |
@ -3,4 +3,17 @@
|
|||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<application
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:fullBackupContent="false"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name_en"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
|
<!-- Disable Impeller -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||||
|
android:value="false" />
|
||||||
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
1
drift_schemas/app_db/drift_schema_v5.json
Normal file
1
drift_schemas/app_db/drift_schema_v5.json
Normal file
File diff suppressed because one or more lines are too long
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/material.dart' show showModalBottomSheet;
|
|
||||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
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:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
@ -26,7 +25,7 @@ class AdaptiveMenuButton<T> extends MenuButton {
|
|||||||
|
|
||||||
/// An adaptive widget that shows a [PopupMenuButton] when screen size is above
|
/// An adaptive widget that shows a [PopupMenuButton] when screen size is above
|
||||||
/// or equal to 640px
|
/// or equal to 640px
|
||||||
/// In smaller screen, a [IconButton] with a [showModalBottomSheet] is shown
|
/// In smaller screen, a [IconButton] with a [openDrawer] is shown
|
||||||
class AdaptivePopSheetList<T> extends StatelessWidget {
|
class AdaptivePopSheetList<T> extends StatelessWidget {
|
||||||
final List<AdaptiveMenuButton<T>> Function(BuildContext context) items;
|
final List<AdaptiveMenuButton<T>> Function(BuildContext context) items;
|
||||||
final Widget? icon;
|
final Widget? icon;
|
||||||
@ -102,15 +101,13 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
showModalBottomSheet(
|
await openDrawer(
|
||||||
context: context,
|
context: context,
|
||||||
enableDrag: true,
|
draggable: true,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
useRootNavigator: true,
|
position: OverlayPosition.bottom,
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: context.theme.borderRadiusMd,
|
borderRadius: context.theme.borderRadiusMd,
|
||||||
),
|
transformBackdrop: false,
|
||||||
backgroundColor: context.theme.colorScheme.card,
|
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final children = childrenModified(context);
|
final children = childrenModified(context);
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
@ -127,7 +124,7 @@ class AdaptivePopSheetList<T> extends StatelessWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
data.onPressed?.call(context);
|
data.onPressed?.call(context);
|
||||||
if (data.autoClose) {
|
if (data.autoClose) {
|
||||||
Navigator.of(context).pop();
|
closeDrawer(context);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leading: data.leading,
|
leading: data.leading,
|
||||||
|
|||||||
@ -74,6 +74,26 @@ class TrackPresentationActionsSection extends HookConsumerWidget {
|
|||||||
ref.watch(presentationStateProvider(options.collection).notifier);
|
ref.watch(presentationStateProvider(options.collection).notifier);
|
||||||
final selectedTracks = state.selectedTracks;
|
final selectedTracks = state.selectedTracks;
|
||||||
|
|
||||||
|
Future<void> actionDownloadTracks({
|
||||||
|
required BuildContext context,
|
||||||
|
required List<Track> tracks,
|
||||||
|
required String action,
|
||||||
|
}) async {
|
||||||
|
final confirmed = audioSource == AudioSource.piped ||
|
||||||
|
(await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return const ConfirmDownloadDialog();
|
||||||
|
},
|
||||||
|
) ??
|
||||||
|
false);
|
||||||
|
if (confirmed != true) return;
|
||||||
|
downloader.batchAddToQueue(tracks);
|
||||||
|
notifier.deselectAllTracks();
|
||||||
|
if (!context.mounted) return;
|
||||||
|
showToastForAction(context, action, tracks.length);
|
||||||
|
}
|
||||||
|
|
||||||
return AdaptivePopSheetList(
|
return AdaptivePopSheetList(
|
||||||
tooltip: context.l10n.more_actions,
|
tooltip: context.l10n.more_actions,
|
||||||
headings: [
|
headings: [
|
||||||
@ -95,22 +115,12 @@ class TrackPresentationActionsSection extends HookConsumerWidget {
|
|||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "download":
|
case "download":
|
||||||
{
|
await actionDownloadTracks(
|
||||||
final confirmed = audioSource == AudioSource.piped ||
|
|
||||||
(await showDialog<bool?>(
|
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
tracks: tracks,
|
||||||
return const ConfirmDownloadDialog();
|
action: action,
|
||||||
},
|
);
|
||||||
) ??
|
|
||||||
false);
|
|
||||||
if (confirmed != true) return;
|
|
||||||
downloader.batchAddToQueue(tracks);
|
|
||||||
notifier.deselectAllTracks();
|
|
||||||
if (!context.mounted) return;
|
|
||||||
showToastForAction(context, action, tracks.length);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case "add-to-playlist":
|
case "add-to-playlist":
|
||||||
{
|
{
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
|||||||
@ -57,7 +57,7 @@ class TrackPresentationTopSection extends HookConsumerWidget {
|
|||||||
Tooltip(
|
Tooltip(
|
||||||
tooltip: TooltipContainer(
|
tooltip: TooltipContainer(
|
||||||
child: Text(context.l10n.shuffle_playlist),
|
child: Text(context.l10n.shuffle_playlist),
|
||||||
),
|
).call,
|
||||||
child: IconButton.secondary(
|
child: IconButton.secondary(
|
||||||
icon: isLoading
|
icon: isLoading
|
||||||
? const Center(
|
? const Center(
|
||||||
@ -73,7 +73,7 @@ class TrackPresentationTopSection extends HookConsumerWidget {
|
|||||||
Tooltip(
|
Tooltip(
|
||||||
tooltip: TooltipContainer(
|
tooltip: TooltipContainer(
|
||||||
child: Text(context.l10n.add_to_queue),
|
child: Text(context.l10n.add_to_queue),
|
||||||
),
|
).call,
|
||||||
child: IconButton.secondary(
|
child: IconButton.secondary(
|
||||||
icon: const Icon(SpotubeIcons.queueAdd),
|
icon: const Icon(SpotubeIcons.queueAdd),
|
||||||
enabled: !isLoading && !isActive,
|
enabled: !isLoading && !isActive,
|
||||||
@ -126,7 +126,7 @@ class TrackPresentationTopSection extends HookConsumerWidget {
|
|||||||
Tooltip(
|
Tooltip(
|
||||||
tooltip: TooltipContainer(
|
tooltip: TooltipContainer(
|
||||||
child: Text(context.l10n.share),
|
child: Text(context.l10n.share),
|
||||||
),
|
).call,
|
||||||
child: IconButton.outline(
|
child: IconButton.outline(
|
||||||
icon: const Icon(SpotubeIcons.share),
|
icon: const Icon(SpotubeIcons.share),
|
||||||
size: ButtonSize.small,
|
size: ButtonSize.small,
|
||||||
|
|||||||
@ -91,24 +91,14 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
) {
|
) {
|
||||||
/// showDialog doesn't work for some reason. So we have to
|
/// showDialog doesn't work for some reason. So we have to
|
||||||
/// manually push a Dialog Route in the Navigator to get it working
|
/// manually push a Dialog Route in the Navigator to get it working
|
||||||
Navigator.push(
|
showDialog(
|
||||||
context,
|
|
||||||
DialogRoute(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
transitionBuilder: (context, animation, secondaryAnimation, child) {
|
|
||||||
return FadeTransition(opacity: animation, child: child);
|
|
||||||
},
|
|
||||||
context: context,
|
context: context,
|
||||||
barrierColor: Colors.black.withValues(alpha: 0.5),
|
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Center(
|
return PlaylistAddTrackDialog(
|
||||||
child: PlaylistAddTrackDialog(
|
|
||||||
tracks: [track],
|
tracks: [track],
|
||||||
openFromPlaylist: playlistId,
|
openFromPlaylist: playlistId,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,6 +328,7 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: icon ?? const Icon(SpotubeIcons.moreHorizontal),
|
icon: icon ?? const Icon(SpotubeIcons.moreHorizontal),
|
||||||
|
variance: ButtonVariance.outline,
|
||||||
headings: [
|
headings: [
|
||||||
Basic(
|
Basic(
|
||||||
leading: AspectRatio(
|
leading: AspectRatio(
|
||||||
|
|||||||
@ -62,7 +62,7 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
AppDatabase() : super(_openConnection());
|
AppDatabase() : super(_openConnection());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 4;
|
int get schemaVersion => 5;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
@ -87,6 +87,33 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
schema.preferencesTable.youtubeClientEngine,
|
schema.preferencesTable.youtubeClientEngine,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
from4To5: (m, schema) async {
|
||||||
|
final columnName = schema.preferencesTable.accentColorScheme
|
||||||
|
.escapedNameFor(SqlDialect.sqlite);
|
||||||
|
final columnNameOld =
|
||||||
|
'"${schema.preferencesTable.accentColorScheme.name}_old"';
|
||||||
|
final tableName = schema.preferencesTable.actualTableName;
|
||||||
|
await customStatement(
|
||||||
|
"ALTER TABLE $tableName "
|
||||||
|
"RENAME COLUMN $columnName to $columnNameOld",
|
||||||
|
);
|
||||||
|
await customStatement(
|
||||||
|
"ALTER TABLE $tableName "
|
||||||
|
"ADD COLUMN $columnName TEXT NOT NULL DEFAULT 'Orange:0xFFf97315'",
|
||||||
|
);
|
||||||
|
await customStatement(
|
||||||
|
"UPDATE $tableName "
|
||||||
|
"SET $columnName = $columnNameOld",
|
||||||
|
);
|
||||||
|
await customStatement(
|
||||||
|
"ALTER TABLE $tableName "
|
||||||
|
"DROP COLUMN $columnNameOld",
|
||||||
|
);
|
||||||
|
await customStatement(
|
||||||
|
"UPDATE $tableName "
|
||||||
|
"SET $columnName = 'Orange:0xFFf97315' WHERE $columnName = 'Blue:0xFF2196F3'",
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -666,7 +666,7 @@ class $PreferencesTableTable extends PreferencesTable
|
|||||||
'accent_color_scheme', aliasedName, false,
|
'accent_color_scheme', aliasedName, false,
|
||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultValue: const Constant("Blue:0xFF2196F3"))
|
defaultValue: const Constant("Orange:0xFFf97315"))
|
||||||
.withConverter<SpotubeColor>(
|
.withConverter<SpotubeColor>(
|
||||||
$PreferencesTableTable.$converteraccentColorScheme);
|
$PreferencesTableTable.$converteraccentColorScheme);
|
||||||
static const VerificationMeta _layoutModeMeta =
|
static const VerificationMeta _layoutModeMeta =
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import 'package:drift/internal/versioned_schema.dart' as i0;
|
import 'package:drift/internal/versioned_schema.dart' as i0;
|
||||||
import 'package:drift/drift.dart' as i1;
|
import 'package:drift/drift.dart' as i1;
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/services/sourced_track/enums.dart'; // ignore_for_file: type=lint,unused_import
|
import 'package:spotube/services/sourced_track/enums.dart'; // ignore_for_file: type=lint,unused_import
|
||||||
@ -1188,10 +1188,232 @@ i1.GeneratedColumn<String> _column_54(String aliasedName) =>
|
|||||||
i1.GeneratedColumn<String>('youtube_client_engine', aliasedName, false,
|
i1.GeneratedColumn<String>('youtube_client_engine', aliasedName, false,
|
||||||
type: i1.DriftSqlType.string,
|
type: i1.DriftSqlType.string,
|
||||||
defaultValue: Constant(YoutubeClientEngine.youtubeExplode.name));
|
defaultValue: Constant(YoutubeClientEngine.youtubeExplode.name));
|
||||||
|
|
||||||
|
final class Schema5 extends i0.VersionedSchema {
|
||||||
|
Schema5({required super.database}) : super(version: 5);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
authenticationTable,
|
||||||
|
blacklistTable,
|
||||||
|
preferencesTable,
|
||||||
|
scrobblerTable,
|
||||||
|
skipSegmentTable,
|
||||||
|
sourceMatchTable,
|
||||||
|
audioPlayerStateTable,
|
||||||
|
playlistTable,
|
||||||
|
playlistMediaTable,
|
||||||
|
historyTable,
|
||||||
|
lyricsTable,
|
||||||
|
uniqueBlacklist,
|
||||||
|
uniqTrackMatch,
|
||||||
|
];
|
||||||
|
late final Shape0 authenticationTable = Shape0(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'authentication_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_2,
|
||||||
|
_column_3,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape1 blacklistTable = Shape1(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'blacklist_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_4,
|
||||||
|
_column_5,
|
||||||
|
_column_6,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape12 preferencesTable = Shape12(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'preferences_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_7,
|
||||||
|
_column_8,
|
||||||
|
_column_9,
|
||||||
|
_column_10,
|
||||||
|
_column_11,
|
||||||
|
_column_12,
|
||||||
|
_column_13,
|
||||||
|
_column_14,
|
||||||
|
_column_15,
|
||||||
|
_column_55,
|
||||||
|
_column_17,
|
||||||
|
_column_18,
|
||||||
|
_column_19,
|
||||||
|
_column_20,
|
||||||
|
_column_21,
|
||||||
|
_column_22,
|
||||||
|
_column_23,
|
||||||
|
_column_24,
|
||||||
|
_column_25,
|
||||||
|
_column_26,
|
||||||
|
_column_54,
|
||||||
|
_column_27,
|
||||||
|
_column_28,
|
||||||
|
_column_29,
|
||||||
|
_column_30,
|
||||||
|
_column_31,
|
||||||
|
_column_53,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape3 scrobblerTable = Shape3(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'scrobbler_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_32,
|
||||||
|
_column_33,
|
||||||
|
_column_34,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape4 skipSegmentTable = Shape4(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'skip_segment_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_35,
|
||||||
|
_column_36,
|
||||||
|
_column_37,
|
||||||
|
_column_32,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape5 sourceMatchTable = Shape5(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'source_match_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_37,
|
||||||
|
_column_38,
|
||||||
|
_column_39,
|
||||||
|
_column_32,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape6 audioPlayerStateTable = Shape6(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'audio_player_state_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_40,
|
||||||
|
_column_41,
|
||||||
|
_column_42,
|
||||||
|
_column_43,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape7 playlistTable = Shape7(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'playlist_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_44,
|
||||||
|
_column_45,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape8 playlistMediaTable = Shape8(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'playlist_media_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_46,
|
||||||
|
_column_47,
|
||||||
|
_column_48,
|
||||||
|
_column_49,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape9 historyTable = Shape9(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'history_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_32,
|
||||||
|
_column_50,
|
||||||
|
_column_51,
|
||||||
|
_column_52,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
late final Shape10 lyricsTable = Shape10(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'lyrics_table',
|
||||||
|
withoutRowId: false,
|
||||||
|
isStrict: false,
|
||||||
|
tableConstraints: [],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_37,
|
||||||
|
_column_52,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null);
|
||||||
|
final i1.Index uniqueBlacklist = i1.Index('unique_blacklist',
|
||||||
|
'CREATE UNIQUE INDEX unique_blacklist ON blacklist_table (element_type, element_id)');
|
||||||
|
final i1.Index uniqTrackMatch = i1.Index('uniq_track_match',
|
||||||
|
'CREATE UNIQUE INDEX uniq_track_match ON source_match_table (track_id, source_id, source_type)');
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_55(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>('accent_color_scheme', aliasedName, false,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultValue: const Constant("Orange:0xFFf97315"));
|
||||||
i0.MigrationStepWithVersion migrationSteps({
|
i0.MigrationStepWithVersion migrationSteps({
|
||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||||
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
@ -1210,6 +1432,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from3To4(migrator, schema);
|
await from3To4(migrator, schema);
|
||||||
return 4;
|
return 4;
|
||||||
|
case 4:
|
||||||
|
final schema = Schema5(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from4To5(migrator, schema);
|
||||||
|
return 5;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
@ -1220,10 +1447,12 @@ i1.OnUpgrade stepByStep({
|
|||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||||
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
|
||||||
}) =>
|
}) =>
|
||||||
i0.VersionedSchema.stepByStepHelper(
|
i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
from1To2: from1To2,
|
from1To2: from1To2,
|
||||||
from2To3: from2To3,
|
from2To3: from2To3,
|
||||||
from3To4: from3To4,
|
from3To4: from3To4,
|
||||||
|
from4To5: from4To5,
|
||||||
));
|
));
|
||||||
|
|||||||
@ -79,7 +79,7 @@ class PreferencesTable extends Table {
|
|||||||
TextColumn get closeBehavior => textEnum<CloseBehavior>()
|
TextColumn get closeBehavior => textEnum<CloseBehavior>()
|
||||||
.withDefault(Constant(CloseBehavior.close.name))();
|
.withDefault(Constant(CloseBehavior.close.name))();
|
||||||
TextColumn get accentColorScheme => text()
|
TextColumn get accentColorScheme => text()
|
||||||
.withDefault(const Constant("Blue:0xFF2196F3"))
|
.withDefault(const Constant("Orange:0xFFf97315"))
|
||||||
.map(const SpotubeColorConverter())();
|
.map(const SpotubeColorConverter())();
|
||||||
TextColumn get layoutMode =>
|
TextColumn get layoutMode =>
|
||||||
textEnum<LayoutMode>().withDefault(Constant(LayoutMode.adaptive.name))();
|
textEnum<LayoutMode>().withDefault(Constant(LayoutMode.adaptive.name))();
|
||||||
@ -130,7 +130,7 @@ class PreferencesTable extends Table {
|
|||||||
systemTitleBar: false,
|
systemTitleBar: false,
|
||||||
skipNonMusic: false,
|
skipNonMusic: false,
|
||||||
closeBehavior: CloseBehavior.close,
|
closeBehavior: CloseBehavior.close,
|
||||||
accentColorScheme: SpotubeColor(Colors.blue.value, name: "Blue"),
|
accentColorScheme: SpotubeColor(Colors.orange.value, name: "Orange"),
|
||||||
layoutMode: LayoutMode.adaptive,
|
layoutMode: LayoutMode.adaptive,
|
||||||
locale: const Locale("system", "system"),
|
locale: const Locale("system", "system"),
|
||||||
market: Market.US,
|
market: Market.US,
|
||||||
|
|||||||
@ -132,7 +132,7 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
Tooltip(
|
Tooltip(
|
||||||
tooltip: TooltipContainer(
|
tooltip: TooltipContainer(
|
||||||
child: Text(context.l10n.details),
|
child: Text(context.l10n.details),
|
||||||
),
|
).call,
|
||||||
child: IconButton.ghost(
|
child: IconButton.ghost(
|
||||||
icon: const Icon(SpotubeIcons.info, size: 18),
|
icon: const Icon(SpotubeIcons.info, size: 18),
|
||||||
onPressed: currentTrack == null
|
onPressed: currentTrack == null
|
||||||
|
|||||||
@ -82,7 +82,7 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (showQueue)
|
if (showQueue)
|
||||||
Tooltip(
|
Tooltip(
|
||||||
tooltip: TooltipContainer(child: Text(context.l10n.queue)),
|
tooltip: TooltipContainer(child: Text(context.l10n.queue)).call,
|
||||||
child: IconButton.ghost(
|
child: IconButton.ghost(
|
||||||
icon: const Icon(SpotubeIcons.queue),
|
icon: const Icon(SpotubeIcons.queue),
|
||||||
enabled: playlist.activeTrack != null,
|
enabled: playlist.activeTrack != null,
|
||||||
@ -119,7 +119,8 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
if (!isLocalTrack)
|
if (!isLocalTrack)
|
||||||
Tooltip(
|
Tooltip(
|
||||||
tooltip: TooltipContainer(
|
tooltip: TooltipContainer(
|
||||||
child: Text(context.l10n.alternative_track_sources)),
|
child: Text(context.l10n.alternative_track_sources),
|
||||||
|
).call,
|
||||||
child: IconButton.ghost(
|
child: IconButton.ghost(
|
||||||
enabled: playlist.activeTrack != null,
|
enabled: playlist.activeTrack != null,
|
||||||
icon: const Icon(SpotubeIcons.alternativeRoute),
|
icon: const Icon(SpotubeIcons.alternativeRoute),
|
||||||
@ -160,7 +161,8 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
else
|
else
|
||||||
Tooltip(
|
Tooltip(
|
||||||
tooltip:
|
tooltip:
|
||||||
TooltipContainer(child: Text(context.l10n.download_track)),
|
TooltipContainer(child: Text(context.l10n.download_track))
|
||||||
|
.call,
|
||||||
child: IconButton.ghost(
|
child: IconButton.ghost(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
isDownloaded ? SpotubeIcons.done : SpotubeIcons.download,
|
isDownloaded ? SpotubeIcons.done : SpotubeIcons.download,
|
||||||
|
|||||||
@ -138,8 +138,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 {
|
||||||
@ -180,7 +182,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,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -90,9 +90,9 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
|
|||||||
Future<void> reset() async {
|
Future<void> reset() async {
|
||||||
final db = ref.read(databaseProvider);
|
final db = ref.read(databaseProvider);
|
||||||
|
|
||||||
final query = db.update(db.preferencesTable)..where((t) => t.id.equals(0));
|
final query = db.update(db.preferencesTable);
|
||||||
|
|
||||||
await query.replace(PreferencesTableCompanion.insert());
|
await query.replace(PreferencesTableCompanion.insert(id: const Value(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<String> getMusicCacheDir() async {
|
static Future<String> getMusicCacheDir() async {
|
||||||
|
|||||||
@ -251,19 +251,18 @@ class YoutubeSourcedTrack extends SourcedTrack {
|
|||||||
.map((YoutubeVideoInfo videoInfo) {
|
.map((YoutubeVideoInfo videoInfo) {
|
||||||
final ytWords = videoInfo.title
|
final ytWords = videoInfo.title
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replaceAll(RegExp(r'[^a-zA-Z0-9\s]+'), '')
|
.replaceAll(RegExp(r'[^\p{L}\p{N}\p{Z}]+', unicode: true), '')
|
||||||
.split(RegExp(r'\s+'))
|
.split(RegExp(r'\p{Z}+', unicode: true))
|
||||||
.where((item) => item.isNotEmpty);
|
.where((item) => item.isNotEmpty);
|
||||||
final spWords = track.name!
|
final spWords = track.name!
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.replaceAll(RegExp(r'\((.*)\)'), '')
|
.replaceAll(RegExp(r'[^\p{L}\p{N}\p{Z}]+', unicode: true), '')
|
||||||
.replaceAll(RegExp(r'[^a-zA-Z0-9\s]+'), '')
|
.split(RegExp(r'\p{Z}+', unicode: true))
|
||||||
.split(RegExp(r'\s+'))
|
|
||||||
.where((item) => item.isNotEmpty);
|
.where((item) => item.isNotEmpty);
|
||||||
// Word match to filter out unrelated results
|
// Single word and duration match with 3 second tolerance
|
||||||
final matchCount =
|
if (ytWords.any((word) => spWords.contains(word)) &&
|
||||||
ytWords.where((word) => spWords.contains(word)).length;
|
(videoInfo.duration - track.duration!)
|
||||||
if (matchCount > spWords.length ~/ 2) {
|
.abs().inMilliseconds <= 3000) {
|
||||||
return videoInfo;
|
return videoInfo;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -3,27 +3,30 @@
|
|||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift/internal/migrations.dart';
|
import 'package:drift/internal/migrations.dart';
|
||||||
import 'schema_v4.dart' as v4;
|
|
||||||
import 'schema_v3.dart' as v3;
|
import 'schema_v3.dart' as v3;
|
||||||
import 'schema_v2.dart' as v2;
|
import 'schema_v5.dart' as v5;
|
||||||
import 'schema_v1.dart' as v1;
|
import 'schema_v1.dart' as v1;
|
||||||
|
import 'schema_v2.dart' as v2;
|
||||||
|
import 'schema_v4.dart' as v4;
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 4:
|
|
||||||
return v4.DatabaseAtV4(db);
|
|
||||||
case 3:
|
case 3:
|
||||||
return v3.DatabaseAtV3(db);
|
return v3.DatabaseAtV3(db);
|
||||||
case 2:
|
case 5:
|
||||||
return v2.DatabaseAtV2(db);
|
return v5.DatabaseAtV5(db);
|
||||||
case 1:
|
case 1:
|
||||||
return v1.DatabaseAtV1(db);
|
return v1.DatabaseAtV1(db);
|
||||||
|
case 2:
|
||||||
|
return v2.DatabaseAtV2(db);
|
||||||
|
case 4:
|
||||||
|
return v4.DatabaseAtV4(db);
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3, 4];
|
static const versions = const [1, 2, 3, 4, 5];
|
||||||
}
|
}
|
||||||
|
|||||||
3433
test/drift/app_db/generated/schema_v5.dart
Normal file
3433
test/drift/app_db/generated/schema_v5.dart
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user