(feat): Implemented Overseerr core architecture, Added platform checks for Firebase, Added module flagging (#425)

This commit is contained in:
Jagandeep Brar
2021-04-13 22:07:14 -05:00
committed by GitHub
parent e7181e70cc
commit e54e4eecec
47 changed files with 739 additions and 99 deletions

View File

@@ -4,6 +4,7 @@ export 'core/extensions.dart';
export 'core/firebase.dart';
export 'core/mixins.dart';
export 'core/models.dart';
export 'core/modules.dart';
export 'core/pages.dart';
export 'core/router.dart';
export 'core/state.dart';

View File

@@ -24,18 +24,15 @@ class Database {
/// Registers all necessary object adapters for Hive.
void _registerAdapters() {
//General
LunaDatabase().registerAdapters();
DashboardDatabase().registerAdapters();
SearchDatabase().registerAdapters();
//Automation
LidarrDatabase().registerAdapters();
RadarrDatabase().registerAdapters();
SonarrDatabase().registerAdapters();
//Clients
NZBGetDatabase().registerAdapters();
SABnzbdDatabase().registerAdapters();
//Monitoring
OverseerrDatabase().registerAdapters();
TautulliDatabase().registerAdapters();
}

View File

@@ -32,6 +32,7 @@ class LunaDatabase extends LunaModuleDatabase {
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET:
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD:
case LunaDatabaseValue.QUICK_ACTIONS_OMBI:
case LunaDatabaseValue.QUICK_ACTIONS_OVERSEERR:
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI:
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH:
case LunaDatabaseValue.USE_24_HOUR_TIME: data[value.key] = value.data; break;
@@ -59,6 +60,7 @@ class LunaDatabase extends LunaModuleDatabase {
case LunaDatabaseValue.QUICK_ACTIONS_SONARR:
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET:
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD:
case LunaDatabaseValue.QUICK_ACTIONS_OVERSEERR:
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI:
case LunaDatabaseValue.QUICK_ACTIONS_OMBI:
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH:
@@ -82,6 +84,7 @@ class LunaDatabase extends LunaModuleDatabase {
case 'LUNASEA_QUICK_ACTIONS_NZBGET': return LunaDatabaseValue.QUICK_ACTIONS_NZBGET;
case 'LUNASEA_QUICK_ACTIONS_SABNZBD': return LunaDatabaseValue.QUICK_ACTIONS_SABNZBD;
case 'LUNASEA_QUICK_ACTIONS_OMBI': return LunaDatabaseValue.QUICK_ACTIONS_OMBI;
case 'LUNASEA_QUICK_ACTIONS_OVERSEERR': return LunaDatabaseValue.QUICK_ACTIONS_OVERSEERR;
case 'LUNASEA_QUICK_ACTIONS_TAUTULLI': return LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI;
case 'LUNASEA_QUICK_ACTIONS_SEARCH': return LunaDatabaseValue.QUICK_ACTIONS_SEARCH;
case 'LUNASEA_USE_24_HOUR_TIME': return LunaDatabaseValue.USE_24_HOUR_TIME;
@@ -104,6 +107,7 @@ enum LunaDatabaseValue {
QUICK_ACTIONS_NZBGET,
QUICK_ACTIONS_SABNZBD,
QUICK_ACTIONS_OMBI,
QUICK_ACTIONS_OVERSEERR,
QUICK_ACTIONS_TAUTULLI,
QUICK_ACTIONS_SEARCH,
USE_24_HOUR_TIME,
@@ -124,6 +128,7 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue {
case LunaDatabaseValue.QUICK_ACTIONS_SONARR: return 'LUNASEA_QUICK_ACTIONS_SONARR';
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: return 'LUNASEA_QUICK_ACTIONS_NZBGET';
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: return 'LUNASEA_QUICK_ACTIONS_SABNZBD';
case LunaDatabaseValue.QUICK_ACTIONS_OVERSEERR: return 'LUNASEA_QUICK_ACTIONS_OVERSEERR';
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return 'LUNASEA_QUICK_ACTIONS_TAUTULLI';
case LunaDatabaseValue.QUICK_ACTIONS_OMBI: return 'LUNASEA_QUICK_ACTIONS_OMBI';
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: return 'LUNASEA_QUICK_ACTIONS_SEARCH';
@@ -147,6 +152,7 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue {
case LunaDatabaseValue.QUICK_ACTIONS_SONARR: return box.get(this.key, defaultValue: false);
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: return box.get(this.key, defaultValue: false);
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: return box.get(this.key, defaultValue: false);
case LunaDatabaseValue.QUICK_ACTIONS_OVERSEERR: return box.get(this.key, defaultValue: false);
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: return box.get(this.key, defaultValue: false);
case LunaDatabaseValue.QUICK_ACTIONS_OMBI: return box.get(this.key, defaultValue: false);
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: return box.get(this.key, defaultValue: false);
@@ -171,6 +177,7 @@ extension LunaDatabaseValueExtension on LunaDatabaseValue {
case LunaDatabaseValue.QUICK_ACTIONS_NZBGET: if(value.runtimeType == bool) box.put(this.key, value); return;
case LunaDatabaseValue.QUICK_ACTIONS_SABNZBD: if(value.runtimeType == bool) box.put(this.key, value); return;
case LunaDatabaseValue.QUICK_ACTIONS_OMBI: if(value.runtimeType == bool) box.put(this.key, value); return;
case LunaDatabaseValue.QUICK_ACTIONS_OVERSEERR: if(value.runtimeType == bool) box.put(this.key, value); return;
case LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI: if(value.runtimeType == bool) box.put(this.key, value); return;
case LunaDatabaseValue.QUICK_ACTIONS_SEARCH: if(value.runtimeType == bool) box.put(this.key, value); return;
case LunaDatabaseValue.USE_24_HOUR_TIME: if(value.runtimeType == bool) box.put(this.key, value); return;

View File

@@ -6,7 +6,9 @@ import 'package:lunasea/core.dart';
class LunaFirebaseAnalytics {
/// Returns true if Firebase Analytics is compatible with this build system/platform.
static bool get isPlatformCompatible => !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows;
static bool get isPlatformCompatible {
return LunaFirebase.isPlatformCompatible && !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows;
}
/// Returns an instance of [FirebaseAnalytics].
///

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
class LunaFirebase {
@@ -6,6 +8,9 @@ class LunaFirebase {
/// Throws an error if [LunaFirebase.initialize] has not been called.
static FirebaseApp get instance => Firebase.app();
/// Returns true if Firebase in its entirety is compatible with this build system/platform.
static bool get isPlatformCompatible => Platform.isIOS || Platform.isAndroid || Platform.isMacOS;
/// Initialize Firebase and configuration.
///
/// This must be called before anything accesses Firebase services, or an exception will be thrown.

View File

@@ -5,7 +5,9 @@ import 'package:lunasea/core.dart';
class LunaFirebaseCrashlytics {
/// Returns true if Firebase Analytics is compatible with this build system/platform.
static bool get isPlatformCompatible => !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows;
static bool get isPlatformCompatible {
return LunaFirebase.isPlatformCompatible && !Platform.isLinux && !Platform.isMacOS && !Platform.isWindows;
}
/// Returns an instance of [FirebaseCrashlytics].
///

View File

@@ -51,6 +51,11 @@ class ProfileHiveObject extends HiveObject {
ombiHost: '',
ombiKey: '',
ombiHeaders: {},
//Overseerr
overseerrEnabled: false,
overseerrHost: '',
overseerrKey: '',
overseerrHeaders: {},
);
/// Create a new [ProfileHiveObject] from another [ProfileHiveObject] (deep-copy).
@@ -97,6 +102,11 @@ class ProfileHiveObject extends HiveObject {
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.
@@ -146,6 +156,11 @@ class ProfileHiveObject extends HiveObject {
ombiHost: profile['ombiHost'] ?? '',
ombiKey: profile['ombiKey'] ?? '',
ombiHeaders: profile['ombiHeaders'] ?? {},
//Overseerr
overseerrEnabled: profile['overseerrEnabled'] ?? false,
overseerrHost: profile['overseerrHost'] ?? '',
overseerrKey: profile['overseerrKey'] ?? '',
overseerrHeaders: profile['overseerrHeaders'] ?? {},
);
@@ -192,6 +207,11 @@ class ProfileHiveObject extends HiveObject {
@required this.ombiHost,
@required this.ombiKey,
@required this.ombiHeaders,
//Overseerr
@required this.overseerrEnabled,
@required this.overseerrHost,
@required this.overseerrKey,
@required this.overseerrHeaders,
});
@override
@@ -241,6 +261,11 @@ class ProfileHiveObject extends HiveObject {
"ombiHost": ombiHost,
"ombiKey": ombiKey,
"ombiHeaders": ombiHeaders,
//Overseerr
"overseerrEnabled": overseerrEnabled,
"overseerrHost": overseerrHost,
"overseerrKey": overseerrKey,
"overseerrHeaders": overseerrHeaders,
};
//Lidarr
@@ -385,6 +410,23 @@ class ProfileHiveObject extends HiveObject {
'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 ?? {},
};
List<LunaModule> get enabledModules => [
...enabledAutomationModules,
...enabledClientModules,
@@ -392,18 +434,19 @@ class ProfileHiveObject extends HiveObject {
];
List<LunaModule> get enabledAutomationModules => [
if(lidarrEnabled ?? false) LunaModule.LIDARR,
if(radarrEnabled ?? false) LunaModule.RADARR,
if(sonarrEnabled ?? false) LunaModule.SONARR,
if(LunaModule.LIDARR.enabled && (lidarrEnabled ?? false)) LunaModule.LIDARR,
if(LunaModule.RADARR.enabled && (radarrEnabled ?? false)) LunaModule.RADARR,
if(LunaModule.SONARR.enabled && (sonarrEnabled ?? false)) LunaModule.SONARR,
];
List<LunaModule> get enabledClientModules => [
if(nzbgetEnabled ?? false) LunaModule.NZBGET,
if(sabnzbdEnabled ?? false) LunaModule.SABNZBD,
if(LunaModule.NZBGET.enabled && (nzbgetEnabled ?? false)) LunaModule.NZBGET,
if(LunaModule.SABNZBD.enabled && (sabnzbdEnabled ?? false)) LunaModule.SABNZBD,
];
List<LunaModule> get enabledMonitoringModules => [
if(tautulliEnabled ?? false) LunaModule.TAUTULLI,
if(LunaModule.OVERSEERR.enabled && (overseerrEnabled ?? false)) LunaModule.OVERSEERR,
if(LunaModule.TAUTULLI.enabled && (tautulliEnabled ?? false)) LunaModule.TAUTULLI,
];
bool get anyAutomationEnabled => enabledAutomationModules.isNotEmpty;

View File

@@ -51,13 +51,17 @@ class ProfileHiveObjectAdapter extends TypeAdapter<ProfileHiveObject> {
ombiHost: fields[37] as String,
ombiKey: fields[38] as String,
ombiHeaders: (fields[39] as Map)?.cast<dynamic, dynamic>(),
overseerrEnabled: fields[40] as bool,
overseerrHost: fields[41] as String,
overseerrKey: fields[42] as String,
overseerrHeaders: (fields[43] as Map)?.cast<dynamic, dynamic>(),
);
}
@override
void write(BinaryWriter writer, ProfileHiveObject obj) {
writer
..writeByte(34)
..writeByte(38)
..writeByte(0)
..write(obj.lidarrEnabled)
..writeByte(1)
@@ -125,7 +129,15 @@ class ProfileHiveObjectAdapter extends TypeAdapter<ProfileHiveObject> {
..writeByte(38)
..write(obj.ombiKey)
..writeByte(39)
..write(obj.ombiHeaders);
..write(obj.ombiHeaders)
..writeByte(40)
..write(obj.overseerrEnabled)
..writeByte(41)
..write(obj.overseerrHost)
..writeByte(42)
..write(obj.overseerrKey)
..writeByte(43)
..write(obj.overseerrHeaders);
}
@override

View File

@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules.dart';
@@ -13,6 +14,7 @@ enum LunaModule {
SETTINGS,
SONARR,
TAUTULLI,
OVERSEERR,
WAKE_ON_LAN,
}
@@ -27,6 +29,7 @@ extension LunaModuleExtension on LunaModule {
case 'search': return LunaModule.SEARCH;
case 'settings': return LunaModule.SETTINGS;
case 'sonarr': return LunaModule.SONARR;
case 'overseerr': return LunaModule.OVERSEERR;
case 'tautulli': return LunaModule.TAUTULLI;
case 'wake_on_lan': return LunaModule.WAKE_ON_LAN;
}
@@ -43,6 +46,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SEARCH: return 'search';
case LunaModule.SETTINGS: return 'settings';
case LunaModule.SONARR: return 'sonarr';
case LunaModule.OVERSEERR: return 'overseerr';
case LunaModule.TAUTULLI: return 'tautulli';
case LunaModule.WAKE_ON_LAN: return 'wake_on_lan';
}
@@ -51,16 +55,19 @@ extension LunaModuleExtension on LunaModule {
bool get enabled {
switch(this) {
case LunaModule.DASHBOARD: return null;
case LunaModule.LIDARR: return Database.currentProfileObject.lidarrEnabled ?? false;
case LunaModule.NZBGET: return Database.currentProfileObject.nzbgetEnabled ?? false;
case LunaModule.RADARR: return Database.currentProfileObject.radarrEnabled ?? false;
case LunaModule.SABNZBD: return Database.currentProfileObject.sabnzbdEnabled ?? false;
case LunaModule.SEARCH: return (Database.indexersBox.values?.length ?? 0) > 0;
case LunaModule.SETTINGS: return null;
case LunaModule.SONARR: return Database.currentProfileObject.sonarrEnabled ?? false;
case LunaModule.TAUTULLI: return Database.currentProfileObject.tautulliEnabled ?? false;
case LunaModule.WAKE_ON_LAN: return Database.currentProfileObject.wakeOnLANEnabled ?? false;
// Always enabled
case LunaModule.DASHBOARD: return true;
case LunaModule.SETTINGS: return true;
// Modules
case LunaModule.SEARCH: return true;
case LunaModule.WAKE_ON_LAN: return true;
case LunaModule.LIDARR: return true;
case LunaModule.RADARR: return true;
case LunaModule.SONARR: return true;
case LunaModule.NZBGET: return true;
case LunaModule.SABNZBD: return true;
case LunaModule.OVERSEERR: return kDebugMode;
case LunaModule.TAUTULLI: return true;
}
throw Exception('Invalid LunaModule');
}
@@ -76,6 +83,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return 'lunasea.Settings'.tr();
case LunaModule.SONARR: return 'Sonarr';
case LunaModule.TAUTULLI: return 'Tautulli';
case LunaModule.OVERSEERR: return 'Overseerr';
case LunaModule.WAKE_ON_LAN: return 'Wake on LAN';
}
throw Exception('Invalid LunaModule');
@@ -92,6 +100,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return SettingsHomeRouter().route();
case LunaModule.SONARR: return SonarrHomeRouter().route();
case LunaModule.TAUTULLI: return TautulliHomeRouter().route();
case LunaModule.OVERSEERR: return OverseerrHomeRouter().route();
case LunaModule.WAKE_ON_LAN: return null;
}
throw Exception('Invalid LunaModule');
@@ -108,6 +117,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return LunaIcons.settings;
case LunaModule.SONARR: return LunaIcons.television;
case LunaModule.TAUTULLI: return LunaIcons.tautulli;
case LunaModule.OVERSEERR: return LunaIcons.overseerr;
case LunaModule.WAKE_ON_LAN: return Icons.settings_remote;
}
throw Exception('Invalid LunaModule');
@@ -124,6 +134,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return LunaColours.accent;
case LunaModule.SONARR: return Color(0xFF3FC6F4);
case LunaModule.TAUTULLI: return Color(0xFFDBA23A);
case LunaModule.OVERSEERR: return Color(0xFF6366F1);
case LunaModule.WAKE_ON_LAN: return LunaColours.accent;
}
throw Exception('Invalid LunaModule');
@@ -140,6 +151,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return null;
case LunaModule.SONARR: return 'https://sonarr.tv';
case LunaModule.TAUTULLI: return 'https://tautulli.com';
case LunaModule.OVERSEERR: return 'https://overseerr.dev';
case LunaModule.WAKE_ON_LAN: return null;
}
throw Exception('Invalid LunaModule');
@@ -156,6 +168,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return null;
case LunaModule.SONARR: return 'https://github.com/Sonarr/Sonarr';
case LunaModule.TAUTULLI: return 'https://github.com/Tautulli/Tautulli';
case LunaModule.OVERSEERR: return 'https://github.com/sct/overseerr';
case LunaModule.WAKE_ON_LAN: return null;
}
throw Exception('Invalid LunaModule');
@@ -172,6 +185,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return 'Configure LunaSea';
case LunaModule.SONARR: return 'Manage Television Series';
case LunaModule.TAUTULLI: return 'View Plex Activity';
case LunaModule.OVERSEERR: return 'Manage Requests for New Content';
case LunaModule.WAKE_ON_LAN: return 'Wake Your Machine';
}
throw Exception('Invalid LunaModule');
@@ -188,6 +202,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return null;
case LunaModule.SONARR: return 'Sonarr is a PVR for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.';
case LunaModule.TAUTULLI: return 'Tautulli is an application that you can run alongside your Plex Media Server to monitor activity and track various statistics. Most importantly, these statistics include what has been watched, who watched it, when and where they watched it, and how it was watched.';
case LunaModule.OVERSEERR: return 'Overseerr is a free and open source software application for managing requests for your media library. It integrates with your existing services, such as Sonarr, Radarr, and Plex!';
case LunaModule.WAKE_ON_LAN: return 'Wake on LAN is an industry standard protocol for waking computers up from a very low power mode remotely by sending a specially constructed packet to the machine.';
}
throw Exception('Invalid LunaModule');
@@ -204,6 +219,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SETTINGS: return ShortcutItem(type: key, localizedTitle: name);
case LunaModule.SONARR: return ShortcutItem(type: key, localizedTitle: name);
case LunaModule.TAUTULLI: return ShortcutItem(type: key, localizedTitle: name);
case LunaModule.OVERSEERR: return ShortcutItem(type: key, localizedTitle: name);
case LunaModule.WAKE_ON_LAN: return null;
}
throw Exception('Invalid LunaModule');
@@ -219,6 +235,7 @@ extension LunaModuleExtension on LunaModule {
case LunaModule.SEARCH: return;
case LunaModule.SETTINGS: return;
case LunaModule.SONARR: return SonarrWebhooks().handle(data);
case LunaModule.OVERSEERR: return OverseerrWebhooks().handle(data);
case LunaModule.TAUTULLI: return TautulliWebhooks().handle(data);
case LunaModule.WAKE_ON_LAN: return;
}

View File

@@ -11,34 +11,30 @@ class LunaRouter {
void intialize() {
router.notFoundHandler = Handler(handlerFunc: (context, params) => LunaInvalidRoute());
DashboardRouter().defineAllRoutes(router);
RadarrRouter().defineAllRoutes(router);
SearchRouter().defineAllRoutes(router);
SettingsRouter().defineAllRoutes(router);
SonarrRouter().defineAllRoutes(router);
TautulliRouter().defineAllRoutes(router);
if(LunaModule.SEARCH.enabled) SearchRouter().defineAllRoutes(router);
if(LunaModule.RADARR.enabled) RadarrRouter().defineAllRoutes(router);
if(LunaModule.SONARR.enabled) SonarrRouter().defineAllRoutes(router);
if(LunaModule.OVERSEERR.enabled) OverseerrRouter().defineAllRoutes(router);
if(LunaModule.TAUTULLI.enabled) TautulliRouter().defineAllRoutes(router);
}
/// **Will be removed when all module routers are integrated.**
///
/// Returns a map of all module routes.
Map<String, WidgetBuilder> get routes => <String, WidgetBuilder> {
..._lidarr,
..._sabnzbd,
..._nzbget,
if(LunaModule.LIDARR.enabled) ..._lidarr,
if(LunaModule.SABNZBD.enabled) ..._sabnzbd,
if(LunaModule.NZBGET.enabled) ..._nzbget,
};
Map<String, WidgetBuilder> get _lidarr => <String, WidgetBuilder> {
// /lidarr
Lidarr.ROUTE_NAME: (context) => Lidarr(),
// /lidarr/add/*
LidarrAddSearch.ROUTE_NAME: (context) => LidarrAddSearch(),
LidarrAddDetails.ROUTE_NAME: (context) => LidarrAddDetails(),
// /lidarr/edit/*
LidarrEditArtist.ROUTE_NAME: (context) => LidarrEditArtist(),
// /lidarr/details/*
LidarrDetailsAlbum.ROUTE_NAME: (context) => LidarrDetailsAlbum(),
LidarrDetailsArtist.ROUTE_NAME: (context) => LidarrDetailsArtist(),
// /lidarr/search/*
LidarrSearchResults.ROUTE_NAME: (context) => LidarrSearchResults(),
};

View File

@@ -11,19 +11,16 @@ class LunaState {
/// Calls `.reset()` on all states which extend [LunaModuleState].
static void reset(BuildContext context) {
// General
Provider.of<DashboardState>(context, listen: false)?.reset();
Provider.of<SearchState>(context, listen: false)?.reset();
Provider.of<SettingsState>(context, listen: false)?.reset();
// Automation
Provider.of<LidarrState>(context, listen: false)?.reset();
Provider.of<RadarrState>(context, listen: false)?.reset();
Provider.of<SonarrState>(context, listen: false)?.reset();
// Clients
Provider.of<NZBGetState>(context, listen: false)?.reset();
Provider.of<SABnzbdState>(context, listen: false)?.reset();
// Monitoring
Provider.of<TautulliState>(context, listen: false)?.reset();
context.read<DashboardState>()?.reset();
context.read<SettingsState>()?.reset();
if(LunaModule.SEARCH.enabled) context.read<SearchState>()?.reset();
if(LunaModule.LIDARR.enabled) context.read<LidarrState>()?.reset();
if(LunaModule.RADARR.enabled) context.read<RadarrState>()?.reset();
if(LunaModule.SONARR.enabled) context.read<SonarrState>()?.reset();
if(LunaModule.NZBGET.enabled) context.read<NZBGetState>()?.reset();
if(LunaModule.SABNZBD.enabled) context.read<SABnzbdState>()?.reset();
if(LunaModule.OVERSEERR.enabled) context.read<OverseerrState>()?.reset();
if(LunaModule.TAUTULLI.enabled) context.read<TautulliState>()?.reset();
}
/// Returns a [MultiProvider] with the provided child.
@@ -31,21 +28,17 @@ class LunaState {
/// The [MultiProvider] has a [ChangeNotifierProvider] provider added for each module global state object.
static MultiProvider providers({ @required Widget child }) => MultiProvider(
providers: [
// General
ChangeNotifierProvider(create: (_) => DashboardState()),
ChangeNotifierProvider(create: (_) => SearchState()),
ChangeNotifierProvider(create: (_) => SettingsState()),
// Automation
ChangeNotifierProvider(create: (_) => SonarrState()),
ChangeNotifierProvider(create: (_) => LidarrState()),
ChangeNotifierProvider(create: (_) => RadarrState()),
// Clients
ChangeNotifierProvider(create: (_) => NZBGetState()),
ChangeNotifierProvider(create: (_) => SABnzbdState()),
// Monitoring
ChangeNotifierProvider(create: (_) => TautulliState()),
if(LunaModule.SEARCH.enabled) ChangeNotifierProvider(create: (_) => SearchState()),
if(LunaModule.LIDARR.enabled) ChangeNotifierProvider(create: (_) => LidarrState()),
if(LunaModule.RADARR.enabled) ChangeNotifierProvider(create: (_) => RadarrState()),
if(LunaModule.SONARR.enabled) ChangeNotifierProvider(create: (_) => SonarrState()),
if(LunaModule.NZBGET.enabled) ChangeNotifierProvider(create: (_) => NZBGetState()),
if(LunaModule.SABNZBD.enabled) ChangeNotifierProvider(create: (_) => SABnzbdState()),
if(LunaModule.OVERSEERR.enabled) ChangeNotifierProvider(create: (_) => OverseerrState()),
if(LunaModule.TAUTULLI.enabled) ChangeNotifierProvider(create: (_) => TautulliState()),
],
child: child,
);
}

View File

@@ -1,8 +1,9 @@
export 'system/configuration.dart';
export 'system/desktop_window.dart';
export 'system/image_cache.dart';
export 'system/in_app_purchases.dart';
export 'system/filesystem.dart';
export 'system/localization.dart';
export 'system/modules.dart';
export 'system/networking.dart';
export 'system/profile.dart';
export 'system/quick_actions.dart';

View File

@@ -52,6 +52,7 @@ class LunaConfiguration {
if(config[LunaModule.SONARR.key] != null) SonarrDatabase().import(config[LunaModule.SONARR.key]);
if(config[LunaModule.NZBGET.key] != null) NZBGetDatabase().import(config[LunaModule.NZBGET.key]);
if(config[LunaModule.SABNZBD.key] != null) SABnzbdDatabase().import(config[LunaModule.SABNZBD.key]);
if(config[LunaModule.OVERSEERR.key] != null) OverseerrDatabase().import(config[LunaModule.OVERSEERR.key]);
if(config[LunaModule.TAUTULLI.key] != null) TautulliDatabase().import(config[LunaModule.TAUTULLI.key]);
} catch (error, stack) {
LunaLogger().error('Failed to import configuration, resetting to default', error, stack);
@@ -76,6 +77,7 @@ class LunaConfiguration {
LunaModule.SONARR.key: SonarrDatabase().export(),
LunaModule.NZBGET.key: NZBGetDatabase().export(),
LunaModule.SABNZBD.key: SABnzbdDatabase().export(),
LunaModule.OVERSEERR.key: OverseerrDatabase().export(),
LunaModule.TAUTULLI.key: TautulliDatabase().export(),
});
}

View File

@@ -18,13 +18,14 @@ class LunaQuickActions {
/// Sets the shortcut items by checking the database and enabling the respective action if enabled.
void setShortcutItems() {
if(isPlatformCompatible) _quickActions.setShortcutItems(<ShortcutItem>[
if(LunaDatabaseValue.QUICK_ACTIONS_SEARCH.data) LunaModule.SEARCH.shortcutItem,
if(LunaDatabaseValue.QUICK_ACTIONS_LIDARR.data) LunaModule.LIDARR.shortcutItem,
if(LunaDatabaseValue.QUICK_ACTIONS_RADARR.data) LunaModule.RADARR.shortcutItem,
if(LunaDatabaseValue.QUICK_ACTIONS_SONARR.data) LunaModule.SONARR.shortcutItem,
if(LunaDatabaseValue.QUICK_ACTIONS_NZBGET.data) LunaModule.NZBGET.shortcutItem,
if(LunaDatabaseValue.QUICK_ACTIONS_SABNZBD.data) LunaModule.SABNZBD.shortcutItem,
if(LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI.data) LunaModule.TAUTULLI.shortcutItem,
if(LunaModule.SEARCH.enabled && LunaDatabaseValue.QUICK_ACTIONS_SEARCH.data) LunaModule.SEARCH.shortcutItem,
if(LunaModule.LIDARR.enabled && LunaDatabaseValue.QUICK_ACTIONS_LIDARR.data) LunaModule.LIDARR.shortcutItem,
if(LunaModule.RADARR.enabled && LunaDatabaseValue.QUICK_ACTIONS_RADARR.data) LunaModule.RADARR.shortcutItem,
if(LunaModule.SONARR.enabled && LunaDatabaseValue.QUICK_ACTIONS_SONARR.data) LunaModule.SONARR.shortcutItem,
if(LunaModule.NZBGET.enabled && LunaDatabaseValue.QUICK_ACTIONS_NZBGET.data) LunaModule.NZBGET.shortcutItem,
if(LunaModule.SABNZBD.enabled && LunaDatabaseValue.QUICK_ACTIONS_SABNZBD.data) LunaModule.SABNZBD.shortcutItem,
if(LunaModule.OVERSEERR.enabled && LunaDatabaseValue.QUICK_ACTIONS_OVERSEERR.data) LunaModule.OVERSEERR.shortcutItem,
if(LunaModule.TAUTULLI.enabled && LunaDatabaseValue.QUICK_ACTIONS_TAUTULLI.data) LunaModule.TAUTULLI.shortcutItem,
LunaModule.SETTINGS.shortcutItem,
]);
}

View File

@@ -36,7 +36,7 @@ class LunaDrawer extends StatelessWidget {
_buildEntry(context: context, module: LunaModule.SETTINGS),
LunaDivider(),
if(showSearch) _buildEntry(context: context, module: LunaModule.SEARCH),
if(LunaModule.WAKE_ON_LAN.enabled) _buildWakeOnLAN(context),
if(LunaModule.WAKE_ON_LAN.enabled && Database.currentProfileObject.wakeOnLANEnabled ?? false) _buildWakeOnLAN(context),
if(automation.length != 0) ...List.generate(
automation.length,
(index) => _buildEntry(context: context, module: automation[index]),

View File

@@ -4,8 +4,8 @@ import 'package:flutter/widgets.dart';
class LunaIcons {
LunaIcons._();
static const _kFontFam = 'CustomIcons';
static const _kFontPkg = null;
static const _kFontFam = 'LunaIcons';
static const String _kFontPkg = null;
static const IconData arrow_right = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData calendar_missing = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg);
@@ -39,4 +39,5 @@ class LunaIcons {
static const IconData tautulli = IconData(0xe81d, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData plex = IconData(0xe81e, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData radarr = IconData(0xe81f, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData overseerr = IconData(0xe820, fontFamily: _kFontFam, fontPackage: _kFontPkg);
}

View File

@@ -1,6 +1,4 @@
export 'utilities/changelog.dart';
export 'utilities/configuration.dart';
export 'utilities/encryption.dart';
export 'utilities/logger.dart';
export 'utilities/profile.dart';
export 'utilities/uuid.dart';

View File

@@ -19,7 +19,7 @@ class LunaEncryption {
final _encrypted = _encrypter.encrypt(data, iv: iv).base64;
return _encrypted;
} catch (error, stack) {
LunaLogger().error('Failed to decrypted "$data" with using key "$encryptionKey"', error, stack);
LunaLogger().error('Failed to decrypted "$data" using the key "$encryptionKey"', error, stack);
}
return ENCRYPTION_FAILURE;
}
@@ -35,7 +35,7 @@ class LunaEncryption {
final _encrypter = Encrypter(AES(key));
return _encrypter.decrypt64(data, iv: iv);
} catch (error) {
LunaLogger().warning('LunaEncryption', 'decrypt', 'Failed to decrypted "$data" with using key "$decryptionKey"');
LunaLogger().warning('LunaEncryption', 'decrypt', 'Failed to decrypted "$data" using the key "$decryptionKey"');
}
return ENCRYPTION_FAILURE;
}

View File

@@ -7,17 +7,19 @@ import 'package:lunasea/core.dart';
///
/// Runs app in guarded zone to attempt to capture fatal (crashing) errors
Future<void> main() async {
await _init();
runZonedGuarded(
() => runApp(
EasyLocalization(
supportedLocales: LunaLocalization().supportedLocales,
path: LunaLocalization().fileDirectory,
fallbackLocale: LunaLocalization().fallbackLocale,
useFallbackTranslations: true,
child: LunaBIOS(),
),
),
() async {
await _init();
return runApp(
EasyLocalization(
supportedLocales: LunaLocalization().supportedLocales,
path: LunaLocalization().fileDirectory,
fallbackLocale: LunaLocalization().fallbackLocale,
useFallbackTranslations: true,
child: LunaBIOS(),
),
);
},
(error, stack) => LunaLogger().critical(error, stack),
);
}

View File

@@ -1,6 +1,7 @@
export 'modules/dashboard.dart';
export 'modules/lidarr.dart';
export 'modules/nzbget.dart';
export 'modules/overseerr.dart';
export 'modules/radarr.dart';
export 'modules/sabnzbd.dart';
export 'modules/search.dart';

View File

@@ -0,0 +1,3 @@
export 'package:overseerr/overseerr.dart';
export 'overseerr/core.dart';
export 'overseerr/routes.dart';

View File

@@ -0,0 +1,5 @@
export 'core/database.dart';
export 'core/dialogs.dart';
export 'core/router.dart';
export 'core/state.dart';
export 'core/webhooks.dart';

View File

@@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class OverseerrDatabase extends LunaModuleDatabase {
@override
void registerAdapters() {}
@override
Map<String, dynamic> export() {
Map<String, dynamic> data = {};
for(OverseerrDatabaseValue value in OverseerrDatabaseValue.values) {
switch(value) {
// Primitive values
case OverseerrDatabaseValue.NAVIGATION_INDEX: data[value.key] = value.data; break;
}
}
return data;
}
@override
void import(Map<String, dynamic> config) {
for(String key in config.keys) {
OverseerrDatabaseValue value = valueFromKey(key);
if(value != null) switch(value) {
// Primitive values
case OverseerrDatabaseValue.NAVIGATION_INDEX: value.put(config[key]); break;
}
}
}
@override
OverseerrDatabaseValue valueFromKey(String key) {
switch(key) {
case 'OVERSEERR_NAVIGATION_INDEX': return OverseerrDatabaseValue.NAVIGATION_INDEX;
default: return null;
}
}
}
enum OverseerrDatabaseValue {
NAVIGATION_INDEX,
}
extension OverseerrDatabaseValueExtension on OverseerrDatabaseValue {
String get key {
switch(this) {
case OverseerrDatabaseValue.NAVIGATION_INDEX: return 'OVERSEERR_NAVIGATION_INDEX';
}
throw Exception('Invalid OverseerrDatabaseValue instance');
}
dynamic get data {
final _box = Database.lunaSeaBox;
switch(this) {
case OverseerrDatabaseValue.NAVIGATION_INDEX: return _box.get(this.key, defaultValue: 0);
}
throw Exception('Invalid OverseerrDatabaseValue instance');
}
void put(dynamic value) {
final box = Database.lunaSeaBox;
switch(this) {
case OverseerrDatabaseValue.NAVIGATION_INDEX: if(value is int) box.put(this.key, value); return;
}
LunaLogger().warning('OverseerrDatabaseValueExtension', 'put', 'Attempted to enter data for invalid OverseerrDatabaseValue: ${this?.toString() ?? 'null'}');
}
ValueListenableBuilder listen({ @required Widget Function(BuildContext, dynamic, Widget) builder }) => ValueListenableBuilder(
valueListenable: Database.lunaSeaBox.listenable(keys: [this.key]),
builder: builder,
);
}

View File

@@ -0,0 +1,3 @@
class OverseerrDialogs {
}

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';
class OverseerrRouter extends LunaModuleRouter {
@override
void defineAllRoutes(FluroRouter router) {
OverseerrHomeRouter().defineRoute(router);
}
}
abstract class OverseerrPageRouter extends LunaPageRouter {
OverseerrPageRouter(String route) : super(route);
@override
void noParameterRouteDefinition(FluroRouter router, Widget widget, { bool homeRoute = false }) => router.define(
fullRoute,
handler: Handler(handlerFunc: (context, params) {
if(!homeRoute && !context.read<OverseerrState>().enabled) return LunaNotEnabledRoute(module: 'Overseerr');
return widget;
}),
transitionType: LunaRouter.transitionType,
);
@override
void withParameterRouteDefinition(
FluroRouter router,
Widget Function(BuildContext, Map<String, List<String>>) handlerFunc,
{ bool homeRoute = false }
) => router.define(
fullRoute,
handler: Handler(handlerFunc: (context, params) {
if(!homeRoute && !context.read<OverseerrState>().enabled) return LunaNotEnabledRoute(module: 'Overseerr');
return handlerFunc(context, params);
}),
transitionType: LunaRouter.transitionType,
);
}

View File

@@ -0,0 +1,53 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';
class OverseerrState extends LunaModuleState {
OverseerrState() {
reset();
}
@override
void reset() {
resetProfile();
}
///////////////
/// PROFILE ///
///////////////
/// API handler instance
Overseerr _api;
Overseerr get api => _api;
/// Is the API enabled?
bool _enabled;
bool get enabled => _enabled;
/// Overseerr host
String _host;
String get host => _host;
/// Overseerr API key
String _apiKey;
String get apiKey => _apiKey;
/// Headers to attach to all requests
Map<dynamic, dynamic> _headers;
Map<dynamic, dynamic> get headers => _headers;
/// Reset the profile data, reinitializes API instance
void resetProfile() {
ProfileHiveObject _profile = Database.currentProfileObject;
// Copy profile into state
_enabled = _profile.overseerrEnabled ?? false;
_host = _profile.overseerrHost ?? '';
_apiKey = _profile.overseerrKey ?? '';
_headers = _profile.overseerrHeaders ?? {};
// Create the API instance if Overseerr is enabled
_api = !_enabled ? null : Overseerr(
host: _host,
apiKey: _apiKey,
headers: Map<String, dynamic>.from(_headers),
);
}
}

View File

@@ -0,0 +1,12 @@
import 'package:lunasea/core.dart';
class OverseerrWebhooks extends LunaWebhooks {
@override
Future<void> handle(Map<dynamic, dynamic> data) async {
LunaLogger().warning(
'OverseerrWebhooks',
'handle',
'Unknown event type: ${data['event'] ?? 'null'}',
);
}
}

View File

@@ -0,0 +1 @@
export 'routes/overseerr.dart';

View File

@@ -0,0 +1,2 @@
export 'overseerr/route.dart';
export 'overseerr/widgets.dart';

View File

@@ -0,0 +1,81 @@
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';
class OverseerrHomeRouter extends OverseerrPageRouter {
OverseerrHomeRouter() : super('/overseerr');
@override
void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _OverseerrHomeRoute(), homeRoute: true);
}
class _OverseerrHomeRoute extends StatefulWidget {
@override
State<_OverseerrHomeRoute> createState() => _State();
}
class _State extends State<_OverseerrHomeRoute> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
LunaPageController _pageController;
@override
void initState() {
super.initState();
_pageController = LunaPageController(initialPage: OverseerrDatabaseValue.NAVIGATION_INDEX.data);
}
@override
Widget build(BuildContext context) {
return LunaScaffold(
scaffoldKey: _scaffoldKey,
drawer: _drawer(),
appBar: _appBar(),
bottomNavigationBar: _bottomNavigationBar(),
body: _body(),
);
}
Widget _drawer() => LunaDrawer(page: LunaModule.OVERSEERR.key);
Widget _bottomNavigationBar() {
if(context.read<OverseerrState>().enabled) return OverseerrNavigationBar(pageController: _pageController);
return null;
}
Widget _appBar() {
List<String> profiles = Database.profilesBox.keys.fold([], (value, element) {
if(Database.profilesBox.get(element)?.overseerrEnabled ?? false) value.add(element);
return value;
});
List<Widget> actions;
if(context.watch<OverseerrState>().enabled) actions = [];
return LunaAppBar.dropdown(
title: LunaModule.OVERSEERR.name,
useDrawer: true,
profiles: profiles,
actions: actions,
pageController: _pageController,
scrollControllers: OverseerrNavigationBar.scrollControllers,
);
}
Widget _body() {
return Selector<OverseerrState, bool>(
selector: (_, state) => state.enabled,
builder: (context, enabled, _) {
if(!enabled) return LunaMessage.moduleNotEnabled(context: context, module: 'Overseerr');
return PageView(
controller: _pageController,
children: [
LunaMessage(
text: 'Coming Soon',
buttonText: 'Go to Dashboard',
onTap: LunaModule.DASHBOARD.launch,
),
],
);
}
);
}
}

View File

@@ -0,0 +1 @@
export 'widgets/navigation_bar.dart';

View File

@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
class OverseerrNavigationBar extends StatelessWidget {
final PageController pageController;
static List<ScrollController> scrollControllers = List.generate(icons.length, (_) => ScrollController());
static const List<IconData> icons = [
LunaIcons.movies,
];
static List<String> get titles => [
'Requests',
];
OverseerrNavigationBar({
Key key,
@required this.pageController,
}): super(key: key);
@override
Widget build(BuildContext context) {
return LunaBottomNavigationBar(
pageController: pageController,
scrollControllers: scrollControllers,
icons: icons,
titles: titles,
);
}
}

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/radarr.dart';

View File

@@ -102,6 +102,7 @@ class _State extends State<SettingsHeaderRoute> with LunaScrollControllerMixin {
case LunaModule.SEARCH: throw Exception('Search does not have a headers page');
case LunaModule.SETTINGS: throw Exception('Settings does not have a headers page');
case LunaModule.WAKE_ON_LAN: throw Exception('Wake on LAN does not have a headers page');
case LunaModule.OVERSEERR: return Database.currentProfileObject.overseerrHeaders;
case LunaModule.TAUTULLI: return Database.currentProfileObject.tautulliHeaders;
}
throw Exception('An unknown LunaModule was passed in.');
@@ -119,6 +120,7 @@ class _State extends State<SettingsHeaderRoute> with LunaScrollControllerMixin {
case LunaModule.SETTINGS: throw Exception('Settings does not have a headers page');
case LunaModule.WAKE_ON_LAN: throw Exception('Wake on LAN does not have a headers page');
case LunaModule.TAUTULLI: return context.read<TautulliState>().reset();
case LunaModule.OVERSEERR: return context.read<OverseerrState>().reset();
}
throw Exception('An unknown LunaModule was passed in.');
}

View File

@@ -49,6 +49,10 @@ class SettingsRouter extends LunaModuleRouter {
SettingsConfigurationSonarrDefaultPagesRouter().defineRoute(router);
SettingsConfigurationSonarrDefaultSortingRouter().defineRoute(router);
SettingsConfigurationSonarrHeadersRouter().defineRoute(router);
// Configuration/Overseerr
SettingsConfigurationOverseerrRouter().defineRoute(router);
SettingsConfigurationOverseerrConnectionDetailsRouter().defineRoute(router);
SettingsConfigurationOverseerrHeadersRouter().defineRoute(router);
// Configuration/Tautulli
SettingsConfigurationTautulliRouter().defineRoute(router);
SettingsConfigurationTautulliConnectionDetailsRouter().defineRoute(router);

View File

@@ -5,6 +5,7 @@ export 'routes/configuration_appearance.dart';
export 'routes/configuration_dashboard.dart';
export 'routes/configuration_lidarr.dart';
export 'routes/configuration_nzbget.dart';
export 'routes/configuration_overseerr.dart';
export 'routes/configuration_quickactions.dart';
export 'routes/configuration_radarr.dart';
export 'routes/configuration_sabnzbd.dart';

View File

@@ -78,18 +78,49 @@ class _State extends State<_SettingsConfigurationRoute> with LunaScrollControlle
onTap: () async => SettingsConfigurationQuickActionsRouter().navigateTo(context),
),
LunaDivider(),
_tileFromModuleMap(LunaModule.DASHBOARD, () async => SettingsConfigurationDashboardRouter().navigateTo(context)),
_tileFromModuleMap(LunaModule.SEARCH, () async => SettingsConfigurationSearchRouter().navigateTo(context)),
_tileFromModuleMap(LunaModule.WAKE_ON_LAN, () async => SettingsConfigurationWakeOnLANRouter().navigateTo(context)),
if(LunaModule.DASHBOARD.enabled) _tileFromModuleMap(
LunaModule.DASHBOARD,
() async => SettingsConfigurationDashboardRouter().navigateTo(context),
),
if(LunaModule.SEARCH.enabled) _tileFromModuleMap(
LunaModule.SEARCH,
() async => SettingsConfigurationSearchRouter().navigateTo(context),
),
if(LunaModule.WAKE_ON_LAN.enabled) _tileFromModuleMap(
LunaModule.WAKE_ON_LAN,
() async => SettingsConfigurationWakeOnLANRouter().navigateTo(context),
),
LunaDivider(),
_tileFromModuleMap(LunaModule.LIDARR, () async => SettingsConfigurationLidarrRouter().navigateTo(context)),
_tileFromModuleMap(LunaModule.RADARR, () async => SettingsConfigurationRadarrRouter().navigateTo(context)),
_tileFromModuleMap(LunaModule.SONARR, () async => SettingsConfigurationSonarrRouter().navigateTo(context)),
if(LunaModule.LIDARR.enabled) _tileFromModuleMap(
LunaModule.LIDARR,
() async => SettingsConfigurationLidarrRouter().navigateTo(context),
),
if(LunaModule.RADARR.enabled) _tileFromModuleMap(
LunaModule.RADARR,
() async => SettingsConfigurationRadarrRouter().navigateTo(context),
),
if(LunaModule.SONARR.enabled) _tileFromModuleMap(
LunaModule.SONARR,
() async => SettingsConfigurationSonarrRouter().navigateTo(context),
),
LunaDivider(),
_tileFromModuleMap(LunaModule.NZBGET, () async => SettingsConfigurationNZBGetRouter().navigateTo(context)),
_tileFromModuleMap(LunaModule.SABNZBD, () async => SettingsConfigurationSABnzbdRouter().navigateTo(context)),
if(LunaModule.NZBGET.enabled) _tileFromModuleMap(
LunaModule.NZBGET,
() async => SettingsConfigurationNZBGetRouter().navigateTo(context),
),
if(LunaModule.SABNZBD.enabled) _tileFromModuleMap(
LunaModule.SABNZBD,
() async => SettingsConfigurationSABnzbdRouter().navigateTo(context),
),
LunaDivider(),
_tileFromModuleMap(LunaModule.TAUTULLI, () async => SettingsConfigurationTautulliRouter().navigateTo(context)),
if(LunaModule.OVERSEERR.enabled) _tileFromModuleMap(
LunaModule.OVERSEERR,
() async => SettingsConfigurationOverseerrRouter().navigateTo(context),
),
if(LunaModule.TAUTULLI.enabled) _tileFromModuleMap(
LunaModule.TAUTULLI,
() async => SettingsConfigurationTautulliRouter().navigateTo(context),
),
],
);
}

View File

@@ -0,0 +1,2 @@
export 'configuration_overseerr/pages.dart';
export 'configuration_overseerr/route.dart';

View File

@@ -0,0 +1,2 @@
export 'pages/connection_details.dart';
export 'pages/headers.dart';

View File

@@ -0,0 +1,114 @@
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';
import 'package:lunasea/modules/settings.dart';
class SettingsConfigurationOverseerrConnectionDetailsRouter extends SettingsPageRouter {
SettingsConfigurationOverseerrConnectionDetailsRouter() : super('/settings/configuration/overseerr/connection');
@override
void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationOverseerrRoute());
}
class _SettingsConfigurationOverseerrRoute extends StatefulWidget {
@override
State<_SettingsConfigurationOverseerrRoute> createState() => _State();
}
class _State extends State<_SettingsConfigurationOverseerrRoute> with LunaScrollControllerMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
return LunaScaffold(
scaffoldKey: _scaffoldKey,
appBar: _appBar(),
body: _body(),
bottomNavigationBar: _bottomActionBar(),
);
}
Widget _appBar() {
return LunaAppBar(
title: 'Connection Details',
scrollControllers: [scrollController],
);
}
Widget _bottomActionBar() {
return LunaBottomActionBar(
actions: [
_testConnection(),
],
);
}
Widget _body() {
return ValueListenableBuilder(
valueListenable: Database.profilesBox.listenable(),
builder: (context, box, _) => LunaListView(
controller: scrollController,
children: [
_host(),
_apiKey(),
_customHeaders(),
],
),
);
}
Widget _host() {
String host = Database.currentProfileObject.overseerrHost;
return LunaListTile(
context: context,
title: LunaText.title(text: 'Host'),
subtitle: LunaText.subtitle(text: (host ?? '').isEmpty ? 'Not Set' : host),
trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded),
onTap: () async {
List<dynamic> _values = await SettingsDialogs.editHost(context, 'Overseerr Host', prefill: Database.currentProfileObject.overseerrHost ?? '');
if(_values[0]) {
Database.currentProfileObject.overseerrHost = _values[1];
Database.currentProfileObject.save();
context.read<OverseerrState>().reset();
}
},
);
}
Widget _apiKey() {
String apiKey = Database.currentProfileObject.overseerrKey;
return LunaListTile(
context: context,
title: LunaText.title(text: 'API Key'),
subtitle: LunaText.subtitle(text: (apiKey ?? '').isEmpty ? 'Not Set' : '••••••••••••'),
trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded),
onTap: () async {
Tuple2<bool, String> _values = await LunaDialogs().editText(context, 'Overseerr API Key', prefill: Database.currentProfileObject.overseerrKey ?? '');
if(_values.item1) {
Database.currentProfileObject.overseerrKey = _values.item2;
Database.currentProfileObject.save();
context.read<OverseerrState>().reset();
}
},
);
}
Widget _testConnection() {
return LunaButton.text(
text: 'Test Connection',
icon: Icons.wifi_tethering_rounded,
onTap: () async {},
);
}
Widget _customHeaders() {
return LunaListTile(
context: context,
title: LunaText.title(text: 'Custom Headers'),
subtitle: LunaText.subtitle(text: 'Add Custom Headers to Requests'),
trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded),
onTap: () async => SettingsConfigurationOverseerrHeadersRouter().navigateTo(context),
);
}
}

View File

@@ -0,0 +1,13 @@
import 'package:fluro/fluro.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/settings.dart';
class SettingsConfigurationOverseerrHeadersRouter extends SettingsPageRouter {
SettingsConfigurationOverseerrHeadersRouter() : super('/settings/configuration/overseerr/headers');
@override
void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(
router,
SettingsHeaderRoute(module: LunaModule.OVERSEERR),
);
}

View File

@@ -0,0 +1,81 @@
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';
import 'package:lunasea/modules/settings.dart';
class SettingsConfigurationOverseerrRouter extends SettingsPageRouter {
SettingsConfigurationOverseerrRouter() : super('/settings/configuration/overseerr');
@override
void defineRoute(FluroRouter router) => super.noParameterRouteDefinition(router, _SettingsConfigurationOverseerrRoute());
}
class _SettingsConfigurationOverseerrRoute extends StatefulWidget {
@override
State<_SettingsConfigurationOverseerrRoute> createState() => _State();
}
class _State extends State<_SettingsConfigurationOverseerrRoute> with LunaScrollControllerMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
return LunaScaffold(
scaffoldKey: _scaffoldKey,
appBar: _appBar(),
body: _body(),
);
}
Widget _appBar() {
return LunaAppBar(
title: LunaModule.OVERSEERR.name,
scrollControllers: [scrollController],
actions: [
LunaIconButton(
icon: Icons.help_outline,
onPressed: () async => SettingsDialogs().moduleInformation(context, LunaModule.OVERSEERR),
),
],
);
}
Widget _body() {
return LunaListView(
controller: scrollController,
children: [
_enabledToggle(),
_connectionDetailsPage(),
],
);
}
Widget _enabledToggle() {
return ValueListenableBuilder(
valueListenable: Database.profilesBox.listenable(),
builder: (context, _, __) => LunaListTile(
context: context,
title: LunaText.title(text: 'Enable ${LunaModule.OVERSEERR.name}'),
trailing: LunaSwitch(
value: Database.currentProfileObject.overseerrEnabled ?? false,
onChanged: (value) {
Database.currentProfileObject.overseerrEnabled = value;
Database.currentProfileObject.save();
context.read<OverseerrState>().reset();
},
),
),
);
}
Widget _connectionDetailsPage() {
return LunaListTile(
context: context,
title: LunaText.title(text: 'Connection Details'),
subtitle: LunaText.subtitle(text: 'Connection Details for ${LunaModule.OVERSEERR.name}'),
trailing: LunaIconButton(icon: Icons.arrow_forward_ios_rounded),
onTap: () async => SettingsConfigurationOverseerrConnectionDetailsRouter().navigateTo(context),
);
}
}

View File

@@ -702,6 +702,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
overseerr:
dependency: "direct main"
description:
name: overseerr
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1-pre.5"
package_config:
dependency: transitive
description:

View File

@@ -37,6 +37,7 @@ dependencies:
intl: ^0.17.0 # ANDROID IOS LINUX MACOS WEB WINDOWS
modal_bottom_sheet: ^2.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS
network_info_plus: ^1.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS
overseerr: ^0.0.1-pre.5 # ANDROID IOS LINUX MACOS WEB WINDOWS
package_info_plus: ^1.0.0 # ANDROID IOS LINUX MACOS WEB WINDOWS
path_provider: ^2.0.1 # ANDROID IOS LINUX MACOS WINDOWS
percent_indicator: ^3.0.1 # ANDROID IOS LINUX MACOS WEB WINDOWS
@@ -55,12 +56,12 @@ dependencies:
url_launcher: ^6.0.3 # ANDROID IOS LINUX MACOS WEB WINDOWS
uuid: ^3.0.4 # ANDROID IOS LINUX MACOS WEB WINDOWS
wake_on_lan: ^2.0.1+1 # ANDROID IOS LINUX MACOS WINDOWS
xml: ^5.1.0 # ANDROID IOS LINUX MACOS WEB WINDOWS
window_size:
git:
url: git://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
ref: 57a2cd88486105d3d813583339eb0e23c9bfd6d2
xml: ^5.1.0 # ANDROID IOS LINUX MACOS WEB WINDOWS
dev_dependencies:
build_runner: ^1.12.2
@@ -79,9 +80,9 @@ flutter:
- assets/images/branding/
- assets/images/services/
fonts:
- family: CustomIcons
- family: LunaIcons
fonts:
- asset: assets/CustomIcons.ttf
- asset: assets/LunaIcons.ttf
flutter_icons:
android: true

0
test/.gitkeep Normal file
View File