From 5835e7cc59fcfdfa410d78a3740a33337dc9b5f5 Mon Sep 17 00:00:00 2001 From: Jamie Berghmans Date: Sun, 3 Sep 2023 21:41:26 +0200 Subject: [PATCH 1/3] feat(manualimport): add support for manually importing file with warnings in queue --- lib/api/sonarr/controllers.dart | 4 ++ .../controllers/command/manual_import.dart | 12 ++++++ lib/api/sonarr/controllers/manual_import.dart | 22 +++++++++++ lib/api/sonarr/controllers/queue.dart | 2 + lib/api/sonarr/sonarr.dart | 4 ++ lib/modules/sonarr/core/api_controller.dart | 38 ++++++++++++++++++- lib/modules/sonarr/core/dialogs.dart | 28 ++++++++++++-- .../routes/queue/widgets/queue_tile.dart | 26 ++++++++++++- 8 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 lib/api/sonarr/controllers/command/manual_import.dart create mode 100644 lib/api/sonarr/controllers/manual_import.dart diff --git a/lib/api/sonarr/controllers.dart b/lib/api/sonarr/controllers.dart index aaee3f925..004284e3e 100644 --- a/lib/api/sonarr/controllers.dart +++ b/lib/api/sonarr/controllers.dart @@ -22,6 +22,7 @@ part 'controllers/command/rescan_series.dart'; part 'controllers/command/rss_sync.dart'; part 'controllers/command/season_search.dart'; part 'controllers/command/series_search.dart'; +part 'controllers/command/manual_import.dart'; // Episode File part 'controllers/episode_file.dart'; @@ -94,3 +95,6 @@ part 'controllers/tag/update_tag.dart'; // Wanted/Missing part 'controllers/wanted.dart'; part 'controllers/wanted/get_missing.dart'; + +// ManualImport +part 'controllers/manual_import.dart'; \ No newline at end of file diff --git a/lib/api/sonarr/controllers/command/manual_import.dart b/lib/api/sonarr/controllers/command/manual_import.dart new file mode 100644 index 000000000..551717103 --- /dev/null +++ b/lib/api/sonarr/controllers/command/manual_import.dart @@ -0,0 +1,12 @@ +part of sonarr_commands; + +Future _commandManualImport( + Dio client, + List records + ) async { + Response response = await client.post('command', data: { + 'name': 'ManualImport', + 'files': records + }); + return SonarrCommand.fromJson(response.data); +} diff --git a/lib/api/sonarr/controllers/manual_import.dart b/lib/api/sonarr/controllers/manual_import.dart new file mode 100644 index 000000000..4727499cb --- /dev/null +++ b/lib/api/sonarr/controllers/manual_import.dart @@ -0,0 +1,22 @@ +part of sonarr_commands; + +/// Sends a request to import a queued item within Sonarr. +/// +/// [SonarrControllerManualImport] internally handles routing the HTTP client to the API calls. +class SonarrControllerManualImport { + final Dio _client; + + /// Create a series command handler using an initialized [Dio] client. + SonarrControllerManualImport(this._client); + + /// Handler for [manualimport] + /// + /// Sends a request to manually import a queued item with warnings. + /// + /// Required Parameters: + /// - `records`: The records to import + Future import({ + required List records + }) async => + _commandManualImport(_client, records); +} diff --git a/lib/api/sonarr/controllers/queue.dart b/lib/api/sonarr/controllers/queue.dart index 993bde475..4a1d4e771 100644 --- a/lib/api/sonarr/controllers/queue.dart +++ b/lib/api/sonarr/controllers/queue.dart @@ -63,4 +63,6 @@ class SonarrControllerQueue { blocklist: blocklist, removeFromClient: removeFromClient, ); + + // Handler for } diff --git a/lib/api/sonarr/sonarr.dart b/lib/api/sonarr/sonarr.dart index 99e68395a..42c7f02d7 100644 --- a/lib/api/sonarr/sonarr.dart +++ b/lib/api/sonarr/sonarr.dart @@ -21,6 +21,7 @@ class SonarrAPI { required this.system, required this.tag, required this.wanted, + required this.manualImport }); factory SonarrAPI({ @@ -60,6 +61,7 @@ class SonarrAPI { system: SonarrControllerSystem(_dio), tag: SonarrControllerTag(_dio), wanted: SonarrControllerWanted(_dio), + manualImport: SonarrControllerManualImport(_dio) ); } @@ -83,6 +85,7 @@ class SonarrAPI { system: SonarrControllerSystem(client), tag: SonarrControllerTag(client), wanted: SonarrControllerWanted(client), + manualImport: SonarrControllerManualImport(client) ); } @@ -103,4 +106,5 @@ class SonarrAPI { final SonarrControllerSystem system; final SonarrControllerTag tag; final SonarrControllerWanted wanted; + final SonarrControllerManualImport manualImport; } diff --git a/lib/modules/sonarr/core/api_controller.dart b/lib/modules/sonarr/core/api_controller.dart index b6260c83c..83c53ea40 100644 --- a/lib/modules/sonarr/core/api_controller.dart +++ b/lib/modules/sonarr/core/api_controller.dart @@ -797,4 +797,40 @@ class SonarrAPIController { } return false; } -} + + Future manualImportFromQueue({ + required BuildContext context, + required SonarrQueueRecord queueRecord, + bool showSnackbar = true + }) async { + if (context.read().enabled) { + return context + .read() + .api! + .manualImport + .import(records: [queueRecord]) + .then((_) { + if (showSnackbar) + showLunaSuccessSnackBar( + title: 'sonarr.ManuallyImportedFromQueue'.tr(), + message: queueRecord.title, + ); + return true; + }).catchError((error, stack) { + LunaLogger().error( + 'Failed to import queue record: ${queueRecord.id}', + error, + stack, + ); + if (showSnackbar) + showLunaErrorSnackBar( + title: 'sonarr.FailedToManuallyImportFromQueue'.tr(), + error: error, + ); + return false; + }); + } + + return false; + } +} \ No newline at end of file diff --git a/lib/modules/sonarr/core/dialogs.dart b/lib/modules/sonarr/core/dialogs.dart index 8111b4a36..7561721df 100644 --- a/lib/modules/sonarr/core/dialogs.dart +++ b/lib/modules/sonarr/core/dialogs.dart @@ -746,21 +746,39 @@ class SonarrDialogs { return _flag; } - Future showQueueStatusMessages( + Future showQueueStatusMessages( BuildContext context, - List messages, + SonarrQueueRecord record, ) async { + bool _flag = false; + + void _setValues(bool flag) { + _flag = flag; + Navigator.of(context, rootNavigator: true).pop(); + } + + var messages = record.statusMessages!; if (messages.isEmpty) { - return LunaDialogs().textPreview( + await LunaDialogs().textPreview( context, 'sonarr.Messages'.tr(), 'sonarr.NoMessagesFound'.tr(), ); + + return false; } await LunaDialog.dialog( context: context, title: 'sonarr.Messages'.tr(), cancelButtonText: 'lunasea.Close'.tr(), + buttons: [ + LunaDialog.button( + text: 'Import', + onPressed: () => { + _setValues(true) + }, + ), + ], contentPadding: LunaDialog.listDialogContentPadding(), content: List.generate( messages.length, @@ -828,8 +846,10 @@ class SonarrDialogs { ), ), ); - } + return _flag; + } + Future> setQueuePageSize(BuildContext context) async { bool _flag = false; GlobalKey _formKey = GlobalKey(); diff --git a/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart b/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart index 2576ce436..7b15a022d 100644 --- a/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart +++ b/lib/modules/sonarr/routes/queue/widgets/queue_tile.dart @@ -192,10 +192,32 @@ class _State extends State { color: LunaColours.orange, text: 'sonarr.Messages'.tr(), onTap: () async { - SonarrDialogs().showQueueStatusMessages( + bool result = await SonarrDialogs().showQueueStatusMessages( context, - widget.queueRecord.statusMessages!, + widget.queueRecord, ); + + if (result) { + SonarrAPIController() + .manualImportFromQueue(context: context, queueRecord: widget.queueRecord) + .then((_) { + switch (widget.type) { + case SonarrQueueTileType.ALL: + context.read().fetchQueue( + context, + hardCheck: true, + ); + break; + case SonarrQueueTileType.EPISODE: + context.read().fetchState( + context, + shouldFetchEpisodes: false, + shouldFetchFiles: false, + ); + break; + } + }); + } }, ), // if (widget.queueRecord.status == SonarrQueueStatus.COMPLETED && From edf95ed7574d841b7898eb596b8c5ec5d263ce8a Mon Sep 17 00:00:00 2001 From: Jamie Berghmans Date: Wed, 6 Sep 2023 09:33:42 +0200 Subject: [PATCH 2/3] fix(model): replace sonarrqueuerecord with sonarrmanualimport in manualimport command replace the usage of sonarrqueuerecord with sonarrmanualimport for the manualimport command --- lib/api/sonarr/controllers.dart | 3 + .../controllers/command/manual_import.dart | 5 +- lib/api/sonarr/controllers/manual_import.dart | 4 +- lib/api/sonarr/models/command/command.dart | 4 ++ .../models/manual_import/manual_import.dart | 55 +++++++++++++++++++ lib/modules/sonarr/core/api_controller.dart | 11 +++- lib/modules/sonarr/core/dialogs.dart | 2 +- 7 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 lib/api/sonarr/models/manual_import/manual_import.dart diff --git a/lib/api/sonarr/controllers.dart b/lib/api/sonarr/controllers.dart index 004284e3e..2fcdac02e 100644 --- a/lib/api/sonarr/controllers.dart +++ b/lib/api/sonarr/controllers.dart @@ -5,6 +5,9 @@ import 'package:lunasea/api/sonarr/models.dart'; import 'package:lunasea/api/sonarr/types.dart'; import 'package:dio/dio.dart'; import 'package:intl/intl.dart'; +import 'package:lunasea/core.dart'; + +import 'models/manual_import/manual_import.dart'; // Calendar part 'controllers/calendar.dart'; diff --git a/lib/api/sonarr/controllers/command/manual_import.dart b/lib/api/sonarr/controllers/command/manual_import.dart index 551717103..7d656e351 100644 --- a/lib/api/sonarr/controllers/command/manual_import.dart +++ b/lib/api/sonarr/controllers/command/manual_import.dart @@ -2,11 +2,12 @@ part of sonarr_commands; Future _commandManualImport( Dio client, - List records + List manualImports ) async { Response response = await client.post('command', data: { 'name': 'ManualImport', - 'files': records + 'files': manualImports, + 'importMode': 'auto' }); return SonarrCommand.fromJson(response.data); } diff --git a/lib/api/sonarr/controllers/manual_import.dart b/lib/api/sonarr/controllers/manual_import.dart index 4727499cb..2675280de 100644 --- a/lib/api/sonarr/controllers/manual_import.dart +++ b/lib/api/sonarr/controllers/manual_import.dart @@ -16,7 +16,7 @@ class SonarrControllerManualImport { /// Required Parameters: /// - `records`: The records to import Future import({ - required List records + required List manualImports }) async => - _commandManualImport(_client, records); + _commandManualImport(_client, manualImports); } diff --git a/lib/api/sonarr/models/command/command.dart b/lib/api/sonarr/models/command/command.dart index de273ea99..0b3e895d6 100644 --- a/lib/api/sonarr/models/command/command.dart +++ b/lib/api/sonarr/models/command/command.dart @@ -74,6 +74,9 @@ class SonarrCommand { @JsonKey(name: 'updateScheduledTask') bool? updateScheduledTask; + @JsonKey(name: 'importMode') + String? importMode; + /// Identifier of command instance @JsonKey(name: 'id') int? id; @@ -90,6 +93,7 @@ class SonarrCommand { this.startedOn, this.sendUpdatesToClient, this.updateScheduledTask, + this.importMode, this.id, }); diff --git a/lib/api/sonarr/models/manual_import/manual_import.dart b/lib/api/sonarr/models/manual_import/manual_import.dart new file mode 100644 index 000000000..6cb20064c --- /dev/null +++ b/lib/api/sonarr/models/manual_import/manual_import.dart @@ -0,0 +1,55 @@ +import 'dart:convert'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:lunasea/modules/sonarr.dart'; + +part 'manual_import.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) +class SonarrManualImport { + + @JsonKey(name: 'path') + String? path; + + @JsonKey(name: 'seriesId') + int? seriesId; + + @JsonKey(name: 'episodeIds') + List? episodeIds; + + @JsonKey(name: 'releaseGroup') + String? releaseGroup; + + @JsonKey(name: 'quality') + SonarrEpisodeFileQuality? quality; + + @JsonKey(name: 'language') + SonarrEpisodeFileLanguage? language; + + @JsonKey(name: 'downloadId') + String? downloadId; + + @JsonKey(name: 'id') + int? id; + + SonarrManualImport({ + this.path, + this.seriesId, + this.episodeIds, + this.releaseGroup, + this.quality, + this.language, + this.downloadId, + this.id, + }); + + /// Returns a JSON-encoded string version of this object. + @override + String toString() => json.encode(this.toJson()); + + /// Deserialize a JSON map to a [SonarrManualImport] object. + factory SonarrManualImport.fromJson(Map json) => + _$SonarrManualImportFromJson(json); + + /// Serialize a [SonarrManualImport] object to a JSON map. + Map toJson() => _$SonarrManualImportToJson(this); +} diff --git a/lib/modules/sonarr/core/api_controller.dart b/lib/modules/sonarr/core/api_controller.dart index 83c53ea40..f028e3b1e 100644 --- a/lib/modules/sonarr/core/api_controller.dart +++ b/lib/modules/sonarr/core/api_controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:lunasea/api/sonarr/models/manual_import/manual_import.dart'; import 'package:lunasea/core.dart'; import 'package:lunasea/extensions/string/string.dart'; import 'package:lunasea/modules/sonarr.dart'; @@ -808,7 +809,15 @@ class SonarrAPIController { .read() .api! .manualImport - .import(records: [queueRecord]) + .import(manualImports: [SonarrManualImport( + path: queueRecord.outputPath, + seriesId: queueRecord.seriesId, + episodeIds: [queueRecord.episodeId!], + quality: queueRecord.quality, + language: queueRecord.language, + downloadId: queueRecord.downloadId, + id: queueRecord.id, + )]) .then((_) { if (showSnackbar) showLunaSuccessSnackBar( diff --git a/lib/modules/sonarr/core/dialogs.dart b/lib/modules/sonarr/core/dialogs.dart index 7561721df..33e00d5b4 100644 --- a/lib/modules/sonarr/core/dialogs.dart +++ b/lib/modules/sonarr/core/dialogs.dart @@ -771,7 +771,7 @@ class SonarrDialogs { context: context, title: 'sonarr.Messages'.tr(), cancelButtonText: 'lunasea.Close'.tr(), - buttons: [ + buttons: record.errorMessage != null ? [] : [ LunaDialog.button( text: 'Import', onPressed: () => { From 357fd64297b9f0e9eb7cf4697a273cc96bcce45e Mon Sep 17 00:00:00 2001 From: Jamie Berghmans Date: Wed, 6 Sep 2023 09:46:19 +0200 Subject: [PATCH 3/3] refactor(comment): undo changes to file accidentally tracked --- lib/api/sonarr/controllers/queue.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/api/sonarr/controllers/queue.dart b/lib/api/sonarr/controllers/queue.dart index 4a1d4e771..993bde475 100644 --- a/lib/api/sonarr/controllers/queue.dart +++ b/lib/api/sonarr/controllers/queue.dart @@ -63,6 +63,4 @@ class SonarrControllerQueue { blocklist: blocklist, removeFromClient: removeFromClient, ); - - // Handler for }