refactor: use drift for skip segments and source matches

This commit is contained in:
Kingkor Roy Tirtho 2024-06-14 21:24:42 +06:00
parent 3fb003ea60
commit 52d4f60ccc
14 changed files with 1019 additions and 351 deletions

View File

@ -22,8 +22,6 @@ import 'package:spotube/provider/server/bonsoir.dart';
import 'package:spotube/provider/server/server.dart'; import 'package:spotube/provider/server/server.dart';
import 'package:spotube/provider/tray_manager/tray_manager.dart'; import 'package:spotube/provider/tray_manager/tray_manager.dart';
import 'package:spotube/l10n/l10n.dart'; import 'package:spotube/l10n/l10n.dart';
import 'package:spotube/models/skip_segment.dart';
import 'package:spotube/models/source_match.dart';
import 'package:spotube/provider/connect/clients.dart'; import 'package:spotube/provider/connect/clients.dart';
import 'package:spotube/provider/palette_provider.dart'; import 'package:spotube/provider/palette_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
@ -84,23 +82,6 @@ Future<void> main(List<String> rawArgs) async {
Hive.init(hiveCacheDir); Hive.init(hiveCacheDir);
Hive.registerAdapter(SkipSegmentAdapter());
Hive.registerAdapter(SourceMatchAdapter());
Hive.registerAdapter(SourceTypeAdapter());
// Cache versioning entities with Adapter
SourceMatch.version = 'v1';
SkipSegment.version = 'v1';
await Hive.openLazyBox<SourceMatch>(
SourceMatch.boxName,
path: hiveCacheDir,
);
await Hive.openLazyBox(
SkipSegment.boxName,
path: hiveCacheDir,
);
await PersistedStateNotifier.initializeBoxes( await PersistedStateNotifier.initializeBoxes(
path: hiveCacheDir, path: hiveCacheDir,
); );

View File

@ -17,11 +17,14 @@ import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
part 'database.g.dart'; part 'database.g.dart';
part 'tables/preferences.dart'; part 'tables/preferences.dart';
part 'tables/source_match.dart';
part 'tables/skip_segment.dart';
part 'typeconverters/color.dart'; part 'typeconverters/color.dart';
part 'typeconverters/locale.dart'; part 'typeconverters/locale.dart';
part 'typeconverters/string_list.dart'; part 'typeconverters/string_list.dart';
@DriftDatabase(tables: [PreferencesTable]) @DriftDatabase(tables: [PreferencesTable, SourceMatchTable, SkipSegmentTable])
class AppDatabase extends _$AppDatabase { class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection()); AppDatabase() : super(_openConnection());

View File

@ -1204,16 +1204,608 @@ class PreferencesTableCompanion extends UpdateCompanion<PreferencesTableData> {
} }
} }
class $SourceMatchTableTable extends SourceMatchTable
with TableInfo<$SourceMatchTableTable, SourceMatchTableData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$SourceMatchTableTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const VerificationMeta _trackIdMeta =
const VerificationMeta('trackId');
@override
late final GeneratedColumn<String> trackId = GeneratedColumn<String>(
'track_id', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _sourceIdMeta =
const VerificationMeta('sourceId');
@override
late final GeneratedColumn<String> sourceId = GeneratedColumn<String>(
'source_id', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _sourceTypeMeta =
const VerificationMeta('sourceType');
@override
late final GeneratedColumnWithTypeConverter<SourceType, String> sourceType =
GeneratedColumn<String>('source_type', aliasedName, false,
type: DriftSqlType.string,
requiredDuringInsert: false,
defaultValue: Constant(SourceType.youtube.name))
.withConverter<SourceType>(
$SourceMatchTableTable.$convertersourceType);
static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt');
@override
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: currentDateAndTime);
@override
List<GeneratedColumn> get $columns =>
[id, trackId, sourceId, sourceType, createdAt];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'source_match_table';
@override
VerificationContext validateIntegrity(
Insertable<SourceMatchTableData> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('track_id')) {
context.handle(_trackIdMeta,
trackId.isAcceptableOrUnknown(data['track_id']!, _trackIdMeta));
} else if (isInserting) {
context.missing(_trackIdMeta);
}
if (data.containsKey('source_id')) {
context.handle(_sourceIdMeta,
sourceId.isAcceptableOrUnknown(data['source_id']!, _sourceIdMeta));
} else if (isInserting) {
context.missing(_sourceIdMeta);
}
context.handle(_sourceTypeMeta, const VerificationResult.success());
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
SourceMatchTableData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return SourceMatchTableData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
trackId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}track_id'])!,
sourceId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}source_id'])!,
sourceType: $SourceMatchTableTable.$convertersourceType.fromSql(
attachedDatabase.typeMapping.read(
DriftSqlType.string, data['${effectivePrefix}source_type'])!),
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
);
}
@override
$SourceMatchTableTable createAlias(String alias) {
return $SourceMatchTableTable(attachedDatabase, alias);
}
static JsonTypeConverter2<SourceType, String, String> $convertersourceType =
const EnumNameConverter<SourceType>(SourceType.values);
}
class SourceMatchTableData extends DataClass
implements Insertable<SourceMatchTableData> {
final int id;
final String trackId;
final String sourceId;
final SourceType sourceType;
final DateTime createdAt;
const SourceMatchTableData(
{required this.id,
required this.trackId,
required this.sourceId,
required this.sourceType,
required this.createdAt});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['track_id'] = Variable<String>(trackId);
map['source_id'] = Variable<String>(sourceId);
{
map['source_type'] = Variable<String>(
$SourceMatchTableTable.$convertersourceType.toSql(sourceType));
}
map['created_at'] = Variable<DateTime>(createdAt);
return map;
}
SourceMatchTableCompanion toCompanion(bool nullToAbsent) {
return SourceMatchTableCompanion(
id: Value(id),
trackId: Value(trackId),
sourceId: Value(sourceId),
sourceType: Value(sourceType),
createdAt: Value(createdAt),
);
}
factory SourceMatchTableData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return SourceMatchTableData(
id: serializer.fromJson<int>(json['id']),
trackId: serializer.fromJson<String>(json['trackId']),
sourceId: serializer.fromJson<String>(json['sourceId']),
sourceType: $SourceMatchTableTable.$convertersourceType
.fromJson(serializer.fromJson<String>(json['sourceType'])),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'trackId': serializer.toJson<String>(trackId),
'sourceId': serializer.toJson<String>(sourceId),
'sourceType': serializer.toJson<String>(
$SourceMatchTableTable.$convertersourceType.toJson(sourceType)),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
}
SourceMatchTableData copyWith(
{int? id,
String? trackId,
String? sourceId,
SourceType? sourceType,
DateTime? createdAt}) =>
SourceMatchTableData(
id: id ?? this.id,
trackId: trackId ?? this.trackId,
sourceId: sourceId ?? this.sourceId,
sourceType: sourceType ?? this.sourceType,
createdAt: createdAt ?? this.createdAt,
);
@override
String toString() {
return (StringBuffer('SourceMatchTableData(')
..write('id: $id, ')
..write('trackId: $trackId, ')
..write('sourceId: $sourceId, ')
..write('sourceType: $sourceType, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, trackId, sourceId, sourceType, createdAt);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is SourceMatchTableData &&
other.id == this.id &&
other.trackId == this.trackId &&
other.sourceId == this.sourceId &&
other.sourceType == this.sourceType &&
other.createdAt == this.createdAt);
}
class SourceMatchTableCompanion extends UpdateCompanion<SourceMatchTableData> {
final Value<int> id;
final Value<String> trackId;
final Value<String> sourceId;
final Value<SourceType> sourceType;
final Value<DateTime> createdAt;
const SourceMatchTableCompanion({
this.id = const Value.absent(),
this.trackId = const Value.absent(),
this.sourceId = const Value.absent(),
this.sourceType = const Value.absent(),
this.createdAt = const Value.absent(),
});
SourceMatchTableCompanion.insert({
this.id = const Value.absent(),
required String trackId,
required String sourceId,
this.sourceType = const Value.absent(),
this.createdAt = const Value.absent(),
}) : trackId = Value(trackId),
sourceId = Value(sourceId);
static Insertable<SourceMatchTableData> custom({
Expression<int>? id,
Expression<String>? trackId,
Expression<String>? sourceId,
Expression<String>? sourceType,
Expression<DateTime>? createdAt,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (trackId != null) 'track_id': trackId,
if (sourceId != null) 'source_id': sourceId,
if (sourceType != null) 'source_type': sourceType,
if (createdAt != null) 'created_at': createdAt,
});
}
SourceMatchTableCompanion copyWith(
{Value<int>? id,
Value<String>? trackId,
Value<String>? sourceId,
Value<SourceType>? sourceType,
Value<DateTime>? createdAt}) {
return SourceMatchTableCompanion(
id: id ?? this.id,
trackId: trackId ?? this.trackId,
sourceId: sourceId ?? this.sourceId,
sourceType: sourceType ?? this.sourceType,
createdAt: createdAt ?? this.createdAt,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (trackId.present) {
map['track_id'] = Variable<String>(trackId.value);
}
if (sourceId.present) {
map['source_id'] = Variable<String>(sourceId.value);
}
if (sourceType.present) {
map['source_type'] = Variable<String>(
$SourceMatchTableTable.$convertersourceType.toSql(sourceType.value));
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('SourceMatchTableCompanion(')
..write('id: $id, ')
..write('trackId: $trackId, ')
..write('sourceId: $sourceId, ')
..write('sourceType: $sourceType, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
}
class $SkipSegmentTableTable extends SkipSegmentTable
with TableInfo<$SkipSegmentTableTable, SkipSegmentTableData> {
@override
final GeneratedDatabase attachedDatabase;
final String? _alias;
$SkipSegmentTableTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _idMeta = const VerificationMeta('id');
@override
late final GeneratedColumn<int> id = GeneratedColumn<int>(
'id', aliasedName, false,
hasAutoIncrement: true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultConstraints:
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
static const VerificationMeta _startMeta = const VerificationMeta('start');
@override
late final GeneratedColumn<int> start = GeneratedColumn<int>(
'start', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _endMeta = const VerificationMeta('end');
@override
late final GeneratedColumn<int> end = GeneratedColumn<int>(
'end', aliasedName, false,
type: DriftSqlType.int, requiredDuringInsert: true);
static const VerificationMeta _trackIdMeta =
const VerificationMeta('trackId');
@override
late final GeneratedColumn<String> trackId = GeneratedColumn<String>(
'track_id', aliasedName, false,
type: DriftSqlType.string, requiredDuringInsert: true);
static const VerificationMeta _createdAtMeta =
const VerificationMeta('createdAt');
@override
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
'created_at', aliasedName, false,
type: DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: currentDateAndTime);
@override
List<GeneratedColumn> get $columns => [id, start, end, trackId, createdAt];
@override
String get aliasedName => _alias ?? actualTableName;
@override
String get actualTableName => $name;
static const String $name = 'skip_segment_table';
@override
VerificationContext validateIntegrity(
Insertable<SkipSegmentTableData> instance,
{bool isInserting = false}) {
final context = VerificationContext();
final data = instance.toColumns(true);
if (data.containsKey('id')) {
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
}
if (data.containsKey('start')) {
context.handle(
_startMeta, start.isAcceptableOrUnknown(data['start']!, _startMeta));
} else if (isInserting) {
context.missing(_startMeta);
}
if (data.containsKey('end')) {
context.handle(
_endMeta, end.isAcceptableOrUnknown(data['end']!, _endMeta));
} else if (isInserting) {
context.missing(_endMeta);
}
if (data.containsKey('track_id')) {
context.handle(_trackIdMeta,
trackId.isAcceptableOrUnknown(data['track_id']!, _trackIdMeta));
} else if (isInserting) {
context.missing(_trackIdMeta);
}
if (data.containsKey('created_at')) {
context.handle(_createdAtMeta,
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
}
return context;
}
@override
Set<GeneratedColumn> get $primaryKey => {id};
@override
SkipSegmentTableData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return SkipSegmentTableData(
id: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
start: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}start'])!,
end: attachedDatabase.typeMapping
.read(DriftSqlType.int, data['${effectivePrefix}end'])!,
trackId: attachedDatabase.typeMapping
.read(DriftSqlType.string, data['${effectivePrefix}track_id'])!,
createdAt: attachedDatabase.typeMapping
.read(DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
);
}
@override
$SkipSegmentTableTable createAlias(String alias) {
return $SkipSegmentTableTable(attachedDatabase, alias);
}
}
class SkipSegmentTableData extends DataClass
implements Insertable<SkipSegmentTableData> {
final int id;
final int start;
final int end;
final String trackId;
final DateTime createdAt;
const SkipSegmentTableData(
{required this.id,
required this.start,
required this.end,
required this.trackId,
required this.createdAt});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
map['id'] = Variable<int>(id);
map['start'] = Variable<int>(start);
map['end'] = Variable<int>(end);
map['track_id'] = Variable<String>(trackId);
map['created_at'] = Variable<DateTime>(createdAt);
return map;
}
SkipSegmentTableCompanion toCompanion(bool nullToAbsent) {
return SkipSegmentTableCompanion(
id: Value(id),
start: Value(start),
end: Value(end),
trackId: Value(trackId),
createdAt: Value(createdAt),
);
}
factory SkipSegmentTableData.fromJson(Map<String, dynamic> json,
{ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return SkipSegmentTableData(
id: serializer.fromJson<int>(json['id']),
start: serializer.fromJson<int>(json['start']),
end: serializer.fromJson<int>(json['end']),
trackId: serializer.fromJson<String>(json['trackId']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
);
}
@override
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
serializer ??= driftRuntimeOptions.defaultSerializer;
return <String, dynamic>{
'id': serializer.toJson<int>(id),
'start': serializer.toJson<int>(start),
'end': serializer.toJson<int>(end),
'trackId': serializer.toJson<String>(trackId),
'createdAt': serializer.toJson<DateTime>(createdAt),
};
}
SkipSegmentTableData copyWith(
{int? id,
int? start,
int? end,
String? trackId,
DateTime? createdAt}) =>
SkipSegmentTableData(
id: id ?? this.id,
start: start ?? this.start,
end: end ?? this.end,
trackId: trackId ?? this.trackId,
createdAt: createdAt ?? this.createdAt,
);
@override
String toString() {
return (StringBuffer('SkipSegmentTableData(')
..write('id: $id, ')
..write('start: $start, ')
..write('end: $end, ')
..write('trackId: $trackId, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
@override
int get hashCode => Object.hash(id, start, end, trackId, createdAt);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is SkipSegmentTableData &&
other.id == this.id &&
other.start == this.start &&
other.end == this.end &&
other.trackId == this.trackId &&
other.createdAt == this.createdAt);
}
class SkipSegmentTableCompanion extends UpdateCompanion<SkipSegmentTableData> {
final Value<int> id;
final Value<int> start;
final Value<int> end;
final Value<String> trackId;
final Value<DateTime> createdAt;
const SkipSegmentTableCompanion({
this.id = const Value.absent(),
this.start = const Value.absent(),
this.end = const Value.absent(),
this.trackId = const Value.absent(),
this.createdAt = const Value.absent(),
});
SkipSegmentTableCompanion.insert({
this.id = const Value.absent(),
required int start,
required int end,
required String trackId,
this.createdAt = const Value.absent(),
}) : start = Value(start),
end = Value(end),
trackId = Value(trackId);
static Insertable<SkipSegmentTableData> custom({
Expression<int>? id,
Expression<int>? start,
Expression<int>? end,
Expression<String>? trackId,
Expression<DateTime>? createdAt,
}) {
return RawValuesInsertable({
if (id != null) 'id': id,
if (start != null) 'start': start,
if (end != null) 'end': end,
if (trackId != null) 'track_id': trackId,
if (createdAt != null) 'created_at': createdAt,
});
}
SkipSegmentTableCompanion copyWith(
{Value<int>? id,
Value<int>? start,
Value<int>? end,
Value<String>? trackId,
Value<DateTime>? createdAt}) {
return SkipSegmentTableCompanion(
id: id ?? this.id,
start: start ?? this.start,
end: end ?? this.end,
trackId: trackId ?? this.trackId,
createdAt: createdAt ?? this.createdAt,
);
}
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
final map = <String, Expression>{};
if (id.present) {
map['id'] = Variable<int>(id.value);
}
if (start.present) {
map['start'] = Variable<int>(start.value);
}
if (end.present) {
map['end'] = Variable<int>(end.value);
}
if (trackId.present) {
map['track_id'] = Variable<String>(trackId.value);
}
if (createdAt.present) {
map['created_at'] = Variable<DateTime>(createdAt.value);
}
return map;
}
@override
String toString() {
return (StringBuffer('SkipSegmentTableCompanion(')
..write('id: $id, ')
..write('start: $start, ')
..write('end: $end, ')
..write('trackId: $trackId, ')
..write('createdAt: $createdAt')
..write(')'))
.toString();
}
}
abstract class _$AppDatabase extends GeneratedDatabase { abstract class _$AppDatabase extends GeneratedDatabase {
_$AppDatabase(QueryExecutor e) : super(e); _$AppDatabase(QueryExecutor e) : super(e);
_$AppDatabaseManager get managers => _$AppDatabaseManager(this); _$AppDatabaseManager get managers => _$AppDatabaseManager(this);
late final $PreferencesTableTable preferencesTable = late final $PreferencesTableTable preferencesTable =
$PreferencesTableTable(this); $PreferencesTableTable(this);
late final $SourceMatchTableTable sourceMatchTable =
$SourceMatchTableTable(this);
late final $SkipSegmentTableTable skipSegmentTable =
$SkipSegmentTableTable(this);
late final Index uniqTrackMatch = Index('uniq_track_match',
'CREATE UNIQUE INDEX uniq_track_match ON source_match_table (track_id, source_id, source_type)');
@override @override
Iterable<TableInfo<Table, Object?>> get allTables => Iterable<TableInfo<Table, Object?>> get allTables =>
allSchemaEntities.whereType<TableInfo<Table, Object?>>(); allSchemaEntities.whereType<TableInfo<Table, Object?>>();
@override @override
List<DatabaseSchemaEntity> get allSchemaEntities => [preferencesTable]; List<DatabaseSchemaEntity> get allSchemaEntities =>
[preferencesTable, sourceMatchTable, skipSegmentTable, uniqTrackMatch];
} }
typedef $$PreferencesTableTableInsertCompanionBuilder typedef $$PreferencesTableTableInsertCompanionBuilder
@ -1699,9 +2291,293 @@ class $$PreferencesTableTableOrderingComposer
ColumnOrderings(column, joinBuilders: joinBuilders)); ColumnOrderings(column, joinBuilders: joinBuilders));
} }
typedef $$SourceMatchTableTableInsertCompanionBuilder
= SourceMatchTableCompanion Function({
Value<int> id,
required String trackId,
required String sourceId,
Value<SourceType> sourceType,
Value<DateTime> createdAt,
});
typedef $$SourceMatchTableTableUpdateCompanionBuilder
= SourceMatchTableCompanion Function({
Value<int> id,
Value<String> trackId,
Value<String> sourceId,
Value<SourceType> sourceType,
Value<DateTime> createdAt,
});
class $$SourceMatchTableTableTableManager extends RootTableManager<
_$AppDatabase,
$SourceMatchTableTable,
SourceMatchTableData,
$$SourceMatchTableTableFilterComposer,
$$SourceMatchTableTableOrderingComposer,
$$SourceMatchTableTableProcessedTableManager,
$$SourceMatchTableTableInsertCompanionBuilder,
$$SourceMatchTableTableUpdateCompanionBuilder> {
$$SourceMatchTableTableTableManager(
_$AppDatabase db, $SourceMatchTableTable table)
: super(TableManagerState(
db: db,
table: table,
filteringComposer:
$$SourceMatchTableTableFilterComposer(ComposerState(db, table)),
orderingComposer:
$$SourceMatchTableTableOrderingComposer(ComposerState(db, table)),
getChildManagerBuilder: (p) =>
$$SourceMatchTableTableProcessedTableManager(p),
getUpdateCompanionBuilder: ({
Value<int> id = const Value.absent(),
Value<String> trackId = const Value.absent(),
Value<String> sourceId = const Value.absent(),
Value<SourceType> sourceType = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
}) =>
SourceMatchTableCompanion(
id: id,
trackId: trackId,
sourceId: sourceId,
sourceType: sourceType,
createdAt: createdAt,
),
getInsertCompanionBuilder: ({
Value<int> id = const Value.absent(),
required String trackId,
required String sourceId,
Value<SourceType> sourceType = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
}) =>
SourceMatchTableCompanion.insert(
id: id,
trackId: trackId,
sourceId: sourceId,
sourceType: sourceType,
createdAt: createdAt,
),
));
}
class $$SourceMatchTableTableProcessedTableManager
extends ProcessedTableManager<
_$AppDatabase,
$SourceMatchTableTable,
SourceMatchTableData,
$$SourceMatchTableTableFilterComposer,
$$SourceMatchTableTableOrderingComposer,
$$SourceMatchTableTableProcessedTableManager,
$$SourceMatchTableTableInsertCompanionBuilder,
$$SourceMatchTableTableUpdateCompanionBuilder> {
$$SourceMatchTableTableProcessedTableManager(super.$state);
}
class $$SourceMatchTableTableFilterComposer
extends FilterComposer<_$AppDatabase, $SourceMatchTableTable> {
$$SourceMatchTableTableFilterComposer(super.$state);
ColumnFilters<int> get id => $state.composableBuilder(
column: $state.table.id,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnFilters<String> get trackId => $state.composableBuilder(
column: $state.table.trackId,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnFilters<String> get sourceId => $state.composableBuilder(
column: $state.table.sourceId,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnWithTypeConverterFilters<SourceType, SourceType, String>
get sourceType => $state.composableBuilder(
column: $state.table.sourceType,
builder: (column, joinBuilders) => ColumnWithTypeConverterFilters(
column,
joinBuilders: joinBuilders));
ColumnFilters<DateTime> get createdAt => $state.composableBuilder(
column: $state.table.createdAt,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
}
class $$SourceMatchTableTableOrderingComposer
extends OrderingComposer<_$AppDatabase, $SourceMatchTableTable> {
$$SourceMatchTableTableOrderingComposer(super.$state);
ColumnOrderings<int> get id => $state.composableBuilder(
column: $state.table.id,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<String> get trackId => $state.composableBuilder(
column: $state.table.trackId,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<String> get sourceId => $state.composableBuilder(
column: $state.table.sourceId,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<String> get sourceType => $state.composableBuilder(
column: $state.table.sourceType,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<DateTime> get createdAt => $state.composableBuilder(
column: $state.table.createdAt,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
}
typedef $$SkipSegmentTableTableInsertCompanionBuilder
= SkipSegmentTableCompanion Function({
Value<int> id,
required int start,
required int end,
required String trackId,
Value<DateTime> createdAt,
});
typedef $$SkipSegmentTableTableUpdateCompanionBuilder
= SkipSegmentTableCompanion Function({
Value<int> id,
Value<int> start,
Value<int> end,
Value<String> trackId,
Value<DateTime> createdAt,
});
class $$SkipSegmentTableTableTableManager extends RootTableManager<
_$AppDatabase,
$SkipSegmentTableTable,
SkipSegmentTableData,
$$SkipSegmentTableTableFilterComposer,
$$SkipSegmentTableTableOrderingComposer,
$$SkipSegmentTableTableProcessedTableManager,
$$SkipSegmentTableTableInsertCompanionBuilder,
$$SkipSegmentTableTableUpdateCompanionBuilder> {
$$SkipSegmentTableTableTableManager(
_$AppDatabase db, $SkipSegmentTableTable table)
: super(TableManagerState(
db: db,
table: table,
filteringComposer:
$$SkipSegmentTableTableFilterComposer(ComposerState(db, table)),
orderingComposer:
$$SkipSegmentTableTableOrderingComposer(ComposerState(db, table)),
getChildManagerBuilder: (p) =>
$$SkipSegmentTableTableProcessedTableManager(p),
getUpdateCompanionBuilder: ({
Value<int> id = const Value.absent(),
Value<int> start = const Value.absent(),
Value<int> end = const Value.absent(),
Value<String> trackId = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
}) =>
SkipSegmentTableCompanion(
id: id,
start: start,
end: end,
trackId: trackId,
createdAt: createdAt,
),
getInsertCompanionBuilder: ({
Value<int> id = const Value.absent(),
required int start,
required int end,
required String trackId,
Value<DateTime> createdAt = const Value.absent(),
}) =>
SkipSegmentTableCompanion.insert(
id: id,
start: start,
end: end,
trackId: trackId,
createdAt: createdAt,
),
));
}
class $$SkipSegmentTableTableProcessedTableManager
extends ProcessedTableManager<
_$AppDatabase,
$SkipSegmentTableTable,
SkipSegmentTableData,
$$SkipSegmentTableTableFilterComposer,
$$SkipSegmentTableTableOrderingComposer,
$$SkipSegmentTableTableProcessedTableManager,
$$SkipSegmentTableTableInsertCompanionBuilder,
$$SkipSegmentTableTableUpdateCompanionBuilder> {
$$SkipSegmentTableTableProcessedTableManager(super.$state);
}
class $$SkipSegmentTableTableFilterComposer
extends FilterComposer<_$AppDatabase, $SkipSegmentTableTable> {
$$SkipSegmentTableTableFilterComposer(super.$state);
ColumnFilters<int> get id => $state.composableBuilder(
column: $state.table.id,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnFilters<int> get start => $state.composableBuilder(
column: $state.table.start,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnFilters<int> get end => $state.composableBuilder(
column: $state.table.end,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnFilters<String> get trackId => $state.composableBuilder(
column: $state.table.trackId,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
ColumnFilters<DateTime> get createdAt => $state.composableBuilder(
column: $state.table.createdAt,
builder: (column, joinBuilders) =>
ColumnFilters(column, joinBuilders: joinBuilders));
}
class $$SkipSegmentTableTableOrderingComposer
extends OrderingComposer<_$AppDatabase, $SkipSegmentTableTable> {
$$SkipSegmentTableTableOrderingComposer(super.$state);
ColumnOrderings<int> get id => $state.composableBuilder(
column: $state.table.id,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<int> get start => $state.composableBuilder(
column: $state.table.start,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<int> get end => $state.composableBuilder(
column: $state.table.end,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<String> get trackId => $state.composableBuilder(
column: $state.table.trackId,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
ColumnOrderings<DateTime> get createdAt => $state.composableBuilder(
column: $state.table.createdAt,
builder: (column, joinBuilders) =>
ColumnOrderings(column, joinBuilders: joinBuilders));
}
class _$AppDatabaseManager { class _$AppDatabaseManager {
final _$AppDatabase _db; final _$AppDatabase _db;
_$AppDatabaseManager(this._db); _$AppDatabaseManager(this._db);
$$PreferencesTableTableTableManager get preferencesTable => $$PreferencesTableTableTableManager get preferencesTable =>
$$PreferencesTableTableTableManager(_db, _db.preferencesTable); $$PreferencesTableTableTableManager(_db, _db.preferencesTable);
$$SourceMatchTableTableTableManager get sourceMatchTable =>
$$SourceMatchTableTableTableManager(_db, _db.sourceMatchTable);
$$SkipSegmentTableTableTableManager get skipSegmentTable =>
$$SkipSegmentTableTableTableManager(_db, _db.skipSegmentTable);
} }

View File

@ -0,0 +1,9 @@
part of '../database.dart';
class SkipSegmentTable extends Table {
IntColumn get id => integer().autoIncrement()();
IntColumn get start => integer()();
IntColumn get end => integer()();
TextColumn get trackId => text()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}

View File

@ -0,0 +1,25 @@
part of '../database.dart';
enum SourceType {
youtube._("YouTube"),
youtubeMusic._("YouTube Music"),
jiosaavn._("JioSaavn");
final String label;
const SourceType._(this.label);
}
@TableIndex(
name: "uniq_track_match",
columns: {#trackId, #sourceId, #sourceType},
unique: true,
)
class SourceMatchTable extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get trackId => text()();
TextColumn get sourceId => text()();
TextColumn get sourceType =>
textEnum<SourceType>().withDefault(Constant(SourceType.youtube.name))();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}

View File

@ -1,25 +0,0 @@
import 'package:hive/hive.dart';
part 'skip_segment.g.dart';
@HiveType(typeId: 2)
class SkipSegment {
@HiveField(0)
final int start;
@HiveField(1)
final int end;
SkipSegment(this.start, this.end);
static String version = 'v1';
static final boxName = "oss.krtirtho.spotube.skip_segments.$version";
static LazyBox get box => Hive.lazyBox(boxName);
SkipSegment.fromJson(Map<String, dynamic> json)
: start = json['start'],
end = json['end'];
Map<String, dynamic> toJson() => {
'start': start,
'end': end,
};
}

View File

@ -1,44 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'skip_segment.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class SkipSegmentAdapter extends TypeAdapter<SkipSegment> {
@override
final int typeId = 2;
@override
SkipSegment read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return SkipSegment(
fields[0] as int,
fields[1] as int,
);
}
@override
void write(BinaryWriter writer, SkipSegment obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.start)
..writeByte(1)
..write(obj.end);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SkipSegmentAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -1,54 +0,0 @@
import 'package:hive/hive.dart';
import 'package:json_annotation/json_annotation.dart';
part 'source_match.g.dart';
@JsonEnum()
@HiveType(typeId: 5)
enum SourceType {
@HiveField(0)
youtube._("YouTube"),
@HiveField(1)
youtubeMusic._("YouTube Music"),
@HiveField(2)
jiosaavn._("JioSaavn");
final String label;
const SourceType._(this.label);
}
@JsonSerializable()
@HiveType(typeId: 6)
class SourceMatch {
@HiveField(0)
String id;
@HiveField(1)
String sourceId;
@HiveField(2)
SourceType sourceType;
@HiveField(3)
DateTime createdAt;
SourceMatch({
required this.id,
required this.sourceId,
required this.sourceType,
required this.createdAt,
});
factory SourceMatch.fromJson(Map<String, dynamic> json) =>
_$SourceMatchFromJson(json);
Map<String, dynamic> toJson() => _$SourceMatchToJson(this);
static String version = 'v1';
static final boxName = "oss.krtirtho.spotube.source_matches.$version";
static LazyBox<SourceMatch> get box => Hive.lazyBox<SourceMatch>(boxName);
}

View File

@ -1,119 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'source_match.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class SourceMatchAdapter extends TypeAdapter<SourceMatch> {
@override
final int typeId = 6;
@override
SourceMatch read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return SourceMatch(
id: fields[0] as String,
sourceId: fields[1] as String,
sourceType: fields[2] as SourceType,
createdAt: fields[3] as DateTime,
);
}
@override
void write(BinaryWriter writer, SourceMatch obj) {
writer
..writeByte(4)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.sourceId)
..writeByte(2)
..write(obj.sourceType)
..writeByte(3)
..write(obj.createdAt);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SourceMatchAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class SourceTypeAdapter extends TypeAdapter<SourceType> {
@override
final int typeId = 5;
@override
SourceType read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return SourceType.youtube;
case 1:
return SourceType.youtubeMusic;
case 2:
return SourceType.jiosaavn;
default:
return SourceType.youtube;
}
}
@override
void write(BinaryWriter writer, SourceType obj) {
switch (obj) {
case SourceType.youtube:
writer.writeByte(0);
break;
case SourceType.youtubeMusic:
writer.writeByte(1);
break;
case SourceType.jiosaavn:
writer.writeByte(2);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SourceTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
SourceMatch _$SourceMatchFromJson(Map json) => SourceMatch(
id: json['id'] as String,
sourceId: json['sourceId'] as String,
sourceType: $enumDecode(_$SourceTypeEnumMap, json['sourceType']),
createdAt: DateTime.parse(json['createdAt'] as String),
);
Map<String, dynamic> _$SourceMatchToJson(SourceMatch instance) =>
<String, dynamic>{
'id': instance.id,
'sourceId': instance.sourceId,
'sourceType': _$SourceTypeEnumMap[instance.sourceType]!,
'createdAt': instance.createdAt.toIso8601String(),
};
const _$SourceTypeEnumMap = {
SourceType.youtube: 'youtube',
SourceType.youtubeMusic: 'youtubeMusic',
SourceType.jiosaavn: 'jiosaavn',
};

View File

@ -1,8 +1,8 @@
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/skip_segment.dart';
import 'package:spotube/provider/server/active_sourced_track.dart'; import 'package:spotube/provider/server/active_sourced_track.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
@ -10,24 +10,21 @@ import 'package:spotube/services/dio/dio.dart';
class SourcedSegments { class SourcedSegments {
final String source; final String source;
final List<SkipSegment> segments; final List<SkipSegmentTableData> segments;
SourcedSegments({required this.source, required this.segments}); SourcedSegments({required this.source, required this.segments});
} }
Future<List<SkipSegment>> getAndCacheSkipSegments(String id) async { Future<List<SkipSegmentTableData>> getAndCacheSkipSegments(
String id, Ref ref) async {
final database = ref.read(databaseProvider);
try { try {
final cached = await SkipSegment.box.get(id) as List?; final cached = await (database.select(database.skipSegmentTable)
if (cached != null && cached.isNotEmpty) { ..where((s) => s.trackId.equals(id)))
return List.castFrom<dynamic, SkipSegment>( .get();
cached
.map( if (cached.isNotEmpty) {
(json) => SkipSegment.fromJson( return cached;
Map.castFrom<dynamic, dynamic, String, dynamic>(json),
),
)
.toList(),
);
} }
final res = await globalDio.getUri( final res = await globalDio.getUri(
@ -55,25 +52,30 @@ Future<List<SkipSegment>> getAndCacheSkipSegments(String id) async {
); );
if (res.data == "Not Found") { if (res.data == "Not Found") {
return List.castFrom<dynamic, SkipSegment>([]); return List.castFrom<dynamic, SkipSegmentTableData>([]);
} }
final data = res.data as List; final data = res.data as List;
final segments = data.map((obj) { final segments = data.map((obj) {
final start = obj["segment"].first.toInt(); final start = obj["segment"].first.toInt();
final end = obj["segment"].last.toInt(); final end = obj["segment"].last.toInt();
return SkipSegment(start, end); return SkipSegmentTableCompanion.insert(
trackId: id,
start: start,
end: end,
);
}).toList(); }).toList();
await SkipSegment.box.put( await database.batch((b) {
id, b.insertAll(database.skipSegmentTable, segments);
segments.map((e) => e.toJson()).toList(), });
);
return List.castFrom<dynamic, SkipSegment>(segments); return await (database.select(database.skipSegmentTable)
..where((s) => s.trackId.equals(id)))
.get();
} catch (e, stack) { } catch (e, stack) {
await SkipSegment.box.put(id, []);
AppLogger.reportError(e, stack); AppLogger.reportError(e, stack);
return List.castFrom<dynamic, SkipSegment>([]); return List.castFrom<dynamic, SkipSegmentTableData>([]);
} }
} }
@ -100,7 +102,7 @@ final segmentProvider = FutureProvider<SourcedSegments?>(
); );
} }
final segments = await getAndCacheSkipSegments(track.sourceInfo.id); final segments = await getAndCacheSkipSegments(track.sourceInfo.id, ref);
return SourcedSegments( return SourcedSegments(
source: track.sourceInfo.id, source: track.sourceInfo.id,

View File

@ -1,7 +1,9 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/models/source_match.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
import 'package:spotube/services/sourced_track/exceptions.dart'; import 'package:spotube/services/sourced_track/exceptions.dart';
import 'package:spotube/services/sourced_track/models/source_info.dart'; import 'package:spotube/services/sourced_track/models/source_info.dart';
@ -39,7 +41,10 @@ class JioSaavnSourcedTrack extends SourcedTrack {
required Ref ref, required Ref ref,
bool weakMatch = false, bool weakMatch = false,
}) async { }) async {
final cachedSource = await SourceMatch.box.get(track.id); final database = ref.read(databaseProvider);
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!)))
.getSingleOrNull();
if (cachedSource == null || if (cachedSource == null ||
cachedSource.sourceType != SourceType.jiosaavn) { cachedSource.sourceType != SourceType.jiosaavn) {
@ -50,15 +55,13 @@ class JioSaavnSourcedTrack extends SourcedTrack {
throw TrackNotFoundError(track); throw TrackNotFoundError(track);
} }
await SourceMatch.box.put( await database.into(database.sourceMatchTable).insert(
track.id!, SourceMatchTableCompanion.insert(
SourceMatch( trackId: track.id!,
id: track.id!, sourceId: siblings.first.info.id,
sourceType: SourceType.jiosaavn, sourceType: const Value(SourceType.jiosaavn),
createdAt: DateTime.now(), ),
sourceId: siblings.first.info.id, );
),
);
return JioSaavnSourcedTrack( return JioSaavnSourcedTrack(
ref: ref, ref: ref,
@ -206,15 +209,14 @@ class JioSaavnSourcedTrack extends SourcedTrack {
final (:info, :source) = toSiblingType(item); final (:info, :source) = toSiblingType(item);
await SourceMatch.box.put( final database = ref.read(databaseProvider);
id!, await database.into(database.sourceMatchTable).insert(
SourceMatch( SourceMatchTableCompanion.insert(
id: id!, trackId: id!,
sourceType: SourceType.jiosaavn, sourceId: info.id,
createdAt: DateTime.now(), sourceType: const Value(SourceType.jiosaavn),
sourceId: info.id, ),
), );
);
return JioSaavnSourcedTrack( return JioSaavnSourcedTrack(
ref: ref, ref: ref,

View File

@ -1,9 +1,10 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:piped_client/piped_client.dart'; import 'package:piped_client/piped_client.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/models/source_match.dart'; import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
@ -49,7 +50,10 @@ class PipedSourcedTrack extends SourcedTrack {
required Track track, required Track track,
required Ref ref, required Ref ref,
}) async { }) async {
final cachedSource = await SourceMatch.box.get(track.id); final database = ref.read(databaseProvider);
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!)))
.getSingleOrNull();
final preferences = ref.read(userPreferencesProvider); final preferences = ref.read(userPreferencesProvider);
final pipedClient = ref.read(pipedProvider); final pipedClient = ref.read(pipedProvider);
@ -59,17 +63,17 @@ class PipedSourcedTrack extends SourcedTrack {
throw TrackNotFoundError(track); throw TrackNotFoundError(track);
} }
await SourceMatch.box.put( await database.into(database.sourceMatchTable).insert(
track.id!, SourceMatchTableCompanion.insert(
SourceMatch( trackId: track.id!,
id: track.id!, sourceId: siblings.first.info.id,
sourceType: preferences.searchMode == SearchMode.youtube sourceType: Value(
? SourceType.youtube preferences.searchMode == SearchMode.youtube
: SourceType.youtubeMusic, ? SourceType.youtube
createdAt: DateTime.now(), : SourceType.youtubeMusic,
sourceId: siblings.first.info.id, ),
), ),
); );
return PipedSourcedTrack( return PipedSourcedTrack(
ref: ref, ref: ref,
@ -268,15 +272,14 @@ class PipedSourcedTrack extends SourcedTrack {
final manifest = await pipedClient.streams(newSourceInfo.id); final manifest = await pipedClient.streams(newSourceInfo.id);
await SourceMatch.box.put( final database = ref.read(databaseProvider);
id!, await database.into(database.sourceMatchTable).insert(
SourceMatch( SourceMatchTableCompanion.insert(
id: id!, trackId: id!,
sourceType: SourceType.jiosaavn, sourceId: newSourceInfo.id,
createdAt: DateTime.now(), sourceType: const Value(SourceType.youtube),
sourceId: newSourceInfo.id, ),
), );
);
return PipedSourcedTrack( return PipedSourcedTrack(
ref: ref, ref: ref,

View File

@ -1,8 +1,10 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/models/source_match.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/song_link/song_link.dart'; import 'package:spotube/services/song_link/song_link.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
@ -46,7 +48,10 @@ class YoutubeSourcedTrack extends SourcedTrack {
required Track track, required Track track,
required Ref ref, required Ref ref,
}) async { }) async {
final cachedSource = await SourceMatch.box.get(track.id); final database = ref.read(databaseProvider);
final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!)))
.getSingleOrNull();
if (cachedSource == null || cachedSource.sourceType != SourceType.youtube) { if (cachedSource == null || cachedSource.sourceType != SourceType.youtube) {
final siblings = await fetchSiblings(ref: ref, track: track); final siblings = await fetchSiblings(ref: ref, track: track);
@ -54,15 +59,13 @@ class YoutubeSourcedTrack extends SourcedTrack {
throw TrackNotFoundError(track); throw TrackNotFoundError(track);
} }
await SourceMatch.box.put( await database.into(database.sourceMatchTable).insert(
track.id!, SourceMatchTableCompanion.insert(
SourceMatch( trackId: track.id!,
id: track.id!, sourceId: siblings.first.info.id,
sourceType: SourceType.youtube, sourceType: const Value(SourceType.youtube),
createdAt: DateTime.now(), ),
sourceId: siblings.first.info.id, );
),
);
return YoutubeSourcedTrack( return YoutubeSourcedTrack(
ref: ref, ref: ref,
@ -283,15 +286,14 @@ class YoutubeSourcedTrack extends SourcedTrack {
onTimeout: () => throw ClientException("Timeout"), onTimeout: () => throw ClientException("Timeout"),
); );
await SourceMatch.box.put( final database = ref.read(databaseProvider);
id!, await database.into(database.sourceMatchTable).insert(
SourceMatch( SourceMatchTableCompanion.insert(
id: id!, trackId: id!,
sourceType: SourceType.jiosaavn, sourceId: newSourceInfo.id,
createdAt: DateTime.now(), sourceType: const Value(SourceType.youtube),
sourceId: newSourceInfo.id, ),
), );
);
return YoutubeSourcedTrack( return YoutubeSourcedTrack(
ref: ref, ref: ref,

View File

@ -6,6 +6,7 @@ import 'package:spotube/modules/library/user_local_tracks.dart';
import 'package:spotube/modules/root/update_dialog.dart'; import 'package:spotube/modules/root/update_dialog.dart';
import 'package:spotube/models/logger.dart'; import 'package:spotube/models/logger.dart';
import 'package:spotube/models/lyrics.dart'; import 'package:spotube/models/lyrics.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/dio/dio.dart'; import 'package:spotube/services/dio/dio.dart';
import 'package:spotube/services/sourced_track/sourced_track.dart'; import 'package:spotube/services/sourced_track/sourced_track.dart';
@ -20,7 +21,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:spotube/collections/env.dart'; import 'package:spotube/collections/env.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:version/version.dart'; import 'package:version/version.dart';
abstract class ServiceUtils { abstract class ServiceUtils {
@ -392,7 +392,14 @@ abstract class ServiceUtils {
WidgetRef ref, WidgetRef ref,
) async { ) async {
if (!Env.enableUpdateChecker) return; if (!Env.enableUpdateChecker) return;
if (!ref.read(userPreferencesProvider.select((s) => s.checkUpdate))) return; final database = ref.read(databaseProvider);
final checkUpdate = await (database.selectOnly(database.preferencesTable)
..addColumns([database.preferencesTable.checkUpdate])
..where(database.preferencesTable.id.equals(0)))
.map((row) => row.read(database.preferencesTable.checkUpdate))
.getSingleOrNull();
if (checkUpdate == false) return;
final packageInfo = await PackageInfo.fromPlatform(); final packageInfo = await PackageInfo.fromPlatform();
if (Env.releaseChannel == ReleaseChannel.nightly) { if (Env.releaseChannel == ReleaseChannel.nightly) {