refactor(core): destructure core folder and reintegrate extensions

This commit is contained in:
Jagandeep Brar
2022-06-14 10:42:47 -04:00
parent 19475ac193
commit 5d52440a43
397 changed files with 2067 additions and 2284 deletions

View File

@@ -1,49 +1 @@
{
"motd": "This release is the first build to be built on Flutter 3, which now supports 120hz displays and is more performant across the board!",
"new": [
{
"module": "-arr",
"changes": [
"(Details) Updated UI for viewing content links"
]
},
{
"module": "LunaSea",
"changes": [
"Support 120hz displays"
]
},
{
"module": "Notifications",
"changes": [
"Option to disable in-app notifications"
]
},
{
"module": "Radarr",
"changes": [
"(Links) Added Letterboxd"
]
},
{
"module": "Settings",
"changes": [
"(System) Ability to check for updates"
]
}
],
"fixes": [
{
"module": "-arr",
"changes": [
"(Releases) Correctly calculate age of releases"
]
},
{
"module": "UI/UX",
"changes": [
"(Scrollbar) Enable interactivity on all platforms"
]
}
]
}
{}

View File

@@ -61,7 +61,6 @@
"lunasea.New": "Neu",
"lunasea.NoModulesEnabled": "Keine Module aktiviert",
"lunasea.NotSet": "Nicht gesetzt",
"lunasea.OneHourAgo": "Vor einer Stunde",
"lunasea.Options": "Optionen",
"lunasea.Page": "Seite",
"lunasea.PlatformSpecific": "Plattformspezifisch",

View File

@@ -41,8 +41,11 @@
"lunasea.ComingSoon": "Coming Soon",
"lunasea.Copied": "Copied",
"lunasea.CopiedContentToTheClipboard": "Copied Content to the Clipboard",
"lunasea.Critical": "Critical",
"lunasea.Dashboard": "Dashboard",
"lunasea.Days": "{} Days",
"lunasea.DaysAgo": "{} Days Ago",
"lunasea.Debug": "Debug",
"lunasea.Delete": "Delete",
"lunasea.Descending": "Descending",
"lunasea.Develop": "Develop",
@@ -50,6 +53,7 @@
"lunasea.Disabled": "Disabled",
"lunasea.Dismiss": "Dismiss",
"lunasea.Edge": "Edge",
"lunasea.Error": "Error",
"lunasea.ExternalModules": "External Modules",
"lunasea.Fixes": "Fixes",
"lunasea.FullChangelog": "Full Changelog",
@@ -57,19 +61,23 @@
"lunasea.GoToSettings": "Go to Settings",
"lunasea.GridView": "Grid View",
"lunasea.Home": "Home",
"lunasea.Hours": "{} Hours",
"lunasea.HoursAgo": "{} Hours Ago",
"lunasea.IncorrectEncryptionKey": "Incorrect encryption key",
"lunasea.Internal": "Internal",
"lunasea.InvalidFileTypeSelected": "Invalid File Type Selected",
"lunasea.JustNow": "Just Now",
"lunasea.Minutes": "{} Minutes",
"lunasea.MinutesAgo": "{} Minutes Ago",
"lunasea.Module": "Module",
"lunasea.ModuleIsNotEnabled": "{} Is Not Enabled",
"lunasea.New": "New",
"lunasea.NotSet": "Not Set",
"lunasea.NoModulesEnabled": "No Modules Enabled",
"lunasea.OneHourAgo": "1 Hour Ago",
"lunasea.OneMinuteAgo": "1 Minute Ago",
"lunasea.OneDay": "1 Day",
"lunasea.OneHour": "1 Hour",
"lunasea.OneMinute": "1 Minute",
"lunasea.OneSecond": "1 Second",
"lunasea.Options": "Options",
"lunasea.Page": "Page",
"lunasea.PlatformSpecific": "Platform-Specific",
@@ -83,6 +91,8 @@
"lunasea.Retry": "Retry",
"lunasea.ReturnToDashboard": "Return to Dashboard",
"lunasea.SearchTextBar": "Search…",
"lunasea.Seconds": "{} Seconds",
"lunasea.SecondsAgo": "{} Seconds Ago",
"lunasea.Set": "Set",
"lunasea.Settings": "Settings",
"lunasea.Stable": "Stable",
@@ -90,12 +100,14 @@
"lunasea.TransactionFailure": "Transaction Failure",
"lunasea.TryAgain": "Try Again",
"lunasea.Tweaks": "Tweaks",
"lunasea.UnderAMinute": "Under a Minute",
"lunasea.Unknown": "Unknown",
"lunasea.UnknownDate": "Unknown Date",
"lunasea.UnknownError": "Unknown Error",
"lunasea.UnknownModule": "Unknown Module",
"lunasea.Update": "Update",
"lunasea.View": "View",
"lunasea.Warning": "Warning",
"lunasea.Website": "Website",
"overseerr.Audio": "Audio",
"overseerr.Approved": "Approved",
@@ -522,6 +534,7 @@
"settings.UsernameValidation": "Username Required",
"settings.Version": "Version",
"settings.ViewRecentChanges": "View Recent Changes",
"settings.ViewTypeLogs": "View {} Logs",
"settings.WeblateDescription": "Help Localize LunaSea",
"settings.Website": "Website",
"settings.WebsiteDescription": "Visit LunaSea's Website",

View File

@@ -64,7 +64,6 @@
"lunasea.New": "Nouveau",
"lunasea.NoModulesEnabled": "Aucun module activé",
"lunasea.NotSet": "Non défini",
"lunasea.OneHourAgo": "Il y a une heure",
"lunasea.Options": "Options",
"lunasea.Page": "Page",
"lunasea.PlatformSpecific": "Spécifique à la plateforme",

View File

@@ -64,7 +64,6 @@
"lunasea.New": "Új",
"lunasea.NoModulesEnabled": "Nincs engedélyezett modul",
"lunasea.NotSet": "Nincs beállítva",
"lunasea.OneHourAgo": "1 órával ezelőtt",
"lunasea.Options": "Opciók",
"lunasea.Page": "Oldal",
"lunasea.PlatformSpecific": "Platform-specifikus",

View File

@@ -62,7 +62,6 @@
"lunasea.New": "Nuovo",
"lunasea.NoModulesEnabled": "Nessun modulo abilitato",
"lunasea.NotSet": "Non impostato",
"lunasea.OneHourAgo": "1 ora fa",
"lunasea.Options": "Opzioni",
"lunasea.Page": "Pagina",
"lunasea.PlatformSpecific": "Specifico della piattaforma",

View File

@@ -59,7 +59,6 @@
"lunasea.New": "Ny",
"lunasea.NoModulesEnabled": "Ingen påslåtte moduler",
"lunasea.NotSet": "Ikke satt",
"lunasea.OneHourAgo": "Én time siden",
"lunasea.Options": "Innstillinger",
"lunasea.Page": "Side",
"lunasea.PlatformSpecific": "Plattformsspesifikk",

View File

@@ -64,7 +64,6 @@
"lunasea.New": "Yeni",
"lunasea.NoModulesEnabled": "Etkin Modül Yok",
"lunasea.NotSet": "Ayarlanmadı",
"lunasea.OneHourAgo": "1 Saat Önce",
"lunasea.Options": "Seçenekler",
"lunasea.Page": "Sayfa",
"lunasea.PlatformSpecific": "Platforma Özgü",

View File

@@ -64,7 +64,6 @@
"lunasea.New": "新的",
"lunasea.NoModulesEnabled": "未启用模块",
"lunasea.NotSet": "未设置",
"lunasea.OneHourAgo": "1小时前",
"lunasea.Options": "选项",
"lunasea.Page": "页",
"lunasea.PlatformSpecific": "平台特定",

View File

@@ -1,32 +1,19 @@
export 'core/cache/memory_lru.dart';
export 'core/cache/memory_store.dart';
export 'core/extensions/async_snapshot.dart';
export 'core/extensions/datetime.dart';
export 'core/extensions/duration.dart';
export 'core/extensions/iap_product_details.dart';
export 'core/extensions/int.dart';
export 'core/extensions/navigator_state.dart';
export 'core/extensions/page_controller.dart';
export 'core/extensions/scroll_controller.dart';
export 'core/models/configuration.dart';
export 'core/models/logs.dart';
export 'core/models/types.dart';
export 'core/models/webhooks.dart';
export 'core/pages/invalid_route.dart';
export 'core/pages/not_enabled.dart';
export 'core/router/module_router.dart';
export 'core/router/page_router.dart';
export 'core/router/router.dart';
export 'core/services/the_movie_db.dart';
export 'core/state/module_state.dart';
export 'core/state/state.dart';
export 'core/utilities/changelog.dart';
export 'core/utilities/dialogs.dart';
/// This file is deprecated and should no longer be actively used.
/// All imports should happen directly and canonical export files will not be used anymore.
export 'deprecated/extensions/datetime.dart';
export 'deprecated/state/module_state.dart';
export 'deprecated/state/state.dart';
export 'deprecated/router/module_router.dart';
export 'deprecated/router/page_router.dart';
export 'deprecated/router/router.dart';
export 'types/loading_state.dart';
export 'database/box.dart';
export 'database/models/profile.dart';
export 'database/tables/lunasea.dart';
export 'system/logger.dart';
export 'system/profile.dart';
export 'utils/dialogs.dart';
export 'widgets/ui.dart';
export 'modules.dart';
export 'vendor.dart'

View File

@@ -1,7 +0,0 @@
import 'package:flutter/material.dart';
extension LunaAsyncSnapshotExtension on AsyncSnapshot {
bool get lunaIsFinishedWithData {
return this.connectionState == ConnectionState.done && this.hasData;
}
}

View File

@@ -1,33 +0,0 @@
import 'package:lunasea/core.dart';
extension DurationExtension on Duration {
/// Return a string representation of the timestamp.
/// `HH:MM:SS` or `MM:SS` if the duration is less than one hour.
String get lunaTimestamp {
return [
if (this.inHours != 0) this.inHours.toString().padLeft(2, '0'),
(this.inMinutes % 60).toString().padLeft(2, '0'),
(this.inSeconds % 60).toString().padLeft(2, '0'),
].join(':');
}
/// Returns a string representation of the timestamp as words.
///
/// Format example: 1 Day, 23 Hours, 10 Minutes
String get lunaTimestampWords {
String? days, hours, minutes;
if (this.inDays == 1) days = '1 Day';
if (this.inDays > 1) days = '${this.inDays.toString()} Days';
if (this.inHours == 1) hours = '1 Hour';
if (this.inHours > 1) hours = '${(this.inHours % 24).toString()} Hours';
if (this.inMinutes == 1) minutes = '1 Minute';
if (this.inMinutes > 1)
minutes = '${(this.inMinutes % 60).toString()} Minutes';
return [
if (days != null) days,
if (hours != null) hours,
if (minutes != null) minutes,
if (minutes == null) LunaUI.TEXT_EMDASH,
].join(' ');
}
}

View File

@@ -1,80 +0,0 @@
extension IntegerBytesExtension on int? {
static const _BIT_SIZES = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb'];
static const _BYTE_SIZES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
String _bytesToString({int decimals = 2, position = 0, bool bytes = true}) {
if (this == null || this! <= 0) return '${0.toStringAsFixed(decimals)} B';
int chunk = bytes ? 1024 : 1000;
double size = this!.toDouble();
while (size > chunk) {
size /= chunk;
position++;
}
return '${size.toStringAsFixed(decimals)} ${bytes ? _BYTE_SIZES[position] : _BIT_SIZES[position]}';
}
/// Given an integer of the number of bytes/bits, return a string representation.
String lunaBytesToString({int decimals = 2, bool bytes = true}) =>
_bytesToString(decimals: decimals, position: 0, bytes: bytes);
/// Given an integer of the number of kilobytes/kilobits, return a string representation.
String lunaKilobytesToString({int decimals = 2, bool bytes = true}) =>
_bytesToString(decimals: decimals, position: 1, bytes: bytes);
/// Given an integer of the number of megabytes/megabits, return a string representation.
String lunaMegabytesToString({int decimals = 2, bool bytes = true}) =>
_bytesToString(decimals: decimals, position: 2, bytes: bytes);
/// Given an integer of the number of gigabytes/gigabits, return a string representation.
String lunaGigabytesToString({int decimals = 2, bool bytes = true}) =>
_bytesToString(decimals: decimals, position: 3, bytes: bytes);
}
extension IntegerTimeExtension on int? {
static const int _MINUTE_IN_SECONDS = 60;
static const int _HOUR_IN_SECONDS = 60 * 60;
static const int _DAY_IN_SECONDS = 60 * 60 * 24;
/// Given an integer duration, converts it to a readable string. Assumes the duration is in seconds, use the `divisor` and `multipler` variables to easily modify the input.
///
/// Only contains the highest factor (2.5 Days would return 2 Days).
String lunaDuration({int divisor = 1, int multiplier = 1}) {
if (this == null) return '';
int duration = ((this! * multiplier) / divisor).floor();
int days = (duration / _DAY_IN_SECONDS).floor();
if (days > 0) return '$days ${days == 1 ? 'Day' : 'Days'}';
int hours = (duration / _HOUR_IN_SECONDS).floor();
if (hours > 0) return '$hours ${hours == 1 ? 'Hour' : 'Hours'}';
int minutes = (duration / _MINUTE_IN_SECONDS).floor();
if (minutes > 0) return '$minutes ${minutes == 1 ? 'Minute' : 'Minutes'}';
return '$duration ${duration == 1 ? 'Second' : 'Seconds'}';
}
/// Given an integer duration, converts it to a readable runtime string. Assumes the duration is in seconds, use the `divisor` and `multipler` variables to easily modify the input.
///
/// Format example: 1h 23m
String lunaRuntime({int divisor = 1, int multiplier = 1}) {
if (this == null || this == 0) return '';
double runtime = ((this! * multiplier) / divisor);
if (runtime < 60) return '${runtime.floor()}m';
return '${(runtime / 60).floor()}h ${(runtime - ((runtime / 60).floor() * 60)).floor()}m';
}
/// Given an integer duration, converts it to a readable timestamp string. Assumes the duration is in seconds, use the `divisor` and `multiplier` variables to easily modify the input.
///
/// Format example: 1:23:45 (1 hour, 23 minutes, and 45 seconds)
String lunaTimestamp({int divisor = 1, int multiplier = 1}) {
if (this == null) return '';
int duration = ((this! * multiplier) / divisor).floor();
int hours = 0, minutes = 0;
while (duration >= _HOUR_IN_SECONDS) {
duration -= _HOUR_IN_SECONDS;
hours++;
}
while (duration >= _MINUTE_IN_SECONDS) {
duration -= _MINUTE_IN_SECONDS;
minutes++;
}
return '${hours.toString().padLeft(1, '0')}:${minutes.toString().padLeft(2, '0')}:${duration.toString().padLeft(2, '0')}';
}
}

View File

@@ -1,14 +0,0 @@
import 'package:flutter/material.dart';
extension NavigatorStateExtension on NavigatorState {
/// Safely pops the route by first checking that the route *can* be popped.
/// If the route can not be popped (it is the first/lowest route in the navigator), nothing occurs.
void lunaSafetyPop() {
if (this.canPop()) this.pop();
}
/// Pops every route until the route is the first/lowest route in the navigator.
void lunaPopToFirst() {
this.popUntil((route) => route.isFirst);
}
}

View File

@@ -1,16 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
extension ScrollControllerExtension on ScrollController {
/// Animate a [Scrollable] back to 1.00, with a predefined duration and curve.
Future<void> lunaAnimateToStart() async {
if (this.hasClients)
this.animateTo(
0.00,
duration: const Duration(
milliseconds: LunaUI.ANIMATION_SPEED_SCROLLING,
),
curve: Curves.easeInOutQuart,
);
}
}

View File

@@ -1,7 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/vendor.dart';
abstract class LunaPageGoRouter {
GoRoute get route;
Future<void> go(BuildContext context);
}

View File

@@ -1,3 +0,0 @@
export 'configuration/external_module.dart';
export 'configuration/indexer.dart';
export 'configuration/profile.dart';

View File

@@ -1,49 +0,0 @@
import 'package:lunasea/core.dart';
part 'external_module.g.dart';
/// Hive database object containing all information on an indexer
@HiveType(typeId: 26, adapterName: 'ExternalModuleHiveObjectAdapter')
class ExternalModuleHiveObject extends HiveObject {
/// Create a new [ExternalModuleHiveObject] object with all fields set to empty values('', false, 0, {}, etc.)
factory ExternalModuleHiveObject.empty() => ExternalModuleHiveObject(
displayName: '',
host: '',
);
/// Create a new [ExternalModuleHiveObject] from another [ExternalModuleHiveObject] (deep-copy).
factory ExternalModuleHiveObject.fromExternalModuleHiveObject(
ExternalModuleHiveObject indexer) =>
ExternalModuleHiveObject(
displayName: indexer.displayName,
host: indexer.host,
);
/// Create a new [ExternalModuleHiveObject] from a map where the keys map 1-to-1 except for "key", which was the old key for apiKey.
///
/// - Does _not_ do type checking, and will throw an error if the type is invalid.
/// - If the key is null, sets to the "empty" value
factory ExternalModuleHiveObject.fromMap(Map module) =>
ExternalModuleHiveObject(
displayName: module['displayName'] ?? '',
host: module['host'] ?? '',
);
ExternalModuleHiveObject({
required this.displayName,
required this.host,
});
@override
String toString() => toMap().toString();
Map<String, dynamic> toMap() => {
"displayName": displayName ?? '',
"host": host ?? '',
};
@HiveField(0)
String? displayName;
@HiveField(1)
String? host;
}

View File

@@ -1,68 +0,0 @@
import 'package:lunasea/core.dart';
part 'indexer.g.dart';
/// Hive database object containing all information on an indexer
@HiveType(typeId: 1, adapterName: 'IndexerHiveObjectAdapter')
class IndexerHiveObject extends HiveObject {
/// Create a new [IndexerHiveObject] object with all fields set to empty values('', false, 0, {}, etc.)
factory IndexerHiveObject.empty() => IndexerHiveObject(
displayName: '',
host: '',
apiKey: '',
headers: {},
icon: null,
);
/// Create a new [IndexerHiveObject] from another [IndexerHiveObject] (deep-copy).
factory IndexerHiveObject.fromIndexerHiveObject(IndexerHiveObject indexer) =>
IndexerHiveObject(
displayName: indexer.displayName,
host: indexer.host,
apiKey: indexer.apiKey,
headers: indexer.headers,
icon: indexer.icon,
);
/// Create a new [IndexerHiveObject] from a map where the keys map 1-to-1 except for "key", which was the old key for apiKey.
///
/// - Does _not_ do type checking, and will throw an error if the type is invalid.
/// - If the key is null, sets to the "empty" value
factory IndexerHiveObject.fromMap(Map indexer) => IndexerHiveObject(
displayName: indexer['displayName'] ?? '',
host: indexer['host'] ?? '',
apiKey: indexer['key'] ?? '',
headers: indexer['headers'] ?? {},
icon: LunaIndexerIcon.GENERIC.fromKey(indexer['icon'] ?? ''),
);
IndexerHiveObject({
required this.displayName,
required this.host,
required this.apiKey,
required this.headers,
required this.icon,
});
@override
String toString() => toMap().toString();
Map<String, dynamic> toMap() => {
"displayName": displayName ?? '',
"host": host ?? '',
"key": apiKey ?? '',
"headers": headers ?? {},
"icon": icon?.key ?? '',
};
@HiveField(0)
String? displayName;
@HiveField(1)
String? host;
@HiveField(2)
String? apiKey;
@HiveField(3)
Map? headers;
@HiveField(4)
LunaIndexerIcon? icon;
}

View File

@@ -1,422 +0,0 @@
import 'package:lunasea/core.dart';
part 'profile.g.dart';
/// Hive database object containing all profile fields
@HiveType(typeId: 0, adapterName: 'ProfileHiveObjectAdapter')
class ProfileHiveObject extends HiveObject {
/// Create a new [ProfileHiveObject] object with all fields set to empty values('', false, 0, {}, etc.)
factory ProfileHiveObject.empty() => ProfileHiveObject(
//Lidarr
lidarrEnabled: false,
lidarrHost: '',
lidarrKey: '',
lidarrHeaders: {},
//Radarr
radarrEnabled: false,
radarrHost: '',
radarrKey: '',
radarrHeaders: {},
//Sonarr
sonarrEnabled: false,
sonarrHost: '',
sonarrKey: '',
sonarrHeaders: {},
//SABnzbd
sabnzbdEnabled: false,
sabnzbdHost: '',
sabnzbdKey: '',
sabnzbdHeaders: {},
//NZBGet
nzbgetEnabled: false,
nzbgetHost: '',
nzbgetUser: '',
nzbgetPass: '',
nzbgetHeaders: {},
//Wake on LAN
wakeOnLANEnabled: false,
wakeOnLANBroadcastAddress: '',
wakeOnLANMACAddress: '',
//Tautulli
tautulliEnabled: false,
tautulliHost: '',
tautulliKey: '',
tautulliHeaders: {},
//Ombi
ombiEnabled: false,
ombiHost: '',
ombiKey: '',
ombiHeaders: {},
//Overseerr
overseerrEnabled: false,
overseerrHost: '',
overseerrKey: '',
overseerrHeaders: {},
);
/// Create a new [ProfileHiveObject] from another [ProfileHiveObject] (deep-copy).
factory ProfileHiveObject.fromProfileHiveObject(ProfileHiveObject profile) =>
ProfileHiveObject(
//Lidarr
lidarrEnabled: profile.lidarrEnabled,
lidarrHost: profile.lidarrHost,
lidarrKey: profile.lidarrKey,
lidarrHeaders: profile.lidarrHeaders,
//Radarr
radarrEnabled: profile.radarrEnabled,
radarrHost: profile.radarrHost,
radarrKey: profile.radarrKey,
radarrHeaders: profile.radarrHeaders,
//Sonarr
sonarrEnabled: profile.sonarrEnabled,
sonarrHost: profile.sonarrHost,
sonarrKey: profile.sonarrKey,
sonarrHeaders: profile.sonarrHeaders,
//SABnzbd
sabnzbdEnabled: profile.sabnzbdEnabled,
sabnzbdHost: profile.sabnzbdHost,
sabnzbdKey: profile.sabnzbdKey,
sabnzbdHeaders: profile.sabnzbdHeaders,
//NZBGet
nzbgetEnabled: profile.nzbgetEnabled,
nzbgetHost: profile.nzbgetHost,
nzbgetUser: profile.nzbgetUser,
nzbgetPass: profile.nzbgetPass,
nzbgetHeaders: profile.nzbgetHeaders,
//Wake On LAN
wakeOnLANEnabled: profile.wakeOnLANEnabled,
wakeOnLANBroadcastAddress: profile.wakeOnLANBroadcastAddress,
wakeOnLANMACAddress: profile.wakeOnLANMACAddress,
//Tautulli
tautulliEnabled: profile.tautulliEnabled,
tautulliHost: profile.tautulliHost,
tautulliKey: profile.tautulliKey,
tautulliHeaders: profile.tautulliHeaders,
//Ombi
ombiEnabled: profile.ombiEnabled,
ombiHost: profile.ombiHost,
ombiKey: profile.ombiKey,
ombiHeaders: profile.ombiHeaders,
//Overseerr
overseerrEnabled: profile.overseerrEnabled,
overseerrHost: profile.overseerrHost,
overseerrKey: profile.overseerrKey,
overseerrHeaders: profile.overseerrHeaders,
);
/// Create a new [ProfileHiveObject] from a map where the keys map 1-to-1.
///
/// - Does _not_ do type checking, and will throw an error if the type is invalid.
/// - If the key is null, sets to the "empty" value
factory ProfileHiveObject.fromMap(Map profile) => ProfileHiveObject(
//Sonarr
sonarrEnabled: profile['sonarrEnabled'] ?? false,
sonarrHost: profile['sonarrHost'] ?? '',
sonarrKey: profile['sonarrKey'] ?? '',
sonarrHeaders: profile['sonarrHeaders'] ?? {},
//Radarr
radarrEnabled: profile['radarrEnabled'] ?? false,
radarrHost: profile['radarrHost'] ?? '',
radarrKey: profile['radarrKey'] ?? '',
radarrHeaders: profile['radarrHeaders'] ?? {},
//Lidarr
lidarrEnabled: profile['lidarrEnabled'] ?? false,
lidarrHost: profile['lidarrHost'] ?? '',
lidarrKey: profile['lidarrKey'] ?? '',
lidarrHeaders: profile['lidarrHeaders'] ?? {},
//SABnzbd
sabnzbdEnabled: profile['sabnzbdEnabled'] ?? false,
sabnzbdHost: profile['sabnzbdHost'] ?? '',
sabnzbdKey: profile['sabnzbdKey'] ?? '',
sabnzbdHeaders: profile['sabnzbdHeaders'] ?? {},
//NZBGet
nzbgetEnabled: profile['nzbgetEnabled'] ?? false,
nzbgetHost: profile['nzbgetHost'] ?? '',
nzbgetUser: profile['nzbgetUser'] ?? '',
nzbgetPass: profile['nzbgetPass'] ?? '',
nzbgetHeaders: profile['nzbgetHeaders'] ?? {},
//Wake on LAN
wakeOnLANEnabled: profile['wakeOnLANEnabled'] ?? false,
wakeOnLANBroadcastAddress: profile['wakeOnLANBroadcastAddress'] ?? '',
wakeOnLANMACAddress: profile['wakeOnLANMACAddress'] ?? '',
//Tautulli
tautulliEnabled: profile['tautulliEnabled'] ?? false,
tautulliHost: profile['tautulliHost'] ?? '',
tautulliKey: profile['tautulliKey'] ?? '',
tautulliHeaders: profile['tautulliHeaders'] ?? {},
//Ombi
ombiEnabled: profile['ombiEnabled'] ?? false,
ombiHost: profile['ombiHost'] ?? '',
ombiKey: profile['ombiKey'] ?? '',
ombiHeaders: profile['ombiHeaders'] ?? {},
//Overseerr
overseerrEnabled: profile['overseerrEnabled'] ?? false,
overseerrHost: profile['overseerrHost'] ?? '',
overseerrKey: profile['overseerrKey'] ?? '',
overseerrHeaders: profile['overseerrHeaders'] ?? {},
);
ProfileHiveObject({
//Lidarr
required this.lidarrEnabled,
required this.lidarrHost,
required this.lidarrKey,
required this.lidarrHeaders,
//Radarr
required this.radarrEnabled,
required this.radarrHost,
required this.radarrKey,
required this.radarrHeaders,
//Sonarr
required this.sonarrEnabled,
required this.sonarrHost,
required this.sonarrKey,
required this.sonarrHeaders,
//SABnzbd
required this.sabnzbdEnabled,
required this.sabnzbdHost,
required this.sabnzbdKey,
required this.sabnzbdHeaders,
//NZBGet
required this.nzbgetEnabled,
required this.nzbgetHost,
required this.nzbgetUser,
required this.nzbgetPass,
required this.nzbgetHeaders,
//Wake On LAN
required this.wakeOnLANEnabled,
required this.wakeOnLANBroadcastAddress,
required this.wakeOnLANMACAddress,
//Tautulli
required this.tautulliEnabled,
required this.tautulliHost,
required this.tautulliKey,
required this.tautulliHeaders,
//Ombi
required this.ombiEnabled,
required this.ombiHost,
required this.ombiKey,
required this.ombiHeaders,
//Overseerr
required this.overseerrEnabled,
required this.overseerrHost,
required this.overseerrKey,
required this.overseerrHeaders,
});
@override
String toString() => toMap().toString();
Map<String, dynamic> toMap() => {
"key": key,
//Sonarr
"sonarrEnabled": sonarrEnabled,
"sonarrHost": sonarrHost,
"sonarrKey": sonarrKey,
"sonarrHeaders": sonarrHeaders,
//Radarr
"radarrEnabled": radarrEnabled,
"radarrHost": radarrHost,
"radarrKey": radarrKey,
"radarrHeaders": radarrHeaders,
//Lidarr
"lidarrEnabled": lidarrEnabled,
"lidarrHost": lidarrHost,
"lidarrKey": lidarrKey,
"lidarrHeaders": lidarrHeaders,
//SABnzbd
"sabnzbdEnabled": sabnzbdEnabled,
"sabnzbdHost": sabnzbdHost,
"sabnzbdKey": sabnzbdKey,
"sabnzbdHeaders": sabnzbdHeaders,
//NZBGet
"nzbgetEnabled": nzbgetEnabled,
"nzbgetHost": nzbgetHost,
"nzbgetUser": nzbgetUser,
"nzbgetPass": nzbgetPass,
"nzbgetHeaders": nzbgetHeaders,
//Wake On LAN
"wakeOnLANEnabled": wakeOnLANEnabled,
"wakeOnLANBroadcastAddress": wakeOnLANBroadcastAddress,
"wakeOnLANMACAddress": wakeOnLANMACAddress,
//Tautulli
"tautulliEnabled": tautulliEnabled,
"tautulliHost": tautulliHost,
"tautulliKey": tautulliKey,
"tautulliHeaders": tautulliHeaders,
//Ombi
"ombiEnabled": ombiEnabled,
"ombiHost": ombiHost,
"ombiKey": ombiKey,
"ombiHeaders": ombiHeaders,
//Overseerr
"overseerrEnabled": overseerrEnabled,
"overseerrHost": overseerrHost,
"overseerrKey": overseerrKey,
"overseerrHeaders": overseerrHeaders,
};
//Lidarr
@HiveField(0)
bool? lidarrEnabled;
@HiveField(1)
String? lidarrHost;
@HiveField(2)
String? lidarrKey;
@HiveField(26)
Map? lidarrHeaders;
Map<String, dynamic> getLidarr() => {
'enabled': lidarrEnabled ?? false,
'host': lidarrHost ?? '',
'key': lidarrKey ?? '',
'headers': lidarrHeaders ?? {},
};
//Radarr
@HiveField(3)
bool? radarrEnabled;
@HiveField(4)
String? radarrHost;
@HiveField(5)
String? radarrKey;
@HiveField(27)
Map? radarrHeaders;
Map<String, dynamic> getRadarr() => {
'enabled': radarrEnabled ?? false,
'host': radarrHost ?? '',
'key': radarrKey ?? '',
'headers': radarrHeaders ?? {},
};
//Sonarr
@HiveField(6)
bool? sonarrEnabled;
@HiveField(7)
String? sonarrHost;
@HiveField(8)
String? sonarrKey;
@HiveField(21)
bool? sonarrVersion3; // Deprecated
@HiveField(28)
Map? sonarrHeaders;
Map<String, dynamic> getSonarr() => {
'enabled': sonarrEnabled ?? false,
'host': sonarrHost ?? '',
'key': sonarrKey ?? '',
'headers': sonarrHeaders ?? {},
};
//SABnzbd
@HiveField(9)
bool? sabnzbdEnabled;
@HiveField(10)
String? sabnzbdHost;
@HiveField(11)
String? sabnzbdKey;
@HiveField(29)
Map? sabnzbdHeaders;
Map<String, dynamic> getSABnzbd() => {
'enabled': sabnzbdEnabled ?? false,
'host': sabnzbdHost ?? '',
'key': sabnzbdKey ?? '',
'headers': sabnzbdHeaders ?? {},
};
//NZBGet
@HiveField(12)
bool? nzbgetEnabled;
@HiveField(13)
String? nzbgetHost;
@HiveField(14)
String? nzbgetUser;
@HiveField(15)
String? nzbgetPass;
@HiveField(22)
bool? nzbgetBasicAuth; // Deprecated
@HiveField(30)
Map? nzbgetHeaders;
Map<String, dynamic> getNZBGet() => {
'enabled': nzbgetEnabled ?? false,
'host': nzbgetHost ?? '',
'user': nzbgetUser ?? '',
'pass': nzbgetPass ?? '',
'headers': nzbgetHeaders ?? {},
};
//Wake On LAN
@HiveField(23)
bool? wakeOnLANEnabled;
@HiveField(24)
String? wakeOnLANBroadcastAddress;
@HiveField(25)
String? wakeOnLANMACAddress;
Map<String, dynamic> getWakeOnLAN() => {
'enabled': wakeOnLANEnabled ?? false,
'broadcastAddress': wakeOnLANBroadcastAddress ?? '',
'MACAddress': wakeOnLANMACAddress ?? '',
};
//Tautulli
@HiveField(31)
bool? tautulliEnabled;
@HiveField(32)
String? tautulliHost;
@HiveField(33)
String? tautulliKey;
@HiveField(35)
Map? tautulliHeaders;
Map<String, dynamic> getTautulli() => {
'enabled': tautulliEnabled ?? false,
'host': tautulliHost ?? '',
'key': tautulliKey ?? '',
'headers': tautulliHeaders ?? {},
};
//Ombi
@HiveField(36)
bool? ombiEnabled;
@HiveField(37)
String? ombiHost;
@HiveField(38)
String? ombiKey;
@HiveField(39)
Map? ombiHeaders;
Map<String, dynamic> getOmbi() => {
'enabled': ombiEnabled ?? false,
'host': ombiHost ?? '',
'key': ombiKey ?? '',
'headers': ombiHeaders ?? {},
};
//Overseerr
@HiveField(40)
bool? overseerrEnabled;
@HiveField(41)
String? overseerrHost;
@HiveField(42)
String? overseerrKey;
@HiveField(43)
Map? overseerrHeaders;
Map<String, dynamic> getOverseerr() => {
'enabled': overseerrEnabled ?? false,
'host': overseerrHost ?? '',
'key': overseerrKey ?? '',
'headers': overseerrHeaders ?? {},
};
bool get anythingEnabled {
for (final module in LunaModule.active) {
if (module.isEnabled) return true;
}
return false;
}
}

View File

@@ -1,2 +0,0 @@
export 'logs/log.dart';
export 'logs/log_type.dart';

View File

@@ -1,5 +0,0 @@
export 'types/browser.dart';
export 'types/indexer_icon.dart';
export 'types/links.dart';
export 'types/list_view_option.dart';
export 'types/loading_state.dart';

View File

@@ -1,17 +0,0 @@
import 'package:lunasea/core.dart';
part 'browser.g.dart';
@HiveType(typeId: 11, adapterName: 'DeprecatedLunaBrowserAdapter')
enum DeprecatedLunaBrowser {
@HiveField(0)
APPLE_SAFARI,
@HiveField(1)
BRAVE_BROWSER,
@HiveField(2)
GOOGLE_CHROME,
@HiveField(3)
MICROSOFT_EDGE,
@HiveField(4)
MOZILLA_FIREFOX,
}

View File

@@ -1,21 +0,0 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string_links.dart';
enum LunaLinks {
CHANGELOG('https://www.lunasea.app/changelog'),
BUILD_CHANNELS('https://www.lunasea.app/build-channels'),
BUILDS('https://www.lunasea.app/builds'),
DISCORD('https://www.lunasea.app/discord'),
DOCUMENTATION('https://www.lunasea.app/docs'),
FEEDBACK_BOARD('https://www.lunasea.app/feedback'),
GITHUB('https://www.lunasea.app/github'),
NOTIFICATIONS_DOC('https://docs.lunasea.app/lunasea/notifications'),
REDDIT('https://www.lunasea.app/reddit'),
WEBLATE('https:/www.lunasea.app/translate'),
WEBSITE('https://www.lunasea.app');
final String url;
const LunaLinks(this.url);
Future<void> launch() async => url.openLink();
}

View File

@@ -1,14 +0,0 @@
import 'package:lunasea/core.dart';
abstract class LunaWebhooks {
/// Handle the incoming webhook for the module.
Future<void> handle(Map<dynamic, dynamic> data);
/// Given a [LunaModule] and Firebase UID, return the module's webhook endpoint for the user.
static String buildUserTokenURL(String? token, LunaModule module) =>
'https://notify.lunasea.app/v1/${module.key}/user/$token';
/// Given a [LunaModule] and Firebase device token, return the module's webhook endpoint for the device.
static String buildDeviceTokenURL(String? token, LunaModule module) =>
'https://notify.lunasea.app/v1/${module.key}/device/$token';
}

View File

@@ -1,30 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class LunaInvalidRoute extends StatelessWidget {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final String title;
final String message;
LunaInvalidRoute({
Key? key,
this.title = 'LunaSea',
this.message = '404: Not Found',
}) : super(key: key);
@override
Widget build(BuildContext context) {
return LunaScaffold(
scaffoldKey: _scaffoldKey,
module: LunaModule.SETTINGS,
appBar: LunaAppBar(
title: title,
scrollControllers: const [],
),
body: LunaMessage.goBack(
context: context,
text: message,
),
);
}
}

View File

@@ -1 +0,0 @@
export 'the_movie_db/api.dart';

View File

@@ -1,16 +0,0 @@
class TheMovieDB {
static const String _IMAGE_ENDPOINT_URL = 'https://image.tmdb.org/t/p/';
static const String _IMAGE_BACKDROP_SIZE = 'w300';
static const String _IMAGE_POSTER_SIZE = 'w185';
static String? getPosterURL(String? id) {
if (id != null) return '$_IMAGE_ENDPOINT_URL$_IMAGE_POSTER_SIZE$id';
return null;
}
static String? getBackdropURL(String? id) {
if (id != null) return '$_IMAGE_ENDPOINT_URL$_IMAGE_BACKDROP_SIZE$id';
return null;
}
}

View File

@@ -1,150 +0,0 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/system/build.dart';
import 'package:lunasea/system/flavor.dart';
class LunaChangelogSheet extends LunaBottomModalSheet {
late _Changelog _changelog;
String _version = '';
bool _isBuildDifferent() {
final db = LunaSeaDatabase.CHANGELOG_LAST_BUILD.read();
final current = _version.split('.').map((s) => int.parse(s)).toList();
final stored = db.split('.').map((s) => int.parse(s)).toList();
if (current[0] != stored[0]) return true;
if (current[1] != stored[1]) return true;
if (current[2] != stored[2]) return true;
return false;
}
@override
Future<dynamic> show({
required BuildContext context,
Widget Function(BuildContext context)? builder,
bool checkBuildVersion = false,
bool showCommitHistory = false,
}) async {
// Do not show unless it is a prod release
if (LunaFlavor.CANDIDATE.isRunningFlavor()) {
if (showCommitHistory) LunaBuild().openCommitHistory();
return;
}
try {
_changelog = await DefaultAssetBundle.of(context)
.loadString('assets/changelog.json')
.then((data) => _Changelog.fromJson(json.decode(data)));
PackageInfo _package = await PackageInfo.fromPlatform();
_version = _package.version;
if (checkBuildVersion) {
if (!_isBuildDifferent()) return;
LunaSeaDatabase.CHANGELOG_LAST_BUILD.update(_version);
}
return this.showModal(context: context, builder: builder);
} catch (error, stack) {
LunaLogger().error('Failed to show changelog sheet', error, stack);
}
}
@override
Widget builder(BuildContext context) {
return LunaListViewModal(
children: [
LunaHeader(
text: _version,
subtitle: _changelog.motd,
),
..._buildChangeBlock(
'lunasea.New'.tr(),
_changelog.changesNew,
),
..._buildChangeBlock(
'lunasea.Tweaks'.tr(),
_changelog.changesTweaks,
),
..._buildChangeBlock(
'lunasea.Fixes'.tr(),
_changelog.changesFixes,
),
const SizedBox(height: LunaUI.DEFAULT_MARGIN_SIZE / 2),
],
actionBar: LunaBottomActionBar(
actions: [
LunaButton.text(
text: 'lunasea.FullChangelog'.tr(),
icon: Icons.track_changes_rounded,
onTap: LunaLinks.CHANGELOG.launch,
),
],
),
);
}
List<Widget> _buildChangeBlock(String header, List<_Change> changes) {
if (changes.isEmpty) return [];
return [
LunaHeader(text: header),
LunaTableCard(
content: List<LunaTableContent>.generate(
changes.length,
(index) {
String _body = changes[index]
.changes
.map((s) => '${LunaUI.TEXT_BULLET.pad()}$s')
.join('\n');
return LunaTableContent(
title: changes[index].module,
body: _body,
);
},
),
),
];
}
}
class _Changelog {
String? motd;
List<_Change> changesNew = [];
List<_Change> changesTweaks = [];
List<_Change> changesFixes = [];
static _Changelog fromJson(Map<String, dynamic> json) {
_Changelog changelog = _Changelog();
List<dynamic>? data;
changelog.motd = json['motd'] ?? 'Welcome to LunaSea!';
data = json['new'];
data?.forEach((c) => changelog.changesNew.add(_Change.fromJson(c)));
data = json['tweaks'];
data?.forEach((c) => changelog.changesTweaks.add(_Change.fromJson(c)));
data = json['fixes'];
data?.forEach((c) => changelog.changesFixes.add(_Change.fromJson(c)));
return changelog;
}
}
class _Change {
late String module;
late List<String> changes;
_Change._();
static _Change fromJson(Map<String, dynamic> json) {
_Change change = _Change._();
change.module = json['module'] as String;
change.changes = (json['changes'] as List).cast<String>();
return change;
}
}

View File

@@ -1,20 +1,20 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:lunasea/core/models/configuration/external_module.dart';
import 'package:lunasea/core/models/configuration/indexer.dart';
import 'package:lunasea/core/models/configuration/profile.dart';
import 'package:lunasea/core/models/logs/log.dart';
import 'package:lunasea/database/models/external_module.dart';
import 'package:lunasea/database/models/indexer.dart';
import 'package:lunasea/database/models/log.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/database/table.dart';
import 'package:lunasea/system/logger.dart';
import 'package:lunasea/vendor.dart';
enum LunaBox<T> {
alerts<dynamic>('alerts'),
externalModules<ExternalModuleHiveObject>('external_modules'),
indexers<IndexerHiveObject>('indexers'),
externalModules<LunaExternalModule>('external_modules'),
indexers<LunaIndexer>('indexers'),
logs<LunaLogHiveObject>('logs'),
lunasea<dynamic>('lunasea'),
profiles<ProfileHiveObject>('profiles');
profiles<LunaProfile>('profiles');
final String key;
const LunaBox(this.key);

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/database/database.dart';
import 'package:lunasea/database/models/external_module.dart';
import 'package:lunasea/database/models/indexer.dart';
import 'package:lunasea/database/table.dart';
class LunaConfig {
@@ -40,7 +42,7 @@ class LunaConfig {
for (final item in data) {
final key = item['key'];
final obj = ProfileHiveObject.fromMap(item);
final obj = LunaProfile.fromJson((item as Map).cast<String, dynamic>());
LunaBox.profiles.update(key, obj);
}
}
@@ -49,7 +51,7 @@ class LunaConfig {
if (data == null) return;
for (final indexer in data) {
final obj = IndexerHiveObject.fromMap(indexer);
final obj = LunaIndexer.fromJson(indexer);
LunaBox.indexers.create(obj);
}
}
@@ -58,7 +60,7 @@ class LunaConfig {
if (data == null) return;
for (final module in data) {
final obj = ExternalModuleHiveObject.fromMap(module);
final obj = LunaExternalModule.fromJson(module);
LunaBox.externalModules.create(obj);
}
}

View File

@@ -1,9 +1,8 @@
import 'package:lunasea/core/models/configuration/profile.dart';
import 'package:lunasea/database/box.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/database/table.dart';
import 'package:lunasea/database/tables/lunasea.dart';
import 'package:lunasea/system/platform.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/vendor.dart';
class LunaDatabase {
@@ -27,7 +26,7 @@ class LunaDatabase {
await clear();
const defaultProfile = LunaProfile.DEFAULT_PROFILE;
LunaBox.profiles.update(defaultProfile, ProfileHiveObject.empty());
LunaBox.profiles.update(defaultProfile, LunaProfile());
LunaSeaDatabase.ENABLED_PROFILE.update(defaultProfile);
}

View File

@@ -0,0 +1,37 @@
import 'package:lunasea/vendor.dart';
part 'deprecated.g.dart';
void registerDeprecatedAdapters() {
Hive.registerAdapter(_Deprecated02Adapter());
Hive.registerAdapter(_Deprecated03Adapter());
Hive.registerAdapter(_Deprecated04Adapter());
Hive.registerAdapter(_Deprecated05Adapter());
Hive.registerAdapter(_Deprecated06Adapter());
Hive.registerAdapter(_Deprecated07Adapter());
Hive.registerAdapter(_Deprecated11Adapter());
}
@HiveType(typeId: 2, adapterName: '_Deprecated02Adapter') // Next: 2
class _Deprecated02 extends HiveObject {}
@HiveType(typeId: 3, adapterName: '_Deprecated03Adapter') // Next: 3
class _Deprecated03 extends HiveObject {}
@HiveType(typeId: 4, adapterName: '_Deprecated04Adapter') // Next: 1
class _Deprecated04 extends HiveObject {}
@HiveType(typeId: 5, adapterName: '_Deprecated05Adapter') // Next: 3
class _Deprecated05 extends HiveObject {}
@HiveType(typeId: 6, adapterName: '_Deprecated06Adapter') // Next: 2
class _Deprecated06 extends HiveObject {}
@HiveType(typeId: 7, adapterName: '_Deprecated07Adapter') // Next: 2
class _Deprecated07 extends HiveObject {}
@HiveType(typeId: 11, adapterName: '_Deprecated11Adapter') // Next: 5
enum _Deprecated11 {
@HiveField(0)
DEPRECATED,
}

View File

@@ -0,0 +1,37 @@
import 'package:lunasea/core.dart';
part 'external_module.g.dart';
@JsonSerializable()
@HiveType(typeId: 26, adapterName: 'LunaExternalModuleAdapter')
class LunaExternalModule extends HiveObject {
@JsonKey()
@HiveField(0)
String displayName;
@JsonKey()
@HiveField(1)
String host;
LunaExternalModule({
this.displayName = '',
this.host = '',
});
@override
String toString() => json.encode(this.toJson());
Map<String, dynamic> toJson() => _$LunaExternalModuleToJson(this);
factory LunaExternalModule.fromJson(Map<String, dynamic> json) {
return _$LunaExternalModuleFromJson(json);
}
factory LunaExternalModule.clone(LunaExternalModule profile) {
return LunaExternalModule.fromJson(profile.toJson());
}
factory LunaExternalModule.get(String key) {
return LunaBox.externalModules.read(key)!;
}
}

View File

@@ -0,0 +1,53 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/types/indexer_icon.dart';
part 'indexer.g.dart';
@JsonSerializable()
@HiveType(typeId: 1, adapterName: 'LunaIndexerAdapter')
class LunaIndexer extends HiveObject {
@JsonKey()
@HiveField(0)
String displayName;
@JsonKey()
@HiveField(1)
String host;
@JsonKey(name: 'key')
@HiveField(2)
String apiKey;
@JsonKey()
@HiveField(3)
Map headers;
@JsonKey()
@HiveField(4)
LunaIndexerIcon? icon;
LunaIndexer({
this.displayName = '',
this.host = '',
this.apiKey = '',
this.headers = const {},
this.icon,
});
@override
String toString() => json.encode(this.toJson());
Map<String, dynamic> toJson() => _$LunaIndexerToJson(this);
factory LunaIndexer.fromJson(Map<String, dynamic> json) {
return _$LunaIndexerFromJson(json);
}
factory LunaIndexer.clone(LunaIndexer profile) {
return LunaIndexer.fromJson(profile.toJson());
}
factory LunaIndexer.get(String key) {
return LunaBox.indexers.read(key)!;
}
}

View File

@@ -1,9 +1,9 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/types/log_type.dart';
import 'package:stack_trace/stack_trace.dart';
part 'log.g.dart';
/// Hive database object containing all information on a log
@HiveType(typeId: 23, adapterName: 'LunaLogHiveObjectAdapter')
class LunaLogHiveObject extends HiveObject {
@HiveField(0)
@@ -71,14 +71,14 @@ class LunaLogHiveObject extends HiveObject {
);
}
Map<String, dynamic> toMap() {
Map<String, dynamic> toJson() {
return {
"timestamp":
DateTime.fromMillisecondsSinceEpoch(timestamp).toIso8601String(),
"type": type.name,
"type": type.title,
"message": message,
if (className?.isNotEmpty ?? false) "class_name": className,
if (methodName?.isNotEmpty ?? false) "method_name": methodName,
"message": message,
if (error?.isNotEmpty ?? false) "error": error,
if (stackTrace?.isNotEmpty ?? false)
"stack_trace": stackTrace?.trim().split('\n') ?? [],

View File

@@ -0,0 +1,290 @@
import 'package:lunasea/database/box.dart';
import 'package:lunasea/database/tables/lunasea.dart';
import 'package:lunasea/modules.dart';
import 'package:lunasea/vendor.dart';
part 'profile.g.dart';
@JsonSerializable()
@HiveType(typeId: 0, adapterName: 'LunaProfileAdapter')
class LunaProfile extends HiveObject {
static const String DEFAULT_PROFILE = 'default';
static LunaProfile get current {
final enabled = LunaSeaDatabase.ENABLED_PROFILE.read();
return LunaBox.profiles.read(enabled) ?? LunaProfile();
}
static List<String> get list {
final profiles = LunaBox.profiles.keys.cast<String>().toList();
profiles.sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
return profiles;
}
@JsonKey()
@HiveField(0)
bool lidarrEnabled;
@JsonKey()
@HiveField(1)
String lidarrHost;
@JsonKey()
@HiveField(2)
String lidarrKey;
@JsonKey()
@HiveField(26)
Map lidarrHeaders;
Map<String, dynamic> getLidarr() {
return {
'enabled': lidarrEnabled,
'host': lidarrHost,
'key': lidarrKey,
'headers': lidarrHeaders,
};
}
@JsonKey()
@HiveField(3)
bool radarrEnabled;
@JsonKey()
@HiveField(4)
String radarrHost;
@JsonKey()
@HiveField(5)
String radarrKey;
@HiveField(27)
Map radarrHeaders;
Map<String, dynamic> getRadarr() {
return {
'enabled': radarrEnabled,
'host': radarrHost,
'key': radarrKey,
'headers': radarrHeaders,
};
}
@JsonKey()
@HiveField(6)
bool sonarrEnabled;
@JsonKey()
@HiveField(7)
String sonarrHost;
@JsonKey()
@HiveField(8)
String sonarrKey;
@JsonKey()
@HiveField(28)
Map sonarrHeaders;
Map<String, dynamic> getSonarr() {
return {
'enabled': sonarrEnabled,
'host': sonarrHost,
'key': sonarrKey,
'headers': sonarrHeaders,
};
}
@JsonKey()
@HiveField(9)
bool sabnzbdEnabled;
@JsonKey()
@HiveField(10)
String sabnzbdHost;
@JsonKey()
@HiveField(11)
String sabnzbdKey;
@JsonKey()
@JsonKey()
@HiveField(29)
Map sabnzbdHeaders;
Map<String, dynamic> getSABnzbd() {
return {
'enabled': sabnzbdEnabled,
'host': sabnzbdHost,
'key': sabnzbdKey,
'headers': sabnzbdHeaders,
};
}
@JsonKey()
@HiveField(12)
bool nzbgetEnabled;
@JsonKey()
@HiveField(13)
String nzbgetHost;
@JsonKey()
@HiveField(14)
String nzbgetUser;
@JsonKey()
@HiveField(15)
String nzbgetPass;
@JsonKey()
@HiveField(30)
Map nzbgetHeaders;
Map<String, dynamic> getNZBGet() {
return {
'enabled': nzbgetEnabled,
'host': nzbgetHost,
'user': nzbgetUser,
'pass': nzbgetPass,
'headers': nzbgetHeaders,
};
}
@JsonKey()
@HiveField(23)
bool wakeOnLANEnabled;
@JsonKey()
@HiveField(24)
String wakeOnLANBroadcastAddress;
@JsonKey()
@HiveField(25)
String wakeOnLANMACAddress;
Map<String, dynamic> getWakeOnLAN() {
return {
'enabled': wakeOnLANEnabled,
'broadcastAddress': wakeOnLANBroadcastAddress,
'MACAddress': wakeOnLANMACAddress,
};
}
@JsonKey()
@HiveField(31)
bool tautulliEnabled;
@JsonKey()
@HiveField(32)
String tautulliHost;
@JsonKey()
@HiveField(33)
String tautulliKey;
@JsonKey()
@HiveField(35)
Map tautulliHeaders;
Map<String, dynamic> getTautulli() {
return {
'enabled': tautulliEnabled,
'host': tautulliHost,
'key': tautulliKey,
'headers': tautulliHeaders,
};
}
@JsonKey()
@HiveField(40)
bool overseerrEnabled;
@JsonKey()
@HiveField(41)
String overseerrHost;
@JsonKey()
@HiveField(42)
String overseerrKey;
@JsonKey()
@HiveField(43)
Map overseerrHeaders;
Map<String, dynamic> getOverseerr() {
return {
'enabled': overseerrEnabled,
'host': overseerrHost,
'key': overseerrKey,
'headers': overseerrHeaders,
};
}
LunaProfile({
//Lidarr
this.lidarrEnabled = false,
this.lidarrHost = '',
this.lidarrKey = '',
this.lidarrHeaders = const {},
//Radarr
this.radarrEnabled = false,
this.radarrHost = '',
this.radarrKey = '',
this.radarrHeaders = const {},
//Sonarr
this.sonarrEnabled = false,
this.sonarrHost = '',
this.sonarrKey = '',
this.sonarrHeaders = const {},
//SABnzbd
this.sabnzbdEnabled = false,
this.sabnzbdHost = '',
this.sabnzbdKey = '',
this.sabnzbdHeaders = const {},
//NZBGet
this.nzbgetEnabled = false,
this.nzbgetHost = '',
this.nzbgetUser = '',
this.nzbgetPass = '',
this.nzbgetHeaders = const {},
//Wake On LAN
this.wakeOnLANEnabled = false,
this.wakeOnLANBroadcastAddress = '',
this.wakeOnLANMACAddress = '',
//Tautulli
this.tautulliEnabled = false,
this.tautulliHost = '',
this.tautulliKey = '',
this.tautulliHeaders = const {},
//Overseerr
this.overseerrEnabled = false,
this.overseerrHost = '',
this.overseerrKey = '',
this.overseerrHeaders = const {},
});
@override
String toString() => json.encode(this.toJson());
Map<String, dynamic> toJson() => _$LunaProfileToJson(this);
factory LunaProfile.fromJson(Map<String, dynamic> json) {
return _$LunaProfileFromJson(json);
}
factory LunaProfile.clone(LunaProfile profile) {
return LunaProfile.fromJson(profile.toJson());
}
factory LunaProfile.get(String key) {
return LunaBox.profiles.read(key)!;
}
bool isAnythingEnabled() {
for (final module in LunaModule.active) {
if (module.isEnabled) return true;
}
return false;
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/database/box.dart';
import 'package:lunasea/database/models/deprecated.dart';
import 'package:lunasea/database/tables/dashboard.dart';
import 'package:lunasea/database/tables/lidarr.dart';
import 'package:lunasea/database/tables/lunasea.dart';
@@ -32,7 +33,8 @@ enum LunaTable<T extends LunaTableMixin> {
});
static void register() {
LunaTable.values.forEach((t) => t.items[0].register());
for (final table in LunaTable.values) table.items[0].register();
registerDeprecatedAdapters();
}
T? _itemFromKey(String key) {
@@ -72,7 +74,7 @@ mixin LunaTableMixin<T> on Enum {
T read() => box.read(key, fallback: fallback);
void update(T value) => box.update(key, value);
/// Default is a no-op and does not register any Hive adapters
/// Default is an empty list and does not register any Hive adapters
void register() {}
/// The list of items that are not imported or exported by default
@@ -86,8 +88,8 @@ mixin LunaTableMixin<T> on Enum {
@mustCallSuper
void import(dynamic value) {
if (blockedFromImportExport.contains(this)) return;
return update(value);
if (blockedFromImportExport.contains(this) || value == null) return;
return update(value as T);
}
ValueListenableBuilder watch({

View File

@@ -33,35 +33,39 @@ enum DashboardDatabase<T> with LunaTableMixin<T> {
@override
dynamic export() {
if (this == DashboardDatabase.CALENDAR_STARTING_DAY) {
return DashboardDatabase.CALENDAR_STARTING_DAY.read().key;
DashboardDatabase db = this;
switch (db) {
case DashboardDatabase.CALENDAR_STARTING_DAY:
return DashboardDatabase.CALENDAR_STARTING_DAY.read().key;
case DashboardDatabase.CALENDAR_STARTING_SIZE:
return DashboardDatabase.CALENDAR_STARTING_SIZE.read().key;
case DashboardDatabase.CALENDAR_STARTING_TYPE:
return DashboardDatabase.CALENDAR_STARTING_TYPE.read().key;
default:
return super.export();
}
if (this == DashboardDatabase.CALENDAR_STARTING_SIZE) {
return DashboardDatabase.CALENDAR_STARTING_SIZE.read().key;
}
if (this == DashboardDatabase.CALENDAR_STARTING_TYPE) {
return DashboardDatabase.CALENDAR_STARTING_TYPE.read().key;
}
return super.export();
}
@override
void import(dynamic value) {
if (this == DashboardDatabase.CALENDAR_STARTING_DAY) {
final item = CalendarStartingDay.MONDAY.fromKey(value.toString());
if (item != null) update(item as T);
return;
DashboardDatabase db = this;
dynamic result;
switch (db) {
case DashboardDatabase.CALENDAR_STARTING_DAY:
result = CalendarStartingDay.MONDAY.fromKey(value.toString());
break;
case DashboardDatabase.CALENDAR_STARTING_SIZE:
result = CalendarStartingSize.ONE_WEEK.fromKey(value.toString());
break;
case DashboardDatabase.CALENDAR_STARTING_TYPE:
result = CalendarStartingType.CALENDAR.fromKey(value.toString());
break;
default:
result = value;
break;
}
if (this == DashboardDatabase.CALENDAR_STARTING_SIZE) {
final item = CalendarStartingSize.ONE_WEEK.fromKey(value.toString());
if (item != null) return update(item as T);
return;
}
if (this == DashboardDatabase.CALENDAR_STARTING_TYPE) {
final item = CalendarStartingType.CALENDAR.fromKey(value.toString());
if (item != null) return update(item as T);
return;
}
return super.import(value);
return super.import(result);
}
}

View File

@@ -1,14 +1,12 @@
import 'package:lunasea/core/models/configuration/external_module.dart';
import 'package:lunasea/core/models/configuration/indexer.dart';
import 'package:lunasea/core/models/configuration/profile.dart';
import 'package:lunasea/core/models/logs/log.dart';
import 'package:lunasea/core/models/logs/log_type.dart';
import 'package:lunasea/core/models/types/browser.dart';
import 'package:lunasea/core/models/types/indexer_icon.dart';
import 'package:lunasea/core/models/types/list_view_option.dart';
import 'package:lunasea/database/models/external_module.dart';
import 'package:lunasea/database/models/indexer.dart';
import 'package:lunasea/database/models/log.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/types/indexer_icon.dart';
import 'package:lunasea/types/list_view_option.dart';
import 'package:lunasea/modules.dart';
import 'package:lunasea/database/table.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/types/log_type.dart';
import 'package:lunasea/vendor.dart';
import 'package:lunasea/widgets/ui.dart';
@@ -43,12 +41,9 @@ enum LunaSeaDatabase<T> with LunaTableMixin<T> {
@override
void register() {
// Deprecated
Hive.registerAdapter(DeprecatedLunaBrowserAdapter());
// Active
Hive.registerAdapter(ExternalModuleHiveObjectAdapter());
Hive.registerAdapter(IndexerHiveObjectAdapter());
Hive.registerAdapter(ProfileHiveObjectAdapter());
Hive.registerAdapter(LunaExternalModuleAdapter());
Hive.registerAdapter(LunaIndexerAdapter());
Hive.registerAdapter(LunaProfileAdapter());
Hive.registerAdapter(LunaLogHiveObjectAdapter());
Hive.registerAdapter(LunaIndexerIconAdapter());
Hive.registerAdapter(LunaLogTypeAdapter());
@@ -58,33 +53,41 @@ enum LunaSeaDatabase<T> with LunaTableMixin<T> {
@override
dynamic export() {
if (this == LunaSeaDatabase.DEFAULT_LAUNCH_MODULE) {
return LunaSeaDatabase.DEFAULT_LAUNCH_MODULE.read().key;
LunaSeaDatabase db = this;
switch (db) {
case LunaSeaDatabase.DEFAULT_LAUNCH_MODULE:
return LunaSeaDatabase.DEFAULT_LAUNCH_MODULE.read().key;
case LunaSeaDatabase.DRAWER_MANUAL_ORDER:
return LunaDrawer.moduleOrderedList()
.map<String>((module) => module.key)
.toList();
default:
return super.export();
}
if (this == LunaSeaDatabase.DRAWER_MANUAL_ORDER) {
return LunaDrawer.moduleOrderedList()
.map<String>((module) => module.key)
.toList();
}
return super.export();
}
@override
void import(dynamic value) {
if (this == LunaSeaDatabase.DEFAULT_LAUNCH_MODULE) {
final item = LunaModule.fromKey(value.toString());
if (item != null) update(item as T);
return;
LunaSeaDatabase db = this;
dynamic result;
switch (db) {
case LunaSeaDatabase.DEFAULT_LAUNCH_MODULE:
result = LunaModule.fromKey(value.toString());
break;
case LunaSeaDatabase.DRAWER_MANUAL_ORDER:
List<LunaModule> item = [];
(value as List).cast<String>().forEach((val) {
LunaModule? module = LunaModule.fromKey(val);
if (module != null) item.add(module);
});
result = item;
break;
default:
result = value;
break;
}
if (this == LunaSeaDatabase.DRAWER_MANUAL_ORDER) {
List<LunaModule> item = [];
(value as List).cast<String>().forEach((val) {
LunaModule? module = LunaModule.fromKey(val);
if (module != null) item.add(module);
});
update(item as T);
return;
}
return super.import(value);
return super.import(result);
}
}

View File

@@ -1,8 +1,5 @@
import 'package:lunasea/core/models/types/list_view_option.dart';
import 'package:lunasea/types/list_view_option.dart';
import 'package:lunasea/database/table.dart';
import 'package:lunasea/modules/radarr/core/deprecated/availability.dart';
import 'package:lunasea/modules/radarr/core/deprecated/qualityprofile.dart';
import 'package:lunasea/modules/radarr/core/deprecated/rootfolder.dart';
import 'package:lunasea/modules/radarr/core/types/filter_movies.dart';
import 'package:lunasea/modules/radarr/core/types/filter_releases.dart';
import 'package:lunasea/modules/radarr/core/types/sorting_movies.dart';
@@ -39,11 +36,6 @@ enum RadarrDatabase<T> with LunaTableMixin<T> {
@override
void register() {
// Deprecated, not in use but necessary to avoid Hive read errors
Hive.registerAdapter(DeprecatedRadarrQualityProfileAdapter());
Hive.registerAdapter(DeprecatedRadarrRootFolderAdapter());
Hive.registerAdapter(DeprecatedRadarrAvailabilityAdapter());
// Active adapters
Hive.registerAdapter(RadarrMoviesSortingAdapter());
Hive.registerAdapter(RadarrMoviesFilterAdapter());
Hive.registerAdapter(RadarrReleasesSortingAdapter());
@@ -60,51 +52,49 @@ enum RadarrDatabase<T> with LunaTableMixin<T> {
@override
dynamic export() {
if (this == RadarrDatabase.DEFAULT_SORTING_MOVIES) {
return RadarrDatabase.DEFAULT_SORTING_MOVIES.read().key;
RadarrDatabase db = this;
switch (db) {
case RadarrDatabase.DEFAULT_SORTING_MOVIES:
return RadarrDatabase.DEFAULT_SORTING_MOVIES.read().key;
case RadarrDatabase.DEFAULT_SORTING_RELEASES:
return RadarrDatabase.DEFAULT_SORTING_RELEASES.read().key;
case RadarrDatabase.DEFAULT_FILTERING_MOVIES:
return RadarrDatabase.DEFAULT_FILTERING_MOVIES.read().key;
case RadarrDatabase.DEFAULT_FILTERING_RELEASES:
return RadarrDatabase.DEFAULT_FILTERING_RELEASES.read().key;
case RadarrDatabase.DEFAULT_VIEW_MOVIES:
return RadarrDatabase.DEFAULT_VIEW_MOVIES.read().key;
default:
return super.export();
}
if (this == RadarrDatabase.DEFAULT_SORTING_RELEASES) {
return RadarrDatabase.DEFAULT_SORTING_RELEASES.read().key;
}
if (this == RadarrDatabase.DEFAULT_FILTERING_MOVIES) {
return RadarrDatabase.DEFAULT_FILTERING_MOVIES.read().key;
}
if (this == RadarrDatabase.DEFAULT_FILTERING_RELEASES) {
return RadarrDatabase.DEFAULT_FILTERING_RELEASES.read().key;
}
if (this == RadarrDatabase.DEFAULT_VIEW_MOVIES) {
return RadarrDatabase.DEFAULT_VIEW_MOVIES.read().key;
}
return super.export();
}
@override
void import(dynamic value) {
if (this == RadarrDatabase.DEFAULT_SORTING_MOVIES) {
final item = RadarrMoviesSorting.ALPHABETICAL.fromKey(value.toString());
if (item != null) update(item as T);
return;
RadarrDatabase db = this;
dynamic result;
switch (db) {
case RadarrDatabase.DEFAULT_SORTING_MOVIES:
result = RadarrMoviesSorting.ALPHABETICAL.fromKey(value.toString());
break;
case RadarrDatabase.DEFAULT_SORTING_RELEASES:
result = RadarrReleasesSorting.ALPHABETICAL.fromKey(value.toString());
break;
case RadarrDatabase.DEFAULT_FILTERING_MOVIES:
result = RadarrMoviesFilter.ALL.fromKey(value.toString());
break;
case RadarrDatabase.DEFAULT_FILTERING_RELEASES:
result = RadarrReleasesFilter.ALL.fromKey(value.toString());
break;
case RadarrDatabase.DEFAULT_VIEW_MOVIES:
result = LunaListViewOption.fromKey(value.toString());
break;
default:
result = value;
break;
}
if (this == RadarrDatabase.DEFAULT_SORTING_RELEASES) {
final item = RadarrReleasesSorting.ALPHABETICAL.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
if (this == RadarrDatabase.DEFAULT_FILTERING_MOVIES) {
final item = RadarrMoviesFilter.ALL.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
if (this == RadarrDatabase.DEFAULT_FILTERING_RELEASES) {
final item = RadarrReleasesFilter.ALL.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
if (this == RadarrDatabase.DEFAULT_VIEW_MOVIES) {
final item = LunaListViewOption.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
return super.import(value);
return super.import(result);
}
}

View File

@@ -1,6 +1,5 @@
import 'package:lunasea/core/models/types/list_view_option.dart';
import 'package:lunasea/types/list_view_option.dart';
import 'package:lunasea/database/table.dart';
import 'package:lunasea/modules/sonarr/core/deprecated.dart';
import 'package:lunasea/modules/sonarr/core/types/filter_releases.dart';
import 'package:lunasea/modules/sonarr/core/types/filter_series.dart';
import 'package:lunasea/modules/sonarr/core/types/monitor_status.dart';
@@ -48,65 +47,58 @@ enum SonarrDatabase<T> with LunaTableMixin<T> {
@override
void register() {
// Deprecated, not in use but necessary to avoid Hive read errors
Hive.registerAdapter(DeprecatedSonarrQualityProfileAdapter());
Hive.registerAdapter(DeprecatedSonarrRootFolderAdapter());
Hive.registerAdapter(DeprecatedSonarrSeriesTypeAdapter());
// Active adapters
Hive.registerAdapter(SonarrMonitorStatusAdapter());
Hive.registerAdapter(SonarrSeriesSortingAdapter());
Hive.registerAdapter(SonarrSeriesFilterAdapter());
Hive.registerAdapter(SonarrReleasesSortingAdapter());
Hive.registerAdapter(SonarrReleasesFilterAdapter());
Hive.registerAdapter(SonarrMonitorStatusAdapter());
Hive.registerAdapter(SonarrSeriesSortingAdapter());
Hive.registerAdapter(SonarrSeriesFilterAdapter());
Hive.registerAdapter(SonarrReleasesSortingAdapter());
Hive.registerAdapter(SonarrReleasesFilterAdapter());
}
@override
dynamic export() {
if (this == SonarrDatabase.DEFAULT_SORTING_SERIES) {
return SonarrDatabase.DEFAULT_SORTING_SERIES.read().key;
SonarrDatabase db = this;
switch (db) {
case SonarrDatabase.DEFAULT_SORTING_SERIES:
return SonarrDatabase.DEFAULT_SORTING_SERIES.read().key;
case SonarrDatabase.DEFAULT_SORTING_RELEASES:
return SonarrDatabase.DEFAULT_SORTING_RELEASES.read().key;
case SonarrDatabase.DEFAULT_FILTERING_SERIES:
return SonarrDatabase.DEFAULT_FILTERING_SERIES.read().key;
case SonarrDatabase.DEFAULT_FILTERING_RELEASES:
return SonarrDatabase.DEFAULT_FILTERING_RELEASES.read().key;
case SonarrDatabase.DEFAULT_VIEW_SERIES:
return SonarrDatabase.DEFAULT_VIEW_SERIES.read().key;
default:
return super.export();
}
if (this == SonarrDatabase.DEFAULT_SORTING_RELEASES) {
return SonarrDatabase.DEFAULT_SORTING_RELEASES.read().key;
}
if (this == SonarrDatabase.DEFAULT_FILTERING_SERIES) {
return SonarrDatabase.DEFAULT_FILTERING_SERIES.read().key;
}
if (this == SonarrDatabase.DEFAULT_FILTERING_RELEASES) {
return SonarrDatabase.DEFAULT_FILTERING_RELEASES.read().key;
}
if (this == SonarrDatabase.DEFAULT_VIEW_SERIES) {
return SonarrDatabase.DEFAULT_VIEW_SERIES.read().key;
}
return super.export();
}
@override
void import(dynamic value) {
if (this == SonarrDatabase.DEFAULT_SORTING_SERIES) {
final item = SonarrSeriesSorting.ALPHABETICAL.fromKey(value.toString());
if (item != null) update(item as T);
return;
SonarrDatabase db = this;
dynamic result;
switch (db) {
case SonarrDatabase.DEFAULT_SORTING_SERIES:
result = SonarrSeriesSorting.ALPHABETICAL.fromKey(value.toString());
break;
case SonarrDatabase.DEFAULT_SORTING_RELEASES:
result = SonarrReleasesSorting.ALPHABETICAL.fromKey(value.toString());
break;
case SonarrDatabase.DEFAULT_FILTERING_SERIES:
result = SonarrSeriesFilter.ALL.fromKey(value.toString());
break;
case SonarrDatabase.DEFAULT_FILTERING_RELEASES:
result = SonarrReleasesFilter.ALL.fromKey(value.toString());
break;
case SonarrDatabase.DEFAULT_VIEW_SERIES:
result = LunaListViewOption.fromKey(value.toString());
break;
default:
result = value;
break;
}
if (this == SonarrDatabase.DEFAULT_SORTING_RELEASES) {
final item = SonarrReleasesSorting.ALPHABETICAL.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
if (this == SonarrDatabase.DEFAULT_FILTERING_SERIES) {
final item = SonarrSeriesFilter.ALL.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
if (this == SonarrDatabase.DEFAULT_FILTERING_RELEASES) {
final item = SonarrReleasesFilter.ALL.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
if (this == SonarrDatabase.DEFAULT_VIEW_SERIES) {
final item = LunaListViewOption.fromKey(value.toString());
if (item != null) update(item as T);
return;
}
return super.import(value);
return super.import(result);
}
}

View File

@@ -1,12 +1,7 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/extensions/string/string.dart';
extension DateTimeExtension on DateTime {
/// Get the floor of a date.
///
/// The floor is a new [DateTime] object with only the year, month, and day copied from the original source.
DateTime get lunaFloor => DateTime(this.year, this.month, this.day);
/// Returns a string representation of the "age" of the [DateTime] object.
///
/// Compares to [DateTime.now().toLocal()] to calculate the age.

View File

@@ -27,7 +27,7 @@ abstract class LunaPageRouter {
/// - Calls [FluroRouter.define] with the [fullRoute]
/// - Creates a basic [Handler]
/// - Sets the transition type to [LunaRouter.transitionType]
/// - Checks that the module is enabled, and if not returns a `[LunaNotEnabledRoute] instead of the intended route
/// - Checks that the module is enabled, and if not returns a `[NotEnabledPage] instead of the intended route
void noParameterRouteDefinition(
FluroRouter router, {
bool homeRoute = false,
@@ -39,7 +39,7 @@ abstract class LunaPageRouter {
/// - Calls [FluroRouter.define] with the [fullRoute]
/// - Creates a basic [Handler]
/// - Sets the transition type to [LunaRouter.transitionType]
/// - Checks that the module is enabled, and if not returns a `[LunaNotEnabledRoute] instead of the intended route
/// - Checks that the module is enabled, and if not returns a `[NotEnabledPage] instead of the intended route
/// - Parses and injects the required parameters into the route for the handler
void withParameterRouteDefinition(
FluroRouter router,

View File

@@ -15,6 +15,7 @@ import 'package:lunasea/modules/nzbget.dart';
import 'package:lunasea/modules/tautulli.dart';
import 'package:lunasea/modules/dashboard/routes/routes.dart' as dashboard;
import 'package:lunasea/widgets/pages/invalid_route.dart';
class LunaRouter {
static FluroRouter router = FluroRouter();
@@ -23,7 +24,7 @@ class LunaRouter {
/// Calls `defineAllRoutes()` on all module routers that implement [LunaModuleRouter].
void initialize() {
router.notFoundHandler = Handler(
handlerFunc: (context, params) => LunaInvalidRoute(),
handlerFunc: (context, params) => const InvalidRoutePage(),
);
router.define(
'/',

View File

@@ -13,6 +13,7 @@ import 'package:lunasea/modules/tautulli/core/state.dart';
import 'package:lunasea/modules.dart';
import 'package:provider/provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart' show ProviderScope;
class LunaState {
LunaState._();
@@ -22,15 +23,14 @@ class LunaState {
GlobalKey<NavigatorState>();
/// Calls `.reset()` on all states which extend [LunaModuleState].
static void reset([BuildContext? ctx]) {
final context = ctx ?? navigatorKey.currentContext!;
LunaModule.values.forEach((module) => module.state(context)?.reset());
static void reset([BuildContext? context]) {
final ctx = context ?? navigatorKey.currentContext!;
LunaModule.values.forEach((module) => module.state(ctx)?.reset());
}
/// Returns a [MultiProvider] with the provided child.
///
/// The [MultiProvider] has a [ChangeNotifierProvider] provider added for each module global state object.
static MultiProvider providers({required Widget child}) => MultiProvider(
static ProviderScope providers({required Widget child}) {
return ProviderScope(
child: MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => dashboard.ModuleState()),
ChangeNotifierProvider(create: (_) => SettingsState()),
@@ -44,5 +44,7 @@ class LunaState {
ChangeNotifierProvider(create: (_) => TautulliState()),
],
child: child,
);
),
);
}
}

View File

@@ -0,0 +1,5 @@
extension DateTimeExtension on DateTime {
DateTime floor() {
return DateTime(this.year, this.month, this.day);
}
}

View File

@@ -1,7 +1,7 @@
import 'package:lunasea/core.dart';
extension DoubleExtension on double? {
String get asTimeAgo {
extension DoubleAsTimeExtension on double? {
String asTimeAgo() {
if (this == null || this! < 0) return LunaUI.TEXT_EMDASH;
double hours = this!;

View File

@@ -0,0 +1,51 @@
import 'package:lunasea/widgets/ui.dart';
extension DurationAsTimestampExtension on Duration? {
String asNumberTimestamp() {
if (this == null) return LunaUI.TEXT_EMDASH;
final hours = this!.inHours.toString().padLeft(2, '0');
final minutes = (this!.inMinutes % 60).toString().padLeft(2, '0');
final seconds = (this!.inSeconds % 60).toString().padLeft(2, '0');
if (hours == '00') return '$minutes:$seconds';
return '$hours:$minutes:$seconds';
}
String asWordsTimestamp({
int multiplier = 1,
int divisor = 1,
}) {
if (this == null) return 'lunasea.Unknown'.tr();
final List<String> words = [];
final days = this!.inDays;
if (days > 0) {
if (days == 1) {
words.add('lunasea.OneDay'.tr());
} else {
words.add('lunasea.Days'.tr(args: [days.toString()]));
}
}
final hours = this!.inHours % 24;
if (hours > 0) {
if (hours == 1) {
words.add('lunasea.OneHour'.tr());
} else {
words.add('lunasea.Hours'.tr(args: [hours.toString()]));
}
}
final minutes = this!.inMinutes % 60;
if (minutes > 0) {
if (minutes == 1) {
words.add('lunasea.OneMinute'.tr());
} else {
words.add('lunasea.Minutes'.tr(args: [minutes.toString()]));
}
}
return words.isEmpty ? 'lunasea.UnderAMinute'.tr() : words.join(' ');
}
}

View File

@@ -0,0 +1,45 @@
extension IntegerAsBytesExtension on int? {
static const _BIT_CHUNK = 1000;
static const _BIT_SIZES = ['b', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb'];
static const _BYTE_CHUNK = 1024;
static const _BYTE_SIZES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
String _toBits(int decimals, int position) {
if (this == null || this! <= 0) return '${0.toStringAsFixed(decimals)} B';
int idx = position;
double size = this!.toDouble();
while (size > _BIT_CHUNK) {
size /= _BIT_CHUNK;
idx++;
}
return '${size.toStringAsFixed(decimals)} ${_BIT_SIZES[idx]}';
}
String _toBytes(int decimals, int position) {
if (this == null || this! <= 0) return '${0.toStringAsFixed(decimals)} B';
int idx = position;
double size = this!.toDouble();
while (size > _BYTE_CHUNK) {
size /= _BYTE_CHUNK;
idx++;
}
return '${size.toStringAsFixed(decimals)} ${_BYTE_SIZES[idx]}';
}
String asBits({int decimals = 2}) => _toBits(decimals, 0);
String asKilobits({int decimals = 2}) => _toBits(decimals, 1);
String asMegabits({int decimals = 2}) => _toBits(decimals, 2);
String asGigabits({int decimals = 2}) => _toBits(decimals, 3);
String asBytes({int decimals = 2}) => _toBytes(decimals, 0);
String asKilobytes({int decimals = 2}) => _toBytes(decimals, 1);
String asMegabytes({int decimals = 2}) => _toBytes(decimals, 2);
String asGigabytes({int decimals = 2}) => _toBytes(decimals, 3);
}

View File

@@ -0,0 +1,72 @@
import 'package:lunasea/vendor.dart';
extension IntegerAsDurationExtension on int? {
static const int _MINUTE_IN_SECONDS = 60;
static const int _HOUR_IN_SECONDS = _MINUTE_IN_SECONDS * 60;
static const int _DAY_IN_SECONDS = _HOUR_IN_SECONDS * 24;
String asWordDuration({
int multiplier = 1,
int divisor = 1,
}) {
if (this == null || this! <= 0) return 'lunasea.Unknown'.tr();
final seconds = ((this! * multiplier) / divisor).floor();
final days = seconds ~/ _DAY_IN_SECONDS;
if (days > 1) {
return 'lunasea.Days'.tr(args: [days.toString()]);
}
final hours = seconds ~/ _HOUR_IN_SECONDS;
if (hours > 1) {
return 'lunasea.Hours'.tr(args: [hours.toString()]);
}
final minutes = seconds ~/ _MINUTE_IN_SECONDS;
if (minutes > 0) {
return 'lunasea.Minutes'.tr(args: [minutes.toString()]);
}
return 'lunasea.Seconds'.tr(args: [seconds.toString()]);
}
String asVideoDuration({
int multiplier = 1,
int divisor = 1,
}) {
if (this == null || this! <= 0) return 'lunasea.Unknown'.tr();
final minutes = ((this! * multiplier) / divisor).floor();
final hours = (minutes ~/ 60);
final remainder = (minutes - (60 * hours));
if (minutes < 60) return '${minutes}m';
return '${hours}h ${remainder}m';
}
String asTrackDuration({
int multiplier = 1,
int divisor = 1,
}) {
if (this == null || this! <= 0) return '00:00';
int duration = ((this! * multiplier) / divisor).floor();
int hourCounter = 0, minuteCounter = 0;
while (duration >= _HOUR_IN_SECONDS) {
duration -= _HOUR_IN_SECONDS;
hourCounter++;
}
while (duration >= _MINUTE_IN_SECONDS) {
duration -= _MINUTE_IN_SECONDS;
minuteCounter++;
}
final hours = hourCounter.toString().padLeft(1, '0');
final minutes = minuteCounter.toString().padLeft(2, '0');
final seconds = duration.toString().padLeft(2, '0');
if (hourCounter > 0) return '$hours:$minutes:$seconds';
return '$minutes:$seconds';
}
}

View File

@@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
extension NavigatorStateExtension on NavigatorState {
void safetyPop() {
if (this.canPop()) this.pop();
}
void popToRootRoute() {
this.popUntil((route) => route.isFirst);
}
}

View File

@@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
extension PageControllerExtension on PageController {
/// Jump a [PageView] to the given index.
Future<void> lunaJumpToPage(int index) async {
Future<void> protectedJumpToPage(int index) async {
if (this.hasClients) this.jumpToPage(index);
}
}

View File

@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
extension ScrollControllerExtension on ScrollController {
static const _duration =
Duration(milliseconds: LunaUI.ANIMATION_SPEED_SCROLLING);
Future<void> animateToStart() async {
if (this.hasClients) {
this.animateTo(
0.00,
duration: _duration,
curve: Curves.easeInOutQuart,
);
}
}
}

View File

@@ -56,4 +56,12 @@ extension StringExtension on String {
.padLeft((this.length + count), padding)
.padRight((this.length + (count * 2)), padding);
}
/// Returns the string with a bullet appended to the front.
String bulleted({
String bullet = LunaUI.TEXT_BULLET,
int padCount = 1,
}) {
return '${bullet.pad(count: padCount)}$this';
}
}

View File

@@ -1,5 +1,6 @@
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/utils/profile_tools.dart';
import 'package:lunasea/firebase/core.dart';
import 'package:lunasea/system/platform.dart';
@@ -115,8 +116,6 @@ class LunaFirebaseMessaging {
LunaModule? module = LunaModule.fromKey(message.data['module']);
if (module == null) {
LunaLogger().warning(
'LunaFirebaseMessaging',
'_handleWebhook',
'Unknown module found inside of RemoteMessage: ${message.data['module'] ?? 'null'}',
);
return;
@@ -124,13 +123,11 @@ class LunaFirebaseMessaging {
String profile = message.data['profile'] ?? '';
if (profile.isEmpty) {
LunaLogger().warning(
'LunaFirebaseMessaging',
'_handleWebhook',
'Invalid profile received in webhook: ${message.data['profile'] ?? 'null'}',
);
return;
}
bool result = LunaProfile().changeTo(profile, popToRoot: true);
bool result = LunaProfileTools().changeTo(profile, popToRootRoute: true);
if (result) {
module.handleWebhook(message.data);
} else {

View File

@@ -4,19 +4,22 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/core/cache/image_cache/image_cache.dart';
import 'package:lunasea/database/database.dart';
import 'package:lunasea/firebase/core.dart';
import 'package:lunasea/firebase/firestore.dart';
import 'package:lunasea/firebase/messaging.dart';
import 'package:lunasea/modules/dashboard/routes/dashboard/route.dart'
show HomeRouter;
import 'package:lunasea/system/build.dart';
import 'package:lunasea/system/cache/image/image_cache.dart';
import 'package:lunasea/system/flavor.dart';
import 'package:lunasea/system/in_app_purchase/in_app_purchase.dart';
import 'package:lunasea/system/localization.dart';
import 'package:lunasea/system/network/network.dart';
import 'package:lunasea/system/quick_actions/quick_actions.dart';
import 'package:lunasea/system/window_manager/window_manager.dart';
import 'package:lunasea/system/platform.dart';
import 'package:lunasea/widgets/changelog/sheet.dart';
/// LunaSea Entry Point: Initialize & Run Application
///
@@ -53,7 +56,7 @@ class LunaBIOS extends StatelessWidget {
LunaTheme theme = LunaTheme();
LunaRouter router = LunaRouter();
return ProviderScope(
return LunaState.providers(
child: DevicePreview(
enabled: kDebugMode && LunaPlatform.isDesktop,
builder: (context) => EasyLocalization(
@@ -62,29 +65,27 @@ class LunaBIOS extends StatelessWidget {
fallbackLocale: LunaLocalization.fallbackLocale,
startLocale: LunaLocalization.fallbackLocale,
useFallbackTranslations: true,
child: LunaState.providers(
child: LunaBox.lunasea.watch(
selectItems: [
LunaSeaDatabase.THEME_AMOLED,
LunaSeaDatabase.THEME_AMOLED_BORDER,
],
builder: (context, _) {
return MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
builder: DevicePreview.appBuilder,
routes: router.routes,
useInheritedMediaQuery: true,
onGenerateRoute: LunaRouter.router.generator,
navigatorKey: LunaState.navigatorKey,
darkTheme: theme.activeTheme(),
theme: theme.activeTheme(),
scrollBehavior: LunaScrollBehavior(),
title: 'LunaSea',
);
},
),
child: LunaBox.lunasea.watch(
selectItems: [
LunaSeaDatabase.THEME_AMOLED,
LunaSeaDatabase.THEME_AMOLED_BORDER,
],
builder: (context, _) {
return MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
builder: DevicePreview.appBuilder,
routes: router.routes,
useInheritedMediaQuery: true,
onGenerateRoute: LunaRouter.router.generator,
navigatorKey: LunaState.navigatorKey,
darkTheme: theme.activeTheme(),
theme: theme.activeTheme(),
scrollBehavior: LunaScrollBehavior(),
title: 'LunaSea',
);
},
),
),
),
@@ -93,9 +94,7 @@ class LunaBIOS extends StatelessWidget {
}
class LunaOS extends StatefulWidget {
const LunaOS({
Key? key,
}) : super(key: key);
const LunaOS({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _State();
@@ -105,7 +104,10 @@ class _State extends State<LunaOS> {
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) => _boot());
SchedulerBinding.instance.addPostFrameCallback((_) {
_boot();
_healthCheck();
});
}
Future<void> _initNotifications() async {
@@ -119,6 +121,14 @@ class _State extends State<LunaOS> {
messaging.registerOnMessageOpenedAppListener();
}
Future<void> _healthCheck() async {
if (LunaFlavor.isStable) {
LunaBuild().isLatestBuildVersion().then((isLatest) {
if (!isLatest.item1) ChangelogSheet().show();
});
}
}
Future<void> _boot() async {
if (LunaFirebaseMessaging.isSupported) _initNotifications();
@@ -127,10 +137,6 @@ class _State extends State<LunaOS> {
Intl.defaultLocale = tag;
if (LunaQuickActions.isSupported) LunaQuickActions().initialize();
LunaChangelogSheet().show(
context: LunaState.navigatorKey.currentContext!,
checkBuildVersion: true,
);
}
@override

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lunasea/extensions/string_links.dart';
import 'package:lunasea/extensions/string/links.dart';
import 'package:quick_actions/quick_actions.dart';
import 'package:lunasea/core.dart';
@@ -124,23 +124,23 @@ extension LunaModuleEnablementExtension on LunaModule {
case LunaModule.SETTINGS:
return true;
case LunaModule.LIDARR:
return LunaProfile.current.lidarrEnabled ?? false;
return LunaProfile.current.lidarrEnabled;
case LunaModule.NZBGET:
return LunaProfile.current.nzbgetEnabled ?? false;
return LunaProfile.current.nzbgetEnabled;
case LunaModule.OVERSEERR:
return LunaProfile.current.overseerrEnabled ?? false;
return LunaProfile.current.overseerrEnabled;
case LunaModule.RADARR:
return LunaProfile.current.radarrEnabled ?? false;
return LunaProfile.current.radarrEnabled;
case LunaModule.SABNZBD:
return LunaProfile.current.sabnzbdEnabled ?? false;
return LunaProfile.current.sabnzbdEnabled;
case LunaModule.SEARCH:
return !LunaBox.indexers.isEmpty;
case LunaModule.SONARR:
return LunaProfile.current.sonarrEnabled ?? false;
return LunaProfile.current.sonarrEnabled;
case LunaModule.TAUTULLI:
return LunaProfile.current.tautulliEnabled ?? false;
return LunaProfile.current.tautulliEnabled;
case LunaModule.WAKE_ON_LAN:
return LunaProfile.current.wakeOnLANEnabled ?? false;
return LunaProfile.current.wakeOnLANEnabled;
case LunaModule.EXTERNAL_MODULES:
return !LunaBox.externalModules.isEmpty;
}

View File

@@ -1,6 +1,6 @@
import 'package:lunasea/core/extensions/datetime.dart';
import 'package:lunasea/core/models/configuration/profile.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/database/tables/dashboard.dart';
import 'package:lunasea/extensions/datetime.dart';
import 'package:lunasea/modules/dashboard/core/api/data/abstract.dart';
import 'package:lunasea/modules/dashboard/core/api/data/lidarr.dart';
import 'package:lunasea/modules/dashboard/core/api/data/radarr.dart';
@@ -19,7 +19,7 @@ class API {
required this.sonarr,
});
factory API.from(ProfileHiveObject? profile) {
factory API.from(LunaProfile? profile) {
return API._internal(
lidarr: profile?.getLidarr(),
radarr: profile?.getRadarr(),
@@ -61,7 +61,7 @@ class API {
if (response.data.length > 0) {
for (var entry in response.data) {
DateTime? date =
DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal().lunaFloor;
DateTime.tryParse(entry['releaseDate'] ?? '')?.toLocal().floor();
if (date != null && _isDateWithinBounds(date, today)) {
List<CalendarData> day = map[date] ?? [];
day.add(CalendarLidarrData(
@@ -108,11 +108,9 @@ class API {
DateTime? physicalRelease =
DateTime.tryParse(entry['physicalRelease'] ?? '')
?.toLocal()
.lunaFloor;
.floor();
DateTime? digitalRelease =
DateTime.tryParse(entry['digitalRelease'] ?? '')
?.toLocal()
.lunaFloor;
DateTime.tryParse(entry['digitalRelease'] ?? '')?.toLocal().floor();
DateTime? release;
if (physicalRelease != null || digitalRelease != null) {
if (physicalRelease == null) release = digitalRelease;
@@ -166,7 +164,7 @@ class API {
if (response.data.length > 0) {
for (var entry in response.data) {
DateTime? date =
DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal().lunaFloor;
DateTime.tryParse(entry['airDateUtc'] ?? '')?.toLocal().floor();
if (date != null && _isDateWithinBounds(date, today)) {
List<CalendarData> day = map[date] ?? [];
day.add(CalendarSonarrData(

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/widgets/ui.dart';
import 'package:lunasea/modules/lidarr/core/api/api.dart';
import 'package:lunasea/modules/lidarr/routes/details_artist.dart';

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core/extensions/int.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/extensions/int/duration.dart';
import 'package:lunasea/extensions/string/string.dart';
import 'package:lunasea/vendor.dart';
import 'package:lunasea/widgets/ui.dart';
import 'package:lunasea/modules/radarr/core/api_helper.dart';
@@ -37,7 +37,7 @@ class CalendarRadarrData extends CalendarData {
children: [
TextSpan(text: year.toString()),
TextSpan(text: LunaUI.TEXT_BULLET.pad()),
TextSpan(text: runtime.lunaRuntime()),
TextSpan(text: runtime.asVideoDuration()),
],
),
TextSpan(text: studio),

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/database/tables/lunasea.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/extensions/string/string.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/system/logger.dart';
import 'package:lunasea/widgets/ui.dart';
import 'package:lunasea/vendor.dart';

View File

@@ -1,6 +1,5 @@
import 'package:lunasea/core/models/configuration/profile.dart';
import 'package:lunasea/core/state/module_state.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/deprecated/state/module_state.dart';
import 'package:lunasea/database/tables/dashboard.dart';
import 'package:lunasea/modules/dashboard/core/adapters/calendar_starting_type.dart';
import 'package:lunasea/modules/dashboard/core/api/api.dart';
@@ -29,7 +28,7 @@ class ModuleState extends LunaModuleState {
API? _api;
API? get api => _api;
void resetAPI() {
ProfileHiveObject? _profile = LunaProfile.current;
LunaProfile? _profile = LunaProfile.current;
_api = API.from(_profile);
notifyListeners();
}

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core/extensions/async_snapshot.dart';
import 'package:provider/provider.dart';
import 'package:lunasea/widgets/ui.dart';
@@ -53,7 +52,8 @@ class _State extends State<CalendarPage>
return LunaMessage.error(onTap: _refreshKey.currentState!.show);
}
if (snapshot.lunaIsFinishedWithData) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final events = snapshot.data!;
return Selector<ModuleState, CalendarStartingType>(
selector: (_, s) => s.calendarStartingType,

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lunasea/modules.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/database/tables/lunasea.dart';
import 'package:lunasea/widgets/ui.dart';
import 'package:lunasea/modules/wake_on_lan/api/wake_on_lan.dart';
@@ -27,7 +27,7 @@ class _State extends State<ModulesPage> with AutomaticKeepAliveClientMixin {
}
Widget _list() {
if (!(LunaProfile.current.anythingEnabled)) {
if (!(LunaProfile.current.isAnythingEnabled())) {
return LunaMessage(
text: 'lunasea.NoModulesEnabled'.tr(),
buttonText: 'lunasea.GoToSettings'.tr(),

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lunasea/core/extensions/datetime.dart';
import 'package:lunasea/database/box.dart';
import 'package:lunasea/database/tables/dashboard.dart';
import 'package:lunasea/extensions/datetime.dart';
import 'package:lunasea/widgets/ui.dart';
import 'package:lunasea/vendor.dart';
import 'package:lunasea/modules/dashboard/core/adapters/calendar_starting_day.dart';
@@ -47,7 +47,7 @@ class _State extends State<CalendarView> {
super.initState();
final size = DashboardDatabase.CALENDAR_STARTING_SIZE.read();
_selected = context.read<ModuleState>().today!.lunaFloor;
_selected = context.read<ModuleState>().today!.floor();
_selectedEvents = widget.events[_selected] ?? [];
_calendarFormat = size.data;
}
@@ -64,8 +64,8 @@ class _State extends State<CalendarView> {
HapticFeedback.selectionClick();
if (mounted)
setState(() {
_selected = selected.lunaFloor;
_selectedEvents = widget.events[selected.lunaFloor] ?? [];
_selected = selected.floor();
_selectedEvents = widget.events[selected.floor()] ?? [];
});
}
@@ -157,7 +157,7 @@ class _State extends State<CalendarView> {
startingDayOfWeek:
DashboardDatabase.CALENDAR_STARTING_DAY.read().data,
selectedDayPredicate: (date) =>
date.lunaFloor == _selected.lunaFloor,
date.floor() == _selected.floor(),
calendarStyle: CalendarStyle(
markersMaxCount: 1,
isTodayHighlighted: true,
@@ -191,7 +191,7 @@ class _State extends State<CalendarView> {
),
eventLoader: (date) {
if (widget.events.isEmpty) return [];
return widget.events[date.lunaFloor] ?? [];
return widget.events[date.floor()] ?? [];
},
calendarFormat: _calendarFormat,
availableCalendarFormats: const {
@@ -214,8 +214,8 @@ class _State extends State<CalendarView> {
/// -1: Date is after today (with content)
/// -100: Event list was empty or null
int _countMissingContent(DateTime date, List<dynamic> events) {
DateTime _date = date.lunaFloor;
DateTime _now = DateTime.now().lunaFloor;
DateTime _date = date.floor();
DateTime _now = DateTime.now().floor();
if (events.isEmpty) return -100;
if (_date.isAfter(_now)) return -1;

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/system/profile.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/widgets/ui.dart';
import 'package:lunasea/modules/dashboard/core/api/data/abstract.dart';
import 'package:lunasea/modules/dashboard/core/api/data/lidarr.dart';

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core/extensions/datetime.dart';
import 'package:lunasea/database/tables/dashboard.dart';
import 'package:lunasea/extensions/datetime.dart';
import 'package:provider/provider.dart';
import 'package:lunasea/widgets/ui.dart';
@@ -27,7 +27,7 @@ class _State extends State<ScheduleView> {
@override
void initState() {
super.initState();
DateTime _floored = context.read<ModuleState>().today!.lunaFloor;
DateTime _floored = context.read<ModuleState>().today!.floor();
_today = _floored;
}

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core/router/module_router.dart';
import 'package:lunasea/core/router/page_router.dart';
import 'package:lunasea/core/router/router.dart';
import 'package:lunasea/deprecated/router/module_router.dart';
import 'package:lunasea/deprecated/router/page_router.dart';
import 'package:lunasea/deprecated/router/router.dart';
import 'package:lunasea/vendor.dart';
import 'package:lunasea/modules/dashboard/routes/dashboard/route.dart';

View File

@@ -61,9 +61,9 @@ class _State extends State<_Widget> with LunaScrollControllerMixin {
final list = LunaBox.externalModules.data
.map((module) => ExternalModulesModuleTile(module: module))
.toList();
list.sort((a, b) => a.module!.displayName!
list.sort((a, b) => a.module!.displayName
.toLowerCase()
.compareTo(b.module!.displayName!.toLowerCase()));
.compareTo(b.module!.displayName.toLowerCase()));
return list;
}

View File

@@ -1,9 +1,10 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string_links.dart';
import 'package:lunasea/database/models/external_module.dart';
import 'package:lunasea/extensions/string/links.dart';
class ExternalModulesModuleTile extends StatelessWidget {
final ExternalModuleHiveObject? module;
final LunaExternalModule? module;
const ExternalModulesModuleTile({
Key? key,
@@ -16,7 +17,7 @@ class ExternalModulesModuleTile extends StatelessWidget {
title: module!.displayName,
body: [TextSpan(text: module!.host)],
trailing: const LunaIconButton.arrow(),
onTap: module!.host!.openLink,
onTap: module!.host.openLink,
);
}
}

View File

@@ -6,7 +6,7 @@ class LidarrAPI {
final Dio _dio;
LidarrAPI._internal(this._values, this._dio);
factory LidarrAPI.from(ProfileHiveObject profile) {
factory LidarrAPI.from(LunaProfile profile) {
Map<String, dynamic> _headers =
Map<String, dynamic>.from(profile.getLidarr()['headers']);
Dio _client = Dio(

View File

@@ -1,4 +1,5 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/int/bytes.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrCatalogueData {
@@ -70,7 +71,7 @@ class LidarrCatalogueData {
return dateAdded;
case LidarrCatalogueSorting.size:
case LidarrCatalogueSorting.alphabetical:
return sizeOnDisk.lunaBytesToString();
return sizeOnDisk.asBytes();
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/int/bytes.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrDialogs {
@@ -229,7 +230,7 @@ class LidarrDialogs {
subtitle: LunaDialog.richText(
children: [
LunaDialog.bolded(
text: folders[index].freeSpace.lunaBytesToString(),
text: folders[index].freeSpace.asBytes(),
fontSize: LunaDialog.BUTTON_SIZE,
),
],

View File

@@ -1,4 +1,5 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/system/webhooks.dart';
class LidarrWebhooks extends LunaWebhooks {
@override
@@ -6,8 +7,6 @@ class LidarrWebhooks extends LunaWebhooks {
_EventType? event = _EventType.GRAB.fromKey(data['event']);
if (event == null)
LunaLogger().warning(
'LidarrWebhooks',
'handle',
'Unknown event type: ${data['event'] ?? 'null'}',
);
event?.execute(data);

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string_links.dart';
import 'package:lunasea/extensions/string/links.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrAddDetailsArguments {

View File

@@ -81,7 +81,7 @@ class _State extends State<LidarrDetailsArtist> {
LunaIconButton(
icon: LunaIcons.LINK,
onPressed: () async {
LinksSheet(artist: _arguments!.data!).show(context: context);
LinksSheet(artist: _arguments!.data!).show();
},
),
LidarrDetailsEditButton(data: _arguments!.data),

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string_links.dart';
import 'package:lunasea/extensions/string/links.dart';
import 'package:lunasea/modules/lidarr.dart';
class Lidarr extends StatefulWidget {
@@ -133,8 +133,7 @@ class _State extends State<Lidarr> {
break;
}
default:
LunaLogger().warning(
'Lidarr', '_enterAddArtist', 'Unknown Case: ${result[0]}');
LunaLogger().warning('[_enterAddArtist] Unknown Case: ${result[0]}');
}
}
@@ -143,8 +142,8 @@ class _State extends State<Lidarr> {
if (values[0])
switch (values[1]) {
case 'web_gui':
ProfileHiveObject profile = LunaProfile.current;
await profile.lidarrHost?.openLink();
LunaProfile profile = LunaProfile.current;
await profile.lidarrHost.openLink();
break;
case 'update_library':
await _api
@@ -188,8 +187,7 @@ class _State extends State<Lidarr> {
break;
}
default:
LunaLogger()
.warning('Lidarr', '_handlePopup', 'Unknown Case: ${values[1]}');
LunaLogger().warning('Unknown Case: ${values[1]}');
}
}

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lunasea/extensions/string_links.dart';
import 'package:lunasea/extensions/string/links.dart';
import 'package:lunasea/modules/lidarr/core/api.dart';
import 'package:lunasea/widgets/ui.dart';

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string_links.dart';
import 'package:lunasea/extensions/string/links.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrAddSearchResultTile extends StatelessWidget {
@@ -63,8 +63,6 @@ class LidarrAddSearchResultTile extends StatelessWidget {
break;
default:
LunaLogger().warning(
'LidarrAddSearchResultTile',
'_enterDetails',
'Unknown Case: ${result[0]}',
);
break;

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/scroll_controller.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrCatalogueSortButton extends StatefulWidget {
@@ -32,7 +33,7 @@ class _State extends State<LidarrCatalogueSortButton> {
model.sortCatalogueAscending = true;
model.sortCatalogueType = result;
}
widget.controller.lunaAnimateToStart();
widget.controller.animateToStart();
},
itemBuilder: (context) =>
List<PopupMenuEntry<LidarrCatalogueSorting>>.generate(

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/extensions/string/string.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrCatalogueTile extends StatefulWidget {
@@ -99,8 +99,7 @@ class _State extends State<LidarrCatalogueTile> {
break;
}
default:
LunaLogger().warning('LidarrCatalogueTile', '_enterArtist',
'Unknown Case: ${result[0]}');
LunaLogger().warning('Unknown Case: ${result[0]}');
}
}
@@ -118,8 +117,8 @@ class _State extends State<LidarrCatalogueTile> {
_removeArtist();
break;
default:
LunaLogger().warning('LidarrCatalogueTile', '_handlePopup',
'Invalid method passed through popup. (${values[1]})');
LunaLogger()
.warning('Invalid method passed through popup. (${values[1]})');
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/int/duration.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrDetailsTrackTile extends StatefulWidget {
@@ -21,7 +22,7 @@ class _State extends State<LidarrDetailsTrackTile> {
Widget build(BuildContext context) => LunaBlock(
title: widget.data.title,
body: [
TextSpan(text: widget.data.duration.lunaTimestamp(divisor: 1000)),
TextSpan(text: widget.data.duration.asTrackDuration(divisor: 1000)),
widget.data.file(widget.monitored),
],
disabled: !widget.monitored,

View File

@@ -40,8 +40,8 @@ class _State extends State<LidarrDetailsSettingsButton> {
_removeArtist(context);
break;
default:
LunaLogger().warning('LidarrDetailsSettingsButton', '_handlePopup',
'Invalid method passed through popup. (${values[1]})');
LunaLogger()
.warning('Invalid method passed through popup. (${values[1]})');
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/scroll_controller.dart';
import 'package:lunasea/modules/lidarr.dart';
class LidarrReleasesSortButton extends StatefulWidget {
@@ -32,7 +33,7 @@ class _State extends State<LidarrReleasesSortButton> {
model.sortReleasesAscending = true;
model.sortReleasesType = result;
}
widget.controller.lunaAnimateToStart();
widget.controller.animateToStart();
},
itemBuilder: (context) =>
List<PopupMenuEntry<LidarrReleasesSorting>>.generate(

View File

@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/double.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/extensions/double/time.dart';
import 'package:lunasea/extensions/int/bytes.dart';
import 'package:lunasea/extensions/string/string.dart';
import 'package:lunasea/modules/lidarr.dart';
import 'package:lunasea/modules/dashboard/routes/dashboard/route.dart'
as dashboard_home;
@@ -56,7 +57,7 @@ class _State extends State<LidarrReleasesTile> {
TextSpan(text: LunaUI.TEXT_BULLET.pad()),
TextSpan(text: widget.release.indexer),
TextSpan(text: LunaUI.TEXT_BULLET.pad()),
TextSpan(text: widget.release.ageHours.asTimeAgo),
TextSpan(text: widget.release.ageHours.asTimeAgo()),
]);
}
@@ -65,7 +66,7 @@ class _State extends State<LidarrReleasesTile> {
children: [
TextSpan(text: widget.release.quality),
TextSpan(text: LunaUI.TEXT_BULLET.pad()),
TextSpan(text: widget.release.size.lunaBytesToString()),
TextSpan(text: widget.release.size.asBytes()),
],
);
}
@@ -96,10 +97,9 @@ class _State extends State<LidarrReleasesTile> {
return [
LunaTableContent(
title: 'source', body: widget.release.protocol.toTitleCase()),
LunaTableContent(title: 'age', body: widget.release.ageHours.asTimeAgo),
LunaTableContent(title: 'age', body: widget.release.ageHours.asTimeAgo()),
LunaTableContent(title: 'indexer', body: widget.release.indexer),
LunaTableContent(
title: 'size', body: widget.release.size.lunaBytesToString()),
LunaTableContent(title: 'size', body: widget.release.size.asBytes()),
LunaTableContent(title: 'quality', body: widget.release.quality),
if (widget.release.protocol == 'torrent' &&
widget.release.seeders != null)

View File

@@ -6,7 +6,7 @@ class NZBGetAPI {
final Dio _dio;
NZBGetAPI._internal(this._values, this._dio);
factory NZBGetAPI.from(ProfileHiveObject profile) {
factory NZBGetAPI.from(LunaProfile profile) {
String _baseURL = Uri.encodeFull(profile.getNZBGet()['host']);
Map<String, dynamic> _headers =
Map<String, dynamic>.from(profile.getNZBGet()['headers']);

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/int/bytes.dart';
class NZBGetHistoryData {
int id;
@@ -38,12 +37,12 @@ class NZBGetHistoryData {
return '0.00 MB/s';
} else {
int speed = (downloaded / downloadTime).floor();
return '${speed.lunaBytesToString()}/s';
return '${speed.asBytes()}/s';
}
}
String get sizeReadable {
return downloaded.lunaBytesToString();
return downloaded.asBytes();
}
DateTime? get timestampObject {

View File

@@ -1,5 +1,6 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/extensions/int/duration.dart';
import 'package:lunasea/extensions/string/string.dart';
class NZBGetQueueData {
int id;
@@ -41,9 +42,9 @@ class NZBGetQueueData {
}
String get timestamp {
return (queueSeconds + remainingTime).lunaTimestamp() == '0:00:00'
return (queueSeconds + remainingTime).asTrackDuration() == '00:00'
? ''
: (queueSeconds + remainingTime).lunaTimestamp();
: (queueSeconds + remainingTime).asTrackDuration();
}
String get statusString {

View File

@@ -1,4 +1,5 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/int/bytes.dart';
import 'package:lunasea/extensions/int/duration.dart';
class NZBGetStatisticsData {
int freeSpaceHigh;
@@ -32,19 +33,19 @@ class NZBGetStatisticsData {
}
String get freeSpaceString {
return freeSpace.lunaBytesToString(decimals: 1);
return freeSpace.asBytes(decimals: 1);
}
String get downloadedString {
return downloaded.lunaBytesToString(decimals: 1);
return downloaded.asBytes(decimals: 1);
}
String get uptimeString {
return uptimeSeconds.lunaDuration();
return uptimeSeconds.asWordDuration();
}
String get speedLimitString {
String limit = speedLimit.lunaBytesToString();
String limit = speedLimit.asBytes();
return limit == '0.00 B' ? 'No Limit Set' : '$limit/s';
}
}

View File

@@ -1,4 +1,5 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/int/bytes.dart';
import 'package:lunasea/extensions/int/duration.dart';
class NZBGetStatusData {
bool paused;
@@ -20,22 +21,20 @@ class NZBGetStatusData {
}
String get currentSpeed {
return '${speed.lunaBytesToString(decimals: 1)}/s';
return '${speed.asBytes(decimals: 1)}/s';
}
String get remainingString {
return remaining.lunaBytesToString(decimals: 1);
return remaining.asBytes(decimals: 1);
}
String get timeLeft {
return speed == 0
? '0:00:00'
: ((remaining / speed).floor()).lunaTimestamp();
: ((remaining / speed).floor()).asTrackDuration();
}
String get speedlimitString {
return speedlimit == 0
? 'Unlimited'
: speedlimit.lunaBytesToString(decimals: 0);
return speedlimit == 0 ? 'Unlimited' : speedlimit.asBytes(decimals: 0);
}
}

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/database/tables/nzbget.dart';
import 'package:lunasea/extensions/string_links.dart';
import 'package:lunasea/extensions/string/links.dart';
import 'package:lunasea/modules/nzbget.dart';
import 'package:lunasea/system/filesystem/file.dart';
@@ -113,8 +113,8 @@ class _State extends State<NZBGet> {
if (values[0])
switch (values[1]) {
case 'web_gui':
ProfileHiveObject profile = LunaProfile.current;
await profile.nzbgetHost?.openLink();
LunaProfile profile = LunaProfile.current;
await profile.nzbgetHost.openLink();
break;
case 'add_nzb':
_addNZB();
@@ -126,8 +126,7 @@ class _State extends State<NZBGet> {
_serverDetails();
break;
default:
LunaLogger()
.warning('NZBGet', '_handlePopup', 'Unknown Case: ${values[1]}');
LunaLogger().warning('Unknown Case: ${values[1]}');
}
}
@@ -142,8 +141,7 @@ class _State extends State<NZBGet> {
_addByFile();
break;
default:
LunaLogger()
.warning('NZBGet', '_addNZB', 'Unknown Case: ${values[1]}');
LunaLogger().warning('Unknown Case: ${values[1]}');
}
}

View File

@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/extensions/int/bytes.dart';
import 'package:lunasea/extensions/string/string.dart';
import 'package:lunasea/modules/nzbget.dart';
class NZBGetAppBarStats extends StatelessWidget {
@@ -72,7 +73,7 @@ class NZBGetAppBarStats extends StatelessWidget {
.then((_) => showLunaSuccessSnackBar(
title: 'Speed Limit Set',
message:
'Set to ${(values[1] as int?).lunaKilobytesToString(decimals: 0)}/s',
'Set to ${(values[1] as int?).asKilobytes(decimals: 0)}/s',
))
.catchError((error) => showLunaErrorSnackBar(
title: 'Failed to Set Speed Limit',
@@ -86,7 +87,7 @@ class NZBGetAppBarStats extends StatelessWidget {
.then((_) => showLunaSuccessSnackBar(
title: 'Speed Limit Set',
message:
'Set to ${(values[1] as int?).lunaKilobytesToString(decimals: 0)}/s',
'Set to ${(values[1] as int?).asKilobytes(decimals: 0)}/s',
))
.catchError((error) => showLunaErrorSnackBar(
title: 'Failed to Set Speed Limit',

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/string.dart';
import 'package:lunasea/extensions/string/string.dart';
import 'package:lunasea/modules/nzbget.dart';
class NZBGetHistoryTile extends StatefulWidget {

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/extensions/int/duration.dart';
import 'package:lunasea/modules/nzbget.dart';
class NZBGetQueueFAB extends StatefulWidget {
@@ -112,7 +113,7 @@ class _State extends State<NZBGetQueueFAB> with TickerProviderStateMixin {
.then((_) => showLunaSuccessSnackBar(
title: 'Pausing Queue',
message:
'For ${(values[1] as int?).lunaDuration(multiplier: 60)}',
'For ${(values[1] as int?).asWordDuration(multiplier: 60)}',
))
.catchError((error) => showLunaErrorSnackBar(
title: 'Failed to Pause Queue',
@@ -124,7 +125,7 @@ class _State extends State<NZBGetQueueFAB> with TickerProviderStateMixin {
.then((_) => showLunaSuccessSnackBar(
title: 'Pausing Queue',
message:
'For ${(values[1] as int).lunaDuration(multiplier: 60)}',
'For ${(values[1] as int).asWordDuration(multiplier: 60)}',
))
.catchError((error) => showLunaErrorSnackBar(
title: 'Failed to Pause Queue',

View File

@@ -59,8 +59,7 @@ class _State extends State<NZBGetQueueTile> {
_helper._delete();
break;
default:
LunaLogger().warning(
'NZBGetQueueTile', '_handlePopup', 'Unknown Case: ${values[1]}');
LunaLogger().warning('Unknown Case: ${values[1]}');
}
}
}

Some files were not shown because too many files have changed in this diff Show More