mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-04-24 05:47:22 -04:00
New: Calculate custom formats on demand
This commit is contained in:
parent
13701498ce
commit
df101258c5
103 changed files with 1901 additions and 1346 deletions
|
@ -331,10 +331,10 @@ stages:
|
|||
artifactName: '$(testName)Tests'
|
||||
targetPath: $(testsFolder)
|
||||
- bash: |
|
||||
wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-9_all.deb
|
||||
sudo dpkg -i repo-mediaarea_1.0-9_all.deb
|
||||
wget https://mediaarea.net/repo/deb/repo-mediaarea_1.0-11_all.deb
|
||||
sudo dpkg -i repo-mediaarea_1.0-11_all.deb
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libmediainfo-dev libmediainfo0v5 mediainfo
|
||||
sudo apt-get install -y --allow-unauthenticated libmediainfo-dev libmediainfo0v5 mediainfo
|
||||
displayName: Install mediainfo
|
||||
condition: and(succeeded(), eq(variables['osName'], 'Linux'))
|
||||
- powershell: Set-Service SCardSvr -StartupType Manual
|
||||
|
|
|
@ -44,6 +44,7 @@ class BlacklistRow extends Component {
|
|||
movie,
|
||||
sourceTitle,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
date,
|
||||
protocol,
|
||||
|
@ -112,11 +113,11 @@ class BlacklistRow extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (name === 'quality.customFormats') {
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieFormats
|
||||
formats={quality.customFormats}
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
|
@ -186,6 +187,7 @@ BlacklistRow.propTypes = {
|
|||
movie: PropTypes.object.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
|
|
|
@ -54,6 +54,7 @@ class HistoryRow extends Component {
|
|||
const {
|
||||
movie,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
qualityCutoffNotMet,
|
||||
eventType,
|
||||
|
@ -126,11 +127,11 @@ class HistoryRow extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (name === 'quality.customFormats') {
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieFormats
|
||||
formats={quality.customFormats}
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
|
@ -219,6 +220,7 @@ HistoryRow.propTypes = {
|
|||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
eventType: PropTypes.string.isRequired,
|
||||
sourceTitle: PropTypes.string.isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
|
|
|
@ -72,6 +72,7 @@ class QueueRow extends Component {
|
|||
errorMessage,
|
||||
movie,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
protocol,
|
||||
indexer,
|
||||
|
@ -169,11 +170,11 @@ class QueueRow extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (name === 'quality.customFormats') {
|
||||
if (name === 'customFormats') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
<MovieFormats
|
||||
formats={quality.customFormats}
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
);
|
||||
|
@ -329,6 +330,7 @@ QueueRow.propTypes = {
|
|||
errorMessage: PropTypes.string,
|
||||
movie: PropTypes.object,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object),
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
protocol: PropTypes.string.isRequired,
|
||||
indexer: PropTypes.string,
|
||||
|
|
|
@ -113,6 +113,7 @@ class InteractiveSearchRow extends Component {
|
|||
seeders,
|
||||
leechers,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
indexerFlags,
|
||||
rejections,
|
||||
|
@ -177,7 +178,7 @@ class InteractiveSearchRow extends Component {
|
|||
|
||||
<TableRowCell className={styles.customFormat}>
|
||||
<MovieFormats
|
||||
formats={quality.customFormats}
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
|
||||
|
@ -279,6 +280,7 @@ InteractiveSearchRow.propTypes = {
|
|||
seeders: PropTypes.number,
|
||||
leechers: PropTypes.number,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
rejections: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
indexerFlags: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
|
|
|
@ -9,6 +9,7 @@ import TableRow from 'Components/Table/TableRow';
|
|||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import Popover from 'Components/Tooltip/Popover';
|
||||
import MovieQuality from 'Movie/MovieQuality';
|
||||
import MovieFormats from 'Movie/MovieFormats';
|
||||
import MovieLanguage from 'Movie/MovieLanguage';
|
||||
import HistoryDetailsConnector from 'Activity/History/Details/HistoryDetailsConnector';
|
||||
import HistoryEventTypeCell from 'Activity/History/HistoryEventTypeCell';
|
||||
|
@ -64,6 +65,7 @@ class MovieHistoryRow extends Component {
|
|||
eventType,
|
||||
sourceTitle,
|
||||
quality,
|
||||
customFormats,
|
||||
languages,
|
||||
qualityCutoffNotMet,
|
||||
date,
|
||||
|
@ -98,6 +100,12 @@ class MovieHistoryRow extends Component {
|
|||
/>
|
||||
</TableRowCell>
|
||||
|
||||
<TableRowCell key={name}>
|
||||
<MovieFormats
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
|
||||
<RelativeDateCellConnector
|
||||
date={date}
|
||||
/>
|
||||
|
@ -152,6 +160,7 @@ MovieHistoryRow.propTypes = {
|
|||
sourceTitle: PropTypes.string.isRequired,
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||
date: PropTypes.string.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
|
|
|
@ -26,6 +26,12 @@ const columns = [
|
|||
label: 'Quality',
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'customFormats',
|
||||
label: 'Custom Formats',
|
||||
isSortable: false,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'date',
|
||||
label: 'Date',
|
||||
|
|
|
@ -86,6 +86,7 @@ class MovieFileEditorRow extends Component {
|
|||
size,
|
||||
quality,
|
||||
qualityCutoffNotMet,
|
||||
customFormats,
|
||||
languages
|
||||
} = this.props;
|
||||
|
||||
|
@ -173,7 +174,7 @@ class MovieFileEditorRow extends Component {
|
|||
className={styles.formats}
|
||||
>
|
||||
<MovieFormats
|
||||
formats={quality.customFormats}
|
||||
formats={customFormats}
|
||||
/>
|
||||
</TableRowCell>
|
||||
|
||||
|
@ -233,6 +234,7 @@ MovieFileEditorRow.propTypes = {
|
|||
size: PropTypes.number.isRequired,
|
||||
relativePath: PropTypes.string.isRequired,
|
||||
quality: PropTypes.object.isRequired,
|
||||
customFormats: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
qualityCutoffNotMet: PropTypes.bool.isRequired,
|
||||
languages: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
mediaInfo: PropTypes.object.isRequired,
|
||||
|
|
|
@ -123,9 +123,6 @@ class CustomFormat extends Component {
|
|||
<div>
|
||||
Are you sure you want to delete custom format '{name}'?
|
||||
</div>
|
||||
<div>
|
||||
This will remove all associations to this format in the DB. This may result in existing files being updated.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
confirmLabel="Delete"
|
||||
|
|
|
@ -16,6 +16,13 @@ const blacklistedProperties = [
|
|||
'id'
|
||||
];
|
||||
|
||||
function createItemMap(data) {
|
||||
return data.reduce((acc, d, index, array) => {
|
||||
acc[d.id] = index;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export default function createHandleActions(handlers, defaultState, section) {
|
||||
return handleActions({
|
||||
|
||||
|
@ -42,7 +49,7 @@ export default function createHandleActions(handlers, defaultState, section) {
|
|||
|
||||
if (_.isArray(payload.data)) {
|
||||
newState.items = payload.data;
|
||||
newState.itemMap = _.zipObject(_.map(payload.data, 'id'), _.range(payload.data.length));
|
||||
newState.itemMap = createItemMap(newState.items);
|
||||
} else {
|
||||
newState.item = payload.data;
|
||||
}
|
||||
|
@ -67,7 +74,7 @@ export default function createHandleActions(handlers, defaultState, section) {
|
|||
const items = newState.items;
|
||||
|
||||
if (!newState.itemMap) {
|
||||
newState.itemMap = _.zipObject(_.map(items, 'id'), _.range(items.length));
|
||||
newState.itemMap = createItemMap(items);
|
||||
}
|
||||
|
||||
const index = payload.id in newState.itemMap ? newState.itemMap[payload.id] : -1;
|
||||
|
@ -126,7 +133,7 @@ export default function createHandleActions(handlers, defaultState, section) {
|
|||
newState.items = [...newState.items];
|
||||
_.remove(newState.items, { id: payload.id });
|
||||
|
||||
newState.itemMap = _.zipObject(_.map(newState.items, 'id'), _.range(newState.items.length));
|
||||
newState.itemMap = createItemMap(newState.items);
|
||||
|
||||
return updateSectionState(state, payloadSection, newState);
|
||||
}
|
||||
|
|
|
@ -51,9 +51,9 @@ export const defaultState = {
|
|||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'quality.customFormats',
|
||||
label: 'Custom Formats',
|
||||
isSortable: true,
|
||||
name: 'customFormats',
|
||||
label: 'Formats',
|
||||
isSortable: false,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -52,9 +52,9 @@ export const defaultState = {
|
|||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'quality.customFormats',
|
||||
label: 'Custom Formats',
|
||||
isSortable: true,
|
||||
name: 'customFormats',
|
||||
label: 'Formats',
|
||||
isSortable: false,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -81,9 +81,9 @@ export const defaultState = {
|
|||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'quality.customFormats',
|
||||
label: 'Custom Formats',
|
||||
isSortable: true,
|
||||
name: 'customFormats',
|
||||
label: 'Formats',
|
||||
isSortable: false,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace NzbDrone.Api.History
|
|||
|
||||
if (model.Movie != null)
|
||||
{
|
||||
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.CutoffNotMet(model.Movie.Profile, model.Quality);
|
||||
resource.QualityCutoffNotMet = _qualityUpgradableSpecification.QualityCutoffNotMet(model.Movie.Profile, model.Quality);
|
||||
}
|
||||
|
||||
return resource;
|
||||
|
|
|
@ -11,11 +11,15 @@ namespace NzbDrone.Api.Qualities
|
|||
public class CustomFormatModule : RadarrRestModule<CustomFormatResource>
|
||||
{
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly IParsingService _parsingService;
|
||||
|
||||
public CustomFormatModule(ICustomFormatService formatService, IParsingService parsingService)
|
||||
public CustomFormatModule(ICustomFormatService formatService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IParsingService parsingService)
|
||||
{
|
||||
_formatService = formatService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_parsingService = parsingService;
|
||||
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
|
@ -103,8 +107,8 @@ namespace NzbDrone.Api.Qualities
|
|||
|
||||
return new CustomFormatTestResource
|
||||
{
|
||||
Matches = _parsingService.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = parsed.Quality.CustomFormats.ToResource()
|
||||
Matches = _formatCalculator.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = _formatCalculator.ParseCustomFormat(parsed).ToResource()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -125,8 +129,8 @@ namespace NzbDrone.Api.Qualities
|
|||
|
||||
return new CustomFormatTestResource
|
||||
{
|
||||
Matches = _parsingService.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = parsed.Quality.CustomFormats.ToResource()
|
||||
Matches = _formatCalculator.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = _formatCalculator.ParseCustomFormat(parsed).ToResource()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace NzbDrone.Api.Qualities
|
|||
|
||||
public static class QualityTagMatchResultResourceMapper
|
||||
{
|
||||
public static FormatTagMatchResultResource ToResource(this FormatTagMatchResult model)
|
||||
public static FormatTagMatchResultResource ToResource(this CustomFormatMatchResult model)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ namespace NzbDrone.Api.Qualities
|
|||
};
|
||||
}
|
||||
|
||||
public static List<FormatTagMatchResultResource> ToResource(this IList<FormatTagMatchResult> models)
|
||||
public static List<FormatTagMatchResultResource> ToResource(this IList<CustomFormatMatchResult> models)
|
||||
{
|
||||
return models.Select(ToResource).ToList();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
@ -22,11 +20,6 @@ namespace NzbDrone.Common.Test
|
|||
container.Register<IMainDatabase>(new MainDatabase(null));
|
||||
container.Resolve<IAppFolderFactory>().Register();
|
||||
|
||||
// A dummy custom format repository since this isn't a DB test
|
||||
var mockCustomFormat = Mocker.GetMock<ICustomFormatRepository>();
|
||||
mockCustomFormat.Setup(x => x.All()).Returns(new List<CustomFormatDefinition>());
|
||||
container.Register<ICustomFormatRepository>(mockCustomFormat.Object);
|
||||
|
||||
Mocker.SetConstant(container);
|
||||
|
||||
var handlers = Subject.BuildAll<IHandle<ApplicationStartedEvent>>()
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Qualities
|
||||
{
|
||||
[TestFixture]
|
||||
public class CustomFormatsComparerFixture : CoreTest
|
||||
{
|
||||
private CustomFormat _customFormat1;
|
||||
private CustomFormat _customFormat2;
|
||||
private CustomFormat _customFormat3;
|
||||
private CustomFormat _customFormat4;
|
||||
|
||||
public CustomFormatsComparer Subject { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
private void GivenDefaultProfileWithFormats()
|
||||
{
|
||||
_customFormat1 = new CustomFormat("My Format 1", "L_ENGLISH") { Id = 1 };
|
||||
_customFormat2 = new CustomFormat("My Format 2", "L_FRENCH") { Id = 2 };
|
||||
_customFormat3 = new CustomFormat("My Format 3", "L_SPANISH") { Id = 3 };
|
||||
_customFormat4 = new CustomFormat("My Format 4", "L_ITALIAN") { Id = 4 };
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(CustomFormat.None, _customFormat1, _customFormat2, _customFormat3, _customFormat4);
|
||||
|
||||
Subject = new CustomFormatsComparer(new Profile { Items = QualityFixture.GetDefaultQualities(), FormatItems = CustomFormatsFixture.GetSampleFormatItems() });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_lesser_when_first_format_is_worse()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat1 };
|
||||
var second = new List<CustomFormat> { _customFormat2 };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeLessThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_zero_when_formats_are_equal()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat2 };
|
||||
var second = new List<CustomFormat> { _customFormat2 };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().Be(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_first_format_is_better()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat3 };
|
||||
var second = new List<CustomFormat> { _customFormat2 };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_multiple_formats_better()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat3, _customFormat4 };
|
||||
var second = new List<CustomFormat> { _customFormat2 };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_best_format_is_better()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat1, _customFormat3 };
|
||||
var second = new List<CustomFormat> { _customFormat2 };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_best_format_equal_but_more_lower_formats()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat1, _customFormat2 };
|
||||
var second = new List<CustomFormat> { _customFormat2 };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_greater_when_best_format_worse_but_more_lower_formats()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat1, _customFormat2, _customFormat3 };
|
||||
var second = new List<CustomFormat> { _customFormat4 };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeLessThan(0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using FluentAssertions;
|
|||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.CustomFormats
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Migration;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Migration
|
||||
{
|
||||
[TestFixture]
|
||||
public class remove_custom_formats_from_quality_modelFixture : MigrationTest<remove_custom_formats_from_quality_model>
|
||||
{
|
||||
[Test]
|
||||
public void should_remove_custom_format_from_pending_releases()
|
||||
{
|
||||
var db = WithDapperMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("PendingReleases").Row(new
|
||||
{
|
||||
MovieId = 1,
|
||||
Title = "Test Movie",
|
||||
Added = DateTime.UtcNow,
|
||||
ParsedMovieInfo = @"{
|
||||
""movieTitle"": ""Skyfall"",
|
||||
""simpleReleaseTitle"": ""A Movie (2012) \u002B Extras (1080p BluRay x265 HEVC 10bit DTS 5.1 SAMPA) [QxR]"",
|
||||
""quality"": {
|
||||
""quality"": {
|
||||
""id"": 7,
|
||||
""name"": ""Bluray-1080p"",
|
||||
""source"": ""bluray"",
|
||||
""resolution"": 1080,
|
||||
""modifier"": ""none""
|
||||
},
|
||||
""customFormats"": [
|
||||
{
|
||||
""name"": ""Standard High Def Surround Sound Movie"",
|
||||
""formatTags"": [
|
||||
{
|
||||
""raw"": ""R_1080"",
|
||||
""tagType"": ""resolution"",
|
||||
""tagModifier"": 0,
|
||||
""value"": ""r1080p""
|
||||
},
|
||||
{
|
||||
""raw"": ""L_English"",
|
||||
""tagType"": ""language"",
|
||||
""tagModifier"": 0,
|
||||
""value"": {
|
||||
""id"": 1,
|
||||
""name"": ""English""
|
||||
}
|
||||
},
|
||||
{
|
||||
""raw"": ""C_DTS"",
|
||||
""tagType"": ""custom"",
|
||||
""tagModifier"": 0,
|
||||
""value"": ""dts""
|
||||
}
|
||||
],
|
||||
""id"": 1
|
||||
}
|
||||
],
|
||||
""revision"": {
|
||||
""version"": 1,
|
||||
""real"": 0,
|
||||
""isRepack"": false
|
||||
},
|
||||
""hardcodedSubs"": null,
|
||||
""qualityDetectionSource"": ""name""
|
||||
},
|
||||
""releaseGroup"": ""QxR"",
|
||||
""releaseHash"": """",
|
||||
""edition"": """",
|
||||
""year"": 2012,
|
||||
""imdbId"": """"
|
||||
}",
|
||||
Release = "{}",
|
||||
Reason = PendingReleaseReason.Delay
|
||||
});
|
||||
});
|
||||
|
||||
var json = db.Query<string>("SELECT ParsedMovieInfo FROM PendingReleases").First();
|
||||
json.Should().NotContain("customFormats");
|
||||
|
||||
var pending = db.Query<ParsedMovieInfo>("SELECT ParsedMovieInfo FROM PendingReleases").First();
|
||||
pending.Quality.Quality.Should().Be(Quality.Bluray1080p);
|
||||
pending.Languages.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fix_quality_for_pending_releases()
|
||||
{
|
||||
var db = WithDapperMigrationTestDb(c =>
|
||||
{
|
||||
c.Insert.IntoTable("PendingReleases").Row(new
|
||||
{
|
||||
MovieId = 1,
|
||||
Title = "Test Movie",
|
||||
Added = DateTime.UtcNow,
|
||||
ParsedMovieInfo = @"{
|
||||
""languages"": [
|
||||
""english""
|
||||
],
|
||||
""movieTitle"": ""Joy"",
|
||||
""simpleReleaseTitle"": ""A Movie.2015.1080p.BluRay.AVC.DTS-HD.MA.5.1-RARBG [f"",
|
||||
""quality"": {
|
||||
""quality"": {
|
||||
""id"": 7,
|
||||
""name"": ""Bluray-1080p"",
|
||||
""source"": ""bluray"",
|
||||
""resolution"": ""r1080P"",
|
||||
""modifier"": ""none""
|
||||
},
|
||||
""customFormats"": [],
|
||||
""revision"": {
|
||||
""version"": 1,
|
||||
""real"": 0
|
||||
}
|
||||
},
|
||||
""releaseGroup"": ""RARBG"",
|
||||
""edition"": """",
|
||||
""year"": 2015,
|
||||
""imdbId"": """"
|
||||
}",
|
||||
Release = "{}",
|
||||
Reason = PendingReleaseReason.Delay
|
||||
});
|
||||
});
|
||||
|
||||
var json = db.Query<string>("SELECT ParsedMovieInfo FROM PendingReleases").First();
|
||||
json.Should().NotContain("customFormats");
|
||||
json.Should().NotContain("resolution");
|
||||
|
||||
var pending = db.Query<ParsedMovieInfo>("SELECT ParsedMovieInfo FROM PendingReleases").First();
|
||||
pending.Quality.Quality.Should().Be(Quality.Bluray1080p);
|
||||
pending.Languages.Should().BeEquivalentTo(new List<Language> { Language.English });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_allow_if_format_is_defined_in_profile()
|
||||
{
|
||||
_remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormat> { _format1 };
|
||||
_remoteMovie.CustomFormats = new List<CustomFormat> { _format1 };
|
||||
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
|
||||
|
@ -56,7 +56,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_deny_if_format_is_defined_in_profile()
|
||||
{
|
||||
_remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormat> { _format2 };
|
||||
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2 };
|
||||
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
|
@ -65,7 +65,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_deny_if_one_format_is_defined_in_profile()
|
||||
{
|
||||
_remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
||||
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
||||
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
|
@ -74,7 +74,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_allow_if_all_format_is_defined_in_profile()
|
||||
{
|
||||
_remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
||||
_remoteMovie.CustomFormats = new List<CustomFormat> { _format2, _format1 };
|
||||
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
|
||||
|
@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_deny_if_no_format_was_parsed_and_none_not_in_profile()
|
||||
{
|
||||
_remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormat> { };
|
||||
_remoteMovie.CustomFormats = new List<CustomFormat> { };
|
||||
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(_format1.Name, _format2.Name);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
|
@ -92,7 +92,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_allow_if_no_format_was_parsed_and_none_in_profile()
|
||||
{
|
||||
_remoteMovie.ParsedMovieInfo.Quality.CustomFormats = new List<CustomFormat> { };
|
||||
_remoteMovie.CustomFormats = new List<CustomFormat> { };
|
||||
_remoteMovie.Movie.Profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems(CustomFormat.None.Name, _format1.Name, _format2.Name);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
|
@ -11,13 +18,55 @@ using NzbDrone.Core.Test.Framework;
|
|||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CutoffSpecificationFixture : CoreTest<UpgradableSpecification>
|
||||
public class CutoffSpecificationFixture : CoreTest<CutoffSpecification>
|
||||
{
|
||||
private CustomFormat _customFormat;
|
||||
private RemoteMovie _remoteMovie;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.SetConstant<IUpgradableSpecification>(Mocker.Resolve<UpgradableSpecification>());
|
||||
|
||||
_remoteMovie = new RemoteMovie()
|
||||
{
|
||||
Movie = Builder<Movie>.CreateNew().Build(),
|
||||
ParsedMovieInfo = Builder<ParsedMovieInfo>.CreateNew().With(x => x.Quality = null).Build()
|
||||
};
|
||||
|
||||
GivenOldCustomFormats(new List<CustomFormat>());
|
||||
}
|
||||
|
||||
private void GivenProfile(Profile profile)
|
||||
{
|
||||
CustomFormatsFixture.GivenCustomFormats(CustomFormat.None);
|
||||
profile.FormatItems = CustomFormatsFixture.GetSampleFormatItems("None");
|
||||
profile.FormatCutoff = CustomFormat.None.Id;
|
||||
_remoteMovie.Movie.Profile = profile;
|
||||
|
||||
Console.WriteLine(profile.ToJson());
|
||||
}
|
||||
|
||||
private void GivenFileQuality(QualityModel quality)
|
||||
{
|
||||
_remoteMovie.Movie.MovieFile = Builder<MovieFile>.CreateNew().With(x => x.Quality = quality).Build();
|
||||
}
|
||||
|
||||
private void GivenNewQuality(QualityModel quality)
|
||||
{
|
||||
_remoteMovie.ParsedMovieInfo.Quality = quality;
|
||||
}
|
||||
|
||||
private void GivenOldCustomFormats(List<CustomFormat> formats)
|
||||
{
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<MovieFile>()))
|
||||
.Returns(formats);
|
||||
}
|
||||
|
||||
private void GivenNewCustomFormats(List<CustomFormat> formats)
|
||||
{
|
||||
_remoteMovie.CustomFormats = formats;
|
||||
}
|
||||
|
||||
private void GivenCustomFormatHigher()
|
||||
|
@ -30,73 +79,80 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_return_true_if_current_episode_is_less_than_cutoff()
|
||||
{
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.DVD, new Revision(version: 2))).Should().BeTrue();
|
||||
GivenProfile(new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() });
|
||||
GivenFileQuality(new QualityModel(Quality.DVD, new Revision(version: 2)));
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_current_episode_is_equal_to_cutoff()
|
||||
{
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeFalse();
|
||||
GivenProfile(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() });
|
||||
GivenFileQuality(new QualityModel(Quality.HDTV720p, new Revision(version: 2)));
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_current_episode_is_greater_than_cutoff()
|
||||
{
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse();
|
||||
GivenProfile(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() });
|
||||
GivenFileQuality(new QualityModel(Quality.Bluray1080p, new Revision(version: 2)));
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_new_episode_is_proper_but_existing_is_not()
|
||||
{
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
|
||||
new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeTrue();
|
||||
GivenProfile(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() });
|
||||
GivenFileQuality(new QualityModel(Quality.HDTV720p, new Revision(version: 1)));
|
||||
GivenNewQuality(new QualityModel(Quality.HDTV720p, new Revision(version: 2)));
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_cutoff_is_met_and_quality_is_higher()
|
||||
{
|
||||
Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() },
|
||||
new QualityModel(Quality.HDTV720p, new Revision(version: 2)),
|
||||
new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse();
|
||||
GivenProfile(new Profile { Cutoff = Quality.HDTV720p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() });
|
||||
GivenFileQuality(new QualityModel(Quality.HDTV720p, new Revision(version: 2)));
|
||||
GivenNewQuality(new QualityModel(Quality.Bluray1080p, new Revision(version: 2)));
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_custom_formats_is_met_and_quality_and_format_higher()
|
||||
{
|
||||
GivenProfile(new Profile
|
||||
{
|
||||
Cutoff = Quality.HDTV720p.Id,
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
FormatCutoff = CustomFormat.None.Id,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None", "My Format")
|
||||
});
|
||||
|
||||
GivenFileQuality(new QualityModel(Quality.HDTV720p));
|
||||
GivenNewQuality(new QualityModel(Quality.Bluray1080p));
|
||||
|
||||
GivenCustomFormatHigher();
|
||||
var old = new QualityModel(Quality.HDTV720p);
|
||||
old.CustomFormats = new List<CustomFormat> { CustomFormat.None };
|
||||
var newQ = new QualityModel(Quality.Bluray1080p);
|
||||
newQ.CustomFormats = new List<CustomFormat> { _customFormat };
|
||||
Subject.CutoffNotMet(
|
||||
new Profile
|
||||
{
|
||||
Cutoff = Quality.HDTV720p.Id,
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
FormatCutoff = CustomFormat.None.Id,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None", "My Format")
|
||||
},
|
||||
old,
|
||||
newQ).Should().BeFalse();
|
||||
|
||||
GivenOldCustomFormats(new List<CustomFormat> { CustomFormat.None });
|
||||
GivenNewCustomFormats(new List<CustomFormat> { _customFormat });
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_cutoffs_are_met_but_is_a_revision_upgrade()
|
||||
{
|
||||
Profile profile = new Profile
|
||||
GivenProfile(new Profile
|
||||
{
|
||||
Cutoff = Quality.HDTV1080p.Id,
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
};
|
||||
});
|
||||
|
||||
Subject.CutoffNotMet(
|
||||
profile,
|
||||
new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)),
|
||||
new QualityModel(Quality.WEBDL1080p, new Revision(version: 2))).Should().BeTrue();
|
||||
GivenFileQuality(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)));
|
||||
GivenNewQuality(new QualityModel(Quality.WEBDL1080p, new Revision(version: 2)));
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
|
||||
using NzbDrone.Core.History;
|
||||
|
@ -12,6 +14,7 @@ using NzbDrone.Core.Movies;
|
|||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
@ -35,14 +38,23 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
Mocker.Resolve<UpgradableSpecification>();
|
||||
_upgradeHistory = Mocker.Resolve<HistorySpecification>();
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(CustomFormat.None);
|
||||
|
||||
_fakeMovie = Builder<Movie>.CreateNew()
|
||||
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.Build();
|
||||
.With(c => c.Profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
Cutoff = Quality.Bluray1080p.Id,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatCutoff = CustomFormat.None.Id
|
||||
})
|
||||
.Build();
|
||||
|
||||
_parseResultSingle = new RemoteMovie
|
||||
{
|
||||
Movie = _fakeMovie,
|
||||
ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }
|
||||
ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
|
||||
CustomFormats = new List<CustomFormat>()
|
||||
};
|
||||
|
||||
_upgradableQuality = new QualityModel(Quality.SDTV, new Revision(version: 1));
|
||||
|
@ -51,6 +63,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.EnableCompletedDownloadHandling)
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<History.History>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
}
|
||||
|
||||
private void GivenMostRecentForEpisode(int episodeId, string downloadId, QualityModel quality, DateTime date, HistoryEventType eventType)
|
||||
|
@ -142,10 +158,21 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing()
|
||||
{
|
||||
_fakeMovie.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
_fakeMovie.Profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
Cutoff = Quality.Bluray1080p.Id,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatCutoff = CustomFormat.None.Id
|
||||
};
|
||||
|
||||
_parseResultSingle.ParsedMovieInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
|
||||
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<History.History>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
|
||||
GivenMostRecentForEpisode(FIRST_EPISODE_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed);
|
||||
|
||||
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
|
||||
|
@ -154,7 +181,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_not_be_upgradable_if_cutoff_already_met()
|
||||
{
|
||||
_fakeMovie.Profile = new Profile { Cutoff = Quality.WEBDL1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
_fakeMovie.Profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
Cutoff = Quality.WEBDL1080p.Id,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatCutoff = CustomFormat.None.Id
|
||||
};
|
||||
|
||||
_parseResultSingle.ParsedMovieInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
|
||||
_upgradableQuality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1));
|
||||
|
||||
|
@ -182,7 +216,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_return_false_if_cutoff_already_met_and_cdh_is_disabled()
|
||||
{
|
||||
GivenCdhDisabled();
|
||||
_fakeMovie.Profile = new Profile { Cutoff = Quality.WEBDL1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
_fakeMovie.Profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
Cutoff = Quality.WEBDL1080p.Id,
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatCutoff = CustomFormat.None.Id
|
||||
};
|
||||
|
||||
_parseResultSingle.ParsedMovieInfo.Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1));
|
||||
_upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
remoteMovie.Release.DownloadProtocol = downloadProtocol;
|
||||
remoteMovie.Release.Title = "A Movie 1998";
|
||||
|
||||
remoteMovie.CustomFormats = new List<CustomFormat>();
|
||||
|
||||
return remoteMovie;
|
||||
}
|
||||
|
||||
|
@ -324,12 +326,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_prefer_better_custom_format()
|
||||
{
|
||||
var quality1 = new QualityModel(Quality.Bluray720p);
|
||||
quality1.CustomFormats.Add(CustomFormat.None);
|
||||
var remoteMovie1 = GivenRemoteMovie(quality1);
|
||||
remoteMovie1.CustomFormats.Add(CustomFormat.None);
|
||||
|
||||
var quality2 = new QualityModel(Quality.Bluray720p);
|
||||
quality2.CustomFormats.Add(_customFormat1);
|
||||
var remoteMovie2 = GivenRemoteMovie(quality2);
|
||||
remoteMovie2.CustomFormats.Add(_customFormat1);
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteMovie1));
|
||||
|
@ -343,12 +345,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_prefer_better_custom_format2()
|
||||
{
|
||||
var quality1 = new QualityModel(Quality.Bluray720p);
|
||||
quality1.CustomFormats.Add(_customFormat1);
|
||||
var remoteMovie1 = GivenRemoteMovie(quality1);
|
||||
remoteMovie1.CustomFormats.Add(_customFormat1);
|
||||
|
||||
var quality2 = new QualityModel(Quality.Bluray720p);
|
||||
quality2.CustomFormats.Add(_customFormat2);
|
||||
var remoteMovie2 = GivenRemoteMovie(quality2);
|
||||
remoteMovie2.CustomFormats.Add(_customFormat2);
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteMovie1));
|
||||
|
@ -362,12 +364,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
public void should_prefer_2_custom_formats()
|
||||
{
|
||||
var quality1 = new QualityModel(Quality.Bluray720p);
|
||||
quality1.CustomFormats.Add(_customFormat1);
|
||||
var remoteMovie1 = GivenRemoteMovie(quality1);
|
||||
remoteMovie1.CustomFormats.Add(_customFormat1);
|
||||
|
||||
var quality2 = new QualityModel(Quality.Bluray720p);
|
||||
quality2.CustomFormats.AddRange(new List<CustomFormat> { _customFormat1, _customFormat2 });
|
||||
var remoteMovie2 = GivenRemoteMovie(quality2);
|
||||
remoteMovie2.CustomFormats.AddRange(new List<CustomFormat> { _customFormat1, _customFormat2 });
|
||||
|
||||
var decisions = new List<DownloadDecision>();
|
||||
decisions.Add(new DownloadDecision(remoteMovie1));
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
using FluentAssertions;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
@ -12,20 +15,39 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
|
||||
public class QualityUpgradeSpecificationFixture : CoreTest<UpgradableSpecification>
|
||||
{
|
||||
private static CustomFormat _customFormat1 = new CustomFormat("My Format 1", "L_ENGLISH") { Id = 1 };
|
||||
private static CustomFormat _customFormat2 = new CustomFormat("My Format 2", "L_FRENCH") { Id = 2 };
|
||||
|
||||
public static object[] IsUpgradeTestCases =
|
||||
{
|
||||
new object[] { Quality.SDTV, 1, Quality.SDTV, 2, Quality.SDTV, true },
|
||||
new object[] { Quality.WEBDL720p, 1, Quality.WEBDL720p, 2, Quality.WEBDL720p, true },
|
||||
new object[] { Quality.SDTV, 1, Quality.SDTV, 1, Quality.SDTV, false },
|
||||
new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.Bluray720p, false },
|
||||
new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.WEBDL720p, false },
|
||||
new object[] { Quality.WEBDL720p, 1, Quality.WEBDL720p, 1, Quality.WEBDL720p, false },
|
||||
new object[] { Quality.WEBDL1080p, 1, Quality.WEBDL1080p, 1, Quality.WEBDL1080p, false }
|
||||
// Quality upgrade trumps custom format
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat>(), Quality.SDTV, 2, new List<CustomFormat>(), true },
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat> { _customFormat1 }, Quality.SDTV, 2, new List<CustomFormat> { _customFormat1 }, true },
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat> { _customFormat1 }, Quality.SDTV, 2, new List<CustomFormat> { _customFormat2 }, true },
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat> { _customFormat2 }, Quality.SDTV, 2, new List<CustomFormat> { _customFormat1 }, true },
|
||||
|
||||
// Revision upgrade trumps custom format
|
||||
new object[] { Quality.WEBDL720p, 1, new List<CustomFormat>(), Quality.WEBDL720p, 2, new List<CustomFormat>(), true },
|
||||
new object[] { Quality.WEBDL720p, 1, new List<CustomFormat> { _customFormat1 }, Quality.WEBDL720p, 2, new List<CustomFormat> { _customFormat1 }, true },
|
||||
new object[] { Quality.WEBDL720p, 1, new List<CustomFormat> { _customFormat1 }, Quality.WEBDL720p, 2, new List<CustomFormat> { _customFormat2 }, true },
|
||||
new object[] { Quality.WEBDL720p, 1, new List<CustomFormat> { _customFormat2 }, Quality.WEBDL720p, 2, new List<CustomFormat> { _customFormat1 }, true },
|
||||
|
||||
// Custom formats apply if quality same
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat>(), Quality.SDTV, 1, new List<CustomFormat>(), false },
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat> { _customFormat1 }, Quality.SDTV, 1, new List<CustomFormat> { _customFormat1 }, false },
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat> { _customFormat1 }, Quality.SDTV, 1, new List<CustomFormat> { _customFormat2 }, true },
|
||||
new object[] { Quality.SDTV, 1, new List<CustomFormat> { _customFormat2 }, Quality.SDTV, 1, new List<CustomFormat> { _customFormat1 }, false },
|
||||
|
||||
new object[] { Quality.WEBDL720p, 1, new List<CustomFormat>(), Quality.HDTV720p, 2, new List<CustomFormat>(), false },
|
||||
new object[] { Quality.WEBDL720p, 1, new List<CustomFormat>(), Quality.HDTV720p, 2, new List<CustomFormat>(), false },
|
||||
new object[] { Quality.WEBDL720p, 1, new List<CustomFormat>(), Quality.WEBDL720p, 1, new List<CustomFormat>(), false },
|
||||
new object[] { Quality.WEBDL1080p, 1, new List<CustomFormat>(), Quality.WEBDL1080p, 1, new List<CustomFormat>(), false }
|
||||
};
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
CustomFormatsFixture.GivenCustomFormats(CustomFormat.None, _customFormat1, _customFormat2);
|
||||
}
|
||||
|
||||
private void GivenAutoDownloadPropers(bool autoDownloadPropers)
|
||||
|
@ -37,13 +59,27 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
|
||||
[Test]
|
||||
[TestCaseSource("IsUpgradeTestCases")]
|
||||
public void IsUpgradeTest(Quality current, int currentVersion, Quality newQuality, int newVersion, Quality cutoff, bool expected)
|
||||
public void IsUpgradeTest(Quality current,
|
||||
int currentVersion,
|
||||
List<CustomFormat> currentFormats,
|
||||
Quality newQuality,
|
||||
int newVersion,
|
||||
List<CustomFormat> newFormats,
|
||||
bool expected)
|
||||
{
|
||||
GivenAutoDownloadPropers(true);
|
||||
|
||||
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
var profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems()
|
||||
};
|
||||
|
||||
Subject.IsUpgradable(profile, new QualityModel(current, new Revision(version: currentVersion)), new QualityModel(newQuality, new Revision(version: newVersion)))
|
||||
Subject.IsUpgradable(profile,
|
||||
new QualityModel(current, new Revision(version: currentVersion)),
|
||||
currentFormats,
|
||||
new QualityModel(newQuality, new Revision(version: newVersion)),
|
||||
newFormats)
|
||||
.Should().Be(expected);
|
||||
}
|
||||
|
||||
|
@ -54,7 +90,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
|
||||
var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() };
|
||||
|
||||
Subject.IsUpgradable(profile, new QualityModel(Quality.DVD, new Revision(version: 2)), new QualityModel(Quality.DVD, new Revision(version: 1)))
|
||||
Subject.IsUpgradable(profile,
|
||||
new QualityModel(Quality.DVD, new Revision(version: 2)),
|
||||
new List<CustomFormat>(),
|
||||
new QualityModel(Quality.DVD, new Revision(version: 1)),
|
||||
new List<CustomFormat>())
|
||||
.Should().BeFalse();
|
||||
}
|
||||
|
||||
|
@ -69,7 +109,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
Subject.IsUpgradable(
|
||||
profile,
|
||||
new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
|
||||
new QualityModel(Quality.HDTV720p, new Revision(version: 1)))
|
||||
new List<CustomFormat>(),
|
||||
new QualityModel(Quality.HDTV720p, new Revision(version: 1)),
|
||||
new List<CustomFormat>())
|
||||
.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,16 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Queue;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
@ -26,10 +29,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
{
|
||||
Mocker.Resolve<UpgradableSpecification>();
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(CustomFormat.None);
|
||||
|
||||
_movie = Builder<Movie>.CreateNew()
|
||||
.With(e => e.Profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatCutoff = CustomFormat.None.Id,
|
||||
UpgradeAllowed = true
|
||||
})
|
||||
.Build();
|
||||
|
@ -39,9 +46,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
.Build();
|
||||
|
||||
_remoteMovie = Builder<RemoteMovie>.CreateNew()
|
||||
.With(r => r.Movie = _movie)
|
||||
.With(r => r.ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD) })
|
||||
.Build();
|
||||
.With(r => r.Movie = _movie)
|
||||
.With(r => r.ParsedMovieInfo = new ParsedMovieInfo { Quality = new QualityModel(Quality.DVD) })
|
||||
.With(x => x.CustomFormats = new List<CustomFormat> { CustomFormat.None })
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<ParsedMovieInfo>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
}
|
||||
|
||||
private void GivenEmptyQueue()
|
||||
|
@ -87,12 +99,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
_movie.Profile.Cutoff = Quality.Bluray1080p.Id;
|
||||
|
||||
var remoteMovie = Builder<RemoteMovie>.CreateNew()
|
||||
.With(r => r.Movie = _movie)
|
||||
.With(r => r.ParsedMovieInfo = new ParsedMovieInfo
|
||||
{
|
||||
Quality = new QualityModel(Quality.SDTV)
|
||||
})
|
||||
.Build();
|
||||
.With(r => r.Movie = _movie)
|
||||
.With(r => r.ParsedMovieInfo = new ParsedMovieInfo
|
||||
{
|
||||
Quality = new QualityModel(Quality.SDTV)
|
||||
})
|
||||
.With(x => x.CustomFormats = new List<CustomFormat> { CustomFormat.None })
|
||||
.Build();
|
||||
|
||||
GivenQueue(new List<RemoteMovie> { remoteMovie });
|
||||
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
|
||||
|
|
|
@ -4,6 +4,7 @@ using FizzWare.NBuilder;
|
|||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
|
@ -75,7 +76,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
|
|||
private void GivenUpgradeForExistingFile()
|
||||
{
|
||||
Mocker.GetMock<IUpgradableSpecification>()
|
||||
.Setup(s => s.IsUpgradable(It.IsAny<Profile>(), It.IsAny<QualityModel>(), It.IsAny<QualityModel>()))
|
||||
.Setup(s => s.IsUpgradable(It.IsAny<Profile>(), It.IsAny<QualityModel>(), It.IsAny<List<CustomFormat>>(), It.IsAny<QualityModel>(), It.IsAny<List<CustomFormat>>()))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
|
@ -27,18 +31,30 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
Mocker.Resolve<UpgradableSpecification>();
|
||||
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(CustomFormat.None);
|
||||
|
||||
_firstFile = new MovieFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now };
|
||||
|
||||
var fakeSeries = Builder<Movie>.CreateNew()
|
||||
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() })
|
||||
.With(e => e.MovieFile = _firstFile)
|
||||
.Build();
|
||||
.With(c => c.Profile = new Profile
|
||||
{
|
||||
Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities(),
|
||||
FormatItems = CustomFormatsFixture.GetSampleFormatItems("None"),
|
||||
FormatCutoff = CustomFormat.None.Id
|
||||
})
|
||||
.With(e => e.MovieFile = _firstFile)
|
||||
.Build();
|
||||
|
||||
_parseResultSingle = new RemoteMovie
|
||||
{
|
||||
Movie = fakeSeries,
|
||||
ParsedMovieInfo = new ParsedMovieInfo() { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) },
|
||||
CustomFormats = new List<CustomFormat>()
|
||||
};
|
||||
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<MovieFile>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
}
|
||||
|
||||
private void WithFirstFileUpgradable()
|
||||
|
@ -63,6 +79,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_not_be_upgradable_if_qualities_are_the_same()
|
||||
{
|
||||
Mocker.GetMock<ICustomFormatCalculationService>()
|
||||
.Setup(x => x.ParseCustomFormat(It.IsAny<MovieFile>()))
|
||||
.Returns(new List<CustomFormat>());
|
||||
|
||||
_firstFile.Quality = new QualityModel(Quality.WEBDL1080p);
|
||||
_parseResultSingle.ParsedMovieInfo.Quality = new QualityModel(Quality.WEBDL1080p);
|
||||
_upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NUnit.Framework;
|
||||
|
@ -11,18 +12,32 @@ namespace NzbDrone.Core.Test.Framework
|
|||
public abstract class MigrationTest<TMigration> : DbTest
|
||||
where TMigration : NzbDroneMigrationBase
|
||||
{
|
||||
protected long MigrationVersion
|
||||
protected long MigrationVersion => ((MigrationAttribute)Attribute.GetCustomAttribute(typeof(TMigration), typeof(MigrationAttribute))).Version;
|
||||
|
||||
[SetUp]
|
||||
public override void SetupDb()
|
||||
{
|
||||
get
|
||||
{
|
||||
var attrib = (MigrationAttribute)Attribute.GetCustomAttribute(typeof(TMigration), typeof(MigrationAttribute));
|
||||
return attrib.Version;
|
||||
}
|
||||
SetupContainer();
|
||||
}
|
||||
|
||||
protected virtual IDirectDataMapper WithMigrationTestDb(Action<TMigration> beforeMigration = null)
|
||||
{
|
||||
var db = WithTestDb(new MigrationContext(MigrationType, MigrationVersion)
|
||||
return WithMigrationAction(beforeMigration).GetDirectDataMapper();
|
||||
}
|
||||
|
||||
protected virtual IDbConnection WithDapperMigrationTestDb(Action<TMigration> beforeMigration = null)
|
||||
{
|
||||
return WithMigrationAction(beforeMigration).OpenConnection();
|
||||
}
|
||||
|
||||
protected override void SetupLogging()
|
||||
{
|
||||
Mocker.SetConstant<ILoggerProvider>(Mocker.Resolve<MigrationLoggerProvider>());
|
||||
}
|
||||
|
||||
private ITestDatabase WithMigrationAction(Action<TMigration> beforeMigration = null)
|
||||
{
|
||||
return WithTestDb(new MigrationContext(MigrationType, MigrationVersion)
|
||||
{
|
||||
BeforeMigration = m =>
|
||||
{
|
||||
|
@ -33,19 +48,6 @@ namespace NzbDrone.Core.Test.Framework
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
return db.GetDirectDataMapper();
|
||||
}
|
||||
|
||||
protected override void SetupLogging()
|
||||
{
|
||||
Mocker.SetConstant<ILoggerProvider>(Mocker.Resolve<MigrationLoggerProvider>());
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public override void SetupDb()
|
||||
{
|
||||
SetupContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
@ -21,6 +22,7 @@ namespace NzbDrone.Core.Test.Framework
|
|||
void Delete<T>(T childModel)
|
||||
where T : ModelBase, new();
|
||||
IDirectDataMapper GetDirectDataMapper();
|
||||
IDbConnection OpenConnection();
|
||||
}
|
||||
|
||||
public class TestDatabase : ITestDatabase
|
||||
|
@ -74,5 +76,10 @@ namespace NzbDrone.Core.Test.Framework
|
|||
{
|
||||
return new DirectDataMapper(_dbConnection);
|
||||
}
|
||||
|
||||
public IDbConnection OpenConnection()
|
||||
{
|
||||
return _dbConnection.OpenConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using Moq;
|
|||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport;
|
||||
|
@ -58,6 +59,10 @@ namespace NzbDrone.Core.Test.MediaFiles
|
|||
.Setup(s => s.UpgradeMovieFile(It.IsAny<MovieFile>(), It.IsAny<LocalMovie>(), It.IsAny<bool>()))
|
||||
.Returns(new MovieFileMoveResult());
|
||||
|
||||
Mocker.GetMock<IHistoryService>()
|
||||
.Setup(x => x.FindByDownloadId(It.IsAny<string>()))
|
||||
.Returns(new List<History.History>());
|
||||
|
||||
_downloadClientItem = Builder<DownloadClientItem>.CreateNew().Build();
|
||||
}
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MovieImport.Aggregation.Aggregators
|
|||
.Returns(AugmentQualityResult.ResolutionOnly((int)Resolution.R1080p, Confidence.MediaInfo));
|
||||
|
||||
_fileExtensionAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Fallback, (int)Resolution.R720p, Confidence.Fallback, Modifier.NONE, Confidence.Fallback, new Revision(), new List<CustomFormat>()));
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Fallback, (int)Resolution.R720p, Confidence.Fallback, Modifier.NONE, Confidence.Fallback, new Revision()));
|
||||
|
||||
_nameAugmenter.Setup(s => s.AugmentQuality(It.IsAny<LocalMovie>()))
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Default, (int)Resolution.R480p, Confidence.Default, Modifier.NONE, Confidence.Default, new Revision(), new List<CustomFormat>()));
|
||||
.Returns(new AugmentQualityResult(Source.TV, Confidence.Default, (int)Resolution.R480p, Confidence.Default, Modifier.NONE, Confidence.Default, new Revision()));
|
||||
}
|
||||
|
||||
private void GivenAugmenters(params Mock<IAugmentQuality>[] mocks)
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles
|
||||
{
|
||||
[TestFixture]
|
||||
public class UpdateMovieFileQualityServiceFixture : CoreTest<UpdateMovieFileQualityService>
|
||||
{
|
||||
private MovieFile _movieFile;
|
||||
private QualityModel _oldQuality;
|
||||
private QualityModel _newQuality;
|
||||
|
||||
private ParsedMovieInfo _newInfo;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_movieFile = Builder<MovieFile>.CreateNew().With(m => m.MovieId = 0).Build();
|
||||
|
||||
_oldQuality = new QualityModel(Quality.Bluray720p);
|
||||
|
||||
_movieFile.Quality = _oldQuality;
|
||||
|
||||
_newQuality = _oldQuality.JsonClone();
|
||||
var format = new CustomFormat("Awesome Format");
|
||||
format.Id = 1;
|
||||
_newQuality.CustomFormats = new List<CustomFormat> { format };
|
||||
|
||||
_newInfo = new ParsedMovieInfo
|
||||
{
|
||||
Quality = _newQuality
|
||||
};
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Setup(s => s.GetMovies(It.IsAny<IEnumerable<int>>()))
|
||||
.Returns(new List<MovieFile> { _movieFile });
|
||||
|
||||
Mocker.GetMock<IHistoryService>().Setup(s => s.GetByMovieId(It.IsAny<int>(), null))
|
||||
.Returns(new List<History.History>());
|
||||
}
|
||||
|
||||
private void ExecuteCommand()
|
||||
{
|
||||
Subject.Execute(new UpdateMovieFileQualityCommand(new List<int> { 0 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_update_if_unable_to_parse()
|
||||
{
|
||||
ExecuteCommand();
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(s => s.Update(It.IsAny<MovieFile>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_update_with_new_formats()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>().Setup(s => s.ParseMovieInfo(It.IsAny<string>(), It.IsAny<List<object>>()))
|
||||
.Returns(_newInfo);
|
||||
|
||||
ExecuteCommand();
|
||||
|
||||
Mocker.GetMock<IMediaFileService>().Verify(s => s.Update(It.Is<MovieFile>(f => f.Quality.CustomFormats == _newQuality.CustomFormats)), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_imported_history_title()
|
||||
{
|
||||
var imported = Builder<History.History>.CreateNew()
|
||||
.With(h => h.EventType = HistoryEventType.DownloadFolderImported)
|
||||
.With(h => h.SourceTitle = "My Movie 2018.mkv").Build();
|
||||
|
||||
Mocker.GetMock<IHistoryService>().Setup(s => s.GetByMovieId(It.IsAny<int>(), null))
|
||||
.Returns(new List<History.History> { imported });
|
||||
|
||||
Mocker.GetMock<IParsingService>().Setup(s => s.ParseMovieInfo("My Movie 2018.mkv", It.IsAny<List<object>>()))
|
||||
.Returns(_newInfo);
|
||||
|
||||
ExecuteCommand();
|
||||
|
||||
Mocker.GetMock<IParsingService>().Verify(s => s.ParseMovieInfo("My Movie 2018.mkv", It.IsAny<List<object>>()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
|
@ -22,19 +23,23 @@ namespace NzbDrone.Core.Test.MovieTests.MovieRepositoryTests
|
|||
{
|
||||
_profileRepository = Mocker.Resolve<ProfileRepository>();
|
||||
Mocker.SetConstant<IProfileRepository>(_profileRepository);
|
||||
|
||||
Mocker.GetMock<ICustomFormatService>()
|
||||
.Setup(x => x.All())
|
||||
.Returns(new List<CustomFormat>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_load_quality_profile()
|
||||
{
|
||||
var profile = new Profile
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
|
||||
FormatItems = CustomFormatsFixture.GetDefaultFormatItems(),
|
||||
FormatCutoff = CustomFormat.None.Id,
|
||||
Cutoff = Quality.Bluray1080p.Id,
|
||||
Name = "TestProfile"
|
||||
};
|
||||
{
|
||||
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
|
||||
FormatItems = CustomFormatsFixture.GetDefaultFormatItems(),
|
||||
FormatCutoff = CustomFormat.None.Id,
|
||||
Cutoff = Quality.Bluray1080p.Id,
|
||||
Name = "TestProfile"
|
||||
};
|
||||
|
||||
_profileRepository.Insert(profile);
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Augmenters;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
|
||||
{
|
||||
|
@ -61,37 +59,6 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests.AugmentersTests
|
|||
result.Languages.Should().BeEquivalentTo(Language.English, Language.French);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_combine_formats()
|
||||
{
|
||||
var folderInfo = new ParsedMovieInfo
|
||||
{
|
||||
Quality = new QualityModel(Quality.Bluray1080p)
|
||||
};
|
||||
|
||||
var format1 = new CustomFormat("Awesome Format");
|
||||
format1.Id = 1;
|
||||
|
||||
var format2 = new CustomFormat("Cool Format");
|
||||
format2.Id = 2;
|
||||
|
||||
folderInfo.Quality.CustomFormats = new List<CustomFormat> { format1 };
|
||||
|
||||
MovieInfo.Quality.CustomFormats = new List<CustomFormat> { format2 };
|
||||
|
||||
var result = Subject.AugmentMovieInfo(MovieInfo, folderInfo);
|
||||
|
||||
result.Quality.CustomFormats.Count.Should().Be(2);
|
||||
result.Quality.CustomFormats.Should().BeEquivalentTo(format2, format1);
|
||||
|
||||
folderInfo.Quality.CustomFormats = new List<CustomFormat> { format1, format2 };
|
||||
|
||||
result = Subject.AugmentMovieInfo(MovieInfo, folderInfo);
|
||||
|
||||
result.Quality.CustomFormats.Count.Should().Be(2);
|
||||
result.Quality.CustomFormats.Should().BeEquivalentTo(format2, format1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_release_group()
|
||||
{
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.CustomFormats;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Qualities
|
||||
|
@ -14,11 +12,6 @@ namespace NzbDrone.Core.Test.Qualities
|
|||
{
|
||||
public QualityModelComparer Subject { get; set; }
|
||||
|
||||
private CustomFormat _customFormat1;
|
||||
private CustomFormat _customFormat2;
|
||||
private CustomFormat _customFormat3;
|
||||
private CustomFormat _customFormat4;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
|
@ -78,18 +71,6 @@ namespace NzbDrone.Core.Test.Qualities
|
|||
Subject = new QualityModelComparer(profile);
|
||||
}
|
||||
|
||||
private void GivenDefaultProfileWithFormats()
|
||||
{
|
||||
_customFormat1 = new CustomFormat("My Format 1", "L_ENGLISH") { Id = 1 };
|
||||
_customFormat2 = new CustomFormat("My Format 2", "L_FRENCH") { Id = 2 };
|
||||
_customFormat3 = new CustomFormat("My Format 3", "L_SPANISH") { Id = 3 };
|
||||
_customFormat4 = new CustomFormat("My Format 4", "L_ITALIAN") { Id = 4 };
|
||||
|
||||
CustomFormatsFixture.GivenCustomFormats(CustomFormat.None, _customFormat1, _customFormat2, _customFormat3, _customFormat4);
|
||||
|
||||
Subject = new QualityModelComparer(new Profile { Items = QualityFixture.GetDefaultQualities(), FormatItems = CustomFormatsFixture.GetSampleFormatItems(), FormatCutoff = _customFormat2.Id });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_first_quality_is_greater_than_second()
|
||||
{
|
||||
|
@ -142,32 +123,6 @@ namespace NzbDrone.Core.Test.Qualities
|
|||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_lesser_when_first_quality_is_worse_format()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new QualityModel(Quality.DVD) { CustomFormats = new List<CustomFormat> { _customFormat1 } };
|
||||
var second = new QualityModel(Quality.DVD) { CustomFormats = new List<CustomFormat> { _customFormat2 } };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeLessThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_first_quality_is_better_format()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new QualityModel(Quality.DVD) { CustomFormats = new List<CustomFormat> { _customFormat2 } };
|
||||
var second = new QualityModel(Quality.DVD) { CustomFormats = new List<CustomFormat> { _customFormat1 } };
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_ignore_group_order_by_default()
|
||||
{
|
||||
|
@ -193,57 +148,5 @@ namespace NzbDrone.Core.Test.Qualities
|
|||
|
||||
compare.Should().BeLessThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_one_format_over_cutoff()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat3 };
|
||||
var second = _customFormat2.Id;
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_multiple_formats_over_cutoff()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat3, _customFormat4 };
|
||||
var second = _customFormat2.Id;
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_greater_when_one_better_one_worse_than_cutoff()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat1, _customFormat3 };
|
||||
var second = _customFormat2.Id;
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_zero_when_one_worse_one_equal_to_cutoff()
|
||||
{
|
||||
GivenDefaultProfileWithFormats();
|
||||
|
||||
var first = new List<CustomFormat> { _customFormat1, _customFormat2 };
|
||||
var second = _customFormat2.Id;
|
||||
|
||||
var compare = Subject.Compare(first, second);
|
||||
|
||||
compare.Should().Be(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using NzbDrone.Core.Datastore;
|
|||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Blacklisting
|
||||
|
@ -19,6 +20,7 @@ namespace NzbDrone.Core.Blacklisting
|
|||
public long? Size { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
public IndexerFlags IndexerFlags { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string TorrentInfoHash { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
|
|
|
@ -152,6 +152,11 @@ namespace NzbDrone.Core.Blacklisting
|
|||
Languages = message.Languages
|
||||
};
|
||||
|
||||
if (Enum.TryParse(message.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags))
|
||||
{
|
||||
blacklist.IndexerFlags = flags;
|
||||
}
|
||||
|
||||
_blacklistRepository.Insert(blacklist);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,6 @@ namespace NzbDrone.Core.CustomFormats
|
|||
{
|
||||
public class CustomFormat : ModelBase, IEquatable<CustomFormat>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<FormatTag> FormatTags { get; set; }
|
||||
|
||||
public CustomFormat()
|
||||
{
|
||||
}
|
||||
|
@ -21,18 +17,25 @@ namespace NzbDrone.Core.CustomFormats
|
|||
FormatTags = tags.Select(t => new FormatTag(t)).ToList();
|
||||
}
|
||||
|
||||
public static implicit operator CustomFormatDefinition(CustomFormat format) => new CustomFormatDefinition { Id = format.Id, Name = format.Name, FormatTags = format.FormatTags };
|
||||
public static CustomFormat None => new CustomFormat
|
||||
{
|
||||
Id = 0,
|
||||
Name = "None",
|
||||
FormatTags = new List<FormatTag>()
|
||||
};
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<FormatTag> FormatTags { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public static CustomFormat None => new CustomFormat("None");
|
||||
|
||||
public bool Equals(CustomFormat other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -47,7 +50,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -67,26 +70,7 @@ namespace NzbDrone.Core.CustomFormats
|
|||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CustomFormatExtensions
|
||||
{
|
||||
public static string ToExtendedString(this IEnumerable<CustomFormat> formats)
|
||||
{
|
||||
return string.Join(", ", formats.Select(f => f.ToString()));
|
||||
}
|
||||
|
||||
public static List<CustomFormat> WithNone(this IEnumerable<CustomFormat> formats)
|
||||
{
|
||||
var list = formats.ToList();
|
||||
if (list.Any())
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
return new List<CustomFormat> { CustomFormat.None };
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public interface ICustomFormatCalculationService
|
||||
{
|
||||
List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo);
|
||||
List<CustomFormat> ParseCustomFormat(MovieFile movieFile);
|
||||
List<CustomFormat> ParseCustomFormat(Blacklist blacklist);
|
||||
List<CustomFormat> ParseCustomFormat(History.History history);
|
||||
List<CustomFormatMatchResult> MatchFormatTags(ParsedMovieInfo movieInfo);
|
||||
}
|
||||
|
||||
public class CustomFormatCalculationService : ICustomFormatCalculationService
|
||||
{
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IMovieService _movieService;
|
||||
|
||||
public CustomFormatCalculationService(ICustomFormatService formatService,
|
||||
IParsingService parsingService,
|
||||
IMovieService movieService)
|
||||
{
|
||||
_formatService = formatService;
|
||||
_parsingService = parsingService;
|
||||
_movieService = movieService;
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
return MatchFormatTags(movieInfo)
|
||||
.Where(m => m.GoodMatch)
|
||||
.Select(r => r.CustomFormat)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(MovieFile movieFile)
|
||||
{
|
||||
return MatchFormatTags(movieFile)
|
||||
.Where(m => m.GoodMatch)
|
||||
.Select(r => r.CustomFormat)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(Blacklist blacklist)
|
||||
{
|
||||
return MatchFormatTags(blacklist)
|
||||
.Where(m => m.GoodMatch)
|
||||
.Select(r => r.CustomFormat)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(History.History history)
|
||||
{
|
||||
return MatchFormatTags(history)
|
||||
.Where(m => m.GoodMatch)
|
||||
.Select(r => r.CustomFormat)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<CustomFormatMatchResult> MatchFormatTags(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
var formats = _formatService.All();
|
||||
|
||||
var matches = new List<CustomFormatMatchResult>();
|
||||
|
||||
foreach (var customFormat in formats)
|
||||
{
|
||||
var tagTypeMatches = customFormat.FormatTags
|
||||
.GroupBy(t => t.TagType)
|
||||
.Select(g => new FormatTagMatchesGroup
|
||||
{
|
||||
Type = g.Key,
|
||||
Matches = g.ToDictionary(t => t, t => t.DoesItMatch(movieInfo))
|
||||
})
|
||||
.ToList();
|
||||
|
||||
matches.Add(new CustomFormatMatchResult
|
||||
{
|
||||
CustomFormat = customFormat,
|
||||
GroupMatches = tagTypeMatches
|
||||
});
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private List<CustomFormatMatchResult> MatchFormatTags(MovieFile file)
|
||||
{
|
||||
var info = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitle = file.Movie.Title,
|
||||
SimpleReleaseTitle = file.GetSceneOrFileName().SimplifyReleaseTitle(),
|
||||
Quality = file.Quality,
|
||||
Languages = file.Languages,
|
||||
ReleaseGroup = file.ReleaseGroup,
|
||||
Edition = file.Edition,
|
||||
Year = file.Movie.Year,
|
||||
ImdbId = file.Movie.ImdbId,
|
||||
ExtraInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "IndexerFlags", file.IndexerFlags },
|
||||
{ "Size", file.Size },
|
||||
{ "Filename", System.IO.Path.GetFileName(file.RelativePath) }
|
||||
}
|
||||
};
|
||||
|
||||
return MatchFormatTags(info);
|
||||
}
|
||||
|
||||
private List<CustomFormatMatchResult> MatchFormatTags(Blacklist blacklist)
|
||||
{
|
||||
var parsed = _parsingService.ParseMovieInfo(blacklist.SourceTitle, null);
|
||||
|
||||
var info = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitle = blacklist.Movie.Title,
|
||||
SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? blacklist.SourceTitle.SimplifyReleaseTitle(),
|
||||
Quality = blacklist.Quality,
|
||||
Languages = blacklist.Languages,
|
||||
ReleaseGroup = parsed?.ReleaseGroup,
|
||||
Edition = parsed?.Edition,
|
||||
Year = blacklist.Movie.Year,
|
||||
ImdbId = blacklist.Movie.ImdbId,
|
||||
ExtraInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "IndexerFlags", blacklist.IndexerFlags },
|
||||
{ "Size", blacklist.Size }
|
||||
}
|
||||
};
|
||||
|
||||
return MatchFormatTags(info);
|
||||
}
|
||||
|
||||
private List<CustomFormatMatchResult> MatchFormatTags(History.History history)
|
||||
{
|
||||
var movie = _movieService.GetMovie(history.MovieId);
|
||||
var parsed = _parsingService.ParseMovieInfo(history.SourceTitle, null);
|
||||
|
||||
Enum.TryParse(history.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags);
|
||||
int.TryParse(history.Data.GetValueOrDefault("size"), out var size);
|
||||
|
||||
var info = new ParsedMovieInfo
|
||||
{
|
||||
MovieTitle = movie.Title,
|
||||
SimpleReleaseTitle = parsed?.SimpleReleaseTitle ?? history.SourceTitle.SimplifyReleaseTitle(),
|
||||
Quality = history.Quality,
|
||||
Languages = history.Languages,
|
||||
ReleaseGroup = parsed?.ReleaseGroup,
|
||||
Edition = parsed?.Edition,
|
||||
Year = movie.Year,
|
||||
ImdbId = movie.ImdbId,
|
||||
ExtraInfo = new Dictionary<string, object>
|
||||
{
|
||||
{ "IndexerFlags", flags },
|
||||
{ "Size", size }
|
||||
}
|
||||
};
|
||||
|
||||
return MatchFormatTags(info);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class CustomFormatDefinition : ModelBase
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public List<FormatTag> FormatTags { get; set; }
|
||||
|
||||
public static implicit operator CustomFormat(CustomFormatDefinition def) => new CustomFormat { Id = def.Id, Name = def.Name, FormatTags = def.FormatTags };
|
||||
}
|
||||
}
|
14
src/NzbDrone.Core/CustomFormats/CustomFormatMatchResult.cs
Normal file
14
src/NzbDrone.Core/CustomFormats/CustomFormatMatchResult.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class CustomFormatMatchResult
|
||||
{
|
||||
public CustomFormat CustomFormat { get; set; }
|
||||
|
||||
public List<FormatTagMatchesGroup> GroupMatches { get; set; }
|
||||
|
||||
public bool GoodMatch => GroupMatches.All(g => g.DidMatch);
|
||||
}
|
||||
}
|
|
@ -3,11 +3,11 @@ using NzbDrone.Core.Messaging.Events;
|
|||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public interface ICustomFormatRepository : IBasicRepository<CustomFormatDefinition>
|
||||
public interface ICustomFormatRepository : IBasicRepository<CustomFormat>
|
||||
{
|
||||
}
|
||||
|
||||
public class CustomFormatRepository : BasicRepository<CustomFormatDefinition>, ICustomFormatRepository
|
||||
public class CustomFormatRepository : BasicRepository<CustomFormat>, ICustomFormatRepository
|
||||
{
|
||||
public CustomFormatRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.CustomFormats.Events;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
|
@ -24,145 +19,21 @@ namespace NzbDrone.Core.CustomFormats
|
|||
public class CustomFormatService : ICustomFormatService
|
||||
{
|
||||
private readonly ICustomFormatRepository _formatRepository;
|
||||
private readonly IHistoryService _historyService;
|
||||
private IProfileService _profileService;
|
||||
|
||||
public IProfileService ProfileService
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_profileService == null)
|
||||
{
|
||||
_profileService = _container.Resolve<IProfileService>();
|
||||
}
|
||||
|
||||
return _profileService;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IContainer _container;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ICached<Dictionary<int, CustomFormat>> _cache;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public static Dictionary<int, CustomFormat> AllCustomFormats;
|
||||
|
||||
public CustomFormatService(ICustomFormatRepository formatRepository,
|
||||
ICacheManager cacheManager,
|
||||
IContainer container,
|
||||
IHistoryService historyService,
|
||||
Logger logger)
|
||||
ICacheManager cacheManager,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
_formatRepository = formatRepository;
|
||||
_container = container;
|
||||
_eventAggregator = eventAggregator;
|
||||
_cache = cacheManager.GetCache<Dictionary<int, CustomFormat>>(typeof(CustomFormat), "formats");
|
||||
_historyService = historyService;
|
||||
_logger = logger;
|
||||
|
||||
// Fill up the cache for subsequent DB lookups
|
||||
All();
|
||||
}
|
||||
|
||||
public void Update(CustomFormat customFormat)
|
||||
{
|
||||
_formatRepository.Update(customFormat);
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
public CustomFormat Insert(CustomFormat customFormat)
|
||||
{
|
||||
var ret = _formatRepository.Insert(customFormat);
|
||||
try
|
||||
{
|
||||
ProfileService.AddCustomFormat(ret);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Failure while trying to add the new custom format to all profiles. Deleting again!");
|
||||
_formatRepository.Delete(ret);
|
||||
throw;
|
||||
}
|
||||
|
||||
_cache.Clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
_cache.Clear();
|
||||
try
|
||||
{
|
||||
//First history:
|
||||
var historyRepo = _container.Resolve<IHistoryRepository>();
|
||||
DeleteInRepo(historyRepo,
|
||||
h => h.Quality.CustomFormats,
|
||||
(h, f) =>
|
||||
{
|
||||
h.Quality.CustomFormats = f;
|
||||
return h;
|
||||
},
|
||||
id);
|
||||
|
||||
//Then Blacklist:
|
||||
var blacklistRepo = _container.Resolve<IBlacklistRepository>();
|
||||
DeleteInRepo(blacklistRepo,
|
||||
h => h.Quality.CustomFormats,
|
||||
(h, f) =>
|
||||
{
|
||||
h.Quality.CustomFormats = f;
|
||||
return h;
|
||||
},
|
||||
id);
|
||||
|
||||
//Then MovieFiles:
|
||||
var moviefileRepo = _container.Resolve<IMediaFileRepository>();
|
||||
DeleteInRepo(moviefileRepo,
|
||||
h => h.Quality.CustomFormats,
|
||||
(h, f) =>
|
||||
{
|
||||
h.Quality.CustomFormats = f;
|
||||
return h;
|
||||
},
|
||||
id);
|
||||
|
||||
//Then Profiles
|
||||
ProfileService.DeleteCustomFormat(id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error(e, "Failed to delete format with id {} from other repositories! Format will not be deleted!", id);
|
||||
throw;
|
||||
}
|
||||
|
||||
//Finally delete the format for real!
|
||||
_formatRepository.Delete(id);
|
||||
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
private void DeleteInRepo<TModel>(IBasicRepository<TModel> repository,
|
||||
Func<TModel, List<CustomFormat>> queryFunc,
|
||||
Func<TModel, List<CustomFormat>, TModel> updateFunc,
|
||||
int customFormatId)
|
||||
where TModel : ModelBase, new()
|
||||
{
|
||||
var allItems = repository.All();
|
||||
|
||||
var toUpdate = allItems.Where(r => queryFunc(r).Exists(c => c.Id == customFormatId)).Select(r =>
|
||||
{
|
||||
return updateFunc(r, queryFunc(r).Where(c => c.Id != customFormatId).ToList());
|
||||
});
|
||||
|
||||
repository.UpdateMany(toUpdate.ToList());
|
||||
}
|
||||
|
||||
private Dictionary<int, CustomFormat> AllDictionary()
|
||||
{
|
||||
return _cache.Get("all", () =>
|
||||
{
|
||||
var all = _formatRepository.All().Select(x => (CustomFormat)x).ToDictionary(m => m.Id);
|
||||
AllCustomFormats = all;
|
||||
return all;
|
||||
});
|
||||
return _cache.Get("all", () => _formatRepository.All().ToDictionary(m => m.Id));
|
||||
}
|
||||
|
||||
public List<CustomFormat> All()
|
||||
|
@ -175,11 +46,35 @@ namespace NzbDrone.Core.CustomFormats
|
|||
return AllDictionary()[id];
|
||||
}
|
||||
|
||||
public static Dictionary<string, List<CustomFormat>> Templates
|
||||
public void Update(CustomFormat customFormat)
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Dictionary<string, List<CustomFormat>>
|
||||
_formatRepository.Update(customFormat);
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
public CustomFormat Insert(CustomFormat customFormat)
|
||||
{
|
||||
// Add to DB then insert into profiles
|
||||
var result = _formatRepository.Insert(customFormat);
|
||||
_cache.Clear();
|
||||
|
||||
_eventAggregator.PublishEvent(new CustomFormatAddedEvent(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
var format = _formatRepository.Get(id);
|
||||
|
||||
// Remove from profiles before removing from DB
|
||||
_eventAggregator.PublishEvent(new CustomFormatDeletedEvent(format));
|
||||
|
||||
_formatRepository.Delete(id);
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
public static Dictionary<string, List<CustomFormat>> Templates => new Dictionary<string, List<CustomFormat>>
|
||||
{
|
||||
{
|
||||
"Easy", new List<CustomFormat>
|
||||
|
@ -207,7 +102,5 @@ namespace NzbDrone.Core.CustomFormats
|
|||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
34
src/NzbDrone.Core/CustomFormats/CustomFormatsComparer.cs
Normal file
34
src/NzbDrone.Core/CustomFormats/CustomFormatsComparer.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Core.Profiles;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class CustomFormatsComparer : IComparer<List<CustomFormat>>
|
||||
{
|
||||
private readonly Profile _profile;
|
||||
|
||||
public CustomFormatsComparer(Profile profile)
|
||||
{
|
||||
Ensure.That(profile, () => profile).IsNotNull();
|
||||
Ensure.That(profile.Items, () => profile.Items).HasItems();
|
||||
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
public int Compare(List<CustomFormat> left, List<CustomFormat> right)
|
||||
{
|
||||
var leftIndicies = _profile.GetIndices(left);
|
||||
var rightIndicies = _profile.GetIndices(right);
|
||||
|
||||
// Summing powers of two ensures last format always trumps, but we order correctly if we
|
||||
// have extra formats lower down the list
|
||||
var leftTotal = leftIndicies.Select(x => Math.Pow(2, x)).Sum();
|
||||
var rightTotal = rightIndicies.Select(x => Math.Pow(2, x)).Sum();
|
||||
|
||||
return leftTotal.CompareTo(rightTotal);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats.Events
|
||||
{
|
||||
public class CustomFormatAddedEvent : IEvent
|
||||
{
|
||||
public CustomFormatAddedEvent(CustomFormat format)
|
||||
{
|
||||
CustomFormat = format;
|
||||
}
|
||||
|
||||
public CustomFormat CustomFormat { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using NzbDrone.Common.Messaging;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats.Events
|
||||
{
|
||||
public class CustomFormatDeletedEvent : IEvent
|
||||
{
|
||||
public CustomFormatDeletedEvent(CustomFormat format)
|
||||
{
|
||||
CustomFormat = format;
|
||||
}
|
||||
|
||||
public CustomFormat CustomFormat { get; private set; }
|
||||
}
|
||||
}
|
|
@ -7,20 +7,21 @@ using NzbDrone.Common.Extensions;
|
|||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class FormatTag
|
||||
{
|
||||
public string Raw { get; set; }
|
||||
public TagType TagType { get; set; }
|
||||
public TagModifier TagModifier { get; set; }
|
||||
public object Value { get; set; }
|
||||
|
||||
public static Regex QualityTagRegex = new Regex(@"^(?<type>R|S|M|E|L|C|I|G)(_((?<m_r>RX)|(?<m_re>RQ)|(?<m_n>N)){0,3})?_(?<value>.*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static Regex SizeTagRegex = new Regex(@"(?<min>\d+(\.\d+)?)\s*<>\s*(?<max>\d+(\.\d+)?)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
// This function is needed for json deserialization to work.
|
||||
public FormatTag()
|
||||
{
|
||||
}
|
||||
|
||||
public FormatTag(string raw)
|
||||
{
|
||||
Raw = raw;
|
||||
|
@ -31,13 +32,13 @@ namespace NzbDrone.Core.CustomFormats
|
|||
throw new ArgumentException("Quality Tag is not in the correct format!");
|
||||
}
|
||||
|
||||
ParseRawMatch(match);
|
||||
ParseFormatTagString(match);
|
||||
}
|
||||
|
||||
// This function is needed for json deserialization to work.
|
||||
private FormatTag()
|
||||
{
|
||||
}
|
||||
public string Raw { get; set; }
|
||||
public TagType TagType { get; set; }
|
||||
public TagModifier TagModifier { get; set; }
|
||||
public object Value { get; set; }
|
||||
|
||||
public bool DoesItMatch(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
|
@ -50,59 +51,64 @@ namespace NzbDrone.Core.CustomFormats
|
|||
return match;
|
||||
}
|
||||
|
||||
private bool MatchString(string compared)
|
||||
{
|
||||
if (compared == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TagModifier.HasFlag(TagModifier.Regex))
|
||||
{
|
||||
var regexValue = (Regex)Value;
|
||||
return regexValue.IsMatch(compared);
|
||||
}
|
||||
else
|
||||
{
|
||||
var stringValue = (string)Value;
|
||||
return compared.ToLower().Contains(stringValue.Replace(" ", string.Empty).ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
private bool DoesItMatchWithoutMods(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
if (movieInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var filename = (string)movieInfo?.ExtraInfo?.GetValueOrDefault("Filename");
|
||||
|
||||
switch (TagType)
|
||||
{
|
||||
case TagType.Edition:
|
||||
return MatchString(movieInfo.Edition);
|
||||
case TagType.Custom:
|
||||
string compared = null;
|
||||
if (TagType == TagType.Custom)
|
||||
{
|
||||
compared = movieInfo.SimpleReleaseTitle;
|
||||
}
|
||||
else
|
||||
{
|
||||
compared = movieInfo.Edition;
|
||||
}
|
||||
|
||||
if (TagModifier.HasFlag(TagModifier.Regex))
|
||||
{
|
||||
Regex regexValue = (Regex)Value;
|
||||
return regexValue.IsMatch(compared);
|
||||
}
|
||||
else
|
||||
{
|
||||
string stringValue = (string)Value;
|
||||
return compared.ToLower().Contains(stringValue.Replace(" ", string.Empty).ToLower());
|
||||
}
|
||||
|
||||
return MatchString(movieInfo.SimpleReleaseTitle) || MatchString(filename);
|
||||
case TagType.Language:
|
||||
return movieInfo.Languages.Contains((Language)Value);
|
||||
return movieInfo?.Languages?.Contains((Language)Value) ?? false;
|
||||
case TagType.Resolution:
|
||||
return movieInfo.Quality.Quality.Resolution == (int)(Resolution)Value;
|
||||
return (movieInfo?.Quality?.Quality?.Resolution ?? (int)Resolution.Unknown) == (int)(Resolution)Value;
|
||||
case TagType.Modifier:
|
||||
return movieInfo.Quality.Quality.Modifier == (Modifier)Value;
|
||||
return (movieInfo?.Quality?.Quality?.Modifier ?? (int)Modifier.NONE) == (Modifier)Value;
|
||||
case TagType.Source:
|
||||
return movieInfo.Quality.Quality.Source == (Source)Value;
|
||||
return (movieInfo?.Quality?.Quality?.Source ?? (int)Source.UNKNOWN) == (Source)Value;
|
||||
case TagType.Size:
|
||||
var size = (movieInfo.ExtraInfo.GetValueOrDefault("Size", 0.0) as long?) ?? 0;
|
||||
var size = (movieInfo?.ExtraInfo?.GetValueOrDefault("Size", 0.0) as long?) ?? 0;
|
||||
var tuple = Value as (long, long)? ?? (0, 0);
|
||||
return size > tuple.Item1 && size < tuple.Item2;
|
||||
case TagType.Indexer:
|
||||
#if !LIBRARY
|
||||
return (movieInfo.ExtraInfo.GetValueOrDefault("IndexerFlags") as IndexerFlags?)?.HasFlag((IndexerFlags)Value) == true;
|
||||
var flags = movieInfo?.ExtraInfo?.GetValueOrDefault("IndexerFlags") as IndexerFlags?;
|
||||
return flags?.HasFlag((IndexerFlags)Value) == true;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseRawMatch(Match match)
|
||||
private void ParseTagModifier(Match match)
|
||||
{
|
||||
var type = match.Groups["type"].Value.ToLower();
|
||||
var value = match.Groups["value"].Value.ToLower();
|
||||
|
||||
if (match.Groups["m_re"].Success)
|
||||
{
|
||||
TagModifier |= TagModifier.AbsolutelyRequired;
|
||||
|
@ -117,137 +123,169 @@ namespace NzbDrone.Core.CustomFormats
|
|||
{
|
||||
TagModifier |= TagModifier.Not;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseResolutionType(string value)
|
||||
{
|
||||
TagType = TagType.Resolution;
|
||||
switch (value)
|
||||
{
|
||||
case "2160":
|
||||
Value = Resolution.R2160p;
|
||||
break;
|
||||
case "1080":
|
||||
Value = Resolution.R1080p;
|
||||
break;
|
||||
case "720":
|
||||
Value = Resolution.R720p;
|
||||
break;
|
||||
case "576":
|
||||
Value = Resolution.R576p;
|
||||
break;
|
||||
case "480":
|
||||
Value = Resolution.R480p;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseSourceType(string value)
|
||||
{
|
||||
TagType = TagType.Source;
|
||||
switch (value)
|
||||
{
|
||||
case "cam":
|
||||
Value = Source.CAM;
|
||||
break;
|
||||
case "telesync":
|
||||
Value = Source.TELESYNC;
|
||||
break;
|
||||
case "telecine":
|
||||
Value = Source.TELECINE;
|
||||
break;
|
||||
case "workprint":
|
||||
Value = Source.WORKPRINT;
|
||||
break;
|
||||
case "dvd":
|
||||
Value = Source.DVD;
|
||||
break;
|
||||
case "tv":
|
||||
Value = Source.TV;
|
||||
break;
|
||||
case "webdl":
|
||||
Value = Source.WEBDL;
|
||||
break;
|
||||
case "bluray":
|
||||
Value = Source.BLURAY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseModifierType(string value)
|
||||
{
|
||||
TagType = TagType.Modifier;
|
||||
switch (value)
|
||||
{
|
||||
case "regional":
|
||||
Value = Modifier.REGIONAL;
|
||||
break;
|
||||
case "screener":
|
||||
Value = Modifier.SCREENER;
|
||||
break;
|
||||
case "rawhd":
|
||||
Value = Modifier.RAWHD;
|
||||
break;
|
||||
case "brdisk":
|
||||
Value = Modifier.BRDISK;
|
||||
break;
|
||||
case "remux":
|
||||
Value = Modifier.REMUX;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseIndexerFlagType(string value)
|
||||
{
|
||||
TagType = TagType.Indexer;
|
||||
var flagValues = Enum.GetValues(typeof(IndexerFlags));
|
||||
|
||||
foreach (IndexerFlags flagValue in flagValues)
|
||||
{
|
||||
var flagString = flagValue.ToString();
|
||||
if (flagString.ToLower().Replace("_", string.Empty) != value.ToLower().Replace("_", string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Value = flagValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseSizeType(string value)
|
||||
{
|
||||
TagType = TagType.Size;
|
||||
var matches = SizeTagRegex.Match(value);
|
||||
var min = double.Parse(matches.Groups["min"].Value, CultureInfo.InvariantCulture);
|
||||
var max = double.Parse(matches.Groups["max"].Value, CultureInfo.InvariantCulture);
|
||||
Value = (min.Gigabytes(), max.Gigabytes());
|
||||
}
|
||||
|
||||
private void ParseString(string value)
|
||||
{
|
||||
if (TagModifier.HasFlag(TagModifier.Regex))
|
||||
{
|
||||
Value = new Regex(value, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseFormatTagString(Match match)
|
||||
{
|
||||
ParseTagModifier(match);
|
||||
|
||||
var type = match.Groups["type"].Value.ToLower();
|
||||
var value = match.Groups["value"].Value.ToLower();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case "r":
|
||||
TagType = TagType.Resolution;
|
||||
switch (value)
|
||||
{
|
||||
case "2160":
|
||||
Value = Resolution.R2160p;
|
||||
break;
|
||||
case "1080":
|
||||
Value = Resolution.R1080p;
|
||||
break;
|
||||
case "720":
|
||||
Value = Resolution.R720p;
|
||||
break;
|
||||
case "576":
|
||||
Value = Resolution.R576p;
|
||||
break;
|
||||
case "480":
|
||||
Value = Resolution.R480p;
|
||||
break;
|
||||
}
|
||||
|
||||
ParseResolutionType(value);
|
||||
break;
|
||||
case "s":
|
||||
TagType = TagType.Source;
|
||||
switch (value)
|
||||
{
|
||||
case "cam":
|
||||
Value = Source.CAM;
|
||||
break;
|
||||
case "telesync":
|
||||
Value = Source.TELESYNC;
|
||||
break;
|
||||
case "telecine":
|
||||
Value = Source.TELECINE;
|
||||
break;
|
||||
case "workprint":
|
||||
Value = Source.WORKPRINT;
|
||||
break;
|
||||
case "dvd":
|
||||
Value = Source.DVD;
|
||||
break;
|
||||
case "tv":
|
||||
Value = Source.TV;
|
||||
break;
|
||||
case "webdl":
|
||||
Value = Source.WEBDL;
|
||||
break;
|
||||
case "bluray":
|
||||
Value = Source.BLURAY;
|
||||
break;
|
||||
}
|
||||
|
||||
ParseSourceType(value);
|
||||
break;
|
||||
case "m":
|
||||
TagType = TagType.Modifier;
|
||||
switch (value)
|
||||
{
|
||||
case "regional":
|
||||
Value = Modifier.REGIONAL;
|
||||
break;
|
||||
case "screener":
|
||||
Value = Modifier.SCREENER;
|
||||
break;
|
||||
case "rawhd":
|
||||
Value = Modifier.RAWHD;
|
||||
break;
|
||||
case "brdisk":
|
||||
Value = Modifier.BRDISK;
|
||||
break;
|
||||
case "remux":
|
||||
Value = Modifier.REMUX;
|
||||
break;
|
||||
}
|
||||
|
||||
ParseModifierType(value);
|
||||
break;
|
||||
case "e":
|
||||
TagType = TagType.Edition;
|
||||
if (TagModifier.HasFlag(TagModifier.Regex))
|
||||
{
|
||||
Value = new Regex(value, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
ParseString(value);
|
||||
break;
|
||||
case "l":
|
||||
TagType = TagType.Language;
|
||||
Value = Parser.LanguageParser.ParseLanguages(value).First();
|
||||
Value = LanguageParser.ParseLanguages(value).First();
|
||||
break;
|
||||
case "i":
|
||||
#if !LIBRARY
|
||||
TagType = TagType.Indexer;
|
||||
var flagValues = Enum.GetValues(typeof(IndexerFlags));
|
||||
|
||||
foreach (IndexerFlags flagValue in flagValues)
|
||||
{
|
||||
var flagString = flagValue.ToString();
|
||||
if (flagString.ToLower().Replace("_", string.Empty) != value.ToLower().Replace("_", string.Empty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Value = flagValue;
|
||||
break;
|
||||
}
|
||||
ParseIndexerFlagType(value);
|
||||
#endif
|
||||
break;
|
||||
case "g":
|
||||
TagType = TagType.Size;
|
||||
var matches = SizeTagRegex.Match(value);
|
||||
var min = double.Parse(matches.Groups["min"].Value, CultureInfo.InvariantCulture);
|
||||
var max = double.Parse(matches.Groups["max"].Value, CultureInfo.InvariantCulture);
|
||||
Value = (min.Gigabytes(), max.Gigabytes());
|
||||
ParseSizeType(value);
|
||||
break;
|
||||
case "c":
|
||||
default:
|
||||
TagType = TagType.Custom;
|
||||
if (TagModifier.HasFlag(TagModifier.Regex))
|
||||
{
|
||||
Value = new Regex(value, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
ParseString(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -272,28 +310,4 @@ namespace NzbDrone.Core.CustomFormats
|
|||
Not = 2, // Do not match
|
||||
AbsolutelyRequired = 4
|
||||
}
|
||||
|
||||
public enum Source
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
CAM,
|
||||
TELESYNC,
|
||||
TELECINE,
|
||||
WORKPRINT,
|
||||
DVD,
|
||||
TV,
|
||||
WEBDL,
|
||||
WEBRIP,
|
||||
BLURAY
|
||||
}
|
||||
|
||||
public enum Modifier
|
||||
{
|
||||
NONE = 0,
|
||||
REGIONAL,
|
||||
SCREENER,
|
||||
RAWHD,
|
||||
BRDISK,
|
||||
REMUX
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class FormatTagMatchResult
|
||||
{
|
||||
public FormatTagMatchResult()
|
||||
{
|
||||
GroupMatches = new List<FormatTagMatchesGroup>();
|
||||
}
|
||||
|
||||
public CustomFormat CustomFormat { get; set; }
|
||||
public List<FormatTagMatchesGroup> GroupMatches { get; set; }
|
||||
public bool GoodMatch { get; set; }
|
||||
}
|
||||
|
||||
public class FormatTagMatchesGroup
|
||||
{
|
||||
public FormatTagMatchesGroup()
|
||||
{
|
||||
Matches = new Dictionary<FormatTag, bool>();
|
||||
}
|
||||
|
||||
public FormatTagMatchesGroup(TagType type, Dictionary<FormatTag, bool> matches)
|
||||
{
|
||||
Type = type;
|
||||
Matches = matches;
|
||||
}
|
||||
|
||||
public TagType Type { get; set; }
|
||||
|
||||
public bool DidMatch
|
||||
{
|
||||
get
|
||||
{
|
||||
return !(Matches.Any(m => m.Key.TagModifier.HasFlag(TagModifier.AbsolutelyRequired) && m.Value == false) ||
|
||||
Matches.All(m => m.Value == false));
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<FormatTag, bool> Matches { get; set; }
|
||||
}
|
||||
}
|
15
src/NzbDrone.Core/CustomFormats/FormatTagMatchesGroup.cs
Normal file
15
src/NzbDrone.Core/CustomFormats/FormatTagMatchesGroup.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Core.CustomFormats
|
||||
{
|
||||
public class FormatTagMatchesGroup
|
||||
{
|
||||
public TagType Type { get; set; }
|
||||
|
||||
public Dictionary<FormatTag, bool> Matches { get; set; }
|
||||
|
||||
public bool DidMatch => !(Matches.Any(m => m.Key.TagModifier.HasFlag(TagModifier.AbsolutelyRequired) && m.Value == false) ||
|
||||
Matches.All(m => m.Value == false));
|
||||
}
|
||||
}
|
|
@ -1,52 +1,15 @@
|
|||
using System;
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Dapper;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class DapperCustomFormatIntConverter : SqlMapper.TypeHandler<CustomFormat>
|
||||
{
|
||||
public override void SetValue(IDbDataParameter parameter, CustomFormat value)
|
||||
{
|
||||
parameter.Value = value.Id;
|
||||
}
|
||||
|
||||
public override CustomFormat Parse(object value)
|
||||
{
|
||||
Console.WriteLine(value.ToJson());
|
||||
|
||||
if (value is DBNull)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(value);
|
||||
|
||||
if (val == 0)
|
||||
{
|
||||
return CustomFormat.None;
|
||||
}
|
||||
|
||||
return CustomFormatService.AllCustomFormats[val];
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomFormatIntConverter : JsonConverter<CustomFormat>
|
||||
{
|
||||
public override CustomFormat Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var val = reader.GetInt32();
|
||||
|
||||
if (val == 0)
|
||||
{
|
||||
return CustomFormat.None;
|
||||
}
|
||||
|
||||
return CustomFormatService.AllCustomFormats[val];
|
||||
return new CustomFormat { Id = reader.GetInt32() };
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, CustomFormat value, JsonSerializerOptions options)
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace NzbDrone.Core.Datastore.Converters
|
|||
var serializerSettings = new JsonSerializerOptions
|
||||
{
|
||||
AllowTrailingCommas = true,
|
||||
IgnoreNullValues = false,
|
||||
IgnoreNullValues = true,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(165)]
|
||||
public class remove_custom_formats_from_quality_model : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Blacklist").AddColumn("IndexerFlags").AsInt32().WithDefaultValue(0);
|
||||
Alter.Table("MovieFiles").AddColumn("IndexerFlags").AsInt32().WithDefaultValue(0);
|
||||
|
||||
// Switch Quality and Language to int in pending releases, remove custom formats
|
||||
Execute.WithConnection(FixPendingReleases);
|
||||
|
||||
// Remove Custom Formats from QualityModel
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<QualityModel165>());
|
||||
Execute.WithConnection((conn, tran) => RemoveCustomFormatFromQuality(conn, tran, "Blacklist"));
|
||||
Execute.WithConnection((conn, tran) => RemoveCustomFormatFromQuality(conn, tran, "History"));
|
||||
Execute.WithConnection((conn, tran) => RemoveCustomFormatFromQuality(conn, tran, "MovieFiles"));
|
||||
|
||||
// Fish out indexer flags from history
|
||||
Execute.WithConnection(AddIndexerFlagsToBlacklist);
|
||||
Execute.WithConnection(AddIndexerFlagsToMovieFiles);
|
||||
}
|
||||
|
||||
private void FixPendingReleases(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedMovieInfo164>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedMovieInfo165>());
|
||||
var rows = conn.Query<ParsedMovieInfoData164>("SELECT Id, ParsedMovieInfo from PendingReleases");
|
||||
|
||||
var newRows = new List<ParsedMovieInfoData165>();
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var old = row.ParsedMovieInfo;
|
||||
|
||||
var newQuality = new QualityModel165
|
||||
{
|
||||
Quality = old.Quality.Quality.Id,
|
||||
Revision = old.Quality.Revision,
|
||||
HardcodedSubs = old.Quality.HardcodedSubs
|
||||
};
|
||||
|
||||
var languages = old.Languages?.Select(x => (Language)x).Select(x => x.Id).ToList();
|
||||
|
||||
var correct = new ParsedMovieInfo165
|
||||
{
|
||||
MovieTitle = old.MovieTitle,
|
||||
SimpleReleaseTitle = old.SimpleReleaseTitle,
|
||||
Quality = newQuality,
|
||||
Languages = languages,
|
||||
ReleaseGroup = old.ReleaseGroup,
|
||||
ReleaseHash = old.ReleaseHash,
|
||||
Edition = old.Edition,
|
||||
Year = old.Year,
|
||||
ImdbId = old.ImdbId
|
||||
};
|
||||
|
||||
newRows.Add(new ParsedMovieInfoData165
|
||||
{
|
||||
Id = row.Id,
|
||||
ParsedMovieInfo = correct
|
||||
});
|
||||
}
|
||||
|
||||
var sql = $"UPDATE PendingReleases SET ParsedMovieInfo = @ParsedMovieInfo WHERE Id = @Id";
|
||||
|
||||
conn.Execute(sql, newRows, transaction: tran);
|
||||
}
|
||||
|
||||
private void RemoveCustomFormatFromQuality(IDbConnection conn, IDbTransaction tran, string table)
|
||||
{
|
||||
var rows = conn.Query<QualityRow>($"SELECT Id, Quality from {table}");
|
||||
|
||||
var sql = $"UPDATE {table} SET Quality = @Quality WHERE Id = @Id";
|
||||
|
||||
conn.Execute(sql, rows, transaction: tran);
|
||||
}
|
||||
|
||||
private void AddIndexerFlagsToBlacklist(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var blacklists = conn.Query<BlacklistData>("SELECT Blacklist.Id, Blacklist.TorrentInfoHash, History.Data " +
|
||||
"FROM Blacklist " +
|
||||
"JOIN History ON Blacklist.MovieId = History.MovieId " +
|
||||
"WHERE History.EventType = 1");
|
||||
|
||||
var toUpdate = new List<IndexerFlagsItem>();
|
||||
|
||||
foreach (var item in blacklists)
|
||||
{
|
||||
var dict = Json.Deserialize<Dictionary<string, string>>(item.Data);
|
||||
|
||||
if (dict.GetValueOrDefault("torrentInfoHash") == item.TorrentInfoHash &&
|
||||
Enum.TryParse(dict.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags))
|
||||
{
|
||||
if (flags != 0)
|
||||
{
|
||||
toUpdate.Add(new IndexerFlagsItem
|
||||
{
|
||||
Id = item.Id,
|
||||
IndexerFlags = (int)flags
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateSql = "UPDATE Blacklist SET IndexerFlags = @IndexerFlags WHERE Id = @Id";
|
||||
conn.Execute(updateSql, toUpdate, transaction: tran);
|
||||
}
|
||||
|
||||
private void AddIndexerFlagsToMovieFiles(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
var movieFiles = conn.Query<MovieFileData>("SELECT MovieFiles.Id, MovieFiles.SceneName, History.SourceTitle, History.Data " +
|
||||
"FROM MovieFiles " +
|
||||
"JOIN History ON MovieFiles.MovieId = History.MovieId " +
|
||||
"WHERE History.EventType = 1");
|
||||
|
||||
var toUpdate = new List<IndexerFlagsItem>();
|
||||
|
||||
foreach (var item in movieFiles)
|
||||
{
|
||||
var dict = Json.Deserialize<Dictionary<string, string>>(item.Data);
|
||||
|
||||
if (item.SourceTitle == item.SceneName &&
|
||||
Enum.TryParse(dict.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags))
|
||||
{
|
||||
if (flags != 0)
|
||||
{
|
||||
toUpdate.Add(new IndexerFlagsItem
|
||||
{
|
||||
Id = item.Id,
|
||||
IndexerFlags = (int)flags
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var updateSql = "UPDATE MovieFiles SET IndexerFlags = @IndexerFlags WHERE Id = @Id";
|
||||
conn.Execute(updateSql, toUpdate, transaction: tran);
|
||||
}
|
||||
|
||||
private class ParsedMovieInfoData164 : ModelBase
|
||||
{
|
||||
public ParsedMovieInfo164 ParsedMovieInfo { get; set; }
|
||||
}
|
||||
|
||||
private class ParsedMovieInfo164
|
||||
{
|
||||
public string MovieTitle { get; set; }
|
||||
public string SimpleReleaseTitle { get; set; }
|
||||
public QualityModel164 Quality { get; set; }
|
||||
public List<string> Languages { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public string ReleaseHash { get; set; }
|
||||
public string Edition { get; set; }
|
||||
public int Year { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
}
|
||||
|
||||
private class QualityModel164
|
||||
{
|
||||
public Quality164 Quality { get; set; }
|
||||
public Revision165 Revision { get; set; }
|
||||
public string HardcodedSubs { get; set; }
|
||||
}
|
||||
|
||||
private class Quality164
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
private class ParsedMovieInfoData165 : ModelBase
|
||||
{
|
||||
public ParsedMovieInfo165 ParsedMovieInfo { get; set; }
|
||||
}
|
||||
|
||||
private class ParsedMovieInfo165
|
||||
{
|
||||
public string MovieTitle { get; set; }
|
||||
public string SimpleReleaseTitle { get; set; }
|
||||
public QualityModel165 Quality { get; set; }
|
||||
public List<int> Languages { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public string ReleaseHash { get; set; }
|
||||
public string Edition { get; set; }
|
||||
public int Year { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
}
|
||||
|
||||
private class BlacklistData : ModelBase
|
||||
{
|
||||
public string TorrentInfoHash { get; set; }
|
||||
public string Data { get; set; }
|
||||
}
|
||||
|
||||
private class MovieFileData : ModelBase
|
||||
{
|
||||
public string SceneName { get; set; }
|
||||
public string SourceTitle { get; set; }
|
||||
public string Data { get; set; }
|
||||
}
|
||||
|
||||
private class IndexerFlagsItem : ModelBase
|
||||
{
|
||||
public int IndexerFlags { get; set; }
|
||||
}
|
||||
|
||||
private class QualityRow : ModelBase
|
||||
{
|
||||
public QualityModel165 Quality { get; set; }
|
||||
}
|
||||
|
||||
private class QualityModel165
|
||||
{
|
||||
public int Quality { get; set; }
|
||||
public Revision165 Revision { get; set; }
|
||||
public string HardcodedSubs { get; set; }
|
||||
}
|
||||
|
||||
private class Revision165
|
||||
{
|
||||
public int Version { get; set; }
|
||||
public int Real { get; set; }
|
||||
public bool IsRepack { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -111,7 +111,7 @@ namespace NzbDrone.Core.Datastore
|
|||
.Ignore(d => d.GroupName)
|
||||
.Ignore(d => d.Weight);
|
||||
|
||||
Mapper.Entity<CustomFormatDefinition>("CustomFormats").RegisterModel();
|
||||
Mapper.Entity<CustomFormat>("CustomFormats").RegisterModel();
|
||||
|
||||
Mapper.Entity<Profile>("Profiles").RegisterModel();
|
||||
Mapper.Entity<Log>("Logs").RegisterModel();
|
||||
|
@ -147,11 +147,10 @@ namespace NzbDrone.Core.Datastore
|
|||
SqlMapper.RemoveTypeMap(typeof(DateTime));
|
||||
SqlMapper.AddTypeHandler(new DapperUtcConverter());
|
||||
SqlMapper.AddTypeHandler(new DapperQualityIntConverter());
|
||||
SqlMapper.AddTypeHandler(new DapperCustomFormatIntConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileQualityItem>>(new QualityIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<ProfileFormatItem>>(new CustomFormatIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<FormatTag>>(new QualityTagStringConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<QualityModel>(new CustomFormatIntConverter(), new QualityIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<QualityModel>(new QualityIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<Dictionary<string, string>>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<IDictionary<string, string>>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<int>>());
|
||||
|
@ -160,7 +159,7 @@ namespace NzbDrone.Core.Datastore
|
|||
SqlMapper.AddTypeHandler(new DapperLanguageIntConverter());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<Language>>(new LanguageIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<List<string>>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedMovieInfo>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ParsedMovieInfo>(new QualityIntConverter(), new LanguageIntConverter()));
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<ReleaseInfo>());
|
||||
SqlMapper.AddTypeHandler(new EmbeddedDocumentConverter<HashSet<int>>());
|
||||
SqlMapper.AddTypeHandler(new OsPathConverter());
|
||||
|
|
|
@ -2,11 +2,9 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles.Delay;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
|
@ -49,12 +47,6 @@ namespace NzbDrone.Core.DecisionEngine
|
|||
return leftValue.CompareTo(rightValue);
|
||||
}
|
||||
|
||||
private int CompareByReverse<TSubject, TValue>(TSubject left, TSubject right, Func<TSubject, TValue> funcValue)
|
||||
where TValue : IComparable<TValue>
|
||||
{
|
||||
return CompareBy(left, right, funcValue) * -1;
|
||||
}
|
||||
|
||||
private int CompareAll(params int[] comparers)
|
||||
{
|
||||
return comparers.Select(comparer => comparer).FirstOrDefault(result => result != 0);
|
||||
|
@ -63,23 +55,9 @@ namespace NzbDrone.Core.DecisionEngine
|
|||
private int CompareQuality(DownloadDecision x, DownloadDecision y)
|
||||
{
|
||||
return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)),
|
||||
CompareCustomFormats(x, y),
|
||||
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Real),
|
||||
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Version));
|
||||
}
|
||||
|
||||
private int CompareCustomFormats(DownloadDecision x, DownloadDecision y)
|
||||
{
|
||||
var left = x.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats.WithNone();
|
||||
var right = y.RemoteMovie.ParsedMovieInfo.Quality.CustomFormats;
|
||||
|
||||
var leftIndicies = QualityModelComparer.GetIndicies(left, x.RemoteMovie.Movie.Profile);
|
||||
var rightIndicies = QualityModelComparer.GetIndicies(right, y.RemoteMovie.Movie.Profile);
|
||||
|
||||
var leftTotal = leftIndicies.Sum();
|
||||
var rightTotal = rightIndicies.Sum();
|
||||
|
||||
return leftTotal.CompareTo(rightTotal);
|
||||
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndices(remoteMovie.CustomFormats).Select(i => Math.Pow(2, i)).Sum()),
|
||||
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Real),
|
||||
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Version));
|
||||
}
|
||||
|
||||
private int ComparePreferredWords(DownloadDecision x, DownloadDecision y)
|
||||
|
|
|
@ -6,6 +6,7 @@ using NzbDrone.Common.Extensions;
|
|||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
@ -26,19 +27,19 @@ namespace NzbDrone.Core.DecisionEngine
|
|||
private readonly IEnumerable<IDecisionEngineSpecification> _specifications;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IQualityDefinitionService _definitionService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadDecisionMaker(IEnumerable<IDecisionEngineSpecification> specifications,
|
||||
IParsingService parsingService,
|
||||
IConfigService configService,
|
||||
IQualityDefinitionService qualityDefinitionService,
|
||||
Logger logger)
|
||||
IParsingService parsingService,
|
||||
IConfigService configService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
Logger logger)
|
||||
{
|
||||
_specifications = specifications;
|
||||
_parsingService = parsingService;
|
||||
_configService = configService;
|
||||
_definitionService = qualityDefinitionService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -106,7 +107,7 @@ namespace NzbDrone.Core.DecisionEngine
|
|||
|
||||
result.ReleaseName = report.Title;
|
||||
var remoteMovie = result.RemoteMovie;
|
||||
|
||||
remoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(parsedMovieInfo);
|
||||
remoteMovie.Release = report;
|
||||
remoteMovie.MappingResult = result.MappingResultType;
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
@ -20,14 +22,14 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
|
||||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var formats = subject.ParsedMovieInfo.Quality.CustomFormats.WithNone();
|
||||
_logger.Debug("Checking if report meets custom format requirements. {0}", formats.ToExtendedString());
|
||||
var formats = subject.CustomFormats.Any() ? subject.CustomFormats : new List<CustomFormat> { CustomFormat.None };
|
||||
_logger.Debug("Checking if report meets custom format requirements. {0}", formats.ConcatToString());
|
||||
var notAllowedFormats = subject.Movie.Profile.FormatItems.Where(v => v.Allowed == false).Select(f => f.Format).ToList();
|
||||
var notWantedFormats = notAllowedFormats.Intersect(formats);
|
||||
if (notWantedFormats.Any())
|
||||
{
|
||||
_logger.Debug("Custom Formats {0} rejected by Movie's profile", notWantedFormats.ToExtendedString());
|
||||
return Decision.Reject("Custom Formats {0} not wanted in profile", notWantedFormats.ToExtendedString());
|
||||
_logger.Debug("Custom Formats {0} rejected by Movie's profile", notWantedFormats.ConcatToString());
|
||||
return Decision.Reject("Custom Formats {0} not wanted in profile", notWantedFormats.ConcatToString());
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
|
@ -6,12 +8,16 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
{
|
||||
public class CutoffSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly UpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly IUpgradableSpecification _upgradableSpecification;
|
||||
private readonly ICustomFormatCalculationService _formatService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CutoffSpecification(UpgradableSpecification qualityUpgradableSpecification, Logger logger)
|
||||
public CutoffSpecification(IUpgradableSpecification upgradableSpecification,
|
||||
ICustomFormatCalculationService formatService,
|
||||
Logger logger)
|
||||
{
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_upgradableSpecification = upgradableSpecification;
|
||||
_formatService = formatService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -21,17 +27,25 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var profile = subject.Movie.Profile;
|
||||
var file = subject.Movie.MovieFile;
|
||||
|
||||
if (subject.Movie.MovieFile != null)
|
||||
if (file != null)
|
||||
{
|
||||
if (!_qualityUpgradableSpecification.CutoffNotMet(profile,
|
||||
subject.Movie.MovieFile.Quality,
|
||||
subject.ParsedMovieInfo.Quality))
|
||||
file.Movie = subject.Movie;
|
||||
var customFormats = _formatService.ParseCustomFormat(file);
|
||||
|
||||
if (!_upgradableSpecification.CutoffNotMet(profile,
|
||||
file.Quality,
|
||||
customFormats,
|
||||
subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
_logger.Debug("Existing custom formats {0} meet cutoff",
|
||||
customFormats.ConcatToString());
|
||||
|
||||
var qualityCutoffIndex = profile.GetIndex(profile.Cutoff);
|
||||
var qualityCutoff = profile.Items[qualityCutoffIndex.Index];
|
||||
|
||||
return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Movie.Profile.Cutoff);
|
||||
return Decision.Reject("Existing file meets cutoff: {0}", qualityCutoff);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Queue;
|
||||
|
@ -9,15 +11,18 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
public class QueueSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IQueueService _queueService;
|
||||
private readonly UpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly UpgradableSpecification _upgradableSpecification;
|
||||
private readonly ICustomFormatCalculationService _formatService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public QueueSpecification(IQueueService queueService,
|
||||
UpgradableSpecification qualityUpgradableSpecification,
|
||||
Logger logger)
|
||||
UpgradableSpecification upgradableSpecification,
|
||||
ICustomFormatCalculationService formatService,
|
||||
Logger logger)
|
||||
{
|
||||
_queueService = queueService;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_upgradableSpecification = upgradableSpecification;
|
||||
_formatService = formatService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -36,25 +41,38 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
var remoteMovie = queueItem.RemoteMovie;
|
||||
var qualityProfile = subject.Movie.Profile;
|
||||
|
||||
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteMovie.ParsedMovieInfo.Quality);
|
||||
var customFormats = _formatService.ParseCustomFormat(remoteMovie.ParsedMovieInfo);
|
||||
|
||||
if (!_qualityUpgradableSpecification.CutoffNotMet(qualityProfile, remoteMovie.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality))
|
||||
_logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0} - {1}",
|
||||
remoteMovie.ParsedMovieInfo.Quality,
|
||||
customFormats.ConcatToString());
|
||||
|
||||
if (!_upgradableSpecification.CutoffNotMet(qualityProfile,
|
||||
remoteMovie.ParsedMovieInfo.Quality,
|
||||
customFormats,
|
||||
subject.ParsedMovieInfo.Quality))
|
||||
{
|
||||
return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteMovie.ParsedMovieInfo.Quality);
|
||||
}
|
||||
|
||||
_logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteMovie.ParsedMovieInfo.Quality);
|
||||
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(qualityProfile, remoteMovie.ParsedMovieInfo.Quality, subject.ParsedMovieInfo.Quality))
|
||||
if (!_upgradableSpecification.IsUpgradable(qualityProfile,
|
||||
remoteMovie.ParsedMovieInfo.Quality,
|
||||
remoteMovie.CustomFormats,
|
||||
subject.ParsedMovieInfo.Quality,
|
||||
subject.CustomFormats))
|
||||
{
|
||||
return Decision.Reject("Quality for release in queue is of equal or higher preference: {0}", remoteMovie.ParsedMovieInfo.Quality);
|
||||
}
|
||||
|
||||
_logger.Debug("Checking if profiles allow upgrading. Queued: {0}", remoteMovie.ParsedMovieInfo.Quality);
|
||||
|
||||
if (!_qualityUpgradableSpecification.IsUpgradeAllowed(subject.Movie.Profile,
|
||||
if (!_upgradableSpecification.IsUpgradeAllowed(subject.Movie.Profile,
|
||||
remoteMovie.ParsedMovieInfo.Quality,
|
||||
subject.ParsedMovieInfo.Quality))
|
||||
remoteMovie.CustomFormats,
|
||||
subject.ParsedMovieInfo.Quality,
|
||||
subject.CustomFormats))
|
||||
{
|
||||
return Decision.Reject("Another release is queued and the Quality profile does not allow upgrades");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
@ -12,16 +13,19 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
|||
{
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
private readonly IUpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly ICustomFormatCalculationService _formatService;
|
||||
private readonly IDelayProfileService _delayProfileService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DelaySpecification(IPendingReleaseService pendingReleaseService,
|
||||
IUpgradableSpecification qualityUpgradableSpecification,
|
||||
ICustomFormatCalculationService formatService,
|
||||
IDelayProfileService delayProfileService,
|
||||
Logger logger)
|
||||
{
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_formatService = formatService;
|
||||
_delayProfileService = delayProfileService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -65,9 +69,16 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
|||
|
||||
var comparer = new QualityModelComparer(profile);
|
||||
|
||||
if (isPreferredProtocol && (subject.Movie.MovieFileId != 0 && subject.Movie.MovieFile != null) && (preferredCount > 0 || preferredWords == null))
|
||||
var file = subject.Movie.MovieFile;
|
||||
|
||||
if (isPreferredProtocol && (subject.Movie.MovieFileId != 0 && file != null) && (preferredCount > 0 || preferredWords == null))
|
||||
{
|
||||
var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, subject.Movie.MovieFile.Quality, subject.ParsedMovieInfo.Quality);
|
||||
var customFormats = _formatService.ParseCustomFormat(file);
|
||||
var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile,
|
||||
file.Quality,
|
||||
customFormats,
|
||||
subject.ParsedMovieInfo.Quality,
|
||||
subject.CustomFormats);
|
||||
|
||||
if (upgradable)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
@ -11,17 +12,20 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
|||
public class HistorySpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly UpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly UpgradableSpecification _upgradableSpecification;
|
||||
private readonly ICustomFormatCalculationService _formatService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public HistorySpecification(IHistoryService historyService,
|
||||
UpgradableSpecification qualityUpgradableSpecification,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
UpgradableSpecification upgradableSpecification,
|
||||
ICustomFormatCalculationService formatService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
_historyService = historyService;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_upgradableSpecification = upgradableSpecification;
|
||||
_formatService = formatService;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -45,10 +49,20 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
|
|||
|
||||
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed)
|
||||
{
|
||||
var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12));
|
||||
var cutoffUnmet = _qualityUpgradableSpecification.CutoffNotMet(subject.Movie.Profile, mostRecent.Quality, subject.ParsedMovieInfo.Quality);
|
||||
var upgradeable = _qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, mostRecent.Quality, subject.ParsedMovieInfo.Quality);
|
||||
var customFormats = _formatService.ParseCustomFormat(mostRecent);
|
||||
|
||||
var cutoffUnmet = _upgradableSpecification.CutoffNotMet(subject.Movie.Profile,
|
||||
mostRecent.Quality,
|
||||
customFormats,
|
||||
subject.ParsedMovieInfo.Quality);
|
||||
|
||||
var upgradeable = _upgradableSpecification.IsUpgradable(subject.Movie.Profile,
|
||||
mostRecent.Quality,
|
||||
customFormats,
|
||||
subject.ParsedMovieInfo.Quality,
|
||||
subject.CustomFormats);
|
||||
|
||||
var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12));
|
||||
if (!recent && cdhEnabled)
|
||||
{
|
||||
return Decision.Accept();
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Profiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
|
@ -7,10 +11,11 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
{
|
||||
public interface IUpgradableSpecification
|
||||
{
|
||||
bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
|
||||
bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
|
||||
bool IsUpgradable(Profile profile, QualityModel currentQuality, List<CustomFormat> currentCustomFormats, QualityModel newQuality, List<CustomFormat> newCustomFormats);
|
||||
bool CutoffNotMet(Profile profile, QualityModel currentQuality, List<CustomFormat> currentFormats, QualityModel newQuality = null);
|
||||
bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null);
|
||||
bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality);
|
||||
bool IsUpgradeAllowed(Profile qualityProfile, QualityModel currentQuality, QualityModel newQuality);
|
||||
bool IsUpgradeAllowed(Profile qualityProfile, QualityModel currentQuality, List<CustomFormat> currentCustomFormats, QualityModel newQuality, List<CustomFormat> newCustomFormats);
|
||||
}
|
||||
|
||||
public class UpgradableSpecification : IUpgradableSpecification
|
||||
|
@ -24,38 +29,51 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
|
||||
public bool IsUpgradable(Profile profile, QualityModel currentQuality, List<CustomFormat> currentCustomFormats, QualityModel newQuality, List<CustomFormat> newCustomFormats)
|
||||
{
|
||||
if (newQuality != null)
|
||||
{
|
||||
int compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality);
|
||||
if (compare <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsRevisionUpgrade(currentQuality, newQuality))
|
||||
{
|
||||
_logger.Debug("New item has a better quality revision");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Debug("New item has a better quality");
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
|
||||
{
|
||||
var comparer = new QualityModelComparer(profile);
|
||||
var cutoffCompare = comparer.Compare(currentQuality.Quality.Id, profile.Cutoff);
|
||||
|
||||
if (cutoffCompare < 0)
|
||||
var qualityComparer = new QualityModelComparer(profile);
|
||||
var qualityCompare = qualityComparer.Compare(newQuality?.Quality, currentQuality.Quality);
|
||||
|
||||
if (qualityCompare > 0)
|
||||
{
|
||||
_logger.Debug("New item has a better quality");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (comparer.Compare(currentQuality.CustomFormats, profile.FormatCutoff) < 0)
|
||||
if (qualityCompare < 0)
|
||||
{
|
||||
_logger.Debug("Existing item has better quality, skipping");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accept unless the user doesn't want to prefer propers, optionally they can
|
||||
// use preferred words to prefer propers/repacks over non-propers/repacks.
|
||||
if (_configService.AutoDownloadPropers &&
|
||||
newQuality?.Revision.CompareTo(currentQuality.Revision) > 0)
|
||||
{
|
||||
_logger.Debug("New item has a better quality revision");
|
||||
return true;
|
||||
}
|
||||
|
||||
var customFormatCompare = new CustomFormatsComparer(profile).Compare(newCustomFormats, currentCustomFormats);
|
||||
|
||||
if (customFormatCompare <= 0)
|
||||
{
|
||||
_logger.Debug("New item's custom formats [{0}] do not improve on [{1}], skipping",
|
||||
newCustomFormats.ConcatToString(),
|
||||
currentCustomFormats.ConcatToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.Debug("New item has a custom format upgrade");
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
|
||||
{
|
||||
var cutoffCompare = new QualityModelComparer(profile).Compare(currentQuality.Quality.Id, profile.Cutoff);
|
||||
|
||||
if (cutoffCompare < 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -68,6 +86,24 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
return false;
|
||||
}
|
||||
|
||||
private bool CustomFormatCutoffNotMet(Profile profile, List<CustomFormat> currentFormats)
|
||||
{
|
||||
var cutoff = new List<CustomFormat> { profile.FormatItems.Single(x => x.Format.Id == profile.FormatCutoff).Format };
|
||||
var cutoffCompare = new CustomFormatsComparer(profile).Compare(currentFormats, cutoff);
|
||||
|
||||
if (cutoffCompare < 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CutoffNotMet(Profile profile, QualityModel currentQuality, List<CustomFormat> currentFormats, QualityModel newQuality = null)
|
||||
{
|
||||
return QualityCutoffNotMet(profile, currentQuality, newQuality) || CustomFormatCutoffNotMet(profile, currentFormats);
|
||||
}
|
||||
|
||||
public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality)
|
||||
{
|
||||
var compare = newQuality.Revision.CompareTo(currentQuality.Revision);
|
||||
|
@ -81,11 +117,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool IsUpgradeAllowed(Profile qualityProfile, QualityModel currentQuality, QualityModel newQuality)
|
||||
public bool IsUpgradeAllowed(Profile qualityProfile, QualityModel currentQuality, List<CustomFormat> currentCustomFormats, QualityModel newQuality, List<CustomFormat> newCustomFormats)
|
||||
{
|
||||
var isQualityUpgrade = new QualityModelComparer(qualityProfile).Compare(newQuality, currentQuality) > 0;
|
||||
var isCustomFormatUpgrade = new CustomFormatsComparer(qualityProfile).Compare(newCustomFormats, currentCustomFormats) > 0;
|
||||
|
||||
if (isQualityUpgrade && qualityProfile.UpgradeAllowed)
|
||||
if ((isQualityUpgrade || isCustomFormatUpgrade) && qualityProfile.UpgradeAllowed)
|
||||
{
|
||||
_logger.Debug("Quality profile allows upgrading");
|
||||
return true;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
|
@ -7,11 +9,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
public class UpgradeAllowedSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly UpgradableSpecification _upgradableSpecification;
|
||||
private readonly ICustomFormatCalculationService _formatService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UpgradeAllowedSpecification(UpgradableSpecification upgradableSpecification, Logger logger)
|
||||
public UpgradeAllowedSpecification(UpgradableSpecification upgradableSpecification,
|
||||
ICustomFormatCalculationService formatService,
|
||||
Logger logger)
|
||||
{
|
||||
_upgradableSpecification = upgradableSpecification;
|
||||
_formatService = formatService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -32,11 +38,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
return Decision.Accept();
|
||||
}
|
||||
|
||||
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
|
||||
file.Movie = subject.Movie;
|
||||
var customFormats = _formatService.ParseCustomFormat(file);
|
||||
_logger.Debug("Comparing file quality with report. Existing file is {0} [{1}]", file.Quality, customFormats.ConcatToString());
|
||||
|
||||
if (!_upgradableSpecification.IsUpgradeAllowed(qualityProfile,
|
||||
file.Quality,
|
||||
subject.ParsedMovieInfo.Quality))
|
||||
customFormats,
|
||||
subject.ParsedMovieInfo.Quality,
|
||||
subject.CustomFormats))
|
||||
{
|
||||
_logger.Debug("Upgrading is not allowed by the quality profile");
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
|
@ -7,11 +9,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
public class UpgradeDiskSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly UpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly ICustomFormatCalculationService _formatService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UpgradeDiskSpecification(UpgradableSpecification qualityUpgradableSpecification, Logger logger)
|
||||
public UpgradeDiskSpecification(UpgradableSpecification qualityUpgradableSpecification,
|
||||
ICustomFormatCalculationService formatService,
|
||||
Logger logger)
|
||||
{
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_formatService = formatService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -25,12 +31,19 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var profile = subject.Movie.Profile;
|
||||
var file = subject.Movie.MovieFile;
|
||||
_logger.Debug("Comparing file quality with report. Existing file is {0}", file.Quality);
|
||||
file.Movie = subject.Movie;
|
||||
var customFormats = _formatService.ParseCustomFormat(file);
|
||||
_logger.Debug("Comparing file quality with report. Existing file is {0} [{1}]", file.Quality, customFormats.ConcatToString());
|
||||
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(subject.Movie.Profile, file.Quality, subject.ParsedMovieInfo.Quality))
|
||||
if (!_qualityUpgradableSpecification.IsUpgradable(profile,
|
||||
file.Quality,
|
||||
customFormats,
|
||||
subject.ParsedMovieInfo.Quality,
|
||||
subject.CustomFormats))
|
||||
{
|
||||
return Decision.Reject("Quality for existing file on disk is of equal or higher preference: {0}", file.Quality);
|
||||
return Decision.Reject("Quality for existing file on disk is of equal or higher preference: {0} [{1}]", file.Quality, customFormats.ConcatToString());
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
|
|
|
@ -5,6 +5,7 @@ using NLog;
|
|||
using NzbDrone.Common.Crypto;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Jobs;
|
||||
|
@ -43,18 +44,20 @@ namespace NzbDrone.Core.Download.Pending
|
|||
private readonly IDelayProfileService _delayProfileService;
|
||||
private readonly ITaskManager _taskManager;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public PendingReleaseService(IIndexerStatusService indexerStatusService,
|
||||
IPendingReleaseRepository repository,
|
||||
IMovieService movieService,
|
||||
IParsingService parsingService,
|
||||
IDelayProfileService delayProfileService,
|
||||
ITaskManager taskManager,
|
||||
IConfigService configService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
IPendingReleaseRepository repository,
|
||||
IMovieService movieService,
|
||||
IParsingService parsingService,
|
||||
IDelayProfileService delayProfileService,
|
||||
ITaskManager taskManager,
|
||||
IConfigService configService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_indexerStatusService = indexerStatusService;
|
||||
_repository = repository;
|
||||
|
@ -63,6 +66,7 @@ namespace NzbDrone.Core.Download.Pending
|
|||
_delayProfileService = delayProfileService;
|
||||
_taskManager = taskManager;
|
||||
_configService = configService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -159,6 +163,8 @@ namespace NzbDrone.Core.Download.Pending
|
|||
{
|
||||
if (pendingRelease.RemoteMovie != null)
|
||||
{
|
||||
pendingRelease.RemoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(pendingRelease.ParsedMovieInfo);
|
||||
|
||||
var ect = pendingRelease.Release.PublishDate.AddMinutes(GetDelay(pendingRelease.RemoteMovie));
|
||||
|
||||
if (ect < nextRssSync.Value)
|
||||
|
|
|
@ -5,6 +5,7 @@ using NLog;
|
|||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
@ -27,20 +28,23 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
private readonly IHistoryService _historyService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IConfigService _config;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly Logger _logger;
|
||||
private readonly ICached<TrackedDownload> _cache;
|
||||
|
||||
public TrackedDownloadService(IParsingService parsingService,
|
||||
ICacheManager cacheManager,
|
||||
IHistoryService historyService,
|
||||
IConfigService config,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
ICacheManager cacheManager,
|
||||
IHistoryService historyService,
|
||||
IConfigService config,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_parsingService = parsingService;
|
||||
_historyService = historyService;
|
||||
_cache = cacheManager.GetCache<TrackedDownload>(GetType());
|
||||
_config = config;
|
||||
_formatCalculator = formatCalculator;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -129,6 +133,12 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||
}
|
||||
}
|
||||
|
||||
// Calculate custom formats
|
||||
if (trackedDownload.RemoteMovie != null)
|
||||
{
|
||||
trackedDownload.RemoteMovie.CustomFormats = _formatCalculator.ParseCustomFormat(parsedMovieInfo);
|
||||
}
|
||||
|
||||
// Track it so it can be displayed in the queue even though we can't determine which movie it is for
|
||||
if (trackedDownload.RemoteMovie == null)
|
||||
{
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Commands
|
||||
{
|
||||
public class UpdateMovieFileQualityCommand : Command
|
||||
{
|
||||
public IEnumerable<int> MovieFileIds { get; set; }
|
||||
|
||||
public override bool SendUpdatesToClient => true;
|
||||
|
||||
public UpdateMovieFileQualityCommand(IEnumerable<int> movieFileIds)
|
||||
{
|
||||
MovieFileIds = movieFileIds;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using NzbDrone.Core.Datastore;
|
|||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
|
@ -18,6 +19,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
public DateTime DateAdded { get; set; }
|
||||
public string SceneName { get; set; }
|
||||
public string ReleaseGroup { get; set; }
|
||||
public IndexerFlags IndexerFlags { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public MediaInfoModel MediaInfo { get; set; }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
@ -33,7 +32,6 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
|||
var modifier = Modifier.NONE;
|
||||
var modifierConfidence = Confidence.Default;
|
||||
var revison = new Revision();
|
||||
var customFormats = new List<CustomFormat>();
|
||||
|
||||
foreach (var augmentedQuality in augmentedQualities)
|
||||
{
|
||||
|
@ -62,18 +60,11 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators
|
|||
{
|
||||
revison = augmentedQuality.Revision;
|
||||
}
|
||||
|
||||
if (augmentedQuality.CustomFormats != null)
|
||||
{
|
||||
var newFormats = augmentedQuality.CustomFormats.Where(c => !customFormats.Any(p => p.Id == c.Id));
|
||||
|
||||
customFormats.AddRange(newFormats);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Trace("Finding quality. Source: {0}. Resolution: {1}. Modifier {2}", source, resolution, modifier);
|
||||
|
||||
var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution, modifier), revison, customFormats);
|
||||
var quality = new QualityModel(QualityFinder.FindBySourceAndResolution(source, resolution, modifier), revison);
|
||||
|
||||
if (resolutionConfidence == Confidence.MediaInfo)
|
||||
{
|
||||
|
|
|
@ -19,8 +19,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenter
|
|||
Confidence.Tag,
|
||||
quality.Quality.Modifier,
|
||||
Confidence.Tag,
|
||||
quality.Revision,
|
||||
quality.CustomFormats);
|
||||
quality.Revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenter
|
|||
confidence,
|
||||
quality.Quality.Modifier,
|
||||
confidence,
|
||||
quality.Revision,
|
||||
quality.CustomFormats);
|
||||
quality.Revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenter
|
|||
Confidence.Tag,
|
||||
quality.Quality.Modifier,
|
||||
Confidence.Tag,
|
||||
quality.Revision,
|
||||
quality.CustomFormats);
|
||||
quality.Revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenters.Quality
|
||||
|
@ -14,16 +12,13 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenter
|
|||
public Confidence ModifierConfidence { get; set; }
|
||||
public Revision Revision { get; set; }
|
||||
|
||||
public List<CustomFormat> CustomFormats { get; set; }
|
||||
|
||||
public AugmentQualityResult(Source source,
|
||||
Confidence sourceConfidence,
|
||||
int resolution,
|
||||
Confidence resolutionConfidence,
|
||||
Modifier modifier,
|
||||
Confidence modifierConfidence,
|
||||
Revision revision,
|
||||
List<CustomFormat> customFormats)
|
||||
Revision revision)
|
||||
{
|
||||
Source = source;
|
||||
SourceConfidence = sourceConfidence;
|
||||
|
@ -32,22 +27,21 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Aggregation.Aggregators.Augmenter
|
|||
Modifier = modifier;
|
||||
ModifierConfidence = modifierConfidence;
|
||||
Revision = revision;
|
||||
CustomFormats = customFormats;
|
||||
}
|
||||
|
||||
public static AugmentQualityResult SourceOnly(Source source, Confidence sourceConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(source, sourceConfidence, 0, Confidence.Default, Modifier.NONE, Confidence.Default, null, null);
|
||||
return new AugmentQualityResult(source, sourceConfidence, 0, Confidence.Default, Modifier.NONE, Confidence.Default, null);
|
||||
}
|
||||
|
||||
public static AugmentQualityResult ResolutionOnly(int resolution, Confidence resolutionConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, resolution, resolutionConfidence, Modifier.NONE, Confidence.Default, null, null);
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, resolution, resolutionConfidence, Modifier.NONE, Confidence.Default, null);
|
||||
}
|
||||
|
||||
public static AugmentQualityResult ModifierOnly(Modifier modifier, Confidence modifierConfidence)
|
||||
{
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, 0, Confidence.Default, modifier, modifierConfidence, null, null);
|
||||
return new AugmentQualityResult(Source.UNKNOWN, Confidence.Default, 0, Confidence.Default, modifier, modifierConfidence, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using NzbDrone.Common.Disk;
|
|||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Extras;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
@ -26,20 +27,23 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
|||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IExtraService _extraService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ImportApprovedMovie(IUpgradeMediaFiles movieFileUpgrader,
|
||||
IMediaFileService mediaFileService,
|
||||
IExtraService extraService,
|
||||
IDiskProvider diskProvider,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
IMediaFileService mediaFileService,
|
||||
IExtraService extraService,
|
||||
IDiskProvider diskProvider,
|
||||
IHistoryService historyService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_movieFileUpgrader = movieFileUpgrader;
|
||||
_mediaFileService = mediaFileService;
|
||||
_extraService = extraService;
|
||||
_diskProvider = diskProvider;
|
||||
_historyService = historyService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
@ -85,6 +89,18 @@ namespace NzbDrone.Core.MediaFiles.MovieImport
|
|||
movieFile.ReleaseGroup = localMovie.ReleaseGroup;
|
||||
movieFile.Edition = localMovie.Edition;
|
||||
|
||||
if (downloadClientItem?.DownloadId.IsNotNullOrWhiteSpace() == true)
|
||||
{
|
||||
var grabHistory = _historyService.FindByDownloadId(downloadClientItem.DownloadId)
|
||||
.OrderByDescending(h => h.Date)
|
||||
.FirstOrDefault(h => h.EventType == HistoryEventType.Grabbed);
|
||||
|
||||
if (Enum.TryParse(grabHistory?.Data.GetValueOrDefault("indexerFlags"), true, out IndexerFlags flags))
|
||||
{
|
||||
movieFile.IndexerFlags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
bool copyOnly;
|
||||
switch (importMode)
|
||||
{
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public interface IUpdateMovieFileQualityService
|
||||
{
|
||||
}
|
||||
|
||||
public class UpdateMovieFileQualityService : IUpdateMovieFileQualityService, IExecute<UpdateMovieFileQualityCommand>
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public UpdateMovieFileQualityService(IMediaFileService mediaFileService,
|
||||
IHistoryService historyService,
|
||||
IParsingService parsingService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_mediaFileService = mediaFileService;
|
||||
_historyService = historyService;
|
||||
_parsingService = parsingService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
//TODO add some good tests for this!
|
||||
public void Execute(UpdateMovieFileQualityCommand command)
|
||||
{
|
||||
var movieFiles = _mediaFileService.GetMovies(command.MovieFileIds);
|
||||
|
||||
var count = 1;
|
||||
|
||||
foreach (var movieFile in movieFiles)
|
||||
{
|
||||
_logger.ProgressInfo("Updating quality for {0}/{1} files.", count, movieFiles.Count);
|
||||
|
||||
var history = _historyService.GetByMovieId(movieFile.MovieId, null).OrderByDescending(h => h.Date);
|
||||
var latestImported = history.FirstOrDefault(h => h.EventType == HistoryEventType.DownloadFolderImported);
|
||||
var latestImportedName = latestImported?.SourceTitle;
|
||||
var latestGrabbed = history.FirstOrDefault(h => h.EventType == HistoryEventType.Grabbed);
|
||||
var sizeMovie = new LocalMovie();
|
||||
sizeMovie.Size = movieFile.Size;
|
||||
|
||||
var helpers = new List<object> { sizeMovie };
|
||||
|
||||
if (movieFile.MediaInfo != null)
|
||||
{
|
||||
helpers.Add(movieFile.MediaInfo);
|
||||
}
|
||||
|
||||
if (latestGrabbed != null)
|
||||
{
|
||||
helpers.Add(latestGrabbed);
|
||||
}
|
||||
|
||||
ParsedMovieInfo parsedMovieInfo = null;
|
||||
|
||||
if (latestImportedName?.IsNotNullOrWhiteSpace() == true)
|
||||
{
|
||||
parsedMovieInfo = _parsingService.ParseMovieInfo(latestImportedName, helpers);
|
||||
}
|
||||
|
||||
if (parsedMovieInfo == null)
|
||||
{
|
||||
_logger.Debug("Could not parse movie info from history source title, using current path instead: {0}.", movieFile.RelativePath);
|
||||
parsedMovieInfo = _parsingService.ParseMovieInfo(movieFile.RelativePath, helpers);
|
||||
}
|
||||
|
||||
//Only update Custom formats for now.
|
||||
if (parsedMovieInfo != null)
|
||||
{
|
||||
movieFile.Quality.CustomFormats = parsedMovieInfo.Quality.CustomFormats;
|
||||
_mediaFileService.Update(movieFile);
|
||||
_eventAggregator.PublishEvent(new MovieFileUpdatedEvent(movieFile));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn("Could not update custom formats for {0}, since it's title could not be parsed!", movieFile);
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Parser.Augmenters
|
||||
{
|
||||
public class AugmentWithAdditionalFormats : IAugmentParsedMovieInfo
|
||||
{
|
||||
public Type HelperType
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(CustomFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper)
|
||||
{
|
||||
if (helper is CustomFormat format)
|
||||
{
|
||||
if (movieInfo.ExtraInfo.GetValueOrDefault("AdditionalFormats") is List<CustomFormat> existing)
|
||||
{
|
||||
existing.Add(format);
|
||||
movieInfo.ExtraInfo["AdditionalFormats"] = existing;
|
||||
}
|
||||
else
|
||||
{
|
||||
movieInfo.ExtraInfo["AdditionalFormats"] = new List<CustomFormat> { format };
|
||||
}
|
||||
}
|
||||
|
||||
return movieInfo;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,12 +30,6 @@ namespace NzbDrone.Core.Parser.Augmenters
|
|||
movieInfo.Edition = otherInfo.Edition;
|
||||
}
|
||||
|
||||
if (otherInfo.Quality != null)
|
||||
{
|
||||
movieInfo.Quality.CustomFormats = movieInfo.Quality.CustomFormats.Union(otherInfo.Quality.CustomFormats)
|
||||
.Distinct().ToList();
|
||||
}
|
||||
|
||||
if (otherInfo.ReleaseGroup.IsNotNullOrWhiteSpace() && movieInfo.ReleaseGroup.IsNullOrWhiteSpace())
|
||||
{
|
||||
movieInfo.ReleaseGroup = otherInfo.ReleaseGroup;
|
||||
|
|
|
@ -10,14 +10,14 @@ namespace NzbDrone.Core.Parser.Model
|
|||
public string MovieTitle { get; set; }
|
||||
public string SimpleReleaseTitle { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, object> ExtraInfo = new Dictionary<string, object>();
|
||||
public List<Language> Languages = new List<Language>();
|
||||
public List<Language> Languages { get; set; } = new List<Language>();
|
||||
public string ReleaseGroup { get; set; }
|
||||
public string ReleaseHash { get; set; }
|
||||
public string Edition { get; set; }
|
||||
public int Year { get; set; }
|
||||
public string ImdbId { get; set; }
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, object> ExtraInfo { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Download.Clients;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
|
@ -7,6 +9,7 @@ namespace NzbDrone.Core.Parser.Model
|
|||
{
|
||||
public ReleaseInfo Release { get; set; }
|
||||
public ParsedMovieInfo ParsedMovieInfo { get; set; }
|
||||
public List<CustomFormat> CustomFormats { get; set; }
|
||||
public Movie Movie { get; set; }
|
||||
public MappingResultType MappingResult { get; set; }
|
||||
public bool DownloadAllowed { get; set; }
|
||||
|
|
|
@ -385,7 +385,7 @@ namespace NzbDrone.Core.Parser
|
|||
return ReplaceGermanUmlauts(NormalizeRegex.Replace(title, string.Empty).ToLower()).RemoveAccent();
|
||||
}
|
||||
|
||||
public static string NormalizeEpisodeTitle(string title)
|
||||
public static string NormalizeEpisodeTitle(this string title)
|
||||
{
|
||||
title = SpecialEpisodeWordRegex.Replace(title, string.Empty);
|
||||
title = PunctuationRegex.Replace(title, " ");
|
||||
|
@ -395,7 +395,7 @@ namespace NzbDrone.Core.Parser
|
|||
.ToLower();
|
||||
}
|
||||
|
||||
public static string NormalizeTitle(string title)
|
||||
public static string NormalizeTitle(this string title)
|
||||
{
|
||||
title = WordDelimiterRegex.Replace(title, " ");
|
||||
title = PunctuationRegex.Replace(title, string.Empty);
|
||||
|
@ -406,6 +406,11 @@ namespace NzbDrone.Core.Parser
|
|||
return title.Trim().ToLower();
|
||||
}
|
||||
|
||||
public static string SimplifyReleaseTitle(this string title)
|
||||
{
|
||||
return SimpleReleaseTitleRegex.Replace(title, string.Empty);
|
||||
}
|
||||
|
||||
public static string ParseReleaseGroup(string title)
|
||||
{
|
||||
title = title.Trim();
|
||||
|
|
|
@ -3,9 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
@ -13,7 +11,6 @@ using NzbDrone.Core.Movies.AlternativeTitles;
|
|||
using NzbDrone.Core.Parser.Augmenters;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Parser.RomanNumerals;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace NzbDrone.Core.Parser
|
||||
{
|
||||
|
@ -25,31 +22,24 @@ namespace NzbDrone.Core.Parser
|
|||
ParsedMovieInfo EnhanceMovieInfo(ParsedMovieInfo parsedMovieInfo, List<object> helpers = null);
|
||||
ParsedMovieInfo ParseMinimalMovieInfo(string path, bool isDir = false);
|
||||
ParsedMovieInfo ParseMinimalPathMovieInfo(string path);
|
||||
List<FormatTagMatchResult> MatchFormatTags(ParsedMovieInfo movieInfo);
|
||||
}
|
||||
|
||||
public class ParsingService : IParsingService
|
||||
{
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IConfigService _config;
|
||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly IEnumerable<IAugmentParsedMovieInfo> _augmenters;
|
||||
private readonly Logger _logger;
|
||||
private static HashSet<ArabicRomanNumeral> _arabicRomanNumeralMappings;
|
||||
|
||||
public ParsingService(
|
||||
IMovieService movieService,
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly IConfigService _config;
|
||||
private readonly IEnumerable<IAugmentParsedMovieInfo> _augmenters;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ParsingService(IMovieService movieService,
|
||||
IConfigService configService,
|
||||
IQualityDefinitionService qualityDefinitionService,
|
||||
ICustomFormatService formatService,
|
||||
IEnumerable<IAugmentParsedMovieInfo> augmenters,
|
||||
Logger logger)
|
||||
{
|
||||
_movieService = movieService;
|
||||
_config = configService;
|
||||
_qualityDefinitionService = qualityDefinitionService;
|
||||
_formatService = formatService;
|
||||
_augmenters = augmenters;
|
||||
_logger = logger;
|
||||
|
||||
|
@ -80,15 +70,6 @@ namespace NzbDrone.Core.Parser
|
|||
minimalInfo = AugmentMovieInfo(minimalInfo, helpers);
|
||||
}
|
||||
|
||||
// minimalInfo.Quality.Quality = QualityFinder.FindBySourceAndResolution(minimalInfo.Quality.Quality.Source, minimalInfo.Quality.Quality.Resolution,
|
||||
// minimalInfo.Quality.Quality.Modifier);
|
||||
if (minimalInfo != null)
|
||||
{
|
||||
minimalInfo.Quality.CustomFormats = ParseCustomFormat(minimalInfo);
|
||||
|
||||
_logger.Debug("Quality parsed: {0}", minimalInfo.Quality);
|
||||
}
|
||||
|
||||
return minimalInfo;
|
||||
}
|
||||
|
||||
|
@ -105,41 +86,6 @@ namespace NzbDrone.Core.Parser
|
|||
return minimalInfo;
|
||||
}
|
||||
|
||||
private List<CustomFormat> ParseCustomFormat(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
var matches = MatchFormatTags(movieInfo);
|
||||
var goodMatches = matches.Where(m => m.GoodMatch);
|
||||
return goodMatches.Select(r => r.CustomFormat).ToList();
|
||||
}
|
||||
|
||||
public List<FormatTagMatchResult> MatchFormatTags(ParsedMovieInfo movieInfo)
|
||||
{
|
||||
var formats = _formatService.All();
|
||||
|
||||
if (movieInfo.ExtraInfo.GetValueOrDefault("AdditionalFormats") is List<CustomFormat> additionalFormats)
|
||||
{
|
||||
formats.AddRange(additionalFormats);
|
||||
}
|
||||
|
||||
var matches = new List<FormatTagMatchResult>();
|
||||
|
||||
foreach (var customFormat in formats)
|
||||
{
|
||||
var formatMatches = customFormat.FormatTags.GroupBy(t => t.TagType).Select(g =>
|
||||
new FormatTagMatchesGroup(g.Key, g.ToList().ToDictionary(t => t, t => t.DoesItMatch(movieInfo))));
|
||||
|
||||
var formatTagMatchesGroups = formatMatches.ToList();
|
||||
matches.Add(new FormatTagMatchResult
|
||||
{
|
||||
CustomFormat = customFormat,
|
||||
GroupMatches = formatTagMatchesGroups,
|
||||
GoodMatch = formatTagMatchesGroups.All(g => g.DidMatch)
|
||||
});
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
public ParsedMovieInfo ParseMinimalMovieInfo(string file, bool isDir = false)
|
||||
{
|
||||
return Parser.ParseMovieTitle(file, _config.ParsingLeniency > 0, isDir);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
@ -73,5 +74,11 @@ namespace NzbDrone.Core.Profiles
|
|||
|
||||
return new QualityIndex();
|
||||
}
|
||||
|
||||
public List<int> GetIndices(List<CustomFormat> formats)
|
||||
{
|
||||
var allFormats = formats.Any() ? formats : new List<CustomFormat> { CustomFormat.None };
|
||||
return allFormats.Select(f => FormatItems.FindIndex(v => Equals(v.Format, f))).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
|
@ -10,9 +14,34 @@ namespace NzbDrone.Core.Profiles
|
|||
|
||||
public class ProfileRepository : BasicRepository<Profile>, IProfileRepository
|
||||
{
|
||||
public ProfileRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||
private readonly ICustomFormatService _customFormatService;
|
||||
|
||||
public ProfileRepository(IMainDatabase database,
|
||||
IEventAggregator eventAggregator,
|
||||
ICustomFormatService customFormatService)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
_customFormatService = customFormatService;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Profile> GetResults(SqlBuilder.Template sql)
|
||||
{
|
||||
var cfs = _customFormatService.All().ToDictionary(c => c.Id);
|
||||
|
||||
var profiles = base.GetResults(sql);
|
||||
|
||||
// Do the conversions from Id to full CustomFormat object here instead of in
|
||||
// CustomFormatIntConverter to remove need to for a static property containing
|
||||
// all the custom formats
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
foreach (var formatItem in profile.FormatItems)
|
||||
{
|
||||
formatItem.Format = formatItem.Format.Id == 0 ? CustomFormat.None : cfs[formatItem.Format.Id];
|
||||
}
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
public bool Exists(int id)
|
||||
|
|
|
@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.CustomFormats.Events;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
@ -15,8 +16,6 @@ namespace NzbDrone.Core.Profiles
|
|||
{
|
||||
Profile Add(Profile profile);
|
||||
void Update(Profile profile);
|
||||
void AddCustomFormat(CustomFormat format);
|
||||
void DeleteCustomFormat(int formatId);
|
||||
void Delete(int id);
|
||||
List<Profile> All();
|
||||
Profile Get(int id);
|
||||
|
@ -24,24 +23,27 @@ namespace NzbDrone.Core.Profiles
|
|||
Profile GetDefaultProfile(string name, Quality cutoff = null, params Quality[] allowed);
|
||||
}
|
||||
|
||||
public class ProfileService : IProfileService, IHandle<ApplicationStartedEvent>
|
||||
public class ProfileService : IProfileService,
|
||||
IHandle<ApplicationStartedEvent>,
|
||||
IHandle<CustomFormatAddedEvent>,
|
||||
IHandle<CustomFormatDeletedEvent>
|
||||
{
|
||||
private readonly IProfileRepository _profileRepository;
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly INetImportFactory _netImportFactory;
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public ProfileService(IProfileRepository profileRepository,
|
||||
IMovieService movieService,
|
||||
INetImportFactory netImportFactory,
|
||||
ICustomFormatService formatService,
|
||||
Logger logger)
|
||||
ICustomFormatService formatService,
|
||||
IMovieService movieService,
|
||||
INetImportFactory netImportFactory,
|
||||
Logger logger)
|
||||
{
|
||||
_profileRepository = profileRepository;
|
||||
_formatService = formatService;
|
||||
_movieService = movieService;
|
||||
_netImportFactory = netImportFactory;
|
||||
_formatService = formatService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -55,36 +57,6 @@ namespace NzbDrone.Core.Profiles
|
|||
_profileRepository.Update(profile);
|
||||
}
|
||||
|
||||
public void AddCustomFormat(CustomFormat customFormat)
|
||||
{
|
||||
var all = All();
|
||||
foreach (var profile in all)
|
||||
{
|
||||
profile.FormatItems.Add(new ProfileFormatItem
|
||||
{
|
||||
Allowed = true,
|
||||
Format = customFormat
|
||||
});
|
||||
|
||||
Update(profile);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteCustomFormat(int formatId)
|
||||
{
|
||||
var all = All();
|
||||
foreach (var profile in all)
|
||||
{
|
||||
profile.FormatItems = profile.FormatItems.Where(c => c.Format.Id != formatId).ToList();
|
||||
if (profile.FormatCutoff == formatId)
|
||||
{
|
||||
profile.FormatCutoff = CustomFormat.None.Id;
|
||||
}
|
||||
|
||||
Update(profile);
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
if (_movieService.GetAllMovies().Any(c => c.ProfileId == id) || _netImportFactory.All().Any(c => c.ProfileId == id))
|
||||
|
@ -110,10 +82,38 @@ namespace NzbDrone.Core.Profiles
|
|||
return _profileRepository.Exists(id);
|
||||
}
|
||||
|
||||
public void Handle(CustomFormatAddedEvent message)
|
||||
{
|
||||
var all = All();
|
||||
foreach (var profile in all)
|
||||
{
|
||||
profile.FormatItems.Add(new ProfileFormatItem
|
||||
{
|
||||
Allowed = true,
|
||||
Format = message.CustomFormat
|
||||
});
|
||||
|
||||
Update(profile);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(CustomFormatDeletedEvent message)
|
||||
{
|
||||
var all = All();
|
||||
foreach (var profile in all)
|
||||
{
|
||||
profile.FormatItems = profile.FormatItems.Where(c => c.Format.Id != message.CustomFormat.Id).ToList();
|
||||
if (profile.FormatCutoff == message.CustomFormat.Id)
|
||||
{
|
||||
profile.FormatCutoff = CustomFormat.None.Id;
|
||||
}
|
||||
|
||||
Update(profile);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ApplicationStartedEvent message)
|
||||
{
|
||||
// Hack to force custom formats to be loaded into memory, if you have a better solution please let me know.
|
||||
_formatService.All();
|
||||
if (All().Any())
|
||||
{
|
||||
return;
|
||||
|
@ -207,9 +207,7 @@ namespace NzbDrone.Core.Profiles
|
|||
public Profile GetDefaultProfile(string name, Quality cutoff = null, params Quality[] allowed)
|
||||
{
|
||||
var groupedQualites = Quality.DefaultQualityDefinitions.GroupBy(q => q.Weight);
|
||||
var formats = _formatService.All();
|
||||
var items = new List<ProfileQualityItem>();
|
||||
var formatItems = new List<ProfileFormatItem>();
|
||||
var groupId = 1000;
|
||||
var profileCutoff = cutoff == null ? Quality.Unknown.Id : cutoff.Id;
|
||||
|
||||
|
@ -245,15 +243,20 @@ namespace NzbDrone.Core.Profiles
|
|||
groupId++;
|
||||
}
|
||||
|
||||
foreach (var format in formats)
|
||||
var formatItems = new List<ProfileFormatItem>
|
||||
{
|
||||
formatItems.Add(new ProfileFormatItem
|
||||
new ProfileFormatItem
|
||||
{
|
||||
Id = format.Id,
|
||||
Format = format,
|
||||
Allowed = false
|
||||
});
|
||||
}
|
||||
Id = 0,
|
||||
Allowed = true,
|
||||
Format = CustomFormat.None
|
||||
}
|
||||
}.Concat(_formatService.All().Select(format => new ProfileFormatItem
|
||||
{
|
||||
Id = format.Id,
|
||||
Allowed = false,
|
||||
Format = format
|
||||
})).ToList();
|
||||
|
||||
var qualityProfile = new Profile
|
||||
{
|
||||
|
@ -262,19 +265,9 @@ namespace NzbDrone.Core.Profiles
|
|||
Items = items,
|
||||
Language = Language.English,
|
||||
FormatCutoff = CustomFormat.None.Id,
|
||||
FormatItems = new List<ProfileFormatItem>
|
||||
{
|
||||
new ProfileFormatItem
|
||||
{
|
||||
Id = 0,
|
||||
Allowed = true,
|
||||
Format = CustomFormat.None
|
||||
}
|
||||
}
|
||||
FormatItems = formatItems
|
||||
};
|
||||
|
||||
qualityProfile.FormatItems.AddRange(formatItems);
|
||||
|
||||
return qualityProfile;
|
||||
}
|
||||
|
||||
|
|
12
src/NzbDrone.Core/Qualities/Modifier.cs
Normal file
12
src/NzbDrone.Core/Qualities/Modifier.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace NzbDrone.Core.Qualities
|
||||
{
|
||||
public enum Modifier
|
||||
{
|
||||
NONE = 0,
|
||||
REGIONAL,
|
||||
SCREENER,
|
||||
RAWHD,
|
||||
BRDISK,
|
||||
REMUX
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Qualities
|
||||
|
@ -10,8 +8,6 @@ namespace NzbDrone.Core.Qualities
|
|||
{
|
||||
public Quality Quality { get; set; }
|
||||
|
||||
public List<CustomFormat> CustomFormats { get; set; }
|
||||
|
||||
public Revision Revision { get; set; }
|
||||
|
||||
public string HardcodedSubs { get; set; }
|
||||
|
@ -24,16 +20,15 @@ namespace NzbDrone.Core.Qualities
|
|||
{
|
||||
}
|
||||
|
||||
public QualityModel(Quality quality, Revision revision = null, List<CustomFormat> customFormats = null)
|
||||
public QualityModel(Quality quality, Revision revision = null)
|
||||
{
|
||||
Quality = quality;
|
||||
Revision = revision ?? new Revision();
|
||||
CustomFormats = customFormats ?? new List<CustomFormat>();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} {1} ({2})", Quality, Revision, CustomFormats.WithNone().ToExtendedString());
|
||||
return string.Format("{0} {1}", Quality, Revision);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Profiles;
|
||||
|
||||
namespace NzbDrone.Core.Qualities
|
||||
{
|
||||
public class QualityModelComparer : IComparer<Quality>, IComparer<QualityModel>, IComparer<CustomFormat>, IComparer<List<CustomFormat>>
|
||||
public class QualityModelComparer : IComparer<Quality>, IComparer<QualityModel>
|
||||
{
|
||||
private readonly Profile _profile;
|
||||
|
||||
|
@ -50,49 +48,10 @@ namespace NzbDrone.Core.Qualities
|
|||
|
||||
if (result == 0)
|
||||
{
|
||||
result = Compare(left.CustomFormats, right.CustomFormats);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
result = left.Revision.CompareTo(right.Revision);
|
||||
}
|
||||
result = left.Revision.CompareTo(right.Revision);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int Compare(List<CustomFormat> left, List<CustomFormat> right)
|
||||
{
|
||||
List<int> leftIndicies = GetIndicies(left, _profile);
|
||||
List<int> rightIndicies = GetIndicies(right, _profile);
|
||||
|
||||
int leftTotal = leftIndicies.Sum();
|
||||
int rightTotal = rightIndicies.Sum();
|
||||
|
||||
return leftTotal.CompareTo(rightTotal);
|
||||
}
|
||||
|
||||
public static List<int> GetIndicies(List<CustomFormat> formats, Profile profile)
|
||||
{
|
||||
return formats.WithNone().Select(f => profile.FormatItems.FindIndex(v => Equals(v.Format, f))).ToList();
|
||||
}
|
||||
|
||||
public int Compare(CustomFormat left, CustomFormat right)
|
||||
{
|
||||
var leftIndex = _profile.FormatItems.FindIndex(v => Equals(v.Format, left));
|
||||
var rightIndex = _profile.FormatItems.FindIndex(v => Equals(v.Format, right));
|
||||
|
||||
return leftIndex.CompareTo(rightIndex);
|
||||
}
|
||||
|
||||
public int Compare(List<CustomFormat> left, int right)
|
||||
{
|
||||
left = left.WithNone();
|
||||
|
||||
var leftIndicies = GetIndicies(left, _profile);
|
||||
var rightIndex = _profile.FormatItems.FindIndex(v => Equals(v.Format.Id, right));
|
||||
|
||||
return leftIndicies.Select(i => i.CompareTo(rightIndex)).Max();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
src/NzbDrone.Core/Qualities/Source.cs
Normal file
16
src/NzbDrone.Core/Qualities/Source.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace NzbDrone.Core.Qualities
|
||||
{
|
||||
public enum Source
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
CAM,
|
||||
TELESYNC,
|
||||
TELECINE,
|
||||
WORKPRINT,
|
||||
DVD,
|
||||
TV,
|
||||
WEBDL,
|
||||
WEBRIP,
|
||||
BLURAY
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ using NUnit.Framework;
|
|||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
|
@ -38,11 +37,6 @@ namespace NzbDrone.App.Test
|
|||
// set up a dummy broadcaster to allow tests to resolve
|
||||
var mockBroadcaster = new Mock<IBroadcastSignalRMessage>();
|
||||
_container.Register<IBroadcastSignalRMessage>(mockBroadcaster.Object);
|
||||
|
||||
// A dummy custom format repository since this isn't a DB test
|
||||
var mockCustomFormat = Mocker.GetMock<ICustomFormatRepository>();
|
||||
mockCustomFormat.Setup(x => x.All()).Returns(new List<CustomFormatDefinition>());
|
||||
_container.Register<ICustomFormatRepository>(mockCustomFormat.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using Radarr.Http;
|
||||
|
||||
|
@ -7,10 +8,14 @@ namespace Radarr.Api.V3.Blacklist
|
|||
public class BlacklistModule : RadarrRestModule<BlacklistResource>
|
||||
{
|
||||
private readonly IBlacklistService _blacklistService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
|
||||
public BlacklistModule(IBlacklistService blacklistService)
|
||||
public BlacklistModule(IBlacklistService blacklistService,
|
||||
ICustomFormatCalculationService formatCalculator)
|
||||
{
|
||||
_blacklistService = blacklistService;
|
||||
_formatCalculator = formatCalculator;
|
||||
|
||||
GetResourcePaged = GetBlacklist;
|
||||
DeleteResource = DeleteBlacklist;
|
||||
}
|
||||
|
@ -19,7 +24,7 @@ namespace Radarr.Api.V3.Blacklist
|
|||
{
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<BlacklistResource, NzbDrone.Core.Blacklisting.Blacklist>("date", SortDirection.Descending);
|
||||
|
||||
return ApplyToPage(_blacklistService.Paged, pagingSpec, BlacklistResourceMapper.MapToResource);
|
||||
return ApplyToPage(_blacklistService.Paged, pagingSpec, (blacklist) => BlacklistResourceMapper.MapToResource(blacklist, _formatCalculator));
|
||||
}
|
||||
|
||||
private void DeleteBlacklist(int id)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using Radarr.Api.V3.CustomFormats;
|
||||
using Radarr.Api.V3.Movies;
|
||||
using Radarr.Http.REST;
|
||||
|
||||
|
@ -14,6 +16,7 @@ namespace Radarr.Api.V3.Blacklist
|
|||
public string SourceTitle { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public List<CustomFormatResource> CustomFormats { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
|
@ -24,7 +27,7 @@ namespace Radarr.Api.V3.Blacklist
|
|||
|
||||
public static class BlacklistResourceMapper
|
||||
{
|
||||
public static BlacklistResource MapToResource(this NzbDrone.Core.Blacklisting.Blacklist model)
|
||||
public static BlacklistResource MapToResource(this NzbDrone.Core.Blacklisting.Blacklist model, ICustomFormatCalculationService formatCalculator)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
|
@ -39,6 +42,7 @@ namespace Radarr.Api.V3.Blacklist
|
|||
SourceTitle = model.SourceTitle,
|
||||
Languages = model.Languages,
|
||||
Quality = model.Quality,
|
||||
CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(),
|
||||
Date = model.Date,
|
||||
Protocol = model.Protocol,
|
||||
Indexer = model.Indexer,
|
||||
|
|
|
@ -6,16 +6,20 @@ using NzbDrone.Core.CustomFormats;
|
|||
using NzbDrone.Core.Parser;
|
||||
using Radarr.Http;
|
||||
|
||||
namespace Radarr.Api.V3.Qualities
|
||||
namespace Radarr.Api.V3.CustomFormats
|
||||
{
|
||||
public class CustomFormatModule : RadarrRestModule<CustomFormatResource>
|
||||
{
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly IParsingService _parsingService;
|
||||
|
||||
public CustomFormatModule(ICustomFormatService formatService, IParsingService parsingService)
|
||||
public CustomFormatModule(ICustomFormatService formatService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IParsingService parsingService)
|
||||
{
|
||||
_formatService = formatService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_parsingService = parsingService;
|
||||
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
|
@ -31,7 +35,7 @@ namespace Radarr.Api.V3.Qualities
|
|||
var allNewTags = c.Split(',').Select(t => t.ToLower());
|
||||
var enumerable = allTags.ToList();
|
||||
var newTags = allNewTags.ToList();
|
||||
return enumerable.All(newTags.Contains) && f.Id != v.Id && enumerable.Count() == newTags.Count();
|
||||
return enumerable.All(newTags.Contains) && f.Id != v.Id && enumerable.Count == newTags.Count;
|
||||
});
|
||||
})
|
||||
.WithMessage("Should be unique.");
|
||||
|
@ -103,8 +107,8 @@ namespace Radarr.Api.V3.Qualities
|
|||
|
||||
return new CustomFormatTestResource
|
||||
{
|
||||
Matches = _parsingService.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = parsed.Quality.CustomFormats.ToResource()
|
||||
Matches = _formatCalculator.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = _formatCalculator.ParseCustomFormat(parsed).ToResource()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -125,8 +129,8 @@ namespace Radarr.Api.V3.Qualities
|
|||
|
||||
return new CustomFormatTestResource
|
||||
{
|
||||
Matches = _parsingService.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = parsed.Quality.CustomFormats.ToResource()
|
||||
Matches = _formatCalculator.MatchFormatTags(parsed).ToResource(),
|
||||
MatchedFormats = _formatCalculator.ParseCustomFormat(parsed).ToResource()
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using Radarr.Http.REST;
|
||||
|
||||
namespace Radarr.Api.V3.Qualities
|
||||
namespace Radarr.Api.V3.CustomFormats
|
||||
{
|
||||
public class CustomFormatResource : RestResource
|
||||
{
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
|
||||
public override int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string FormatTags { get; set; }
|
||||
public string Simplicity { get; set; }
|
||||
|
@ -24,8 +27,18 @@ namespace Radarr.Api.V3.Qualities
|
|||
};
|
||||
}
|
||||
|
||||
public static List<CustomFormatResource> ToResource(this IEnumerable<CustomFormat> models)
|
||||
{
|
||||
return models.Select(m => m.ToResource()).ToList();
|
||||
}
|
||||
|
||||
public static CustomFormat ToModel(this CustomFormatResource resource)
|
||||
{
|
||||
if (resource.Id == 0 && resource.Name == "None")
|
||||
{
|
||||
return CustomFormat.None;
|
||||
}
|
||||
|
||||
return new CustomFormat
|
||||
{
|
||||
Id = resource.Id,
|
||||
|
@ -33,10 +46,5 @@ namespace Radarr.Api.V3.Qualities
|
|||
FormatTags = resource.FormatTags.Split(',').Select(s => new FormatTag(s)).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public static List<CustomFormatResource> ToResource(this IEnumerable<CustomFormat> models)
|
||||
{
|
||||
return models.Select(m => m.ToResource()).ToList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@ using NzbDrone.Common.Extensions;
|
|||
using NzbDrone.Core.CustomFormats;
|
||||
using Radarr.Http.REST;
|
||||
|
||||
namespace Radarr.Api.V3.Qualities
|
||||
namespace Radarr.Api.V3.CustomFormats
|
||||
{
|
||||
public class FormatTagMatchResultResource : RestResource
|
||||
public class CustomFormatMatchResultResource : RestResource
|
||||
{
|
||||
public CustomFormatResource CustomFormat { get; set; }
|
||||
public List<FormatTagGroupMatchesResource> GroupMatches { get; set; }
|
||||
|
@ -21,27 +21,27 @@ namespace Radarr.Api.V3.Qualities
|
|||
|
||||
public class CustomFormatTestResource : RestResource
|
||||
{
|
||||
public List<FormatTagMatchResultResource> Matches { get; set; }
|
||||
public List<CustomFormatMatchResultResource> Matches { get; set; }
|
||||
public List<CustomFormatResource> MatchedFormats { get; set; }
|
||||
}
|
||||
|
||||
public static class QualityTagMatchResultResourceMapper
|
||||
{
|
||||
public static FormatTagMatchResultResource ToResource(this FormatTagMatchResult model)
|
||||
public static CustomFormatMatchResultResource ToResource(this CustomFormatMatchResult model)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new FormatTagMatchResultResource
|
||||
return new CustomFormatMatchResultResource
|
||||
{
|
||||
CustomFormat = model.CustomFormat.ToResource(),
|
||||
GroupMatches = model.GroupMatches.ToResource()
|
||||
};
|
||||
}
|
||||
|
||||
public static List<FormatTagMatchResultResource> ToResource(this IList<FormatTagMatchResult> models)
|
||||
public static List<CustomFormatMatchResultResource> ToResource(this IList<CustomFormatMatchResult> models)
|
||||
{
|
||||
return models.Select(ToResource).ToList();
|
||||
}
|
|
@ -3,7 +3,7 @@ using System.Linq;
|
|||
using FluentValidation.Validators;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
|
||||
namespace Radarr.Api.V3.Qualities
|
||||
namespace Radarr.Api.V3.CustomFormats
|
||||
{
|
||||
public class FormatTagValidator : PropertyValidator
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ namespace Radarr.Api.V3.Qualities
|
|||
|
||||
var invalidTags = tags.Where(t => !FormatTag.QualityTagRegex.IsMatch(t));
|
||||
|
||||
if (invalidTags.Count() == 0)
|
||||
if (!invalidTags.Any())
|
||||
{
|
||||
return true;
|
||||
}
|
|
@ -2,10 +2,12 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Movies;
|
||||
using Radarr.Api.V3.Movies;
|
||||
using Radarr.Http;
|
||||
using Radarr.Http.Extensions;
|
||||
|
@ -16,14 +18,20 @@ namespace Radarr.Api.V3.History
|
|||
public class HistoryModule : RadarrRestModule<HistoryResource>
|
||||
{
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly IUpgradableSpecification _upgradableSpecification;
|
||||
private readonly IFailedDownloadService _failedDownloadService;
|
||||
|
||||
public HistoryModule(IHistoryService historyService,
|
||||
IMovieService movieService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IUpgradableSpecification upgradableSpecification,
|
||||
IFailedDownloadService failedDownloadService)
|
||||
{
|
||||
_historyService = historyService;
|
||||
_movieService = movieService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_upgradableSpecification = upgradableSpecification;
|
||||
_failedDownloadService = failedDownloadService;
|
||||
GetResourcePaged = GetHistory;
|
||||
|
@ -35,7 +43,12 @@ namespace Radarr.Api.V3.History
|
|||
|
||||
protected HistoryResource MapToResource(NzbDrone.Core.History.History model, bool includeMovie)
|
||||
{
|
||||
var resource = model.ToResource();
|
||||
if (model.Movie == null)
|
||||
{
|
||||
model.Movie = _movieService.GetMovie(model.MovieId);
|
||||
}
|
||||
|
||||
var resource = model.ToResource(_formatCalculator);
|
||||
|
||||
if (includeMovie)
|
||||
{
|
||||
|
@ -44,7 +57,7 @@ namespace Radarr.Api.V3.History
|
|||
|
||||
if (model.Movie != null)
|
||||
{
|
||||
resource.QualityCutoffNotMet = _upgradableSpecification.CutoffNotMet(model.Movie.Profile, model.Quality);
|
||||
resource.QualityCutoffNotMet = _upgradableSpecification.QualityCutoffNotMet(model.Movie.Profile, model.Quality);
|
||||
}
|
||||
|
||||
return resource;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using Radarr.Api.V3.CustomFormats;
|
||||
using Radarr.Api.V3.Movies;
|
||||
using Radarr.Http.REST;
|
||||
|
||||
|
@ -14,6 +16,7 @@ namespace Radarr.Api.V3.History
|
|||
public string SourceTitle { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public List<CustomFormatResource> CustomFormats { get; set; }
|
||||
public bool QualityCutoffNotMet { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public string DownloadId { get; set; }
|
||||
|
@ -27,7 +30,7 @@ namespace Radarr.Api.V3.History
|
|||
|
||||
public static class HistoryResourceMapper
|
||||
{
|
||||
public static HistoryResource ToResource(this NzbDrone.Core.History.History model)
|
||||
public static HistoryResource ToResource(this NzbDrone.Core.History.History model, ICustomFormatCalculationService formatCalculator)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
|
@ -42,6 +45,7 @@ namespace Radarr.Api.V3.History
|
|||
SourceTitle = model.SourceTitle,
|
||||
Languages = model.Languages,
|
||||
Quality = model.Quality,
|
||||
CustomFormats = formatCalculator.ParseCustomFormat(model).ToResource(),
|
||||
|
||||
//QualityCutoffNotMet
|
||||
Date = model.Date,
|
||||
|
|
|
@ -7,6 +7,7 @@ using NzbDrone.Core.Indexers;
|
|||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using Radarr.Api.V3.CustomFormats;
|
||||
using Radarr.Http.REST;
|
||||
|
||||
namespace Radarr.Api.V3.Indexers
|
||||
|
@ -15,6 +16,7 @@ namespace Radarr.Api.V3.Indexers
|
|||
{
|
||||
public string Guid { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public List<CustomFormatResource> CustomFormats { get; set; }
|
||||
public int QualityWeight { get; set; }
|
||||
public int Age { get; set; }
|
||||
public double AgeHours { get; set; }
|
||||
|
@ -68,6 +70,7 @@ namespace Radarr.Api.V3.Indexers
|
|||
{
|
||||
Guid = releaseInfo.Guid,
|
||||
Quality = parsedMovieInfo.Quality,
|
||||
CustomFormats = remoteMovie.CustomFormats.ToResource(),
|
||||
|
||||
//QualityWeight
|
||||
Age = releaseInfo.Age,
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using Nancy;
|
||||
using NLog;
|
||||
using NzbDrone.Core.CustomFormats;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
@ -11,6 +12,7 @@ using NzbDrone.Core.MediaFiles.Events;
|
|||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Movies;
|
||||
using NzbDrone.SignalR;
|
||||
using Radarr.Api.V3.CustomFormats;
|
||||
using Radarr.Http;
|
||||
using Radarr.Http.Extensions;
|
||||
using BadRequestException = Radarr.Http.REST.BadRequestException;
|
||||
|
@ -24,6 +26,7 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly IMovieService _movieService;
|
||||
private readonly ICustomFormatCalculationService _formatCalculator;
|
||||
private readonly IUpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly Logger _logger;
|
||||
|
||||
|
@ -31,6 +34,7 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
IMediaFileService mediaFileService,
|
||||
IRecycleBinProvider recycleBinProvider,
|
||||
IMovieService movieService,
|
||||
ICustomFormatCalculationService formatCalculator,
|
||||
IUpgradableSpecification qualityUpgradableSpecification,
|
||||
Logger logger)
|
||||
: base(signalRBroadcaster)
|
||||
|
@ -38,6 +42,7 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
_mediaFileService = mediaFileService;
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
_movieService = movieService;
|
||||
_formatCalculator = formatCalculator;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_logger = logger;
|
||||
|
||||
|
@ -54,8 +59,11 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
{
|
||||
var movieFile = _mediaFileService.GetMovie(id);
|
||||
var movie = _movieService.GetMovie(movieFile.MovieId);
|
||||
movieFile.Movie = movie;
|
||||
|
||||
return movieFile.ToResource(movie, _qualityUpgradableSpecification);
|
||||
var resource = movieFile.ToResource(movie, _qualityUpgradableSpecification);
|
||||
resource.CustomFormats = _formatCalculator.ParseCustomFormat(movieFile).ToResource();
|
||||
return resource;
|
||||
}
|
||||
|
||||
private List<MovieFileResource> GetMovieFiles()
|
||||
|
@ -72,8 +80,18 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
{
|
||||
int movieId = Convert.ToInt32(movieIdQuery.Value);
|
||||
var movie = _movieService.GetMovie(movieId);
|
||||
var file = _mediaFileService.GetFilesByMovie(movieId).FirstOrDefault();
|
||||
|
||||
return _mediaFileService.GetFilesByMovie(movieId).ConvertAll(f => f.ToResource(movie, _qualityUpgradableSpecification));
|
||||
if (file == null)
|
||||
{
|
||||
return new List<MovieFileResource>();
|
||||
}
|
||||
|
||||
var resource = file.ToResource(movie, _qualityUpgradableSpecification);
|
||||
file.Movie = movie;
|
||||
resource.CustomFormats = _formatCalculator.ParseCustomFormat(file).ToResource();
|
||||
|
||||
return new List<MovieFileResource> { resource };
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -95,6 +113,7 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
private void SetMovieFile(MovieFileResource movieFileResource)
|
||||
{
|
||||
var movieFile = _mediaFileService.GetMovie(movieFileResource.Id);
|
||||
movieFile.IndexerFlags = movieFileResource.IndexerFlags;
|
||||
movieFile.Quality = movieFileResource.Quality;
|
||||
movieFile.Languages = movieFileResource.Languages;
|
||||
_mediaFileService.Update(movieFile);
|
||||
|
|
|
@ -4,7 +4,9 @@ using System.IO;
|
|||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Languages;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using Radarr.Api.V3.CustomFormats;
|
||||
using Radarr.Http.REST;
|
||||
|
||||
namespace Radarr.Api.V3.MovieFiles
|
||||
|
@ -17,7 +19,9 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
public long Size { get; set; }
|
||||
public DateTime DateAdded { get; set; }
|
||||
public string SceneName { get; set; }
|
||||
public IndexerFlags IndexerFlags { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public List<CustomFormatResource> CustomFormats { get; set; }
|
||||
public MediaInfoResource MediaInfo { get; set; }
|
||||
public bool QualityCutoffNotMet { get; set; }
|
||||
public List<Language> Languages { get; set; }
|
||||
|
@ -43,6 +47,7 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
Size = model.Size,
|
||||
DateAdded = model.DateAdded,
|
||||
SceneName = model.SceneName,
|
||||
IndexerFlags = model.IndexerFlags,
|
||||
Quality = model.Quality,
|
||||
Languages = model.Languages,
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
|
||||
|
@ -68,6 +73,7 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
Size = model.Size,
|
||||
DateAdded = model.DateAdded,
|
||||
SceneName = model.SceneName,
|
||||
IndexerFlags = model.IndexerFlags,
|
||||
Quality = model.Quality,
|
||||
Languages = model.Languages,
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName)
|
||||
|
@ -91,10 +97,11 @@ namespace Radarr.Api.V3.MovieFiles
|
|||
Size = model.Size,
|
||||
DateAdded = model.DateAdded,
|
||||
SceneName = model.SceneName,
|
||||
IndexerFlags = model.IndexerFlags,
|
||||
Quality = model.Quality,
|
||||
Languages = model.Languages,
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
|
||||
QualityCutoffNotMet = upgradableSpecification.CutoffNotMet(movie.Profile, model.Quality)
|
||||
QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(movie.Profile, model.Quality)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue