From da4f6b7df96a2e5ceb95978deeabd0ae821bf8d3 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 3 Dec 2022 17:16:08 -0800 Subject: [PATCH] New: Show Custom Formats on Manual Import / Manage Episodes Closes #5241 --- .../InteractiveImportModalContent.js | 9 +++++++ .../Interactive/InteractiveImportRow.css | 4 +++ .../Interactive/InteractiveImportRow.js | 25 ++++++++++++++++++- .../CustomFormatCalculationService.cs | 25 ++++++++++++++++++- .../EpisodeImport/ImportDecisionMaker.cs | 4 +++ .../EpisodeImport/Manual/ManualImportItem.cs | 7 ++++++ .../Manual/ManualImportService.cs | 7 ++++++ .../ManualImport/ManualImportResource.cs | 3 +++ 8 files changed, 82 insertions(+), 2 deletions(-) diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js index dfb79de68..7f87d84b8 100644 --- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js +++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js @@ -77,6 +77,15 @@ const columns = [ isSortable: true, isVisible: true }, + { + name: 'customFormats', + label: React.createElement(Icon, { + name: icons.INTERACTIVE, + title: 'Custom Format' + }), + isSortable: true, + isVisible: true + }, { name: 'rejections', label: React.createElement(Icon, { diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css index b2304553f..d3cfb118a 100644 --- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css +++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.css @@ -23,3 +23,7 @@ margin-top: 0; text-align: start; } + +.customFormatTooltip { + max-width: 250px; +} diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js index 15975697a..e2f0b6ed6 100644 --- a/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js +++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportRow.js @@ -7,6 +7,7 @@ import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton'; import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; import TableRow from 'Components/Table/TableRow'; import Popover from 'Components/Tooltip/Popover'; +import EpisodeFormats from 'Episode/EpisodeFormats'; import EpisodeLanguages from 'Episode/EpisodeLanguages'; import EpisodeQuality from 'Episode/EpisodeQuality'; import { icons, kinds, tooltipPositions } from 'Helpers/Props'; @@ -213,6 +214,7 @@ class InteractiveImportRow extends Component { languages, releaseGroup, size, + customFormats, rejections, isReprocessing, isSelected, @@ -366,7 +368,26 @@ class InteractiveImportRow extends Component { { - rejections && rejections.length ? + customFormats?.length ? + + } + title="Formats" + body={ +
+ +
+ } + position={tooltipPositions.LEFT} + /> : + null + } +
+ + + { + rejections.length ? } position={tooltipPositions.LEFT} + canFlip={false} /> : null } @@ -462,6 +484,7 @@ InteractiveImportRow.propTypes = { quality: PropTypes.object, languages: PropTypes.arrayOf(PropTypes.object), size: PropTypes.number.isRequired, + customFormats: PropTypes.arrayOf(PropTypes.object), rejections: PropTypes.arrayOf(PropTypes.object).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired, episodeFileId: PropTypes.number, diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs index 043e4d36b..06c39cd66 100644 --- a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs +++ b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.CustomFormats List ParseCustomFormat(EpisodeFile episodeFile); List ParseCustomFormat(Blocklist blocklist, Series series); List ParseCustomFormat(EpisodeHistory history, Series series); + List ParseCustomFormat(LocalEpisode localEpisode); } public class CustomFormatCalculationService : ICustomFormatCalculationService @@ -102,6 +103,28 @@ namespace NzbDrone.Core.CustomFormats return ParseCustomFormat(input); } + public List ParseCustomFormat(LocalEpisode localEpisode) + { + var episodeInfo = new ParsedEpisodeInfo + { + SeriesTitle = localEpisode.Series.Title, + ReleaseTitle = localEpisode.SceneName, + Quality = localEpisode.Quality, + Languages = localEpisode.Languages, + ReleaseGroup = localEpisode.ReleaseGroup + }; + + var input = new CustomFormatInput + { + EpisodeInfo = episodeInfo, + Series = localEpisode.Series, + Size = localEpisode.Size, + Languages = localEpisode.Languages + }; + + return ParseCustomFormat(input); + } + private List ParseCustomFormat(CustomFormatInput input) { return ParseCustomFormat(input, _formatService.All()); @@ -148,7 +171,7 @@ namespace NzbDrone.Core.CustomFormats var episodeInfo = new ParsedEpisodeInfo { - SeriesTitle = episodeFile.Series.Value.Title, + SeriesTitle = series.Title, ReleaseTitle = sceneName, Quality = episodeFile.Quality, Languages = episodeFile.Languages, diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs index fc3807606..6c20be9ef 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/ImportDecisionMaker.cs @@ -4,6 +4,7 @@ using System.Linq; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.EpisodeImport.Aggregation; @@ -28,6 +29,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport private readonly IAggregationService _aggregationService; private readonly IDiskProvider _diskProvider; private readonly IDetectSample _detectSample; + private readonly ICustomFormatCalculationService _formatCalculator; private readonly Logger _logger; public ImportDecisionMaker(IEnumerable specifications, @@ -35,6 +37,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport IAggregationService aggregationService, IDiskProvider diskProvider, IDetectSample detectSample, + ICustomFormatCalculationService formatCalculator, Logger logger) { _specifications = specifications; @@ -42,6 +45,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport _aggregationService = aggregationService; _diskProvider = diskProvider; _detectSample = detectSample; + _formatCalculator = formatCalculator; _logger = logger; } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs index bb251364b..c1010de9f 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportItem.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; using NzbDrone.Core.Qualities; @@ -21,6 +22,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual public List Languages { get; set; } public string ReleaseGroup { get; set; } public string DownloadId { get; set; } + public List CustomFormats { get; set; } public IEnumerable Rejections { get; set; } + + public ManualImportItem() + { + CustomFormats = new List(); + } } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs index b9c20d0b8..5696c10ae 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Manual/ManualImportService.cs @@ -6,6 +6,7 @@ using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; using NzbDrone.Core.Download.TrackedDownloads; @@ -40,6 +41,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual private readonly ITrackedDownloadService _trackedDownloadService; private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService; private readonly IMediaFileService _mediaFileService; + private readonly ICustomFormatCalculationService _formatCalculator; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; @@ -54,6 +56,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual ITrackedDownloadService trackedDownloadService, IDownloadedEpisodesImportService downloadedEpisodesImportService, IMediaFileService mediaFileService, + ICustomFormatCalculationService formatCalculator, IEventAggregator eventAggregator, Logger logger) { @@ -68,6 +71,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual _trackedDownloadService = trackedDownloadService; _downloadedEpisodesImportService = downloadedEpisodesImportService; _mediaFileService = mediaFileService; + _formatCalculator = formatCalculator; _eventAggregator = eventAggregator; _logger = logger; } @@ -382,6 +386,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual if (decision.LocalEpisode.Series != null) { item.Series = decision.LocalEpisode.Series; + + item.CustomFormats = _formatCalculator.ParseCustomFormat(decision.LocalEpisode); } if (decision.LocalEpisode.Episodes.Any() && decision.LocalEpisode.Episodes.Select(c => c.SeasonNumber).Distinct().Count() == 1) @@ -429,6 +435,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual item.Size = _diskProvider.GetFileSize(item.Path); item.Rejections = Enumerable.Empty(); item.EpisodeFileId = episodeFile.Id; + item.CustomFormats = _formatCalculator.ParseCustomFormat(episodeFile, series); return item; } diff --git a/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs b/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs index 57bcdfde5..cfa4b286d 100644 --- a/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs +++ b/src/Sonarr.Api.V3/ManualImport/ManualImportResource.cs @@ -5,6 +5,7 @@ using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Languages; using NzbDrone.Core.MediaFiles.EpisodeImport.Manual; using NzbDrone.Core.Qualities; +using Sonarr.Api.V3.CustomFormats; using Sonarr.Api.V3.Episodes; using Sonarr.Api.V3.Series; using Sonarr.Http.REST; @@ -27,6 +28,7 @@ namespace Sonarr.Api.V3.ManualImport public List Languages { get; set; } public int QualityWeight { get; set; } public string DownloadId { get; set; } + public List CustomFormats { get; set; } public IEnumerable Rejections { get; set; } } @@ -54,6 +56,7 @@ namespace Sonarr.Api.V3.ManualImport ReleaseGroup = model.ReleaseGroup, Quality = model.Quality, Languages = model.Languages, + CustomFormats = model.CustomFormats.ToResource(), // QualityWeight DownloadId = model.DownloadId,