Skip to content

Commit

Permalink
feat(overseerr): integrate request list and request tiles
Browse files Browse the repository at this point in the history
  • Loading branch information
JagandeepBrar committed Feb 11, 2022
1 parent a4fbe5e commit b7fc3c0
Show file tree
Hide file tree
Showing 20 changed files with 329 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .vscode/cspell.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"ignorePaths": [
"*.json",
"*.lock",
"*.yaml",
"*.yml",
Expand All @@ -27,6 +26,7 @@
"Discogs",
"fanart",
"fastlane",
"hardlink",
"keychain",
"Lidarr",
"lunasea",
Expand Down
8 changes: 8 additions & 0 deletions assets/localization/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -494,9 +494,17 @@
"tautulli.Year": "Year",
"lidarr.StartSearchFor": "Start Search For…",
"lidarr.StartSearchForMissingAlbums": "Start search for missing albums",
"overseerr.Approved": "Approved",
"overseerr.Available": "Available",
"overseerr.Declined": "Declined",
"overseerr.NoRequests": "No Requests",
"overseerr.OneRequest": "1 Request",
"overseerr.PartiallyAvailable": "Partially Available",
"overseerr.Pending": "Pending",
"overseerr.Processing": "Processing",
"overseerr.SomeRequests": "{} Requests",
"overseerr.Requested": "Requested",
"overseerr.RequestedBy": "Requested by {}",
"overseerr.Requests": "Requests",
"overseerr.NoRequestsFound": "No Requests Found",
"overseerr.NoUsersFound": "No Users Found",
Expand Down
2 changes: 1 addition & 1 deletion lib/core/cache/memory_lru.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class LunaLRUCache {
Cache _getCache({
EvictionPolicy evictionPolicy = const LruEvictionPolicy(),
ExpiryPolicy expiryPolicy = const EternalExpiryPolicy(),
int maxEntries = 50,
int maxEntries = 25,
KeySampler sampler = const FullSampler(),
EventListenerMode eventListenerMode = EventListenerMode.disabled,
}) {
Expand Down
11 changes: 9 additions & 2 deletions lib/core/services/the_movie_db/api.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
class TheMovieDB {
static const String _IMAGE_ENDPOINT_URL = 'https://image.tmdb.org/t/p/';
static const String _IMAGE_POSTER_SIZE = 'w154';

static String? getImageURL(String? id) {
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;
}
}
7 changes: 6 additions & 1 deletion lib/core/ui/list_view/paged_list_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class LunaPagedListView<T> extends StatefulWidget {
final double? itemExtent;
final EdgeInsetsGeometry? padding;
final String noItemsFoundMessage;
final Function? onRefresh;

const LunaPagedListView({
Key? key,
Expand All @@ -19,6 +20,7 @@ class LunaPagedListView<T> extends StatefulWidget {
required this.itemBuilder,
required this.noItemsFoundMessage,
required this.scrollController,
this.onRefresh,
this.itemExtent,
this.padding,
}) : super(key: key);
Expand All @@ -40,7 +42,10 @@ class _State<T> extends State<LunaPagedListView<T>> {
return LunaRefreshIndicator(
key: widget.refreshKey,
context: context,
onRefresh: () => Future.sync(() => widget.pagingController.refresh()),
onRefresh: () => Future.sync(() {
widget.onRefresh?.call();
widget.pagingController.refresh();
}),
child: Scrollbar(
controller: widget.scrollController,
child: PagedListView<int, T>(
Expand Down
2 changes: 1 addition & 1 deletion lib/modules/overseerr/core/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ extension OverseerrDatabaseValueExtension on OverseerrDatabaseValue {
case OverseerrDatabaseValue.NAVIGATION_INDEX:
return 0;
case OverseerrDatabaseValue.CONTENT_PAGE_SIZE:
return 25;
return 10;
}
}
}
4 changes: 4 additions & 0 deletions lib/modules/overseerr/core/extensions.dart
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export 'extensions/overseerr_movie.dart';
export 'extensions/overseerr_request_status.dart';
export 'extensions/overseerr_request.dart';
export 'extensions/overseerr_series.dart';
export 'extensions/overseerr_user.dart';
14 changes: 14 additions & 0 deletions lib/modules/overseerr/core/extensions/overseerr_movie.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';

extension LunaOverseerrMovieExtension on OverseerrMovie? {
String lunaTitle() {
if (this?.title == null) return LunaUI.TEXT_EMDASH;
return this!.title!;
}

String lunaYear() {
if (this?.releaseDate?.isEmpty ?? true) return LunaUI.TEXT_EMDASH;
return this!.releaseDate!.split('-')[0];
}
}
25 changes: 25 additions & 0 deletions lib/modules/overseerr/core/extensions/overseerr_request.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';

extension LunaOverseerrRequestExtension on OverseerrRequest? {
String lunaRequestStatus() {
if (this?.status == null) return LunaUI.TEXT_EMDASH;
return this!.status.lunaName(this?.media?.status);
}

String lunaRequestedBy() {
return 'overseerr.RequestedBy'.tr(args: [
this?.requestedBy.lunaDisplayName() ?? 'overseerr.UnknownUser'.tr(),
]);
}

bool lunaIs4K() {
if (this?.is4k ?? false) return true;
return false;
}

OverseerrMediaStatus? lunaMediaStatus() {
if (lunaIs4K()) return this?.media?.status4k;
return this?.media?.status;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';

extension LunaOverseerrRequestStatusExtension on OverseerrRequestStatus? {
String lunaName(OverseerrMediaStatus? mediaStatus) {
if (mediaStatus != null) {
switch (mediaStatus) {
case OverseerrMediaStatus.PENDING:
return 'overseerr.Pending'.tr();
case OverseerrMediaStatus.PROCESSING:
return 'overseerr.Requested'.tr();
case OverseerrMediaStatus.PARTIALLY_AVAILABLE:
return 'overseerr.PartiallyAvailable'.tr();
case OverseerrMediaStatus.AVAILABLE:
return 'overseerr.Available'.tr();
case OverseerrMediaStatus.UNKNOWN:
break;
}
}
switch (this) {
case OverseerrRequestStatus.PENDING:
return 'overseerr.Pending'.tr();
case OverseerrRequestStatus.APPROVED:
return 'overseerr.Approved'.tr();
case OverseerrRequestStatus.DECLINED:
return 'overseerr.Declined'.tr();
default:
return LunaUI.TEXT_EMDASH;
}
}

Color lunaColour(OverseerrMediaStatus? mediaStatus) {
if (mediaStatus != null) {
switch (mediaStatus) {
case OverseerrMediaStatus.PARTIALLY_AVAILABLE:
case OverseerrMediaStatus.AVAILABLE:
return LunaColours.accent;
case OverseerrMediaStatus.PENDING:
return LunaColours.orange;
case OverseerrMediaStatus.PROCESSING:
return LunaColours.purple;
case OverseerrMediaStatus.UNKNOWN:
default:
}
}
switch (this) {
case OverseerrRequestStatus.PENDING:
return LunaColours.orange;
case OverseerrRequestStatus.APPROVED:
return LunaColours.purple;
case OverseerrRequestStatus.DECLINED:
return LunaColours.red;
default:
return LunaColours.blueGrey;
}
}
}
14 changes: 14 additions & 0 deletions lib/modules/overseerr/core/extensions/overseerr_series.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';

extension LunaOverseerrSeriesExtension on OverseerrSeries? {
String lunaTitle() {
if (this?.name != null) return this!.name!;
return LunaUI.TEXT_EMDASH;
}

String lunaYear() {
if (this?.firstAirDate?.isEmpty ?? true) return LunaUI.TEXT_EMDASH;
return this!.firstAirDate!.split('-')[0];
}
}
20 changes: 10 additions & 10 deletions lib/modules/overseerr/core/extensions/overseerr_user.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import 'package:lunasea/core.dart';
import 'package:lunasea/modules/overseerr.dart';

extension OverseerrUserExtension on OverseerrUser {
String? lunaDisplayName() {
if (this.displayName != null) return this.displayName;
return 'overseerr.UnknownUser'.tr();
extension LunaOverseerrUserExtension on OverseerrUser? {
String lunaDisplayName() {
if (this?.displayName?.isEmpty ?? true) return 'overseerr.UnknownUser'.tr();
return this!.displayName!;
}

String? lunaEmail() {
if (this.email != null) return this.email;
return LunaUI.TEXT_EMDASH;
String lunaEmail() {
if (this?.email?.isEmpty ?? true) return LunaUI.TEXT_EMDASH;
return this!.email!;
}

String lunaAmountOfRequests() {
if (this.requestCount == 0) return 'overseerr.NoRequests'.tr();
if (this.requestCount == 1) return 'overseerr.OneRequest'.tr();
return 'overseerr.SomeRequests'.tr(args: [this.requestCount.toString()]);
if ((this?.requestCount ?? 0) == 0) return 'overseerr.NoRequests'.tr();
if (this!.requestCount == 1) return 'overseerr.OneRequest'.tr();
return 'overseerr.SomeRequests'.tr(args: [this!.requestCount.toString()]);
}
}
4 changes: 2 additions & 2 deletions lib/modules/overseerr/core/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ abstract class OverseerrPageRouter extends LunaPageRouter {
fullRoute,
handler: Handler(
handlerFunc: (context, params) {
if (!homeRoute && !context!.read<OverseerrState>().enabled!) {
if (!homeRoute && !context!.read<OverseerrState>().enabled) {
return LunaNotEnabledRoute(module: LunaModule.OVERSEERR.name);
}
return widget();
Expand All @@ -41,7 +41,7 @@ abstract class OverseerrPageRouter extends LunaPageRouter {
fullRoute,
handler: Handler(
handlerFunc: (context, params) {
if (!homeRoute && !context!.read<OverseerrState>().enabled!) {
if (!homeRoute && !context!.read<OverseerrState>().enabled) {
return LunaNotEnabledRoute(module: LunaModule.OVERSEERR.name);
}
return handlerFunc(context, params);
Expand Down
88 changes: 73 additions & 15 deletions lib/modules/overseerr/core/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ class OverseerrState extends LunaModuleState {

@override
void reset() {
_movieCache.clear();
_seriesCache.clear();
resetProfile();
notifyListeners();
}

///////////////
Expand All @@ -20,36 +23,91 @@ class OverseerrState extends LunaModuleState {
Overseerr? get api => _api;

/// Is the API enabled?
bool? _enabled;
bool? get enabled => _enabled;
bool _enabled = false;
bool get enabled => _enabled;

/// Overseerr host
String? _host;
String? get host => _host;
String _host = '';
String get host => _host;

/// Overseerr API key
String? _apiKey;
String? get apiKey => _apiKey;
String _apiKey = '';
String get apiKey => _apiKey;

/// Headers to attach to all requests
Map<dynamic, dynamic>? _headers;
Map<dynamic, dynamic>? get headers => _headers;
Map<dynamic, dynamic> _headers = {};
Map<dynamic, dynamic> get headers => _headers;

/// Reset the profile data, reinitializes API instance
void resetProfile() {
ProfileHiveObject _profile = LunaProfile.current;
// Copy profile into state
_api = null;
_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!),
);
if (_enabled) {
_api = Overseerr(
host: _host,
apiKey: _apiKey,
headers: Map<String, dynamic>.from(_headers),
);
}
}

////////////////
/// REQUESTS ///
////////////////
final LunaLRUCache _movieCache = LunaLRUCache(
maxEntries: 50,
module: LunaModule.OVERSEERR,
id: 'requests_movie_cache',
);

final LunaLRUCache _seriesCache = LunaLRUCache(
maxEntries: 50,
module: LunaModule.OVERSEERR,
id: 'requests_series_cache',
);

Future<void> fetchMovie(int movieId, {bool force = false}) async {
if (_enabled) {
String id = movieId.toString();
bool exists = await _movieCache.contains(id);

if (force || !exists) {
await _movieCache.put(id, api!.getMovie(id: movieId));
}
}
notifyListeners();
}

Future<void> fetchSeries(int seriesId, {bool force = false}) async {
if (_enabled) {
String id = seriesId.toString();
bool exists = await _seriesCache.contains(id);

if (force || !exists) {
await _seriesCache.put(id, api!.getSeries(id: seriesId));
}
}
notifyListeners();
}

Future<OverseerrMovie?> getMovie(int movieId) async {
String id = movieId.toString();
return _movieCache.get(id).then((movie) {
return movie as OverseerrMovie?;
});
}

Future<OverseerrSeries?> getSeries(int seriesId) {
String id = seriesId.toString();
return _seriesCache.get(id).then((series) {
return series as OverseerrSeries?;
});
}
}
Loading

0 comments on commit b7fc3c0

Please sign in to comment.