New: Import List Tags (#505)

* New: Import List Tags

* New: Show ImportLists where Tag is Used in Tag manager

* Fixed: SignalR Errors due to handleTag missing

* Fixed: Clarify Lidarr Tags, not to be confused with LastFmTags
pull/6/head
Qstick 6 years ago committed by GitHub
parent b17cccd736
commit 27736649c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,6 +11,7 @@ import { setAppValue, setVersion } from 'Store/Actions/appActions';
import { update, updateItem, removeItem } from 'Store/Actions/baseActions'; import { update, updateItem, removeItem } from 'Store/Actions/baseActions';
import { fetchHealth } from 'Store/Actions/systemActions'; import { fetchHealth } from 'Store/Actions/systemActions';
import { fetchQueue, fetchQueueDetails } from 'Store/Actions/queueActions'; import { fetchQueue, fetchQueueDetails } from 'Store/Actions/queueActions';
import { fetchTags, fetchTagDetails } from 'Store/Actions/tagActions';
function getState(status) { function getState(status) {
switch (status) { switch (status) {
@ -68,7 +69,9 @@ const mapDispatchToProps = {
dispatchRemoveItem: removeItem, dispatchRemoveItem: removeItem,
dispatchFetchHealth: fetchHealth, dispatchFetchHealth: fetchHealth,
dispatchFetchQueue: fetchQueue, dispatchFetchQueue: fetchQueue,
dispatchFetchQueueDetails: fetchQueueDetails dispatchFetchQueueDetails: fetchQueueDetails,
dispatchFetchTags: fetchTags,
dispatchFetchTagDetails: fetchTagDetails
}; };
class SignalRConnector extends Component { class SignalRConnector extends Component {
@ -263,6 +266,14 @@ class SignalRConnector extends Component {
// No-op for now, we may want this later // No-op for now, we may want this later
} }
handleTag = (body) => {
if (body.action === 'sync') {
this.props.dispatchFetchTags();
this.props.dispatchFetchTagDetails();
return;
}
}
// //
// Listeners // Listeners
@ -362,7 +373,9 @@ SignalRConnector.propTypes = {
dispatchRemoveItem: PropTypes.func.isRequired, dispatchRemoveItem: PropTypes.func.isRequired,
dispatchFetchHealth: PropTypes.func.isRequired, dispatchFetchHealth: PropTypes.func.isRequired,
dispatchFetchQueue: PropTypes.func.isRequired, dispatchFetchQueue: PropTypes.func.isRequired,
dispatchFetchQueueDetails: PropTypes.func.isRequired dispatchFetchQueueDetails: PropTypes.func.isRequired,
dispatchFetchTags: PropTypes.func.isRequired,
dispatchFetchTagDetails: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(SignalRConnector); export default connect(createMapStateToProps, mapDispatchToProps)(SignalRConnector);

@ -44,6 +44,7 @@ function EditImportListModalContent(props) {
qualityProfileId, qualityProfileId,
languageProfileId, languageProfileId,
metadataProfileId, metadataProfileId,
tags,
fields fields
} = item; } = item;
@ -152,6 +153,18 @@ function EditImportListModalContent(props) {
/> />
</FormGroup> </FormGroup>
<FormGroup>
<FormLabel>Lidarr Tags</FormLabel>
<FormInputGroup
type={inputTypes.TAG}
name="tags"
helpText="Add artists from this list with these tags"
{...tags}
onChange={onInputChange}
/>
</FormGroup>
{ {
!!fields && !!fields.length && !!fields && !!fields.length &&
<div> <div>

@ -17,6 +17,7 @@ function TagDetailsModalContent(props) {
isTagUsed, isTagUsed,
artist, artist,
delayProfiles, delayProfiles,
importLists,
notifications, notifications,
restrictions, restrictions,
onModalClose, onModalClose,
@ -80,6 +81,21 @@ function TagDetailsModalContent(props) {
</FieldSet> </FieldSet>
} }
{
!!importLists.length &&
<FieldSet legend="Import Lists">
{
importLists.map((item) => {
return (
<div key={item.id}>
{item.name}
</div>
);
})
}
</FieldSet>
}
{ {
!!restrictions.length && !!restrictions.length &&
<FieldSet legend="Restrictions"> <FieldSet legend="Restrictions">
@ -154,6 +170,7 @@ TagDetailsModalContent.propTypes = {
isTagUsed: PropTypes.bool.isRequired, isTagUsed: PropTypes.bool.isRequired,
artist: PropTypes.arrayOf(PropTypes.object).isRequired, artist: PropTypes.arrayOf(PropTypes.object).isRequired,
delayProfiles: PropTypes.arrayOf(PropTypes.object).isRequired, delayProfiles: PropTypes.arrayOf(PropTypes.object).isRequired,
importLists: PropTypes.arrayOf(PropTypes.object).isRequired,
notifications: PropTypes.arrayOf(PropTypes.object).isRequired, notifications: PropTypes.arrayOf(PropTypes.object).isRequired,
restrictions: PropTypes.arrayOf(PropTypes.object).isRequired, restrictions: PropTypes.arrayOf(PropTypes.object).isRequired,
onModalClose: PropTypes.func.isRequired, onModalClose: PropTypes.func.isRequired,

@ -25,6 +25,14 @@ function createMatchingDelayProfilesSelector() {
); );
} }
function createMatchingImportListsSelector() {
return createSelector(
(state, { importListIds }) => importListIds,
(state) => state.settings.importLists.items,
findMatchingItems
);
}
function createMatchingNotificationsSelector() { function createMatchingNotificationsSelector() {
return createSelector( return createSelector(
(state, { notificationIds }) => notificationIds, (state, { notificationIds }) => notificationIds,
@ -45,12 +53,14 @@ function createMapStateToProps() {
return createSelector( return createSelector(
createMatchingArtistSelector(), createMatchingArtistSelector(),
createMatchingDelayProfilesSelector(), createMatchingDelayProfilesSelector(),
createMatchingImportListsSelector(),
createMatchingNotificationsSelector(), createMatchingNotificationsSelector(),
createMatchingRestrictionsSelector(), createMatchingRestrictionsSelector(),
(artist, delayProfiles, notifications, restrictions) => { (artist, delayProfiles, importLists, notifications, restrictions) => {
return { return {
artist, artist,
delayProfiles, delayProfiles,
importLists,
notifications, notifications,
restrictions restrictions
}; };

@ -53,6 +53,7 @@ class Tag extends Component {
const { const {
label, label,
delayProfileIds, delayProfileIds,
importListIds,
notificationIds, notificationIds,
restrictionIds, restrictionIds,
artistIds artistIds
@ -65,6 +66,7 @@ class Tag extends Component {
const isTagUsed = !!( const isTagUsed = !!(
delayProfileIds.length || delayProfileIds.length ||
importListIds.length ||
notificationIds.length || notificationIds.length ||
restrictionIds.length || restrictionIds.length ||
artistIds.length artistIds.length
@ -97,6 +99,13 @@ class Tag extends Component {
</div> </div>
} }
{
!!importListIds.length &&
<div>
{importListIds.length} import list{importListIds.length > 1 && 's'}
</div>
}
{ {
!!notificationIds.length && !!notificationIds.length &&
<div> <div>
@ -125,6 +134,7 @@ class Tag extends Component {
isTagUsed={isTagUsed} isTagUsed={isTagUsed}
artistIds={artistIds} artistIds={artistIds}
delayProfileIds={delayProfileIds} delayProfileIds={delayProfileIds}
importListIds={importListIds}
notificationIds={notificationIds} notificationIds={notificationIds}
restrictionIds={restrictionIds} restrictionIds={restrictionIds}
isOpen={isDetailsModalOpen} isOpen={isDetailsModalOpen}
@ -150,6 +160,7 @@ Tag.propTypes = {
id: PropTypes.number.isRequired, id: PropTypes.number.isRequired,
label: PropTypes.string.isRequired, label: PropTypes.string.isRequired,
delayProfileIds: PropTypes.arrayOf(PropTypes.number).isRequired, delayProfileIds: PropTypes.arrayOf(PropTypes.number).isRequired,
importListIds: PropTypes.arrayOf(PropTypes.number).isRequired,
notificationIds: PropTypes.arrayOf(PropTypes.number).isRequired, notificationIds: PropTypes.arrayOf(PropTypes.number).isRequired,
restrictionIds: PropTypes.arrayOf(PropTypes.number).isRequired, restrictionIds: PropTypes.arrayOf(PropTypes.number).isRequired,
artistIds: PropTypes.arrayOf(PropTypes.number).isRequired, artistIds: PropTypes.arrayOf(PropTypes.number).isRequired,
@ -158,6 +169,7 @@ Tag.propTypes = {
Tag.defaultProps = { Tag.defaultProps = {
delayProfileIds: [], delayProfileIds: [],
importListIds: [],
notificationIds: [], notificationIds: [],
restrictionIds: [], restrictionIds: [],
artistIds: [] artistIds: []

@ -3,7 +3,7 @@ import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { fetchTagDetails } from 'Store/Actions/tagActions'; import { fetchTagDetails } from 'Store/Actions/tagActions';
import { fetchDelayProfiles, fetchNotifications, fetchRestrictions } from 'Store/Actions/settingsActions'; import { fetchDelayProfiles, fetchNotifications, fetchRestrictions, fetchImportLists } from 'Store/Actions/settingsActions';
import Tags from './Tags'; import Tags from './Tags';
function createMapStateToProps() { function createMapStateToProps() {
@ -27,6 +27,7 @@ function createMapStateToProps() {
const mapDispatchToProps = { const mapDispatchToProps = {
dispatchFetchTagDetails: fetchTagDetails, dispatchFetchTagDetails: fetchTagDetails,
dispatchFetchDelayProfiles: fetchDelayProfiles, dispatchFetchDelayProfiles: fetchDelayProfiles,
dispatchFetchImportLists: fetchImportLists,
dispatchFetchNotifications: fetchNotifications, dispatchFetchNotifications: fetchNotifications,
dispatchFetchRestrictions: fetchRestrictions dispatchFetchRestrictions: fetchRestrictions
}; };
@ -40,12 +41,14 @@ class MetadatasConnector extends Component {
const { const {
dispatchFetchTagDetails, dispatchFetchTagDetails,
dispatchFetchDelayProfiles, dispatchFetchDelayProfiles,
dispatchFetchImportLists,
dispatchFetchNotifications, dispatchFetchNotifications,
dispatchFetchRestrictions dispatchFetchRestrictions
} = this.props; } = this.props;
dispatchFetchTagDetails(); dispatchFetchTagDetails();
dispatchFetchDelayProfiles(); dispatchFetchDelayProfiles();
dispatchFetchImportLists();
dispatchFetchNotifications(); dispatchFetchNotifications();
dispatchFetchRestrictions(); dispatchFetchRestrictions();
} }
@ -65,6 +68,7 @@ class MetadatasConnector extends Component {
MetadatasConnector.propTypes = { MetadatasConnector.propTypes = {
dispatchFetchTagDetails: PropTypes.func.isRequired, dispatchFetchTagDetails: PropTypes.func.isRequired,
dispatchFetchDelayProfiles: PropTypes.func.isRequired, dispatchFetchDelayProfiles: PropTypes.func.isRequired,
dispatchFetchImportLists: PropTypes.func.isRequired,
dispatchFetchNotifications: PropTypes.func.isRequired, dispatchFetchNotifications: PropTypes.func.isRequired,
dispatchFetchRestrictions: PropTypes.func.isRequired dispatchFetchRestrictions: PropTypes.func.isRequired
}; };

@ -9,6 +9,7 @@ namespace Lidarr.Api.V1.Tags
{ {
public string Label { get; set; } public string Label { get; set; }
public List<int> DelayProfileIds { get; set; } public List<int> DelayProfileIds { get; set; }
public List<int> ImportListIds { get; set; }
public List<int> NotificationIds { get; set; } public List<int> NotificationIds { get; set; }
public List<int> RestrictionIds { get; set; } public List<int> RestrictionIds { get; set; }
public List<int> ArtistIds { get; set; } public List<int> ArtistIds { get; set; }
@ -25,6 +26,7 @@ namespace Lidarr.Api.V1.Tags
Id = model.Id, Id = model.Id,
Label = model.Label, Label = model.Label,
DelayProfileIds = model.DelayProfileIds, DelayProfileIds = model.DelayProfileIds,
ImportListIds = model.ImportListIds,
NotificationIds = model.NotificationIds, NotificationIds = model.NotificationIds,
RestrictionIds = model.RestrictionIds, RestrictionIds = model.RestrictionIds,
ArtistIds = model.ArtistIds ArtistIds = model.ArtistIds

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(22)]
public class import_list_tags : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("ImportLists").AddColumn("Tags").AsString().Nullable();
}
}
}

@ -66,8 +66,7 @@ namespace NzbDrone.Core.Datastore
.Ignore(d => d.Tags); .Ignore(d => d.Tags);
Mapper.Entity<ImportListDefinition>().RegisterDefinition("ImportLists") Mapper.Entity<ImportListDefinition>().RegisterDefinition("ImportLists")
.Ignore(i => i.Enable) .Ignore(i => i.Enable);
.Ignore(d => d.Tags);
Mapper.Entity<NotificationDefinition>().RegisterDefinition("Notifications") Mapper.Entity<NotificationDefinition>().RegisterDefinition("Notifications")
.Ignore(i => i.SupportsOnGrab) .Ignore(i => i.SupportsOnGrab)

@ -125,6 +125,7 @@ namespace NzbDrone.Core.ImportLists
ProfileId = importList.ProfileId, ProfileId = importList.ProfileId,
LanguageProfileId = importList.LanguageProfileId, LanguageProfileId = importList.LanguageProfileId,
MetadataProfileId = importList.MetadataProfileId, MetadataProfileId = importList.MetadataProfileId,
Tags = importList.Tags,
AlbumFolder = true, AlbumFolder = true,
AddOptions = new AddArtistOptions { SearchForMissingAlbums = true, Monitored = importList.ShouldMonitor, SelectedOption = 0 } AddOptions = new AddArtistOptions { SearchForMissingAlbums = true, Monitored = importList.ShouldMonitor, SelectedOption = 0 }
}); });

@ -196,6 +196,7 @@
<Compile Include="Datastore\Migration\018_album_disambiguation.cs" /> <Compile Include="Datastore\Migration\018_album_disambiguation.cs" />
<Compile Include="Datastore\Migration\019_add_ape_quality_in_profiles.cs" /> <Compile Include="Datastore\Migration\019_add_ape_quality_in_profiles.cs" />
<Compile Include="Datastore\Migration\021_add_custom_filters.cs" /> <Compile Include="Datastore\Migration\021_add_custom_filters.cs" />
<Compile Include="Datastore\Migration\022_import_list_tags.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" />

@ -10,5 +10,6 @@ namespace NzbDrone.Core.Tags
public List<int> NotificationIds { get; set; } public List<int> NotificationIds { get; set; }
public List<int> RestrictionIds { get; set; } public List<int> RestrictionIds { get; set; }
public List<int> DelayProfileIds { get; set; } public List<int> DelayProfileIds { get; set; }
public List<int> ImportListIds { get; set; }
} }
} }

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Notifications; using NzbDrone.Core.Notifications;
using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Profiles.Delay;
@ -25,6 +26,7 @@ namespace NzbDrone.Core.Tags
private readonly ITagRepository _repo; private readonly ITagRepository _repo;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly IDelayProfileService _delayProfileService; private readonly IDelayProfileService _delayProfileService;
private readonly IImportListFactory _importListFactory;
private readonly INotificationFactory _notificationFactory; private readonly INotificationFactory _notificationFactory;
private readonly IRestrictionService _restrictionService; private readonly IRestrictionService _restrictionService;
private readonly IArtistService _artistService; private readonly IArtistService _artistService;
@ -32,6 +34,7 @@ namespace NzbDrone.Core.Tags
public TagService(ITagRepository repo, public TagService(ITagRepository repo,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
IDelayProfileService delayProfileService, IDelayProfileService delayProfileService,
ImportListFactory importListFactory,
INotificationFactory notificationFactory, INotificationFactory notificationFactory,
IRestrictionService restrictionService, IRestrictionService restrictionService,
IArtistService artistService) IArtistService artistService)
@ -39,6 +42,7 @@ namespace NzbDrone.Core.Tags
_repo = repo; _repo = repo;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_delayProfileService = delayProfileService; _delayProfileService = delayProfileService;
_importListFactory = importListFactory;
_notificationFactory = notificationFactory; _notificationFactory = notificationFactory;
_restrictionService = restrictionService; _restrictionService = restrictionService;
_artistService = artistService; _artistService = artistService;
@ -65,6 +69,7 @@ namespace NzbDrone.Core.Tags
{ {
var tag = GetTag(tagId); var tag = GetTag(tagId);
var delayProfiles = _delayProfileService.AllForTag(tagId); var delayProfiles = _delayProfileService.AllForTag(tagId);
var importLists = _importListFactory.AllForTag(tagId);
var notifications = _notificationFactory.AllForTag(tagId); var notifications = _notificationFactory.AllForTag(tagId);
var restrictions = _restrictionService.AllForTag(tagId); var restrictions = _restrictionService.AllForTag(tagId);
var artist = _artistService.AllForTag(tagId); var artist = _artistService.AllForTag(tagId);
@ -74,6 +79,7 @@ namespace NzbDrone.Core.Tags
Id = tagId, Id = tagId,
Label = tag.Label, Label = tag.Label,
DelayProfileIds = delayProfiles.Select(c => c.Id).ToList(), DelayProfileIds = delayProfiles.Select(c => c.Id).ToList(),
ImportListIds = importLists.Select(c => c.Id).ToList(),
NotificationIds = notifications.Select(c => c.Id).ToList(), NotificationIds = notifications.Select(c => c.Id).ToList(),
RestrictionIds = restrictions.Select(c => c.Id).ToList(), RestrictionIds = restrictions.Select(c => c.Id).ToList(),
ArtistIds = artist.Select(c => c.Id).ToList() ArtistIds = artist.Select(c => c.Id).ToList()
@ -84,6 +90,7 @@ namespace NzbDrone.Core.Tags
{ {
var tags = All(); var tags = All();
var delayProfiles = _delayProfileService.All(); var delayProfiles = _delayProfileService.All();
var importLists = _importListFactory.All();
var notifications = _notificationFactory.All(); var notifications = _notificationFactory.All();
var restrictions = _restrictionService.All(); var restrictions = _restrictionService.All();
var artists = _artistService.GetAllArtists(); var artists = _artistService.GetAllArtists();
@ -97,6 +104,7 @@ namespace NzbDrone.Core.Tags
Id = tag.Id, Id = tag.Id,
Label = tag.Label, Label = tag.Label,
DelayProfileIds = delayProfiles.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), DelayProfileIds = delayProfiles.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
ImportListIds = importLists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
NotificationIds = notifications.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), NotificationIds = notifications.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
RestrictionIds = restrictions.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(), RestrictionIds = restrictions.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList(),
ArtistIds = artists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList() ArtistIds = artists.Where(c => c.Tags.Contains(tag.Id)).Select(c => c.Id).ToList()

Loading…
Cancel
Save