freezer/lib/api/cache.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
}