diff --git a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.tsx b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.tsx index 2b06cc2e78..64398debbb 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/EditImportListModalContent.tsx @@ -78,6 +78,7 @@ function EditImportListModalContent({ qualityProfileId, searchOnAdd, tags, + tagExisting, fields, } = item; @@ -256,6 +257,18 @@ function EditImportListModalContent({ /> + + {translate('TagExisting')} + + + + {fields?.length ? (
{fields.map((field) => { diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx index 74b1662207..5c55519a79 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/Edit/ManageImportListsEditModalContent.tsx @@ -18,6 +18,7 @@ interface SavePayload { qualityProfileId?: number; minimumAvailability?: string; rootFolderPath?: string; + tagExisting?: boolean; } interface ManageImportListsEditModalContentProps { @@ -62,7 +63,7 @@ function ManageImportListsEditModalContent( ); const [minimumAvailability, setMinimumAvailability] = useState(NO_CHANGE); const [rootFolderPath, setRootFolderPath] = useState(NO_CHANGE); - + const [tagExisting, setTagExisting] = useState(NO_CHANGE); const save = useCallback(() => { let hasChanges = false; const payload: SavePayload = {}; @@ -91,6 +92,10 @@ function ManageImportListsEditModalContent( hasChanges = true; payload.rootFolderPath = rootFolderPath; } + if (tagExisting !== NO_CHANGE) { + hasChanges = true; + payload.tagExisting = tagExisting === 'enabled'; + } if (hasChanges) { onSavePress(payload); @@ -103,6 +108,7 @@ function ManageImportListsEditModalContent( qualityProfileId, minimumAvailability, rootFolderPath, + tagExisting, onSavePress, onModalClose, ]); @@ -124,6 +130,9 @@ function ManageImportListsEditModalContent( case 'rootFolderPath': setRootFolderPath(value as string); break; + case 'tagExisting': + setTagExisting(value as string); + break; default: console.warn(`EditImportListModalContent Unknown Input: '${name}'`); } @@ -199,6 +208,18 @@ function ManageImportListsEditModalContent( onChange={onInputChange} /> + + + {translate('TagExisting')} + + + diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx index b73132eb85..23acb0b4bf 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalContent.tsx @@ -82,6 +82,12 @@ const COLUMNS = [ isSortable: true, isVisible: true, }, + { + name: 'tagExisting', + label: () => translate('TagExisting'), + isSortable: true, + isVisible: true, + }, ]; interface ManageImportListsModalContentProps { diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css index 7045ed925b..f18c48db21 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css @@ -5,6 +5,7 @@ .minimumAvailability, .qualityProfileId, .rootFolderPath, +.tagExisting, .implementation { composes: cell from '~Components/Table/Cells/TableRowCell.css'; diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css.d.ts b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css.d.ts index c6cb99a370..78ff64f037 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css.d.ts +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.css.d.ts @@ -8,6 +8,7 @@ interface CssExports { 'name': string; 'qualityProfileId': string; 'rootFolderPath': string; + 'tagExisting': string; 'tags': string; } export const cssExports: CssExports; diff --git a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx index 7cc9c272f1..cb25677406 100644 --- a/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx +++ b/frontend/src/Settings/ImportLists/ImportLists/Manage/ManageImportListsModalRow.tsx @@ -19,6 +19,7 @@ interface ManageImportListsModalRowProps { minimumAvailability: string; implementation: string; tags: number[]; + tagExisting: boolean; enabled: boolean; enableAuto: boolean; columns: Column[]; @@ -38,6 +39,7 @@ function ManageImportListsModalRow(props: ManageImportListsModalRowProps) { enabled, enableAuto, tags, + tagExisting, onSelectedChange, } = props; @@ -91,6 +93,10 @@ function ManageImportListsModalRow(props: ManageImportListsModalRowProps) { + + + {tagExisting ? translate('Yes') : translate('No')} + ); } diff --git a/frontend/src/typings/ImportList.ts b/frontend/src/typings/ImportList.ts index 42d805b065..69672d4d0d 100644 --- a/frontend/src/typings/ImportList.ts +++ b/frontend/src/typings/ImportList.ts @@ -15,6 +15,7 @@ interface ImportList extends Provider { minRefreshInterval: string; name: string; tags: number[]; + tagExisting: boolean; } export default ImportList; diff --git a/src/NzbDrone.Core/Datastore/Migration/243_add_tag_existing_to_importlists.cs b/src/NzbDrone.Core/Datastore/Migration/243_add_tag_existing_to_importlists.cs new file mode 100644 index 0000000000..cf26ab3954 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/243_add_tag_existing_to_importlists.cs @@ -0,0 +1,13 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration; + +[Migration(243)] +public class add_tag_existing_to_importlists : NzbDroneMigrationBase +{ + protected override void MainDbUpgrade() + { + Alter.Table("ImportLists").AddColumn("TagExisting").AsBoolean().WithDefaultValue(false); + } +} diff --git a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs index 277f26eb29..268b7d27b1 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListDefinition.cs @@ -16,6 +16,7 @@ public class ImportListDefinition : ProviderDefinition, IEquatable Enabled; diff --git a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs index b96e07a0fb..9d48ff61d2 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs @@ -76,18 +76,25 @@ private void SyncList(ImportListDefinition definition) private void ProcessMovieReport(ImportListDefinition importList, ImportListMovie report, List listExclusions, List dbMovies, List moviesToAdd) { - if (report.TmdbId == 0 || !importList.EnableAuto) + if (report.TmdbId == 0) { return; } - // Check to see if movie in DB + // Check to see if movie in DB and maybe apply tags if (dbMovies.Contains(report.TmdbId)) { + TagExisting(importList, report); _logger.Debug("{0} [{1}] Rejected, Movie Exists in DB", report.TmdbId, report.Title); return; } + // Now that retro-tags are applied, end if auto import is disabled + if (!importList.EnableAuto) + { + return; + } + // Check to see if movie excluded var excludedMovie = listExclusions.SingleOrDefault(s => s.TmdbId == report.TmdbId); @@ -123,6 +130,26 @@ private void ProcessMovieReport(ImportListDefinition importList, ImportListMovie } } + private void TagExisting(ImportListDefinition importList, ImportListMovie report) + { + if (importList.TagExisting) + { + var movie = _movieService.FindByTmdbId(report.TmdbId); + + var preCount = movie.Tags.Count; + foreach (var tag in importList.Tags) + { + movie.Tags.Add(tag); + } + + if (preCount != movie.Tags.Count) + { + _movieService.UpdateMovie(movie); + _logger.Debug("{0} [{1}] Tagged existing movie", report.TmdbId, report.Title); + } + } + } + private void ProcessListItems(ImportListFetchResult listFetchResult) { listFetchResult.Movies = listFetchResult.Movies.DistinctBy(x => diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 283f20cade..90274ff5cc 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -1697,6 +1697,8 @@ "Result": "Result", "Retention": "Retention", "RetentionHelpText": "Usenet only: Set to zero to set for unlimited retention", + "TagExistingHelpText": "Tag existing movies in {appName}", + "TagExisting": "Tag Existing", "RetryingDownloadOn": "Retrying download on {date} at {time}", "RootFolder": "Root Folder", "RootFolderCheckMultipleMessage": "Multiple root folders are missing: {rootFolderPaths}", diff --git a/src/Radarr.Api.V3/ImportLists/ImportListBulkResource.cs b/src/Radarr.Api.V3/ImportLists/ImportListBulkResource.cs index c7633785e9..73e6712c63 100644 --- a/src/Radarr.Api.V3/ImportLists/ImportListBulkResource.cs +++ b/src/Radarr.Api.V3/ImportLists/ImportListBulkResource.cs @@ -11,6 +11,7 @@ public class ImportListBulkResource : ProviderBulkResource @@ -29,6 +30,7 @@ public override List UpdateModel(ImportListBulkResource re existing.RootFolderPath = resource.RootFolderPath ?? existing.RootFolderPath; existing.QualityProfileId = resource.QualityProfileId ?? existing.QualityProfileId; existing.MinimumAvailability = resource.MinimumAvailability ?? existing.MinimumAvailability; + existing.TagExisting = resource.TagExisting ?? existing.TagExisting; }); return existingDefinitions; diff --git a/src/Radarr.Api.V3/ImportLists/ImportListResource.cs b/src/Radarr.Api.V3/ImportLists/ImportListResource.cs index 7093f08eaf..27eabb0d89 100644 --- a/src/Radarr.Api.V3/ImportLists/ImportListResource.cs +++ b/src/Radarr.Api.V3/ImportLists/ImportListResource.cs @@ -16,6 +16,7 @@ public class ImportListResource : ProviderResource public ImportListType ListType { get; set; } public int ListOrder { get; set; } public TimeSpan MinRefreshInterval { get; set; } + public bool TagExisting { get; set; } } public class ImportListResourceMapper : ProviderResourceMapper @@ -39,6 +40,7 @@ public override ImportListResource ToResource(ImportListDefinition definition) resource.ListType = definition.ListType; resource.ListOrder = (int)definition.ListType; resource.MinRefreshInterval = definition.MinRefreshInterval; + resource.TagExisting = definition.TagExisting; return resource; } @@ -61,6 +63,7 @@ public override ImportListDefinition ToModel(ImportListResource resource, Import definition.MinimumAvailability = resource.MinimumAvailability; definition.ListType = resource.ListType; definition.MinRefreshInterval = resource.MinRefreshInterval; + definition.TagExisting = resource.TagExisting; return definition; } diff --git a/src/Radarr.Api.V3/ProviderResource.cs b/src/Radarr.Api.V3/ProviderResource.cs index c5e8a1cd6f..2af43b9f38 100644 --- a/src/Radarr.Api.V3/ProviderResource.cs +++ b/src/Radarr.Api.V3/ProviderResource.cs @@ -16,7 +16,6 @@ public class ProviderResource : RestResource public string InfoLink { get; set; } public ProviderMessage Message { get; set; } public HashSet Tags { get; set; } - public List Presets { get; set; } }