diff --git a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js index 76598147e..38eeccf9e 100644 --- a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js +++ b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContent.js @@ -14,6 +14,7 @@ import FormLabel from 'Components/Form/FormLabel'; import FormInputGroup from 'Components/Form/FormInputGroup'; import PrimaryTypeItems from './PrimaryTypeItems'; import SecondaryTypeItems from './SecondaryTypeItems'; +import ReleaseStatusItems from './ReleaseStatusItems'; import styles from './EditMetadataProfileModalContent.css'; function EditMetadataProfileModalContent(props) { @@ -37,7 +38,8 @@ function EditMetadataProfileModalContent(props) { id, name, primaryAlbumTypes: itemPrimaryAlbumTypes, - secondaryAlbumTypes: itemSecondaryAlbumTypes + secondaryAlbumTypes: itemSecondaryAlbumTypes, + releaseStatuses: itemReleaseStatuses } = item; return ( @@ -89,6 +91,14 @@ function EditMetadataProfileModalContent(props) { {...otherProps} /> + + } @@ -134,6 +144,7 @@ EditMetadataProfileModalContent.propTypes = { saveError: PropTypes.object, primaryAlbumTypes: PropTypes.arrayOf(PropTypes.object).isRequired, secondaryAlbumTypes: PropTypes.arrayOf(PropTypes.object).isRequired, + releaseStatuses: PropTypes.arrayOf(PropTypes.object).isRequired, item: PropTypes.object.isRequired, isInUse: PropTypes.bool.isRequired, onInputChange: PropTypes.func.isRequired, diff --git a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContentConnector.js b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContentConnector.js index c1a7f8e51..8506a2b8a 100644 --- a/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContentConnector.js +++ b/frontend/src/Settings/Profiles/Metadata/EditMetadataProfileModalContentConnector.js @@ -54,16 +54,41 @@ function createSecondaryAlbumTypesSelector() { ); } +function createReleaseStatusesSelector() { + return createSelector( + createProviderSettingsSelector(), + (metadataProfile) => { + const releaseStatuses = metadataProfile.item.releaseStatuses; + if (!releaseStatuses || !releaseStatuses.value) { + return []; + } + + return _.reduceRight(releaseStatuses.value, (result, { allowed, releaseStatus }) => { + if (allowed) { + result.push({ + key: releaseStatus.id, + value: releaseStatus.name + }); + } + + return result; + }, []); + } + ); +} + function createMapStateToProps() { return createSelector( createProviderSettingsSelector(), createPrimaryAlbumTypesSelector(), createSecondaryAlbumTypesSelector(), + createReleaseStatusesSelector(), createProfileInUseSelector('metadataProfileId'), - (metadataProfile, primaryAlbumTypes, secondaryAlbumTypes, isInUse) => { + (metadataProfile, primaryAlbumTypes, secondaryAlbumTypes, releaseStatuses, isInUse) => { return { primaryAlbumTypes, secondaryAlbumTypes, + releaseStatuses, ...metadataProfile, isInUse }; @@ -138,6 +163,18 @@ class EditMetadataProfileModalContentConnector extends Component { }); } + onMetadataReleaseStatusItemAllowedChange = (id, allowed) => { + const metadataProfile = _.cloneDeep(this.props.item); + + const item = _.find(metadataProfile.releaseStatuses.value, (i) => i.releaseStatus.id === id); + item.allowed = allowed; + + this.props.setMetadataProfileValue({ + name: 'releaseStatuses', + value: metadataProfile.releaseStatuses.value + }); + } + // // Render @@ -154,6 +191,7 @@ class EditMetadataProfileModalContentConnector extends Component { onInputChange={this.onInputChange} onMetadataPrimaryTypeItemAllowedChange={this.onMetadataPrimaryTypeItemAllowedChange} onMetadataSecondaryTypeItemAllowedChange={this.onMetadataSecondaryTypeItemAllowedChange} + onMetadataReleaseStatusItemAllowedChange={this.onMetadataReleaseStatusItemAllowedChange} /> ); } diff --git a/frontend/src/Settings/Profiles/Metadata/ReleaseStatusItem.js b/frontend/src/Settings/Profiles/Metadata/ReleaseStatusItem.js new file mode 100644 index 000000000..71fe7f76c --- /dev/null +++ b/frontend/src/Settings/Profiles/Metadata/ReleaseStatusItem.js @@ -0,0 +1,60 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import classNames from 'classnames'; +import CheckInput from 'Components/Form/CheckInput'; +import styles from './TypeItem.css'; + +class ReleaseStatusItem extends Component { + + // + // Listeners + + onAllowedChange = ({ value }) => { + const { + albumTypeId, + onMetadataReleaseStatusItemAllowedChange + } = this.props; + + onMetadataReleaseStatusItemAllowedChange(albumTypeId, value); + } + + // + // Render + + render() { + const { + name, + allowed + } = this.props; + + return ( +
+ +
+ ); + } +} + +ReleaseStatusItem.propTypes = { + albumTypeId: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, + allowed: PropTypes.bool.isRequired, + sortIndex: PropTypes.number.isRequired, + onMetadataReleaseStatusItemAllowedChange: PropTypes.func +}; + +export default ReleaseStatusItem; diff --git a/frontend/src/Settings/Profiles/Metadata/ReleaseStatusItems.js b/frontend/src/Settings/Profiles/Metadata/ReleaseStatusItems.js new file mode 100644 index 000000000..31a24dff3 --- /dev/null +++ b/frontend/src/Settings/Profiles/Metadata/ReleaseStatusItems.js @@ -0,0 +1,87 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; +import FormGroup from 'Components/Form/FormGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import FormInputHelpText from 'Components/Form/FormInputHelpText'; +import ReleaseStatusItem from './ReleaseStatusItem'; +import styles from './TypeItems.css'; + +class ReleaseStatusItems extends Component { + + // + // Render + + render() { + const { + metadataProfileItems, + errors, + warnings, + ...otherProps + } = this.props; + + return ( + + Release Statuses +
+ + { + errors.map((error, index) => { + return ( + + ); + }) + } + + { + warnings.map((warning, index) => { + return ( + + ); + }) + } + +
+ { + metadataProfileItems.map(({ allowed, releaseStatus }, index) => { + return ( + + ); + }) + } +
+
+
+ ); + } +} + +ReleaseStatusItems.propTypes = { + metadataProfileItems: PropTypes.arrayOf(PropTypes.object).isRequired, + errors: PropTypes.arrayOf(PropTypes.object), + warnings: PropTypes.arrayOf(PropTypes.object), + formLabel: PropTypes.string +}; + +ReleaseStatusItems.defaultProps = { + errors: [], + warnings: [] +}; + +export default ReleaseStatusItems; diff --git a/frontend/src/Settings/Profiles/Metadata/SecondaryTypeItems.js b/frontend/src/Settings/Profiles/Metadata/SecondaryTypeItems.js index 0fb407f68..3f46d710a 100644 --- a/frontend/src/Settings/Profiles/Metadata/SecondaryTypeItems.js +++ b/frontend/src/Settings/Profiles/Metadata/SecondaryTypeItems.js @@ -63,7 +63,7 @@ class SecondaryTypeItems extends Component { {...otherProps} /> ); - }).reverse() + }) } diff --git a/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileModule.cs b/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileModule.cs index dffc500f2..fc7774b96 100644 --- a/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileModule.cs +++ b/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileModule.cs @@ -15,6 +15,7 @@ namespace Lidarr.Api.V1.Profiles.Metadata SharedValidator.RuleFor(c => c.Name).NotEmpty(); SharedValidator.RuleFor(c => c.PrimaryAlbumTypes).MustHaveAllowedPrimaryType(); SharedValidator.RuleFor(c => c.SecondaryAlbumTypes).MustHaveAllowedSecondaryType(); + SharedValidator.RuleFor(c => c.ReleaseStatuses).MustHaveAllowedReleaseStatus(); GetResourceAll = GetAll; GetResourceById = GetById; diff --git a/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs b/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs index a7fca5bea..0f6dd8bb4 100644 --- a/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs +++ b/src/Lidarr.Api.V1/Profiles/Metadata/MetadataProfileResource.cs @@ -10,6 +10,7 @@ namespace Lidarr.Api.V1.Profiles.Metadata public string Name { get; set; } public List PrimaryAlbumTypes { get; set; } public List SecondaryAlbumTypes { get; set; } + public List ReleaseStatuses { get; set; } } public class ProfilePrimaryAlbumTypeItemResource : RestResource @@ -24,6 +25,12 @@ namespace Lidarr.Api.V1.Profiles.Metadata public bool Allowed { get; set; } } + public class ProfileReleaseStatusItemResource : RestResource + { + public NzbDrone.Core.Music.ReleaseStatus ReleaseStatus { get; set; } + public bool Allowed { get; set; } + } + public static class MetadataProfileResourceMapper { public static MetadataProfileResource ToResource(this MetadataProfile model) @@ -35,7 +42,8 @@ namespace Lidarr.Api.V1.Profiles.Metadata Id = model.Id, Name = model.Name, PrimaryAlbumTypes = model.PrimaryAlbumTypes.ConvertAll(ToResource), - SecondaryAlbumTypes = model.SecondaryAlbumTypes.ConvertAll(ToResource) + SecondaryAlbumTypes = model.SecondaryAlbumTypes.ConvertAll(ToResource), + ReleaseStatuses = model.ReleaseStatuses.ConvertAll(ToResource) }; } @@ -64,6 +72,20 @@ namespace Lidarr.Api.V1.Profiles.Metadata }; } + public static ProfileReleaseStatusItemResource ToResource(this ProfileReleaseStatusItem model) + { + if (model == null) + { + return null; + } + + return new ProfileReleaseStatusItemResource + { + ReleaseStatus = model.ReleaseStatus, + Allowed = model.Allowed + }; + } + public static MetadataProfile ToModel(this MetadataProfileResource resource) { if (resource == null) @@ -76,7 +98,8 @@ namespace Lidarr.Api.V1.Profiles.Metadata Id = resource.Id, Name = resource.Name, PrimaryAlbumTypes = resource.PrimaryAlbumTypes.ConvertAll(ToModel), - SecondaryAlbumTypes = resource.SecondaryAlbumTypes.ConvertAll(ToModel) + SecondaryAlbumTypes = resource.SecondaryAlbumTypes.ConvertAll(ToModel), + ReleaseStatuses = resource.ReleaseStatuses.ConvertAll(ToModel) }; } @@ -102,7 +125,18 @@ namespace Lidarr.Api.V1.Profiles.Metadata }; } - public static List ToResource(this IEnumerable models) + public static ProfileReleaseStatusItem ToModel(this ProfileReleaseStatusItemResource resource) + { + if (resource == null) return null; + + return new ProfileReleaseStatusItem + { + ReleaseStatus = (NzbDrone.Core.Music.ReleaseStatus)resource.ReleaseStatus.Id, + Allowed = resource.Allowed + }; + } + + public static List ToResource(this IEnumerable models) { return models.Select(ToResource).ToList(); } diff --git a/src/Lidarr.Api.V1/Profiles/Metadata/MetadataValidator.cs b/src/Lidarr.Api.V1/Profiles/Metadata/MetadataValidator.cs index 830a4aeb8..596176fbb 100644 --- a/src/Lidarr.Api.V1/Profiles/Metadata/MetadataValidator.cs +++ b/src/Lidarr.Api.V1/Profiles/Metadata/MetadataValidator.cs @@ -20,6 +20,13 @@ namespace Lidarr.Api.V1.Profiles.Metadata return ruleBuilder.SetValidator(new SecondaryTypeValidator()); } + + public static IRuleBuilderOptions> MustHaveAllowedReleaseStatus(this IRuleBuilder> ruleBuilder) + { + ruleBuilder.SetValidator(new NotEmptyValidator(null)); + + return ruleBuilder.SetValidator(new ReleaseStatusValidator()); + } } @@ -72,4 +79,29 @@ namespace Lidarr.Api.V1.Profiles.Metadata return true; } } + + public class ReleaseStatusValidator : PropertyValidator + { + public ReleaseStatusValidator() + : base("Must have at least one allowed release status") + { + } + + protected override bool IsValid(PropertyValidatorContext context) + { + var list = context.PropertyValue as IList; + + if (list == null) + { + return false; + } + + if (!list.Any(c => c.Allowed)) + { + return false; + } + + return true; + } + } } diff --git a/src/NzbDrone.Core.Test/Profiles/Metadata/MetadataProfileRepositoryFixture.cs b/src/NzbDrone.Core.Test/Profiles/Metadata/MetadataProfileRepositoryFixture.cs index 3294725c1..e5fd56ed9 100644 --- a/src/NzbDrone.Core.Test/Profiles/Metadata/MetadataProfileRepositoryFixture.cs +++ b/src/NzbDrone.Core.Test/Profiles/Metadata/MetadataProfileRepositoryFixture.cs @@ -27,16 +27,22 @@ namespace NzbDrone.Core.Test.Profiles.Metadata Allowed = l == SecondaryAlbumType.Studio }).ToList(), + ReleaseStatuses = ReleaseStatus.All.OrderByDescending(l => l.Name).Select(l => new ProfileReleaseStatusItem + { + ReleaseStatus = l, + Allowed = l == ReleaseStatus.Official + }).ToList(), + Name = "TestProfile" }; Subject.Insert(profile); - StoredModel.Name.Should().Be(profile.Name); StoredModel.PrimaryAlbumTypes.Should().Equal(profile.PrimaryAlbumTypes, (a, b) => a.PrimaryAlbumType == b.PrimaryAlbumType && a.Allowed == b.Allowed); StoredModel.SecondaryAlbumTypes.Should().Equal(profile.SecondaryAlbumTypes, (a, b) => a.SecondaryAlbumType == b.SecondaryAlbumType && a.Allowed == b.Allowed); + StoredModel.ReleaseStatuses.Should().Equal(profile.ReleaseStatuses, (a, b) => a.ReleaseStatus == b.ReleaseStatus && a.Allowed == b.Allowed); } } } diff --git a/src/NzbDrone.Core/Datastore/Converters/ReleaseStatusIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/ReleaseStatusIntConverter.cs new file mode 100644 index 000000000..8d09de185 --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Converters/ReleaseStatusIntConverter.cs @@ -0,0 +1,63 @@ +using Marr.Data.Converters; +using Marr.Data.Mapping; +using Newtonsoft.Json; +using NzbDrone.Core.Music; +using System; + +namespace NzbDrone.Core.Datastore.Converters +{ + public class ReleaseStatusIntConverter : JsonConverter, IConverter + { + public object FromDB(ConverterContext context) + { + if (context.DbValue == DBNull.Value) + { + return ReleaseStatus.Official; + } + + var val = Convert.ToInt32(context.DbValue); + + return (ReleaseStatus) val; + } + + public object FromDB(ColumnMap map, object dbValue) + { + return FromDB(new ConverterContext {ColumnMap = map, DbValue = dbValue}); + } + + public object ToDB(object clrValue) + { + if (clrValue == DBNull.Value) + { + return 0; + } + + if (clrValue as ReleaseStatus == null) + { + throw new InvalidOperationException("Attempted to save a albumtype that isn't really a albumtype"); + } + + var language = (ReleaseStatus) clrValue; + return (int) language; + } + + public Type DbType => typeof(int); + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(ReleaseStatus); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + var item = reader.Value; + return (ReleaseStatus) Convert.ToInt32(item); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(ToDB(value)); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/012_add_release_status.cs b/src/NzbDrone.Core/Datastore/Migration/012_add_release_status.cs new file mode 100644 index 000000000..c586d533c --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/012_add_release_status.cs @@ -0,0 +1,138 @@ +using System.Collections.Generic; +using System.Data; +using System.Linq; +using FluentMigrator; +using Newtonsoft.Json; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(12)] + public class add_release_status : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("MetadataProfiles").AddColumn("ReleaseStatuses").AsString().WithDefaultValue(""); + Execute.WithConnection(ConvertProfile); + } + + private void ConvertProfile(IDbConnection conn, IDbTransaction tran) + { + var updater = new ProfileUpdater11(conn, tran); + updater.AddDefaultReleaseStatus(); + updater.Commit(); + } + } + + public class Profile12 + { + public int Id { get; set; } + public string Name { get; set; } + public List ReleaseStatuses { get; set; } + } + + public class ProfileItem12 + { + public int ReleaseStatus { get; set; } + public bool Allowed { get; set; } + } + + public enum ReleaseStatus12 + { + Official = 0, + Promotional = 1, + Bootleg = 2, + Pseudo = 3 + } + + public class ProfileUpdater11 + { + private readonly IDbConnection _connection; + private readonly IDbTransaction _transaction; + + private List _profiles; + + public ProfileUpdater11(IDbConnection conn, IDbTransaction tran) + { + _connection = conn; + _transaction = tran; + + _profiles = GetProfiles(); + } + + public void Commit() + { + foreach (var profile in _profiles) + { + using (var updateProfileCmd = _connection.CreateCommand()) + { + updateProfileCmd.Transaction = _transaction; + updateProfileCmd.CommandText = + "UPDATE MetadataProfiles SET ReleaseStatuses = ? WHERE Id = ?"; + updateProfileCmd.AddParameter(profile.ReleaseStatuses.ToJson()); + updateProfileCmd.AddParameter(profile.Id); + + updateProfileCmd.ExecuteNonQuery(); + } + } + + _profiles.Clear(); + } + + public void AddDefaultReleaseStatus() + { + foreach (var profile in _profiles) + { + profile.ReleaseStatuses = new List + { + new ProfileItem12 + { + ReleaseStatus = (int)ReleaseStatus12.Official, + Allowed = true + }, + new ProfileItem12 + { + ReleaseStatus = (int)ReleaseStatus12.Promotional, + Allowed = false + }, + new ProfileItem12 + { + ReleaseStatus = (int)ReleaseStatus12.Bootleg, + Allowed = false + }, + new ProfileItem12 + { + ReleaseStatus = (int)ReleaseStatus12.Pseudo, + Allowed = false + } + }; + } + } + + private List GetProfiles() + { + var profiles = new List(); + + using (var getProfilesCmd = _connection.CreateCommand()) + { + getProfilesCmd.Transaction = _transaction; + getProfilesCmd.CommandText = @"SELECT Id, Name FROM MetadataProfiles"; + + using (var profileReader = getProfilesCmd.ExecuteReader()) + { + while (profileReader.Read()) + { + profiles.Add(new Profile12 + { + Id = profileReader.GetInt32(0), + Name = profileReader.GetString(1) + }); + } + } + } + + return profiles; + } + } +} diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 997e2835d..c4ecfed48 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -162,6 +162,7 @@ namespace NzbDrone.Core.Datastore MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new LanguageIntConverter())); MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new PrimaryAlbumTypeIntConverter())); MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new SecondaryAlbumTypeIntConverter())); + MapRepository.Instance.RegisterTypeConverter(typeof(List), new EmbeddedDocumentConverter(new ReleaseStatusIntConverter())); MapRepository.Instance.RegisterTypeConverter(typeof(ParsedAlbumInfo), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(ParsedTrackInfo), new EmbeddedDocumentConverter()); MapRepository.Instance.RegisterTypeConverter(typeof(ReleaseInfo), new EmbeddedDocumentConverter()); diff --git a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs index acfaef35e..f8a15715e 100644 --- a/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs +++ b/src/NzbDrone.Core/MetadataSource/SkyHook/SkyHookProxy.cs @@ -52,16 +52,17 @@ namespace NzbDrone.Core.MetadataSource.SkyHook SetCustomProvider(); - var metadataProfile = _metadataProfileService.Exists(metadataProfileId) ? _metadataProfileService.Get(metadataProfileId) : _metadataProfileService.All().FirstOrDefault(); - + var metadataProfile = _metadataProfileService.Exists(metadataProfileId) ? _metadataProfileService.Get(metadataProfileId) : _metadataProfileService.All().First(); var primaryTypes = metadataProfile.PrimaryAlbumTypes.Where(s => s.Allowed).Select(s => s.PrimaryAlbumType.Name); var secondaryTypes = metadataProfile.SecondaryAlbumTypes.Where(s => s.Allowed).Select(s => s.SecondaryAlbumType.Name); + var releaseStatuses = metadataProfile.ReleaseStatuses.Where(s => s.Allowed).Select(s => s.ReleaseStatus.Name); var httpRequest = _customerRequestBuilder.Create() .SetSegment("route", "artist/" + foreignArtistId) .AddQueryParam("primTypes", string.Join("|", primaryTypes)) .AddQueryParam("secTypes", string.Join("|", secondaryTypes)) + .AddQueryParam("releaseStatuses", string.Join("|", releaseStatuses)) .Build(); httpRequest.AllowAutoRedirect = true; diff --git a/src/NzbDrone.Core/Music/ReleaseStatus.cs b/src/NzbDrone.Core/Music/ReleaseStatus.cs new file mode 100644 index 000000000..bb296fab9 --- /dev/null +++ b/src/NzbDrone.Core/Music/ReleaseStatus.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Core.Datastore; + +namespace NzbDrone.Core.Music +{ + public class ReleaseStatus : IEmbeddedDocument, IEquatable + { + public int Id { get; set; } + public string Name { get; set; } + + public ReleaseStatus() + { + } + + private ReleaseStatus(int id, string name) + { + Id = id; + Name = name; + } + + public override string ToString() + { + return Name; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + public bool Equals(ReleaseStatus other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + if (ReferenceEquals(this, other)) + { + return true; + } + return Id.Equals(other.Id); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + return ReferenceEquals(this, obj) || Equals(obj as ReleaseStatus); + } + + public static bool operator ==(ReleaseStatus left, ReleaseStatus right) + { + return Equals(left, right); + } + + public static bool operator !=(ReleaseStatus left, ReleaseStatus right) + { + return !Equals(left, right); + } + + public static ReleaseStatus Official => new ReleaseStatus(0, "Official"); + public static ReleaseStatus Promotional => new ReleaseStatus(1, "Promotional"); + public static ReleaseStatus Bootleg => new ReleaseStatus(2, "Bootleg"); + public static ReleaseStatus Pseudo => new ReleaseStatus(3, "Pseudo"); + + + public static readonly List All = new List + { + Official, + Promotional, + Bootleg, + Pseudo + }; + + + public static ReleaseStatus FindById(int id) + { + if (id == 0) + { + return Official; + } + + ReleaseStatus albumType = All.FirstOrDefault(v => v.Id == id); + + if (albumType == null) + { + throw new ArgumentException(@"ID does not match a known album type", nameof(id)); + } + + return albumType; + } + + public static explicit operator ReleaseStatus(int id) + { + return FindById(id); + } + + public static explicit operator int(ReleaseStatus albumType) + { + return albumType.Id; + } + + public static explicit operator ReleaseStatus(string type) + { + var releaseStatus = All.FirstOrDefault(v => v.Name.Equals(type, StringComparison.InvariantCultureIgnoreCase)); + + if (releaseStatus == null) + { + throw new ArgumentException(@"Status does not match a known release status", nameof(type)); + } + + return releaseStatus; + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 2ec7c2ca5..ac9306487 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -148,6 +148,7 @@ + @@ -181,6 +182,7 @@ + @@ -811,8 +813,9 @@ - + + @@ -935,6 +938,7 @@ + diff --git a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs index 7b0023cc9..79082f28e 100644 --- a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs +++ b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfile.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.Profiles.Metadata public string Name { get; set; } public List PrimaryAlbumTypes { get; set; } public List SecondaryAlbumTypes { get; set; } + public List ReleaseStatuses { get; set; } } } diff --git a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs index 9ec454adf..0a4f18594 100644 --- a/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs +++ b/src/NzbDrone.Core/Profiles/Metadata/MetadataProfileService.cs @@ -66,23 +66,29 @@ namespace NzbDrone.Core.Profiles.Metadata return _profileRepository.Exists(id); } - private void AddDefaultProfile(string name, List primAllowed, List secAllowed) + private void AddDefaultProfile(string name, List primAllowed, List secAllowed, List relAllowed) { var primaryTypes = PrimaryAlbumType.All - .OrderByDescending(l => l.Name) - .Select(v => new ProfilePrimaryAlbumTypeItem { PrimaryAlbumType = v, Allowed = primAllowed.Contains(v) }) - .ToList(); + .OrderByDescending(l => l.Name) + .Select(v => new ProfilePrimaryAlbumTypeItem {PrimaryAlbumType = v, Allowed = primAllowed.Contains(v)}) + .ToList(); var secondaryTypes = SecondaryAlbumType.All - .OrderByDescending(l => l.Name) - .Select(v => new ProfileSecondaryAlbumTypeItem { SecondaryAlbumType = v, Allowed = secAllowed.Contains(v) }) - .ToList(); + .OrderByDescending(l => l.Name) + .Select(v => new ProfileSecondaryAlbumTypeItem {SecondaryAlbumType = v, Allowed = secAllowed.Contains(v)}) + .ToList(); + + var releaseStatues = ReleaseStatus.All + .OrderByDescending(l => l.Name) + .Select(v => new ProfileReleaseStatusItem {ReleaseStatus = v, Allowed = relAllowed.Contains(v)}) + .ToList(); var profile = new MetadataProfile { Name = name, PrimaryAlbumTypes = primaryTypes, - SecondaryAlbumTypes = secondaryTypes + SecondaryAlbumTypes = secondaryTypes, + ReleaseStatuses = releaseStatues }; Add(profile); @@ -97,7 +103,7 @@ namespace NzbDrone.Core.Profiles.Metadata _logger.Info("Setting up default metadata profile"); - AddDefaultProfile("Standard", new List{PrimaryAlbumType.Album}, new List{ SecondaryAlbumType.Studio }); + AddDefaultProfile("Standard", new List{PrimaryAlbumType.Album}, new List{ SecondaryAlbumType.Studio }, new List{ReleaseStatus.Official}); } } } diff --git a/src/NzbDrone.Core/Profiles/Metadata/ProfileReleaseStatusTypeItem.cs b/src/NzbDrone.Core/Profiles/Metadata/ProfileReleaseStatusTypeItem.cs new file mode 100644 index 000000000..2475c2534 --- /dev/null +++ b/src/NzbDrone.Core/Profiles/Metadata/ProfileReleaseStatusTypeItem.cs @@ -0,0 +1,11 @@ +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Music; + +namespace NzbDrone.Core.Profiles.Metadata +{ + public class ProfileReleaseStatusItem : IEmbeddedDocument + { + public ReleaseStatus ReleaseStatus { get; set; } + public bool Allowed { get; set; } + } +}