202 lines
4.7 KiB
Dart
202 lines
4.7 KiB
Dart
import 'package:freezer/api/definitions.dart';
|
|
import 'package:freezer/api/paths.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
|
|
import 'dart:async';
|
|
|
|
part 'cache.g.dart';
|
|
|
|
late Cache cache;
|
|
|
|
class CacheEntryAdapter extends TypeAdapter<CacheEntry> {
|
|
@override
|
|
final int typeId = 20;
|
|
|
|
@override
|
|
CacheEntry read(BinaryReader reader) {
|
|
final numOfFields = reader.readByte();
|
|
final fields = <int, dynamic>{
|
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
|
};
|
|
return CacheEntry(
|
|
fields[0],
|
|
updatedAt: fields[1] as DateTime?,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void write(BinaryWriter writer, CacheEntry obj) {
|
|
writer
|
|
..writeByte(2)
|
|
..writeByte(0)
|
|
..write(obj.value)
|
|
..writeByte(1)
|
|
..write(obj.updatedAt);
|
|
}
|
|
|
|
@override
|
|
int get hashCode => typeId.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is CacheEntryAdapter &&
|
|
runtimeType == other.runtimeType &&
|
|
typeId == other.typeId;
|
|
}
|
|
|
|
class CacheEntry<T> {
|
|
final T value;
|
|
final DateTime updatedAt;
|
|
|
|
CacheEntry(this.value, {DateTime? updatedAt})
|
|
: updatedAt = updatedAt ?? DateTime.now();
|
|
}
|
|
|
|
//Cache for miscellaneous things
|
|
@HiveType(typeId: 22)
|
|
class Cache {
|
|
static Future<LazyBox<Cache>> get _box async =>
|
|
Hive.openLazyBox<Cache>('metacache', path: await Paths.cacheDir());
|
|
//ID's of tracks that are in library
|
|
@HiveField(0, defaultValue: [])
|
|
List<String> libraryTracks = [];
|
|
|
|
//Track ID of logged track, to prevent duplicates
|
|
@HiveField(1)
|
|
String? loggedTrackId;
|
|
|
|
@HiveField(2)
|
|
List<Track> history = [];
|
|
|
|
//All sorting cached
|
|
@HiveField(3)
|
|
List<Sorting?> sorts = [];
|
|
|
|
//Sleep timer
|
|
DateTime? sleepTimerTime;
|
|
// ignore: cancel_subscriptions
|
|
StreamSubscription? sleepTimer;
|
|
|
|
//Search history
|
|
@HiveField(4)
|
|
List<DeezerMediaItem> searchHistory = [];
|
|
|
|
//If download threads warning was shown
|
|
@HiveField(5)
|
|
bool threadsWarning = false;
|
|
|
|
//Last time update check
|
|
@HiveField(6)
|
|
DateTime? lastUpdateCheck;
|
|
|
|
@HiveField(7)
|
|
String? favoritesPlaylistId;
|
|
|
|
@HiveField(8, defaultValue: false)
|
|
bool canStreamHQ = false;
|
|
@HiveField(9, defaultValue: false)
|
|
bool canStreamLossless = false;
|
|
|
|
bool wakelock = false;
|
|
|
|
@HiveField(10, defaultValue: null)
|
|
CacheEntry<Map<String, Playlist>>? favoritePlaylists;
|
|
@HiveField(11, defaultValue: null)
|
|
CacheEntry<Map<String, Artist>>? favoriteArtists;
|
|
@HiveField(12, defaultValue: null)
|
|
CacheEntry<Map<String, Album>>? favoriteAlbums;
|
|
@HiveField(13, defaultValue: null)
|
|
CacheEntry<List<Track>>? favoriteTracks;
|
|
|
|
Cache();
|
|
|
|
//Wrapper to test if track is favorite against cache
|
|
bool checkTrackFavorite(Track t) {
|
|
if (t.favorite != null && t.favorite!) return true;
|
|
if (libraryTracks.isEmpty) return false;
|
|
return libraryTracks.contains(t.id);
|
|
}
|
|
|
|
/// Add [item] to the corresponding favorite* cached item
|
|
void addFavorite(DeezerMediaItem item) {
|
|
switch (item) {
|
|
case final Track track:
|
|
favoriteTracks?.value.add(track);
|
|
libraryTracks.add(track.id);
|
|
break;
|
|
case final Album album:
|
|
favoriteAlbums?.value[album.id!] = album;
|
|
break;
|
|
case final Playlist playlist:
|
|
favoritePlaylists?.value[playlist.id] = playlist;
|
|
break;
|
|
case final Artist artist:
|
|
favoriteArtists?.value[artist.id] = artist;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Add to history
|
|
void addToSearchHistory(DeezerMediaItem item) async {
|
|
// Remove duplicate
|
|
int i = searchHistory.indexWhere((e) => e.id == item.id);
|
|
if (i != -1) {
|
|
searchHistory.removeAt(i);
|
|
}
|
|
|
|
searchHistory.add(item);
|
|
|
|
await save();
|
|
}
|
|
|
|
//Save, load
|
|
|
|
static Future wipe() async {
|
|
await (await _box).clear();
|
|
}
|
|
|
|
// TODO: improve
|
|
static Future<Cache> load([int tryN = 0]) async {
|
|
final box = await _box;
|
|
|
|
if (tryN > 1) {
|
|
// recursion error, something's wrong
|
|
throw Exception("can't load cache");
|
|
}
|
|
|
|
if (box.isNotEmpty) {
|
|
try {
|
|
return (await box.get(0))!;
|
|
} catch (e) {
|
|
// invalidate cache on error
|
|
await box.clear();
|
|
}
|
|
}
|
|
|
|
// create cache if non-existent or error occurred
|
|
final c = Cache();
|
|
await c.save();
|
|
return c;
|
|
}
|
|
|
|
Future<void> save() async {
|
|
final box = await _box;
|
|
await box.clear();
|
|
await box.put(0, this);
|
|
}
|
|
}
|
|
|
|
@deprecated
|
|
@HiveType(typeId: 21)
|
|
enum SearchHistoryItemType {
|
|
@HiveField(0)
|
|
track,
|
|
@HiveField(1)
|
|
album,
|
|
@HiveField(2)
|
|
artist,
|
|
@HiveField(3)
|
|
playlist
|
|
}
|