first commit
Build / Build for Android (push) Has been cancelled
Build / Build for iOS (push) Has been cancelled

This commit is contained in:
Zakaria
2026-05-18 14:15:38 -04:00
commit 1563409cb1
382 changed files with 45347 additions and 0 deletions
+618
View File
@@ -0,0 +1,618 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:hive/hive.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:path/path.dart' as path_helper;
import 'package:uuid/uuid.dart';
import '../services/finamp_settings_helper.dart';
import '../services/get_internal_song_dir.dart';
import 'jellyfin_models.dart';
part 'finamp_models.g.dart';
@HiveType(typeId: 8)
class FinampUser {
FinampUser({
required this.id,
required this.baseUrl,
required this.accessToken,
required this.serverId,
this.currentViewId,
required this.views,
});
@HiveField(0)
String id;
@HiveField(1)
String baseUrl;
@HiveField(2)
String accessToken;
@HiveField(3)
String serverId;
@HiveField(4)
String? currentViewId;
@HiveField(5)
Map<String, BaseItemDto> views;
BaseItemDto? get currentView => views[currentViewId];
}
// These consts are so that we can easily keep the same default for
// FinampSettings's constructor and Hive's defaultValue.
const _songShuffleItemCountDefault = 250;
const _contentViewType = ContentViewType.list;
const _contentGridViewCrossAxisCountPortrait = 2;
const _contentGridViewCrossAxisCountLandscape = 3;
const _showTextOnGridView = true;
const _sleepTimerSeconds = 1800; // 30 Minutes
const _showCoverAsPlayerBackground = true;
const _hideSongArtistsIfSameAsAlbumArtists = true;
const _disableGesture = false;
const _showFastScroller = true;
const _bufferDurationSeconds = 50;
const _tabOrder = TabContentType.values;
const _swipeInsertQueueNext = false;
@HiveType(typeId: 28)
class FinampSettings {
FinampSettings({
this.isOffline = false,
this.shouldTranscode = false,
this.transcodeBitrate = 320000,
// downloadLocations is required since the other values can be created with
// default values. create() is used to return a FinampSettings with
// downloadLocations.
required this.downloadLocations,
this.androidStopForegroundOnPause = true,
required this.showTabs,
this.isFavourite = false,
this.sortBy = SortBy.sortName,
this.sortOrder = SortOrder.ascending,
this.songShuffleItemCount = _songShuffleItemCountDefault,
this.contentViewType = _contentViewType,
this.contentGridViewCrossAxisCountPortrait =
_contentGridViewCrossAxisCountPortrait,
this.contentGridViewCrossAxisCountLandscape =
_contentGridViewCrossAxisCountLandscape,
this.showTextOnGridView = _showTextOnGridView,
this.sleepTimerSeconds = _sleepTimerSeconds,
required this.downloadLocationsMap,
this.showCoverAsPlayerBackground = _showCoverAsPlayerBackground,
this.hideSongArtistsIfSameAsAlbumArtists =
_hideSongArtistsIfSameAsAlbumArtists,
this.bufferDurationSeconds = _bufferDurationSeconds,
required this.tabSortBy,
required this.tabSortOrder,
this.tabOrder = _tabOrder,
this.hasCompletedBlurhashImageMigration = true,
this.hasCompletedBlurhashImageMigrationIdFix = true,
this.swipeInsertQueueNext = _swipeInsertQueueNext,
});
@HiveField(0)
bool isOffline;
@HiveField(1)
bool shouldTranscode;
@HiveField(2)
int transcodeBitrate;
@Deprecated("Use downloadedLocationsMap instead")
@HiveField(3)
List<DownloadLocation> downloadLocations;
@HiveField(4)
bool androidStopForegroundOnPause;
@HiveField(5)
Map<TabContentType, bool> showTabs;
/// Used to remember if the user has set their music screen to favourites
/// mode.
@HiveField(6)
bool isFavourite;
/// Current sort by setting.
@Deprecated("Use per-tab sort by instead")
@HiveField(7)
SortBy sortBy;
/// Current sort order setting.
@Deprecated("Use per-tab sort order instead")
@HiveField(8)
SortOrder sortOrder;
/// Amount of songs to get when shuffling songs.
@HiveField(9, defaultValue: _songShuffleItemCountDefault)
int songShuffleItemCount;
/// The content view type used by the music screen.
@HiveField(10, defaultValue: _contentViewType)
ContentViewType contentViewType;
/// Amount of grid tiles to use per-row when portrait.
@HiveField(11, defaultValue: _contentGridViewCrossAxisCountPortrait)
int contentGridViewCrossAxisCountPortrait;
/// Amount of grid tiles to use per-row when landscape.
@HiveField(12, defaultValue: _contentGridViewCrossAxisCountLandscape)
int contentGridViewCrossAxisCountLandscape;
/// Whether or not to show the text (title, artist etc) on the grid music
/// screen.
@HiveField(13, defaultValue: _showTextOnGridView)
bool showTextOnGridView = _showTextOnGridView;
/// The number of seconds to wait in a sleep timer. This is so that the app
/// can remember the last duration. I'd use a Duration type here but Hive
/// doesn't come with an adapter for it by default.
@HiveField(14, defaultValue: _sleepTimerSeconds)
int sleepTimerSeconds;
@HiveField(15, defaultValue: {})
Map<String, DownloadLocation> downloadLocationsMap;
/// Whether or not to use blurred cover art as background on player screen.
@HiveField(16, defaultValue: _showCoverAsPlayerBackground)
bool showCoverAsPlayerBackground = _showCoverAsPlayerBackground;
@HiveField(17, defaultValue: _hideSongArtistsIfSameAsAlbumArtists)
bool hideSongArtistsIfSameAsAlbumArtists =
_hideSongArtistsIfSameAsAlbumArtists;
@HiveField(18, defaultValue: _bufferDurationSeconds)
int bufferDurationSeconds;
@HiveField(19, defaultValue: _disableGesture)
bool disableGesture = _disableGesture;
@HiveField(20, defaultValue: {})
Map<TabContentType, SortBy> tabSortBy;
@HiveField(21, defaultValue: {})
Map<TabContentType, SortOrder> tabSortOrder;
@HiveField(22, defaultValue: _tabOrder)
List<TabContentType> tabOrder;
@HiveField(23, defaultValue: false)
bool hasCompletedBlurhashImageMigration;
@HiveField(24, defaultValue: false)
bool hasCompletedBlurhashImageMigrationIdFix;
@HiveField(25, defaultValue: _showFastScroller)
bool showFastScroller = _showFastScroller;
@HiveField(26, defaultValue: _swipeInsertQueueNext)
bool swipeInsertQueueNext;
static Future<FinampSettings> create() async {
final internalSongDir = await getInternalSongDir();
final downloadLocation = DownloadLocation.create(
name: "Internal Storage",
path: internalSongDir.path,
useHumanReadableNames: false,
deletable: false,
);
return FinampSettings(
downloadLocations: [],
// Create a map of TabContentType from TabContentType's values.
showTabs: Map.fromEntries(
TabContentType.values.map(
(e) => MapEntry(e, true),
),
),
downloadLocationsMap: {downloadLocation.id: downloadLocation},
tabSortBy: {},
tabSortOrder: {},
);
}
/// Returns the DownloadLocation that is the internal song dir. See the
/// description of the "deletable" property to see how this works. This can
/// technically throw a StateError, but that should never happen™.
DownloadLocation get internalSongDir =>
downloadLocationsMap.values.firstWhere((element) => !element.deletable);
Duration get bufferDuration => Duration(seconds: bufferDurationSeconds);
set bufferDuration(Duration duration) =>
bufferDurationSeconds = duration.inSeconds;
SortBy getTabSortBy(TabContentType tabType) {
return tabSortBy[tabType] ?? SortBy.sortName;
}
SortOrder getSortOrder(TabContentType tabType) {
return tabSortOrder[tabType] ?? SortOrder.ascending;
}
bool get shouldRunBlurhashImageMigrationIdFix =>
hasCompletedBlurhashImageMigration &&
!hasCompletedBlurhashImageMigrationIdFix;
}
/// Custom storage locations for storing music.
@HiveType(typeId: 31)
class DownloadLocation {
DownloadLocation(
{required this.name,
required this.path,
required this.useHumanReadableNames,
required this.deletable,
required this.id});
/// Human-readable name for the path (shown in settings)
@HiveField(0)
String name;
/// The path. We store this as a string since it's easier to put into Hive.
@HiveField(1)
String path;
/// If true, store songs using their actual names instead of Jellyfin item IDs.
@HiveField(2)
bool useHumanReadableNames;
/// If true, the user can delete this storage location. It's a bit of a hack,
/// but the only undeletable location is the internal storage dir, so we can
/// use this value to get the internal song dir.
@HiveField(3)
bool deletable;
/// Unique ID for the DownloadLocation. If this DownloadLocation was created
/// before 0.6, it will be "0", very temporarily until it is changed on
/// startup.
@HiveField(4, defaultValue: "0")
String id;
/// Initialises a new DownloadLocation. id will be a UUID.
static DownloadLocation create({
required String name,
required String path,
required bool useHumanReadableNames,
required bool deletable,
}) {
return DownloadLocation(
name: name,
path: path,
useHumanReadableNames: useHumanReadableNames,
deletable: deletable,
id: const Uuid().v4(),
);
}
}
/// Class used in AddDownloadLocationScreen. Basically just a DownloadLocation
/// with nullable values. Shouldn't be used for actually storing download
/// locations.
class NewDownloadLocation {
NewDownloadLocation({
this.name,
this.path,
this.useHumanReadableNames,
required this.deletable,
});
String? name;
String? path;
bool? useHumanReadableNames;
bool deletable;
}
/// Supported tab types in MusicScreenTabView.
@HiveType(typeId: 36)
enum TabContentType {
@HiveField(0)
albums,
@HiveField(1)
artists,
@HiveField(2)
playlists,
@HiveField(3)
genres,
@HiveField(4)
songs;
/// Human-readable version of the [TabContentType]. For example, toString() on
/// [TabContentType.songs], toString() would return "TabContentType.songs".
/// With this function, the same input would return "Songs".
@override
@Deprecated("Use toLocalisedString when possible")
String toString() => _humanReadableName(this);
String toLocalisedString(BuildContext context) =>
_humanReadableLocalisedName(this, context);
String _humanReadableName(TabContentType tabContentType) {
switch (tabContentType) {
case TabContentType.songs:
return "Songs";
case TabContentType.albums:
return "Albums";
case TabContentType.artists:
return "Artists";
case TabContentType.genres:
return "Genres";
case TabContentType.playlists:
return "Playlists";
}
}
String _humanReadableLocalisedName(
TabContentType tabContentType, BuildContext context) {
switch (tabContentType) {
case TabContentType.songs:
return AppLocalizations.of(context)!.songs;
case TabContentType.albums:
return AppLocalizations.of(context)!.albums;
case TabContentType.artists:
return AppLocalizations.of(context)!.artists;
case TabContentType.genres:
return AppLocalizations.of(context)!.genres;
case TabContentType.playlists:
return AppLocalizations.of(context)!.playlists;
}
}
}
@HiveType(typeId: 39)
enum ContentViewType {
@HiveField(0)
list,
@HiveField(1)
grid;
/// Human-readable version of this enum. I've written longer descriptions on
/// enums like [TabContentType], and I can't be bothered to copy and paste it
/// again.
@override
@Deprecated("Use toLocalisedString when possible")
String toString() => _humanReadableName(this);
String toLocalisedString(BuildContext context) =>
_humanReadableLocalisedName(this, context);
String _humanReadableName(ContentViewType contentViewType) {
switch (contentViewType) {
case ContentViewType.list:
return "List";
case ContentViewType.grid:
return "Grid";
}
}
String _humanReadableLocalisedName(
ContentViewType contentViewType, BuildContext context) {
switch (contentViewType) {
case ContentViewType.list:
return AppLocalizations.of(context)!.list;
case ContentViewType.grid:
return AppLocalizations.of(context)!.grid;
}
}
}
@HiveType(typeId: 3)
@JsonSerializable(
explicitToJson: true,
anyMap: true,
)
class DownloadedSong {
DownloadedSong({
required this.song,
required this.mediaSourceInfo,
required this.downloadId,
required this.requiredBy,
required this.path,
required this.useHumanReadableNames,
required this.viewId,
this.isPathRelative = true,
required this.downloadLocationId,
});
/// The Jellyfin item for the song
@HiveField(0)
BaseItemDto song;
/// The media source info for the song (used to get file format)
@HiveField(1)
MediaSourceInfo mediaSourceInfo;
/// The download ID of the song (for FlutterDownloader)
@HiveField(2)
String downloadId;
/// The list of parent item IDs the item is downloaded for. If this is 0, the
/// song should be deleted.
@HiveField(3)
List<String> requiredBy;
/// The path of the song file. if [isPathRelative] is true, this will be a
/// relative path from the song's DownloadLocation.
@HiveField(4)
String path;
/// Whether or not the file is stored with a human readable name. We need this
/// when deleting downloads, as we need to check for empty folders when
/// deleting files with human readable names.
@HiveField(5)
bool useHumanReadableNames;
/// The view that this download is in. Used for sorting in offline mode.
@HiveField(6)
String viewId;
/// Whether or not [path] is relative.
@HiveField(7, defaultValue: false)
bool isPathRelative;
/// The ID of the DownloadLocation that holds this file. Will be null if made
/// before 0.6.
@HiveField(8)
String? downloadLocationId;
File get file {
if (isPathRelative) {
final downloadLocation = FinampSettingsHelper
.finampSettings.downloadLocationsMap[downloadLocationId];
if (downloadLocation == null) {
throw "DownloadLocation was null in file getter for DownloadsSong!";
}
return File(path_helper.join(downloadLocation.path, path));
}
return File(path);
}
DownloadLocation? get downloadLocation => FinampSettingsHelper
.finampSettings.downloadLocationsMap[downloadLocationId];
Future<DownloadTask?> get downloadTask async {
final tasks = await FlutterDownloader.loadTasksWithRawQuery(
query: "SELECT * FROM task WHERE task_id = '$downloadId'");
if (tasks?.isEmpty == false) {
return tasks!.first;
}
return null;
}
factory DownloadedSong.fromJson(Map<String, dynamic> json) =>
_$DownloadedSongFromJson(json);
Map<String, dynamic> toJson() => _$DownloadedSongToJson(this);
}
@HiveType(typeId: 4)
class DownloadedParent {
DownloadedParent({
required this.item,
required this.downloadedChildren,
required this.viewId,
});
@HiveField(0)
BaseItemDto item;
@HiveField(1)
Map<String, BaseItemDto> downloadedChildren;
/// The view that this download is in. Used for sorting in offline mode.
@HiveField(2)
String viewId;
}
@HiveType(typeId: 40)
class DownloadedImage {
DownloadedImage({
required this.id,
required this.downloadId,
required this.path,
required this.requiredBy,
required this.downloadLocationId,
});
/// The image ID
@HiveField(0)
String id;
/// The download ID of the song (for FlutterDownloader)
@HiveField(1)
String downloadId;
/// The relative path to the image file. To get the absolute path, use the
/// file getter.
@HiveField(2)
String path;
/// The list of item IDs that use this image. If this is empty, the image
/// should be deleted.
/// TODO: Investigate adding set support to Hive
@HiveField(3)
List<String> requiredBy;
/// The ID of the DownloadLocation that holds this file.
@HiveField(4)
String downloadLocationId;
DownloadLocation? get downloadLocation => FinampSettingsHelper
.finampSettings.downloadLocationsMap[downloadLocationId];
File get file {
if (downloadLocation == null) {
throw "Download location is null for image $id, this shouldn't happen...";
}
return File(path_helper.join(downloadLocation!.path, path));
}
Future<DownloadTask?> get downloadTask async {
final tasks = await FlutterDownloader.loadTasksWithRawQuery(
query: "SELECT * FROM task WHERE task_id = '$downloadId'");
if (tasks?.isEmpty == false) {
return tasks!.first;
}
return null;
}
/// Creates a new DownloadedImage. Does not actually handle downloading or
/// anything. This is only really a thing since having to manually specify
/// empty lists is a bit jank.
static DownloadedImage create({
required String id,
required String downloadId,
required String path,
List<String>? requiredBy,
required String downloadLocationId,
}) =>
DownloadedImage(
id: id,
downloadId: downloadId,
path: path,
requiredBy: requiredBy ?? [],
downloadLocationId: downloadLocationId,
);
}
@HiveType(typeId: 43)
class OfflineListen {
OfflineListen({
required this.timestamp,
required this.userId,
required this.itemId,
required this.name,
this.artist,
this.album,
this.trackMbid,
});
/// The stop timestamp of the listen, measured in seconds since the epoch.
@HiveField(0)
int timestamp;
@HiveField(1)
String userId;
@HiveField(2)
String itemId;
@HiveField(3)
String name;
@HiveField(4)
String? artist;
@HiveField(5)
String? album;
// The MusicBrainz ID of the track, if available.
@HiveField(6)
String? trackMbid;
}
+558
View File
@@ -0,0 +1,558 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'finamp_models.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class FinampUserAdapter extends TypeAdapter<FinampUser> {
@override
final int typeId = 8;
@override
FinampUser read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return FinampUser(
id: fields[0] as String,
baseUrl: fields[1] as String,
accessToken: fields[2] as String,
serverId: fields[3] as String,
currentViewId: fields[4] as String?,
views: (fields[5] as Map).cast<String, BaseItemDto>(),
);
}
@override
void write(BinaryWriter writer, FinampUser obj) {
writer
..writeByte(6)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.baseUrl)
..writeByte(2)
..write(obj.accessToken)
..writeByte(3)
..write(obj.serverId)
..writeByte(4)
..write(obj.currentViewId)
..writeByte(5)
..write(obj.views);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FinampUserAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class FinampSettingsAdapter extends TypeAdapter<FinampSettings> {
@override
final int typeId = 28;
@override
FinampSettings read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return FinampSettings(
isOffline: fields[0] as bool,
shouldTranscode: fields[1] as bool,
transcodeBitrate: fields[2] as int,
downloadLocations: (fields[3] as List).cast<DownloadLocation>(),
androidStopForegroundOnPause: fields[4] as bool,
showTabs: (fields[5] as Map).cast<TabContentType, bool>(),
isFavourite: fields[6] as bool,
sortBy: fields[7] as SortBy,
sortOrder: fields[8] as SortOrder,
songShuffleItemCount: fields[9] == null ? 250 : fields[9] as int,
contentViewType: fields[10] == null
? ContentViewType.list
: fields[10] as ContentViewType,
contentGridViewCrossAxisCountPortrait:
fields[11] == null ? 2 : fields[11] as int,
contentGridViewCrossAxisCountLandscape:
fields[12] == null ? 3 : fields[12] as int,
showTextOnGridView: fields[13] == null ? true : fields[13] as bool,
sleepTimerSeconds: fields[14] == null ? 1800 : fields[14] as int,
downloadLocationsMap: fields[15] == null
? {}
: (fields[15] as Map).cast<String, DownloadLocation>(),
showCoverAsPlayerBackground:
fields[16] == null ? true : fields[16] as bool,
hideSongArtistsIfSameAsAlbumArtists:
fields[17] == null ? true : fields[17] as bool,
bufferDurationSeconds: fields[18] == null ? 50 : fields[18] as int,
tabSortBy: fields[20] == null
? {}
: (fields[20] as Map).cast<TabContentType, SortBy>(),
tabSortOrder: fields[21] == null
? {}
: (fields[21] as Map).cast<TabContentType, SortOrder>(),
tabOrder: fields[22] == null
? [
TabContentType.albums,
TabContentType.artists,
TabContentType.playlists,
TabContentType.genres,
TabContentType.songs
]
: (fields[22] as List).cast<TabContentType>(),
hasCompletedBlurhashImageMigration:
fields[23] == null ? false : fields[23] as bool,
hasCompletedBlurhashImageMigrationIdFix:
fields[24] == null ? false : fields[24] as bool,
swipeInsertQueueNext: fields[26] == null ? false : fields[26] as bool,
)
..disableGesture = fields[19] == null ? false : fields[19] as bool
..showFastScroller = fields[25] == null ? true : fields[25] as bool;
}
@override
void write(BinaryWriter writer, FinampSettings obj) {
writer
..writeByte(27)
..writeByte(0)
..write(obj.isOffline)
..writeByte(1)
..write(obj.shouldTranscode)
..writeByte(2)
..write(obj.transcodeBitrate)
..writeByte(3)
..write(obj.downloadLocations)
..writeByte(4)
..write(obj.androidStopForegroundOnPause)
..writeByte(5)
..write(obj.showTabs)
..writeByte(6)
..write(obj.isFavourite)
..writeByte(7)
..write(obj.sortBy)
..writeByte(8)
..write(obj.sortOrder)
..writeByte(9)
..write(obj.songShuffleItemCount)
..writeByte(10)
..write(obj.contentViewType)
..writeByte(11)
..write(obj.contentGridViewCrossAxisCountPortrait)
..writeByte(12)
..write(obj.contentGridViewCrossAxisCountLandscape)
..writeByte(13)
..write(obj.showTextOnGridView)
..writeByte(14)
..write(obj.sleepTimerSeconds)
..writeByte(15)
..write(obj.downloadLocationsMap)
..writeByte(16)
..write(obj.showCoverAsPlayerBackground)
..writeByte(17)
..write(obj.hideSongArtistsIfSameAsAlbumArtists)
..writeByte(18)
..write(obj.bufferDurationSeconds)
..writeByte(19)
..write(obj.disableGesture)
..writeByte(20)
..write(obj.tabSortBy)
..writeByte(21)
..write(obj.tabSortOrder)
..writeByte(22)
..write(obj.tabOrder)
..writeByte(23)
..write(obj.hasCompletedBlurhashImageMigration)
..writeByte(24)
..write(obj.hasCompletedBlurhashImageMigrationIdFix)
..writeByte(25)
..write(obj.showFastScroller)
..writeByte(26)
..write(obj.swipeInsertQueueNext);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FinampSettingsAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class DownloadLocationAdapter extends TypeAdapter<DownloadLocation> {
@override
final int typeId = 31;
@override
DownloadLocation read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DownloadLocation(
name: fields[0] as String,
path: fields[1] as String,
useHumanReadableNames: fields[2] as bool,
deletable: fields[3] as bool,
id: fields[4] == null ? '0' : fields[4] as String,
);
}
@override
void write(BinaryWriter writer, DownloadLocation obj) {
writer
..writeByte(5)
..writeByte(0)
..write(obj.name)
..writeByte(1)
..write(obj.path)
..writeByte(2)
..write(obj.useHumanReadableNames)
..writeByte(3)
..write(obj.deletable)
..writeByte(4)
..write(obj.id);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DownloadLocationAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class DownloadedSongAdapter extends TypeAdapter<DownloadedSong> {
@override
final int typeId = 3;
@override
DownloadedSong read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DownloadedSong(
song: fields[0] as BaseItemDto,
mediaSourceInfo: fields[1] as MediaSourceInfo,
downloadId: fields[2] as String,
requiredBy: (fields[3] as List).cast<String>(),
path: fields[4] as String,
useHumanReadableNames: fields[5] as bool,
viewId: fields[6] as String,
isPathRelative: fields[7] == null ? false : fields[7] as bool,
downloadLocationId: fields[8] as String?,
);
}
@override
void write(BinaryWriter writer, DownloadedSong obj) {
writer
..writeByte(9)
..writeByte(0)
..write(obj.song)
..writeByte(1)
..write(obj.mediaSourceInfo)
..writeByte(2)
..write(obj.downloadId)
..writeByte(3)
..write(obj.requiredBy)
..writeByte(4)
..write(obj.path)
..writeByte(5)
..write(obj.useHumanReadableNames)
..writeByte(6)
..write(obj.viewId)
..writeByte(7)
..write(obj.isPathRelative)
..writeByte(8)
..write(obj.downloadLocationId);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DownloadedSongAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class DownloadedParentAdapter extends TypeAdapter<DownloadedParent> {
@override
final int typeId = 4;
@override
DownloadedParent read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DownloadedParent(
item: fields[0] as BaseItemDto,
downloadedChildren: (fields[1] as Map).cast<String, BaseItemDto>(),
viewId: fields[2] as String,
);
}
@override
void write(BinaryWriter writer, DownloadedParent obj) {
writer
..writeByte(3)
..writeByte(0)
..write(obj.item)
..writeByte(1)
..write(obj.downloadedChildren)
..writeByte(2)
..write(obj.viewId);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DownloadedParentAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class DownloadedImageAdapter extends TypeAdapter<DownloadedImage> {
@override
final int typeId = 40;
@override
DownloadedImage read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DownloadedImage(
id: fields[0] as String,
downloadId: fields[1] as String,
path: fields[2] as String,
requiredBy: (fields[3] as List).cast<String>(),
downloadLocationId: fields[4] as String,
);
}
@override
void write(BinaryWriter writer, DownloadedImage obj) {
writer
..writeByte(5)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.downloadId)
..writeByte(2)
..write(obj.path)
..writeByte(3)
..write(obj.requiredBy)
..writeByte(4)
..write(obj.downloadLocationId);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DownloadedImageAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class OfflineListenAdapter extends TypeAdapter<OfflineListen> {
@override
final int typeId = 43;
@override
OfflineListen read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return OfflineListen(
timestamp: fields[0] as int,
userId: fields[1] as String,
itemId: fields[2] as String,
name: fields[3] as String,
artist: fields[4] as String?,
album: fields[5] as String?,
trackMbid: fields[6] as String?,
);
}
@override
void write(BinaryWriter writer, OfflineListen obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.timestamp)
..writeByte(1)
..write(obj.userId)
..writeByte(2)
..write(obj.itemId)
..writeByte(3)
..write(obj.name)
..writeByte(4)
..write(obj.artist)
..writeByte(5)
..write(obj.album)
..writeByte(6)
..write(obj.trackMbid);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is OfflineListenAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class TabContentTypeAdapter extends TypeAdapter<TabContentType> {
@override
final int typeId = 36;
@override
TabContentType read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return TabContentType.albums;
case 1:
return TabContentType.artists;
case 2:
return TabContentType.playlists;
case 3:
return TabContentType.genres;
case 4:
return TabContentType.songs;
default:
return TabContentType.albums;
}
}
@override
void write(BinaryWriter writer, TabContentType obj) {
switch (obj) {
case TabContentType.albums:
writer.writeByte(0);
break;
case TabContentType.artists:
writer.writeByte(1);
break;
case TabContentType.playlists:
writer.writeByte(2);
break;
case TabContentType.genres:
writer.writeByte(3);
break;
case TabContentType.songs:
writer.writeByte(4);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TabContentTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class ContentViewTypeAdapter extends TypeAdapter<ContentViewType> {
@override
final int typeId = 39;
@override
ContentViewType read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return ContentViewType.list;
case 1:
return ContentViewType.grid;
default:
return ContentViewType.list;
}
}
@override
void write(BinaryWriter writer, ContentViewType obj) {
switch (obj) {
case ContentViewType.list:
writer.writeByte(0);
break;
case ContentViewType.grid:
writer.writeByte(1);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ContentViewTypeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DownloadedSong _$DownloadedSongFromJson(Map json) => DownloadedSong(
song:
BaseItemDto.fromJson(Map<String, dynamic>.from(json['song'] as Map)),
mediaSourceInfo: MediaSourceInfo.fromJson(
Map<String, dynamic>.from(json['mediaSourceInfo'] as Map)),
downloadId: json['downloadId'] as String,
requiredBy: (json['requiredBy'] as List<dynamic>)
.map((e) => e as String)
.toList(),
path: json['path'] as String,
useHumanReadableNames: json['useHumanReadableNames'] as bool,
viewId: json['viewId'] as String,
isPathRelative: json['isPathRelative'] as bool? ?? true,
downloadLocationId: json['downloadLocationId'] as String?,
);
Map<String, dynamic> _$DownloadedSongToJson(DownloadedSong instance) =>
<String, dynamic>{
'song': instance.song.toJson(),
'mediaSourceInfo': instance.mediaSourceInfo.toJson(),
'downloadId': instance.downloadId,
'requiredBy': instance.requiredBy,
'path': instance.path,
'useHumanReadableNames': instance.useHumanReadableNames,
'viewId': instance.viewId,
'isPathRelative': instance.isPathRelative,
'downloadLocationId': instance.downloadLocationId,
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
class LocaleAdapter extends TypeAdapter<Locale> {
@override
int get typeId => 42;
@override
Locale read(BinaryReader reader) {
final localeList = reader.readStringList();
final languageCode = localeList[0];
// scriptCode and countryCode are empty strings when null (see write)
final scriptCode = localeList[1] == "" ? null : localeList[1];
final countryCode = localeList[2] == "" ? null : localeList[2];
return Locale.fromSubtags(
languageCode: languageCode,
scriptCode: scriptCode,
countryCode: countryCode,
);
}
@override
void write(BinaryWriter writer, Locale obj) {
writer.writeStringList([
obj.languageCode,
obj.scriptCode ?? "",
obj.countryCode ?? "",
]);
}
}
+46
View File
@@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
class ThemeModeAdapter extends TypeAdapter<ThemeMode> {
@override
final int typeId = 41;
@override
ThemeMode read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return ThemeMode.system;
case 1:
return ThemeMode.light;
case 2:
return ThemeMode.dark;
default:
return ThemeMode.system;
}
}
@override
void write(BinaryWriter writer, ThemeMode obj) {
switch (obj) {
case ThemeMode.system:
writer.writeByte(0);
break;
case ThemeMode.light:
writer.writeByte(1);
break;
case ThemeMode.dark:
writer.writeByte(2);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ThemeModeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}