Medium Support (Multi-disc Albums), Quality Grouping (#121)
* Multi Disc Stage 1 - Backend Work * Quality Group Functionality * Fixed: Only show wanted album types on ArtistDetail page * Add Media Count Column to ArtistDetail Page * Parser updates for multidisc cases, other usenet release title formats * Search for Tracks by Medium Number in Addition to Title and TrackNumber * Medium Renaming Token for Track Naming * fixup Codacy and Comment Cleanup * fixup remove commentspull/123/head
parent
e1e7cad951
commit
21428cba6f
@ -1,5 +1,7 @@
|
||||
export const EXTRA_SMALL = 'extraSmall';
|
||||
export const SMALL = 'small';
|
||||
export const MEDIUM = 'medium';
|
||||
export const LARGE = 'large';
|
||||
export const EXTRA_LARGE = 'extraLarge';
|
||||
|
||||
export const all = [SMALL, MEDIUM, LARGE];
|
||||
export const all = [EXTRA_SMALL, SMALL, MEDIUM, LARGE, EXTRA_LARGE];
|
||||
|
@ -1,3 +1,18 @@
|
||||
.formGroupsContainer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.formGroupWrapper {
|
||||
flex: 0 0 calc($formGroupSmallWidth - 100px);
|
||||
}
|
||||
|
||||
.deleteButtonContainer {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointLarge) {
|
||||
.formGroupsContainer {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,105 @@
|
||||
.qualityProfileItemGroup {
|
||||
width: 100%;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
background: #fafafa;
|
||||
|
||||
&.editGroups {
|
||||
background: #fcfcfc;
|
||||
}
|
||||
}
|
||||
|
||||
.qualityProfileItemGroupInfo {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.checkInputContainer {
|
||||
composes: checkInputContainer from './QualityProfileItem.css';
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.checkInput {
|
||||
composes: checkInput from './QualityProfileItem.css';
|
||||
}
|
||||
|
||||
.nameInput {
|
||||
composes: text from 'Components/Form/TextInput.css';
|
||||
|
||||
margin-top: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.nameContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex-shrink: 0;
|
||||
|
||||
&.notAllowed {
|
||||
color: #c6c6c6;
|
||||
}
|
||||
}
|
||||
|
||||
.groupQualities {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
flex-wrap: wrap;
|
||||
margin: 2px 0 2px 10px;
|
||||
}
|
||||
|
||||
.qualityNameContainer {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-grow: 1;
|
||||
margin-bottom: 0;
|
||||
margin-left: 2px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.qualityNameLabel {
|
||||
composes: qualityNameContainer;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.deleteGroupButton {
|
||||
composes: buton from 'Components/Link/IconButton.css';
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-right: 5px;
|
||||
margin-left: 8px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.dragHandle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
width: $dragHandleWidth;
|
||||
text-align: center;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.dragIcon {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.isDragging {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.items {
|
||||
margin: 0 50px 0 35px;
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import Icon from 'Components/Icon';
|
||||
import Label from 'Components/Label';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import CheckInput from 'Components/Form/CheckInput';
|
||||
import TextInput from 'Components/Form/TextInput';
|
||||
import QualityProfileItemDragSource from './QualityProfileItemDragSource';
|
||||
import styles from './QualityProfileItemGroup.css';
|
||||
|
||||
class QualityProfileItemGroup extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onAllowedChange = ({ value }) => {
|
||||
const {
|
||||
groupId,
|
||||
onItemGroupAllowedChange
|
||||
} = this.props;
|
||||
|
||||
onItemGroupAllowedChange(groupId, value);
|
||||
}
|
||||
|
||||
onNameChange = ({ value }) => {
|
||||
const {
|
||||
groupId,
|
||||
onItemGroupNameChange
|
||||
} = this.props;
|
||||
|
||||
onItemGroupNameChange(groupId, value);
|
||||
}
|
||||
|
||||
onDeleteGroupPress = ({ value }) => {
|
||||
const {
|
||||
groupId,
|
||||
onDeleteGroupPress
|
||||
} = this.props;
|
||||
|
||||
onDeleteGroupPress(groupId, value);
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
editGroups,
|
||||
groupId,
|
||||
name,
|
||||
allowed,
|
||||
items,
|
||||
qualityIndex,
|
||||
isDragging,
|
||||
isDraggingUp,
|
||||
isDraggingDown,
|
||||
connectDragSource,
|
||||
onQualityProfileItemAllowedChange,
|
||||
onQualityProfileItemDragMove,
|
||||
onQualityProfileItemDragEnd
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
styles.qualityProfileItemGroup,
|
||||
editGroups && styles.editGroups,
|
||||
isDragging && styles.isDragging,
|
||||
)}
|
||||
>
|
||||
<div className={styles.qualityProfileItemGroupInfo}>
|
||||
{
|
||||
editGroups &&
|
||||
<div className={styles.qualityNameContainer}>
|
||||
<IconButton
|
||||
className={styles.deleteGroupButton}
|
||||
name={icons.UNGROUP}
|
||||
title="Ungroup"
|
||||
onPress={this.onDeleteGroupPress}
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
className={styles.nameInput}
|
||||
name="name"
|
||||
value={name}
|
||||
onChange={this.onNameChange}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!editGroups &&
|
||||
<label
|
||||
className={styles.qualityNameLabel}
|
||||
>
|
||||
<CheckInput
|
||||
className={styles.checkInput}
|
||||
containerClassName={styles.checkInputContainer}
|
||||
name="allowed"
|
||||
value={allowed}
|
||||
onChange={this.onAllowedChange}
|
||||
/>
|
||||
|
||||
<div className={styles.nameContainer}>
|
||||
<div className={classNames(
|
||||
styles.name,
|
||||
!allowed && styles.notAllowed
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
|
||||
<div className={styles.groupQualities}>
|
||||
{
|
||||
items.map(({ quality }) => {
|
||||
return (
|
||||
<Label key={quality.id}>
|
||||
{quality.name}
|
||||
</Label>
|
||||
);
|
||||
}).reverse()
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
}
|
||||
|
||||
{
|
||||
connectDragSource(
|
||||
<div className={styles.dragHandle}>
|
||||
<Icon
|
||||
className={styles.dragIcon}
|
||||
name={icons.REORDER}
|
||||
title="Reorder"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
editGroups &&
|
||||
<div className={styles.items}>
|
||||
{
|
||||
items.map(({ quality }, index) => {
|
||||
return (
|
||||
<QualityProfileItemDragSource
|
||||
key={quality.id}
|
||||
editGroups={editGroups}
|
||||
groupId={groupId}
|
||||
qualityId={quality.id}
|
||||
name={quality.name}
|
||||
allowed={allowed}
|
||||
items={items}
|
||||
qualityIndex={`${qualityIndex}.${index + 1}`}
|
||||
isDragging={isDragging}
|
||||
isDraggingUp={isDraggingUp}
|
||||
isDraggingDown={isDraggingDown}
|
||||
isInGroup={true}
|
||||
onQualityProfileItemAllowedChange={onQualityProfileItemAllowedChange}
|
||||
onQualityProfileItemDragMove={onQualityProfileItemDragMove}
|
||||
onQualityProfileItemDragEnd={onQualityProfileItemDragEnd}
|
||||
/>
|
||||
);
|
||||
}).reverse()
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QualityProfileItemGroup.propTypes = {
|
||||
editGroups: PropTypes.bool,
|
||||
groupId: PropTypes.number.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
allowed: PropTypes.bool.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
qualityIndex: PropTypes.string.isRequired,
|
||||
isDragging: PropTypes.bool.isRequired,
|
||||
isDraggingUp: PropTypes.bool.isRequired,
|
||||
isDraggingDown: PropTypes.bool.isRequired,
|
||||
connectDragSource: PropTypes.func,
|
||||
onItemGroupAllowedChange: PropTypes.func.isRequired,
|
||||
onQualityProfileItemAllowedChange: PropTypes.func.isRequired,
|
||||
onItemGroupNameChange: PropTypes.func.isRequired,
|
||||
onDeleteGroupPress: PropTypes.func.isRequired,
|
||||
onQualityProfileItemDragMove: PropTypes.func.isRequired,
|
||||
onQualityProfileItemDragEnd: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
QualityProfileItemGroup.defaultProps = {
|
||||
// The drag preview will not connect the drag handle.
|
||||
connectDragSource: (node) => node
|
||||
};
|
||||
|
||||
export default QualityProfileItemGroup;
|
@ -1,6 +1,15 @@
|
||||
.editGroupsButton {
|
||||
composes: button from 'Components/Link/Button.css';
|
||||
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.editGroupsButtonIcon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.qualities {
|
||||
margin-top: 10px;
|
||||
/* TODO: This should consider the number of qualities in the list */
|
||||
min-height: 550px;
|
||||
transition: min-height 200ms;
|
||||
user-select: none;
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
export default function getQualities(qualities) {
|
||||
if (!qualities) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return qualities.reduce((acc, item) => {
|
||||
if (item.quality) {
|
||||
acc.push(item.quality);
|
||||
} else {
|
||||
const groupQualities = item.items.map((i) => i.quality);
|
||||
acc.push(...groupQualities);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
@ -1,32 +1,30 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
export default function createAjaxRequest() {
|
||||
return function(ajaxOptions) {
|
||||
const requestXHR = new window.XMLHttpRequest();
|
||||
let aborted = false;
|
||||
let complete = false;
|
||||
export default function createAjaxRequest(ajaxOptions) {
|
||||
const requestXHR = new window.XMLHttpRequest();
|
||||
let aborted = false;
|
||||
let complete = false;
|
||||
|
||||
function abortRequest() {
|
||||
if (!complete) {
|
||||
aborted = true;
|
||||
requestXHR.abort();
|
||||
}
|
||||
function abortRequest() {
|
||||
if (!complete) {
|
||||
aborted = true;
|
||||
requestXHR.abort();
|
||||
}
|
||||
}
|
||||
|
||||
const request = $.ajax({
|
||||
xhr: () => requestXHR,
|
||||
...ajaxOptions
|
||||
}).then(null, (xhr, textStatus, errorThrown) => {
|
||||
xhr.aborted = aborted;
|
||||
const request = $.ajax({
|
||||
xhr: () => requestXHR,
|
||||
...ajaxOptions
|
||||
}).then(null, (xhr, textStatus, errorThrown) => {
|
||||
xhr.aborted = aborted;
|
||||
|
||||
return $.Deferred().reject(xhr, textStatus, errorThrown).promise();
|
||||
}).always(() => {
|
||||
complete = true;
|
||||
});
|
||||
return $.Deferred().reject(xhr, textStatus, errorThrown).promise();
|
||||
}).always(() => {
|
||||
complete = true;
|
||||
});
|
||||
|
||||
return {
|
||||
request,
|
||||
abortRequest
|
||||
};
|
||||
return {
|
||||
request,
|
||||
abortRequest
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace Lidarr.Api.V1.Albums
|
||||
{
|
||||
public class MediumResource
|
||||
{
|
||||
public int MediumNumber { get; set; }
|
||||
public string MediumName { get; set; }
|
||||
public string MediumFormat { get; set; }
|
||||
}
|
||||
|
||||
public static class SeasonResourceMapper
|
||||
{
|
||||
public static MediumResource ToResource(this Medium model)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MediumResource
|
||||
{
|
||||
MediumNumber = model.Number,
|
||||
MediumName = model.Name,
|
||||
MediumFormat = model.Format
|
||||
};
|
||||
}
|
||||
|
||||
public static Medium ToModel(this MediumResource resource)
|
||||
{
|
||||
if (resource == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Medium
|
||||
{
|
||||
Number = resource.MediumNumber,
|
||||
Name = resource.MediumName,
|
||||
Format = resource.MediumFormat
|
||||
};
|
||||
}
|
||||
|
||||
public static List<MediumResource> ToResource(this IEnumerable<Medium> models)
|
||||
{
|
||||
return models.Select(ToResource).ToList();
|
||||
}
|
||||
|
||||
public static List<Medium> ToModel(this IEnumerable<MediumResource> resources)
|
||||
{
|
||||
return resources.Select(ToModel).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Validators;
|
||||
|
||||
namespace Lidarr.Api.V1.Profiles.Quality
|
||||
{
|
||||
public static class QualityCutoffValidator
|
||||
{
|
||||
public static IRuleBuilderOptions<T, int> ValidCutoff<T>(this IRuleBuilder<T, int> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.SetValidator(new ValidCutoffValidator<T>());
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidCutoffValidator<T> : PropertyValidator
|
||||
{
|
||||
public ValidCutoffValidator()
|
||||
: base("Cutoff must be an allowed quality or group")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var cutoff = (int)context.PropertyValue;
|
||||
dynamic instance = context.ParentContext.InstanceToValidate;
|
||||
var items = instance.Items as IList<QualityProfileQualityItemResource>;
|
||||
|
||||
var cutoffItem = items.SingleOrDefault(i => i.Id == cutoff || (i.Quality != null && i.Quality.Id == cutoff));
|
||||
|
||||
if (cutoffItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cutoffItem.Allowed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Validators;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace Lidarr.Api.V1.Profiles.Quality
|
||||
{
|
||||
public static class QualityItemsValidator
|
||||
{
|
||||
public static IRuleBuilderOptions<T, IList<QualityProfileQualityItemResource>> ValidItems<T>(this IRuleBuilder<T, IList<QualityProfileQualityItemResource>> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
ruleBuilder.SetValidator(new AllowedValidator<T>());
|
||||
ruleBuilder.SetValidator(new QualityNameValidator<T>());
|
||||
ruleBuilder.SetValidator(new EmptyItemGroupNameValidator<T>());
|
||||
ruleBuilder.SetValidator(new ItemGroupIdValidator<T>());
|
||||
ruleBuilder.SetValidator(new UniqueIdValidator<T>());
|
||||
ruleBuilder.SetValidator(new UniqueQualityIdValidator<T>());
|
||||
return ruleBuilder.SetValidator(new ItemGroupNameValidator<T>());
|
||||
}
|
||||
}
|
||||
|
||||
public class AllowedValidator<T> : PropertyValidator
|
||||
{
|
||||
public AllowedValidator()
|
||||
: base("Must contain at least one allowed quality")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var list = context.PropertyValue as IList<QualityProfileQualityItemResource>;
|
||||
|
||||
if (list == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!list.Any(c => c.Allowed))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class EmptyItemGroupNameValidator<T> : PropertyValidator
|
||||
{
|
||||
public EmptyItemGroupNameValidator()
|
||||
: base("Groups must not be empty")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
|
||||
|
||||
if (items.Any(i => i.Name.IsNotNullOrWhiteSpace() && i.Items.Empty()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class QualityNameValidator<T> : PropertyValidator
|
||||
{
|
||||
public QualityNameValidator()
|
||||
: base("Individual qualities should not be named")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
|
||||
|
||||
if (items.Any(i => i.Name.IsNotNullOrWhiteSpace() && i.Quality != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemGroupNameValidator<T> : PropertyValidator
|
||||
{
|
||||
public ItemGroupNameValidator()
|
||||
: base("Groups must have a name")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
|
||||
|
||||
if (items.Any(i => i.Quality == null && i.Name.IsNullOrWhiteSpace()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemGroupIdValidator<T> : PropertyValidator
|
||||
{
|
||||
public ItemGroupIdValidator()
|
||||
: base("Groups must have an ID")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
|
||||
|
||||
if (items.Any(i => i.Quality == null && i.Id == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class UniqueIdValidator<T> : PropertyValidator
|
||||
{
|
||||
public UniqueIdValidator()
|
||||
: base("Groups must have a unique ID")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
|
||||
|
||||
if (items.Where(i => i.Id > 0).Select(i => i.Id).GroupBy(i => i).Any(g => g.Count() > 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class UniqueQualityIdValidator<T> : PropertyValidator
|
||||
{
|
||||
public UniqueQualityIdValidator()
|
||||
: base("Qualities can only be used once")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
|
||||
var qualityIds = new HashSet<int>();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Id > 0)
|
||||
{
|
||||
foreach (var quality in item.Items)
|
||||
{
|
||||
if (qualityIds.Contains(quality.Quality.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qualityIds.Add(quality.Quality.Id);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (qualityIds.Contains(item.Quality.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qualityIds.Add(item.Quality.Id);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.Profiles.Languages;
|
||||
|
||||
namespace NzbDrone.Core.Test.MusicTests.ArtistRepositoryTests
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class ArtistRepositoryFixture : DbTest<ArtistRepository, Artist>
|
||||
{
|
||||
[Test]
|
||||
public void should_lazyload_quality_profile()
|
||||
{
|
||||
var profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.FLAC, Quality.MP3_192, Quality.MP3_320),
|
||||
|
||||
Cutoff = Quality.FLAC.Id,
|
||||
Name = "TestProfile"
|
||||
};
|
||||
|
||||
var langProfile = new LanguageProfile
|
||||
{
|
||||
Name = "TestProfile",
|
||||
Languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English),
|
||||
Cutoff = Language.English
|
||||
};
|
||||
|
||||
|
||||
Mocker.Resolve<ProfileRepository>().Insert(profile);
|
||||
Mocker.Resolve<LanguageProfileRepository>().Insert(langProfile);
|
||||
|
||||
var series = Builder<Artist>.CreateNew().BuildNew();
|
||||
series.ProfileId = profile.Id;
|
||||
series.LanguageProfileId = langProfile.Id;
|
||||
|
||||
Subject.Insert(series);
|
||||
|
||||
|
||||
StoredModel.Profile.Should().NotBeNull();
|
||||
StoredModel.LanguageProfile.Should().NotBeNull();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue