✨ Lyrics offset
This commit is contained in:
@@ -12,6 +12,9 @@ class Tracks extends Table {
|
|||||||
TextColumn get path => text().unique()();
|
TextColumn get path => text().unique()();
|
||||||
TextColumn get artUri => text().nullable()(); // Path to local cover art
|
TextColumn get artUri => text().nullable()(); // Path to local cover art
|
||||||
TextColumn get lyrics => text().nullable()(); // JSON formatted lyrics
|
TextColumn get lyrics => text().nullable()(); // JSON formatted lyrics
|
||||||
|
IntColumn get lyricsOffset => integer().withDefault(
|
||||||
|
const Constant(0),
|
||||||
|
)(); // Offset in milliseconds for lyrics timing
|
||||||
DateTimeColumn get addedAt => dateTime().withDefault(currentDateAndTime)();
|
DateTimeColumn get addedAt => dateTime().withDefault(currentDateAndTime)();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +37,7 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
AppDatabase() : super(_openConnection());
|
AppDatabase() : super(_openConnection());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 4; // Bump version for lyrics column
|
int get schemaVersion => 5; // Bump version for lyricsOffset column
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
@@ -53,6 +56,9 @@ class AppDatabase extends _$AppDatabase {
|
|||||||
if (from < 4) {
|
if (from < 4) {
|
||||||
await m.addColumn(tracks, tracks.lyrics);
|
await m.addColumn(tracks, tracks.lyrics);
|
||||||
}
|
}
|
||||||
|
if (from < 5) {
|
||||||
|
await m.addColumn(tracks, tracks.lyricsOffset);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,18 @@ class $TracksTable extends Tracks with TableInfo<$TracksTable, Track> {
|
|||||||
type: DriftSqlType.string,
|
type: DriftSqlType.string,
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
);
|
);
|
||||||
|
static const VerificationMeta _lyricsOffsetMeta = const VerificationMeta(
|
||||||
|
'lyricsOffset',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<int> lyricsOffset = GeneratedColumn<int>(
|
||||||
|
'lyrics_offset',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: DriftSqlType.int,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultValue: const Constant(0),
|
||||||
|
);
|
||||||
static const VerificationMeta _addedAtMeta = const VerificationMeta(
|
static const VerificationMeta _addedAtMeta = const VerificationMeta(
|
||||||
'addedAt',
|
'addedAt',
|
||||||
);
|
);
|
||||||
@@ -109,6 +121,7 @@ class $TracksTable extends Tracks with TableInfo<$TracksTable, Track> {
|
|||||||
path,
|
path,
|
||||||
artUri,
|
artUri,
|
||||||
lyrics,
|
lyrics,
|
||||||
|
lyricsOffset,
|
||||||
addedAt,
|
addedAt,
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
@@ -172,6 +185,15 @@ class $TracksTable extends Tracks with TableInfo<$TracksTable, Track> {
|
|||||||
lyrics.isAcceptableOrUnknown(data['lyrics']!, _lyricsMeta),
|
lyrics.isAcceptableOrUnknown(data['lyrics']!, _lyricsMeta),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('lyrics_offset')) {
|
||||||
|
context.handle(
|
||||||
|
_lyricsOffsetMeta,
|
||||||
|
lyricsOffset.isAcceptableOrUnknown(
|
||||||
|
data['lyrics_offset']!,
|
||||||
|
_lyricsOffsetMeta,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (data.containsKey('added_at')) {
|
if (data.containsKey('added_at')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_addedAtMeta,
|
_addedAtMeta,
|
||||||
@@ -219,6 +241,10 @@ class $TracksTable extends Tracks with TableInfo<$TracksTable, Track> {
|
|||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
data['${effectivePrefix}lyrics'],
|
data['${effectivePrefix}lyrics'],
|
||||||
),
|
),
|
||||||
|
lyricsOffset: attachedDatabase.typeMapping.read(
|
||||||
|
DriftSqlType.int,
|
||||||
|
data['${effectivePrefix}lyrics_offset'],
|
||||||
|
)!,
|
||||||
addedAt: attachedDatabase.typeMapping.read(
|
addedAt: attachedDatabase.typeMapping.read(
|
||||||
DriftSqlType.dateTime,
|
DriftSqlType.dateTime,
|
||||||
data['${effectivePrefix}added_at'],
|
data['${effectivePrefix}added_at'],
|
||||||
@@ -241,6 +267,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
final String path;
|
final String path;
|
||||||
final String? artUri;
|
final String? artUri;
|
||||||
final String? lyrics;
|
final String? lyrics;
|
||||||
|
final int lyricsOffset;
|
||||||
final DateTime addedAt;
|
final DateTime addedAt;
|
||||||
const Track({
|
const Track({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -251,6 +278,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
required this.path,
|
required this.path,
|
||||||
this.artUri,
|
this.artUri,
|
||||||
this.lyrics,
|
this.lyrics,
|
||||||
|
required this.lyricsOffset,
|
||||||
required this.addedAt,
|
required this.addedAt,
|
||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
@@ -274,6 +302,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
if (!nullToAbsent || lyrics != null) {
|
if (!nullToAbsent || lyrics != null) {
|
||||||
map['lyrics'] = Variable<String>(lyrics);
|
map['lyrics'] = Variable<String>(lyrics);
|
||||||
}
|
}
|
||||||
|
map['lyrics_offset'] = Variable<int>(lyricsOffset);
|
||||||
map['added_at'] = Variable<DateTime>(addedAt);
|
map['added_at'] = Variable<DateTime>(addedAt);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@@ -298,6 +327,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
lyrics: lyrics == null && nullToAbsent
|
lyrics: lyrics == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(lyrics),
|
: Value(lyrics),
|
||||||
|
lyricsOffset: Value(lyricsOffset),
|
||||||
addedAt: Value(addedAt),
|
addedAt: Value(addedAt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -316,6 +346,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
path: serializer.fromJson<String>(json['path']),
|
path: serializer.fromJson<String>(json['path']),
|
||||||
artUri: serializer.fromJson<String?>(json['artUri']),
|
artUri: serializer.fromJson<String?>(json['artUri']),
|
||||||
lyrics: serializer.fromJson<String?>(json['lyrics']),
|
lyrics: serializer.fromJson<String?>(json['lyrics']),
|
||||||
|
lyricsOffset: serializer.fromJson<int>(json['lyricsOffset']),
|
||||||
addedAt: serializer.fromJson<DateTime>(json['addedAt']),
|
addedAt: serializer.fromJson<DateTime>(json['addedAt']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -331,6 +362,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
'path': serializer.toJson<String>(path),
|
'path': serializer.toJson<String>(path),
|
||||||
'artUri': serializer.toJson<String?>(artUri),
|
'artUri': serializer.toJson<String?>(artUri),
|
||||||
'lyrics': serializer.toJson<String?>(lyrics),
|
'lyrics': serializer.toJson<String?>(lyrics),
|
||||||
|
'lyricsOffset': serializer.toJson<int>(lyricsOffset),
|
||||||
'addedAt': serializer.toJson<DateTime>(addedAt),
|
'addedAt': serializer.toJson<DateTime>(addedAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -344,6 +376,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
String? path,
|
String? path,
|
||||||
Value<String?> artUri = const Value.absent(),
|
Value<String?> artUri = const Value.absent(),
|
||||||
Value<String?> lyrics = const Value.absent(),
|
Value<String?> lyrics = const Value.absent(),
|
||||||
|
int? lyricsOffset,
|
||||||
DateTime? addedAt,
|
DateTime? addedAt,
|
||||||
}) => Track(
|
}) => Track(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
@@ -354,6 +387,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
path: path ?? this.path,
|
path: path ?? this.path,
|
||||||
artUri: artUri.present ? artUri.value : this.artUri,
|
artUri: artUri.present ? artUri.value : this.artUri,
|
||||||
lyrics: lyrics.present ? lyrics.value : this.lyrics,
|
lyrics: lyrics.present ? lyrics.value : this.lyrics,
|
||||||
|
lyricsOffset: lyricsOffset ?? this.lyricsOffset,
|
||||||
addedAt: addedAt ?? this.addedAt,
|
addedAt: addedAt ?? this.addedAt,
|
||||||
);
|
);
|
||||||
Track copyWithCompanion(TracksCompanion data) {
|
Track copyWithCompanion(TracksCompanion data) {
|
||||||
@@ -366,6 +400,9 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
path: data.path.present ? data.path.value : this.path,
|
path: data.path.present ? data.path.value : this.path,
|
||||||
artUri: data.artUri.present ? data.artUri.value : this.artUri,
|
artUri: data.artUri.present ? data.artUri.value : this.artUri,
|
||||||
lyrics: data.lyrics.present ? data.lyrics.value : this.lyrics,
|
lyrics: data.lyrics.present ? data.lyrics.value : this.lyrics,
|
||||||
|
lyricsOffset: data.lyricsOffset.present
|
||||||
|
? data.lyricsOffset.value
|
||||||
|
: this.lyricsOffset,
|
||||||
addedAt: data.addedAt.present ? data.addedAt.value : this.addedAt,
|
addedAt: data.addedAt.present ? data.addedAt.value : this.addedAt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -381,6 +418,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
..write('path: $path, ')
|
..write('path: $path, ')
|
||||||
..write('artUri: $artUri, ')
|
..write('artUri: $artUri, ')
|
||||||
..write('lyrics: $lyrics, ')
|
..write('lyrics: $lyrics, ')
|
||||||
|
..write('lyricsOffset: $lyricsOffset, ')
|
||||||
..write('addedAt: $addedAt')
|
..write('addedAt: $addedAt')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
@@ -396,6 +434,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
path,
|
path,
|
||||||
artUri,
|
artUri,
|
||||||
lyrics,
|
lyrics,
|
||||||
|
lyricsOffset,
|
||||||
addedAt,
|
addedAt,
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
@@ -410,6 +449,7 @@ class Track extends DataClass implements Insertable<Track> {
|
|||||||
other.path == this.path &&
|
other.path == this.path &&
|
||||||
other.artUri == this.artUri &&
|
other.artUri == this.artUri &&
|
||||||
other.lyrics == this.lyrics &&
|
other.lyrics == this.lyrics &&
|
||||||
|
other.lyricsOffset == this.lyricsOffset &&
|
||||||
other.addedAt == this.addedAt);
|
other.addedAt == this.addedAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,6 +462,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
final Value<String> path;
|
final Value<String> path;
|
||||||
final Value<String?> artUri;
|
final Value<String?> artUri;
|
||||||
final Value<String?> lyrics;
|
final Value<String?> lyrics;
|
||||||
|
final Value<int> lyricsOffset;
|
||||||
final Value<DateTime> addedAt;
|
final Value<DateTime> addedAt;
|
||||||
const TracksCompanion({
|
const TracksCompanion({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
@@ -432,6 +473,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
this.path = const Value.absent(),
|
this.path = const Value.absent(),
|
||||||
this.artUri = const Value.absent(),
|
this.artUri = const Value.absent(),
|
||||||
this.lyrics = const Value.absent(),
|
this.lyrics = const Value.absent(),
|
||||||
|
this.lyricsOffset = const Value.absent(),
|
||||||
this.addedAt = const Value.absent(),
|
this.addedAt = const Value.absent(),
|
||||||
});
|
});
|
||||||
TracksCompanion.insert({
|
TracksCompanion.insert({
|
||||||
@@ -443,6 +485,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
required String path,
|
required String path,
|
||||||
this.artUri = const Value.absent(),
|
this.artUri = const Value.absent(),
|
||||||
this.lyrics = const Value.absent(),
|
this.lyrics = const Value.absent(),
|
||||||
|
this.lyricsOffset = const Value.absent(),
|
||||||
this.addedAt = const Value.absent(),
|
this.addedAt = const Value.absent(),
|
||||||
}) : title = Value(title),
|
}) : title = Value(title),
|
||||||
path = Value(path);
|
path = Value(path);
|
||||||
@@ -455,6 +498,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
Expression<String>? path,
|
Expression<String>? path,
|
||||||
Expression<String>? artUri,
|
Expression<String>? artUri,
|
||||||
Expression<String>? lyrics,
|
Expression<String>? lyrics,
|
||||||
|
Expression<int>? lyricsOffset,
|
||||||
Expression<DateTime>? addedAt,
|
Expression<DateTime>? addedAt,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
@@ -466,6 +510,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
if (path != null) 'path': path,
|
if (path != null) 'path': path,
|
||||||
if (artUri != null) 'art_uri': artUri,
|
if (artUri != null) 'art_uri': artUri,
|
||||||
if (lyrics != null) 'lyrics': lyrics,
|
if (lyrics != null) 'lyrics': lyrics,
|
||||||
|
if (lyricsOffset != null) 'lyrics_offset': lyricsOffset,
|
||||||
if (addedAt != null) 'added_at': addedAt,
|
if (addedAt != null) 'added_at': addedAt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -479,6 +524,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
Value<String>? path,
|
Value<String>? path,
|
||||||
Value<String?>? artUri,
|
Value<String?>? artUri,
|
||||||
Value<String?>? lyrics,
|
Value<String?>? lyrics,
|
||||||
|
Value<int>? lyricsOffset,
|
||||||
Value<DateTime>? addedAt,
|
Value<DateTime>? addedAt,
|
||||||
}) {
|
}) {
|
||||||
return TracksCompanion(
|
return TracksCompanion(
|
||||||
@@ -490,6 +536,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
path: path ?? this.path,
|
path: path ?? this.path,
|
||||||
artUri: artUri ?? this.artUri,
|
artUri: artUri ?? this.artUri,
|
||||||
lyrics: lyrics ?? this.lyrics,
|
lyrics: lyrics ?? this.lyrics,
|
||||||
|
lyricsOffset: lyricsOffset ?? this.lyricsOffset,
|
||||||
addedAt: addedAt ?? this.addedAt,
|
addedAt: addedAt ?? this.addedAt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -521,6 +568,9 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
if (lyrics.present) {
|
if (lyrics.present) {
|
||||||
map['lyrics'] = Variable<String>(lyrics.value);
|
map['lyrics'] = Variable<String>(lyrics.value);
|
||||||
}
|
}
|
||||||
|
if (lyricsOffset.present) {
|
||||||
|
map['lyrics_offset'] = Variable<int>(lyricsOffset.value);
|
||||||
|
}
|
||||||
if (addedAt.present) {
|
if (addedAt.present) {
|
||||||
map['added_at'] = Variable<DateTime>(addedAt.value);
|
map['added_at'] = Variable<DateTime>(addedAt.value);
|
||||||
}
|
}
|
||||||
@@ -538,6 +588,7 @@ class TracksCompanion extends UpdateCompanion<Track> {
|
|||||||
..write('path: $path, ')
|
..write('path: $path, ')
|
||||||
..write('artUri: $artUri, ')
|
..write('artUri: $artUri, ')
|
||||||
..write('lyrics: $lyrics, ')
|
..write('lyrics: $lyrics, ')
|
||||||
|
..write('lyricsOffset: $lyricsOffset, ')
|
||||||
..write('addedAt: $addedAt')
|
..write('addedAt: $addedAt')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
@@ -1135,6 +1186,7 @@ typedef $$TracksTableCreateCompanionBuilder =
|
|||||||
required String path,
|
required String path,
|
||||||
Value<String?> artUri,
|
Value<String?> artUri,
|
||||||
Value<String?> lyrics,
|
Value<String?> lyrics,
|
||||||
|
Value<int> lyricsOffset,
|
||||||
Value<DateTime> addedAt,
|
Value<DateTime> addedAt,
|
||||||
});
|
});
|
||||||
typedef $$TracksTableUpdateCompanionBuilder =
|
typedef $$TracksTableUpdateCompanionBuilder =
|
||||||
@@ -1147,6 +1199,7 @@ typedef $$TracksTableUpdateCompanionBuilder =
|
|||||||
Value<String> path,
|
Value<String> path,
|
||||||
Value<String?> artUri,
|
Value<String?> artUri,
|
||||||
Value<String?> lyrics,
|
Value<String?> lyrics,
|
||||||
|
Value<int> lyricsOffset,
|
||||||
Value<DateTime> addedAt,
|
Value<DateTime> addedAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1224,6 +1277,11 @@ class $$TracksTableFilterComposer
|
|||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnFilters<int> get lyricsOffset => $composableBuilder(
|
||||||
|
column: $table.lyricsOffset,
|
||||||
|
builder: (column) => ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnFilters<DateTime> get addedAt => $composableBuilder(
|
ColumnFilters<DateTime> get addedAt => $composableBuilder(
|
||||||
column: $table.addedAt,
|
column: $table.addedAt,
|
||||||
builder: (column) => ColumnFilters(column),
|
builder: (column) => ColumnFilters(column),
|
||||||
@@ -1304,6 +1362,11 @@ class $$TracksTableOrderingComposer
|
|||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ColumnOrderings<int> get lyricsOffset => $composableBuilder(
|
||||||
|
column: $table.lyricsOffset,
|
||||||
|
builder: (column) => ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
ColumnOrderings<DateTime> get addedAt => $composableBuilder(
|
ColumnOrderings<DateTime> get addedAt => $composableBuilder(
|
||||||
column: $table.addedAt,
|
column: $table.addedAt,
|
||||||
builder: (column) => ColumnOrderings(column),
|
builder: (column) => ColumnOrderings(column),
|
||||||
@@ -1343,6 +1406,11 @@ class $$TracksTableAnnotationComposer
|
|||||||
GeneratedColumn<String> get lyrics =>
|
GeneratedColumn<String> get lyrics =>
|
||||||
$composableBuilder(column: $table.lyrics, builder: (column) => column);
|
$composableBuilder(column: $table.lyrics, builder: (column) => column);
|
||||||
|
|
||||||
|
GeneratedColumn<int> get lyricsOffset => $composableBuilder(
|
||||||
|
column: $table.lyricsOffset,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
|
||||||
GeneratedColumn<DateTime> get addedAt =>
|
GeneratedColumn<DateTime> get addedAt =>
|
||||||
$composableBuilder(column: $table.addedAt, builder: (column) => column);
|
$composableBuilder(column: $table.addedAt, builder: (column) => column);
|
||||||
|
|
||||||
@@ -1408,6 +1476,7 @@ class $$TracksTableTableManager
|
|||||||
Value<String> path = const Value.absent(),
|
Value<String> path = const Value.absent(),
|
||||||
Value<String?> artUri = const Value.absent(),
|
Value<String?> artUri = const Value.absent(),
|
||||||
Value<String?> lyrics = const Value.absent(),
|
Value<String?> lyrics = const Value.absent(),
|
||||||
|
Value<int> lyricsOffset = const Value.absent(),
|
||||||
Value<DateTime> addedAt = const Value.absent(),
|
Value<DateTime> addedAt = const Value.absent(),
|
||||||
}) => TracksCompanion(
|
}) => TracksCompanion(
|
||||||
id: id,
|
id: id,
|
||||||
@@ -1418,6 +1487,7 @@ class $$TracksTableTableManager
|
|||||||
path: path,
|
path: path,
|
||||||
artUri: artUri,
|
artUri: artUri,
|
||||||
lyrics: lyrics,
|
lyrics: lyrics,
|
||||||
|
lyricsOffset: lyricsOffset,
|
||||||
addedAt: addedAt,
|
addedAt: addedAt,
|
||||||
),
|
),
|
||||||
createCompanionCallback:
|
createCompanionCallback:
|
||||||
@@ -1430,6 +1500,7 @@ class $$TracksTableTableManager
|
|||||||
required String path,
|
required String path,
|
||||||
Value<String?> artUri = const Value.absent(),
|
Value<String?> artUri = const Value.absent(),
|
||||||
Value<String?> lyrics = const Value.absent(),
|
Value<String?> lyrics = const Value.absent(),
|
||||||
|
Value<int> lyricsOffset = const Value.absent(),
|
||||||
Value<DateTime> addedAt = const Value.absent(),
|
Value<DateTime> addedAt = const Value.absent(),
|
||||||
}) => TracksCompanion.insert(
|
}) => TracksCompanion.insert(
|
||||||
id: id,
|
id: id,
|
||||||
@@ -1440,6 +1511,7 @@ class $$TracksTableTableManager
|
|||||||
path: path,
|
path: path,
|
||||||
artUri: artUri,
|
artUri: artUri,
|
||||||
lyrics: lyrics,
|
lyrics: lyrics,
|
||||||
|
lyricsOffset: lyricsOffset,
|
||||||
addedAt: addedAt,
|
addedAt: addedAt,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ final class TrackRepositoryProvider
|
|||||||
TrackRepository create() => TrackRepository();
|
TrackRepository create() => TrackRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$trackRepositoryHash() => r'ad77006c472739d9d5067d394d6c5a3437535a11';
|
String _$trackRepositoryHash() => r'244e5fc82fcaa34cb1276a41e4158a0eefcc7258';
|
||||||
|
|
||||||
abstract class _$TrackRepository extends $AsyncNotifier<void> {
|
abstract class _$TrackRepository extends $AsyncNotifier<void> {
|
||||||
FutureOr<void> build();
|
FutureOr<void> build();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ final class LyricsFetcherProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$lyricsFetcherHash() => r'52296b2ccb55755ec5ad7ab751fe974dc3c64024';
|
String _$lyricsFetcherHash() => r'071b83cb569812a6f90d42d7b7cf6954ac9631d7';
|
||||||
|
|
||||||
abstract class _$LyricsFetcher extends $Notifier<LyricsFetcherState> {
|
abstract class _$LyricsFetcher extends $Notifier<LyricsFetcherState> {
|
||||||
LyricsFetcherState build();
|
LyricsFetcherState build();
|
||||||
|
|||||||
@@ -372,7 +372,11 @@ class _PlayerLyrics extends HookConsumerWidget {
|
|||||||
if (lyricsData.type == 'timed') {
|
if (lyricsData.type == 'timed') {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
_TimedLyricsView(lyrics: lyricsData, player: player),
|
_TimedLyricsView(
|
||||||
|
lyrics: lyricsData,
|
||||||
|
player: player,
|
||||||
|
trackPath: trackPath!,
|
||||||
|
),
|
||||||
_LyricsRefreshButton(trackPath: trackPath!),
|
_LyricsRefreshButton(trackPath: trackPath!),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -603,12 +607,14 @@ class _LyricsRefreshButton extends HookConsumerWidget {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Refresh Lyrics'),
|
title: const Text('Lyrics Options'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Text('Choose an action:'),
|
const Text('Choose an action:'),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -661,7 +667,9 @@ class _LyricsRefreshButton extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
debugPrint('Cleared lyrics from database');
|
debugPrint('Cleared lyrics from database');
|
||||||
// Invalidate the track provider to refresh the UI
|
// Invalidate the track provider to refresh the UI
|
||||||
ref.invalidate(trackByPathProvider(trackPath));
|
ref.invalidate(
|
||||||
|
trackByPathProvider(trackPath),
|
||||||
|
);
|
||||||
debugPrint(
|
debugPrint(
|
||||||
'Invalidated track provider for $trackPath',
|
'Invalidated track provider for $trackPath',
|
||||||
);
|
);
|
||||||
@@ -673,6 +681,30 @@ class _LyricsRefreshButton extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
icon: const Icon(Icons.tune),
|
||||||
|
label: const Text('Adjust Timing'),
|
||||||
|
onPressed: trackAsync.maybeWhen(
|
||||||
|
data: (track) => track != null
|
||||||
|
? () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
_showLyricsOffsetDialog(
|
||||||
|
context,
|
||||||
|
ref,
|
||||||
|
track,
|
||||||
|
trackPath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
orElse: () => null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
@@ -684,27 +716,77 @@ class _LyricsRefreshButton extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showLyricsOffsetDialog(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
db.Track track,
|
||||||
|
String trackPath,
|
||||||
|
) {
|
||||||
|
final offsetController = TextEditingController(
|
||||||
|
text: track.lyricsOffset.toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Adjust Lyrics Timing'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Enter offset in milliseconds.\nPositive values delay lyrics, negative values advance them.',
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: offsetController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Offset (ms)',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final offset = int.tryParse(offsetController.text) ?? 0;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
final database = ref.read(databaseProvider);
|
||||||
|
await (database.update(database.tracks)
|
||||||
|
..where((t) => t.id.equals(track.id)))
|
||||||
|
.write(db.TracksCompanion(lyricsOffset: drift.Value(offset)));
|
||||||
|
|
||||||
|
// Invalidate the track provider to refresh the UI
|
||||||
|
ref.invalidate(trackByPathProvider(trackPath));
|
||||||
|
},
|
||||||
|
child: const Text('Save'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provider to fetch a single track by path
|
class _TimedLyricsView extends HookConsumerWidget {
|
||||||
final trackByPathProvider = FutureProvider.family<db.Track?, String>((
|
|
||||||
ref,
|
|
||||||
trackPath,
|
|
||||||
) async {
|
|
||||||
final database = ref.watch(databaseProvider);
|
|
||||||
return (database.select(
|
|
||||||
database.tracks,
|
|
||||||
)..where((t) => t.path.equals(trackPath))).getSingleOrNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
class _TimedLyricsView extends HookWidget {
|
|
||||||
final LyricsData lyrics;
|
final LyricsData lyrics;
|
||||||
final Player player;
|
final Player player;
|
||||||
|
final String trackPath;
|
||||||
|
|
||||||
const _TimedLyricsView({required this.lyrics, required this.player});
|
const _TimedLyricsView({
|
||||||
|
required this.lyrics,
|
||||||
|
required this.player,
|
||||||
|
required this.trackPath,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final isDesktop = MediaQuery.sizeOf(context).width > 640;
|
final isDesktop = MediaQuery.sizeOf(context).width > 640;
|
||||||
|
|
||||||
final listController = useMemoized(() => ListController(), []);
|
final listController = useMemoized(() => ListController(), []);
|
||||||
@@ -715,12 +797,21 @@ class _TimedLyricsView extends HookWidget {
|
|||||||
);
|
);
|
||||||
final previousIndex = useState(-1);
|
final previousIndex = useState(-1);
|
||||||
|
|
||||||
|
// Get track data to access lyrics offset
|
||||||
|
final trackAsync = ref.watch(trackByPathProvider(trackPath));
|
||||||
|
|
||||||
|
return trackAsync.when(
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (e, _) => Center(child: Text('Error: $e')),
|
||||||
|
data: (track) {
|
||||||
|
final lyricsOffset = track?.lyricsOffset ?? 0;
|
||||||
|
|
||||||
return StreamBuilder<Duration>(
|
return StreamBuilder<Duration>(
|
||||||
stream: player.stream.position,
|
stream: player.stream.position,
|
||||||
initialData: player.state.position,
|
initialData: player.state.position,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final position = snapshot.data ?? Duration.zero;
|
final position = snapshot.data ?? Duration.zero;
|
||||||
final positionMs = position.inMilliseconds;
|
final positionMs = position.inMilliseconds + lyricsOffset;
|
||||||
|
|
||||||
// Find current line index
|
// Find current line index
|
||||||
int currentIndex = 0;
|
int currentIndex = 0;
|
||||||
@@ -796,9 +887,10 @@ class _TimedLyricsView extends HookWidget {
|
|||||||
: FontWeight.normal,
|
: FontWeight.normal,
|
||||||
color: isActive
|
color: isActive
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(
|
: Theme.of(context)
|
||||||
context,
|
.colorScheme
|
||||||
).colorScheme.onSurface.withOpacity(0.7),
|
.onSurface
|
||||||
|
.withOpacity(0.7),
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -843,7 +935,9 @@ class _TimedLyricsView extends HookWidget {
|
|||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
fontSize: isActive ? 20 : 16,
|
fontSize: isActive ? 20 : 16,
|
||||||
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
|
fontWeight: isActive
|
||||||
|
? FontWeight.bold
|
||||||
|
: FontWeight.normal,
|
||||||
color: isActive
|
color: isActive
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Theme.of(
|
: Theme.of(
|
||||||
@@ -859,6 +953,8 @@ class _TimedLyricsView extends HookWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,11 +1050,11 @@ class _PlayerControls extends HookWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_formatDuration(
|
formatDuration(
|
||||||
Duration(milliseconds: currentValue.toInt()),
|
Duration(milliseconds: currentValue.toInt()),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(_formatDuration(totalDuration)),
|
Text(formatDuration(totalDuration)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -1117,9 +1213,20 @@ class _PlayerControls extends HookWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _formatDuration(Duration d) {
|
String formatDuration(Duration d) {
|
||||||
final minutes = d.inMinutes;
|
final minutes = d.inMinutes;
|
||||||
final seconds = d.inSeconds % 60;
|
final seconds = d.inSeconds % 60;
|
||||||
return '$minutes:${seconds.toString().padLeft(2, '0')}';
|
return '$minutes:${seconds.toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provider to fetch a single track by path
|
||||||
|
final trackByPathProvider = FutureProvider.family<db.Track?, String>((
|
||||||
|
ref,
|
||||||
|
trackPath,
|
||||||
|
) async {
|
||||||
|
final database = ref.watch(databaseProvider);
|
||||||
|
return (database.select(
|
||||||
|
database.tracks,
|
||||||
|
)..where((t) => t.path.equals(trackPath))).getSingleOrNull();
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user