mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-13 20:46:13 -04:00
Compare commits
49 Commits
transfer-p
...
v2.0.0.515
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99ee59e9cc | ||
|
|
c9ff55f601 | ||
|
|
ecb0438b16 | ||
|
|
d0acbe992e | ||
|
|
fb44e67ea8 | ||
|
|
3f86c10c44 | ||
|
|
9e7f0c859f | ||
|
|
a9c54a1f01 | ||
|
|
1c36bc5fee | ||
|
|
b529270f71 | ||
|
|
bcf45bb68a | ||
|
|
9744873e7a | ||
|
|
9a61bb13e6 | ||
|
|
2f69a158b1 | ||
|
|
4975793b45 | ||
|
|
d93645fb46 | ||
|
|
d617b6c6e3 | ||
|
|
cc01608f0a | ||
|
|
8a3db99811 | ||
|
|
883f7b88cf | ||
|
|
03da779ddb | ||
|
|
7c95fc23d0 | ||
|
|
aa2174b43c | ||
|
|
a023732c1c | ||
|
|
90f9dce44a | ||
|
|
ea54b10bf7 | ||
|
|
223ccb5a73 | ||
|
|
968d8159a6 | ||
|
|
245bf6c7d6 | ||
|
|
ee673cd152 | ||
|
|
a1b6095f6e | ||
|
|
5663eb527b | ||
|
|
8e8da76467 | ||
|
|
8a6acd999a | ||
|
|
f32e8beab8 | ||
|
|
d25e5fe329 | ||
|
|
d0e8aef949 | ||
|
|
ad10349878 | ||
|
|
560fe31280 | ||
|
|
c67a4a61a4 | ||
|
|
6e85b8782e | ||
|
|
f719c5ccf1 | ||
|
|
e8c5e417b6 | ||
|
|
3492d6bbaa | ||
|
|
15bd181f16 | ||
|
|
96108cb758 | ||
|
|
459d6ea906 | ||
|
|
747f3e171c | ||
|
|
b371296e78 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -130,6 +130,7 @@ output/*
|
||||
|
||||
#OS X metadata files
|
||||
._*
|
||||
.DS_Store
|
||||
|
||||
_start
|
||||
_temp_*/**/*
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace NzbDrone.Api.Config
|
||||
SharedValidator.RuleFor(c => c.MinimumAge)
|
||||
.GreaterThanOrEqualTo(0);
|
||||
|
||||
SharedValidator.RuleFor(c => c.MaximumSize)
|
||||
.GreaterThanOrEqualTo(0);
|
||||
|
||||
SharedValidator.RuleFor(c => c.Retention)
|
||||
.GreaterThanOrEqualTo(0);
|
||||
|
||||
@@ -25,4 +28,4 @@ namespace NzbDrone.Api.Config
|
||||
return IndexerConfigResourceMapper.ToResource(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace NzbDrone.Api.Config
|
||||
public class IndexerConfigResource : RestResource
|
||||
{
|
||||
public int MinimumAge { get; set; }
|
||||
public int MaximumSize { get; set; }
|
||||
public int Retention { get; set; }
|
||||
public int RssSyncInterval { get; set; }
|
||||
}
|
||||
@@ -17,6 +18,7 @@ namespace NzbDrone.Api.Config
|
||||
return new IndexerConfigResource
|
||||
{
|
||||
MinimumAge = model.MinimumAge,
|
||||
MaximumSize = model.MaximumSize,
|
||||
Retention = model.Retention,
|
||||
RssSyncInterval = model.RssSyncInterval,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace NzbDrone.Api.Config
|
||||
public string RecycleBin { get; set; }
|
||||
public bool AutoDownloadPropers { get; set; }
|
||||
public bool CreateEmptySeriesFolders { get; set; }
|
||||
public bool DeleteEmptyFolders { get; set; }
|
||||
public FileDateType FileDate { get; set; }
|
||||
|
||||
public bool SetPermissionsLinux { get; set; }
|
||||
@@ -35,6 +36,7 @@ namespace NzbDrone.Api.Config
|
||||
RecycleBin = model.RecycleBin,
|
||||
AutoDownloadPropers = model.AutoDownloadPropers,
|
||||
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
|
||||
DeleteEmptyFolders = model.DeleteEmptyFolders,
|
||||
FileDate = model.FileDate,
|
||||
|
||||
SetPermissionsLinux = model.SetPermissionsLinux,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@@ -15,6 +15,7 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
public DateTime DateAdded { get; set; }
|
||||
public string SceneName { get; set; }
|
||||
public QualityModel Quality { get; set; }
|
||||
public MediaInfoResource MediaInfo { get; set; }
|
||||
|
||||
public bool QualityCutoffNotMet { get; set; }
|
||||
}
|
||||
@@ -37,6 +38,7 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
DateAdded = model.DateAdded,
|
||||
SceneName = model.SceneName,
|
||||
Quality = model.Quality,
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName)
|
||||
//QualityCutoffNotMet
|
||||
};
|
||||
}
|
||||
@@ -57,7 +59,8 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
DateAdded = model.DateAdded,
|
||||
SceneName = model.SceneName,
|
||||
Quality = model.Quality,
|
||||
QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, model.Quality)
|
||||
QualityCutoffNotMet = qualityUpgradableSpecification.CutoffNotMet(series.Profile.Value, model.Quality),
|
||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
30
src/NzbDrone.Api/EpisodeFiles/MediaInfoResource.cs
Normal file
30
src/NzbDrone.Api/EpisodeFiles/MediaInfoResource.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
|
||||
namespace NzbDrone.Api.EpisodeFiles
|
||||
{
|
||||
public class MediaInfoResource : RestResource
|
||||
{
|
||||
public decimal AudioChannels { get; set; }
|
||||
public string AudioCodec { get; set; }
|
||||
public string VideoCodec { get; set; }
|
||||
}
|
||||
|
||||
public static class MediaInfoResourceMapper
|
||||
{
|
||||
public static MediaInfoResource ToResource(this MediaInfoModel model, string sceneName)
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MediaInfoResource
|
||||
{
|
||||
AudioChannels = MediaInfoFormatter.FormatAudioChannels(model),
|
||||
AudioCodec = MediaInfoFormatter.FormatAudioCodec(model, sceneName),
|
||||
VideoCodec = MediaInfoFormatter.FormatVideoCodec(model, sceneName)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Nancy;
|
||||
|
||||
namespace NzbDrone.Api.Extensions
|
||||
@@ -42,5 +42,17 @@ namespace NzbDrone.Api.Extensions
|
||||
return request.Path.StartsWith("/MediaCover/", StringComparison.InvariantCultureIgnoreCase) ||
|
||||
request.Path.StartsWith("/Content/Images/", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool GetBooleanQueryParameter(this Request request, string parameter, bool defaultValue = false)
|
||||
{
|
||||
var parameterValue = request.Query[parameter];
|
||||
|
||||
if (parameterValue.HasValue)
|
||||
{
|
||||
return bool.Parse(parameterValue.Value);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NodaTime, Version=1.3.0.0, Culture=neutral, PublicKeyToken=4226afe0d9b296d1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Ical.Net.2.2.32\lib\net40\NodaTime.dll</HintPath>
|
||||
@@ -104,6 +104,7 @@
|
||||
<Compile Include="ClientSchema\SelectOption.cs" />
|
||||
<Compile Include="Commands\CommandModule.cs" />
|
||||
<Compile Include="Commands\CommandResource.cs" />
|
||||
<Compile Include="EpisodeFiles\MediaInfoResource.cs" />
|
||||
<Compile Include="Extensions\AccessControlHeaders.cs" />
|
||||
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
@@ -17,7 +17,9 @@ namespace NzbDrone.Api.RootFolders
|
||||
DroneFactoryValidator droneFactoryValidator,
|
||||
MappedNetworkDriveValidator mappedNetworkDriveValidator,
|
||||
StartupFolderValidator startupFolderValidator,
|
||||
FolderWritableValidator folderWritableValidator)
|
||||
SystemFolderValidator systemFolderValidator,
|
||||
FolderWritableValidator folderWritableValidator
|
||||
)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_rootFolderService = rootFolderService;
|
||||
@@ -35,6 +37,7 @@ namespace NzbDrone.Api.RootFolders
|
||||
.SetValidator(mappedNetworkDriveValidator)
|
||||
.SetValidator(startupFolderValidator)
|
||||
.SetValidator(pathExistsValidator)
|
||||
.SetValidator(systemFolderValidator)
|
||||
.SetValidator(folderWritableValidator);
|
||||
}
|
||||
|
||||
@@ -60,4 +63,4 @@ namespace NzbDrone.Api.RootFolders
|
||||
_rootFolderService.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
@@ -9,6 +9,7 @@ namespace NzbDrone.Api.RootFolders
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public long? FreeSpace { get; set; }
|
||||
public long? TotalSpace { get; set; }
|
||||
|
||||
public List<UnmappedFolder> UnmappedFolders { get; set; }
|
||||
}
|
||||
@@ -25,6 +26,7 @@ namespace NzbDrone.Api.RootFolders
|
||||
|
||||
Path = model.Path,
|
||||
FreeSpace = model.FreeSpace,
|
||||
TotalSpace = model.TotalSpace,
|
||||
UnmappedFolders = model.UnmappedFolders
|
||||
};
|
||||
}
|
||||
@@ -48,4 +50,4 @@ namespace NzbDrone.Api.RootFolders
|
||||
return models.Select(ToResource).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.Tv;
|
||||
namespace NzbDrone.Api.Series
|
||||
{
|
||||
@@ -8,18 +9,20 @@ namespace NzbDrone.Api.Series
|
||||
public int SeasonNumber { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public SeasonStatisticsResource Statistics { get; set; }
|
||||
public List<MediaCover> Images { get; set; }
|
||||
}
|
||||
|
||||
public static class SeasonResourceMapper
|
||||
{
|
||||
public static SeasonResource ToResource(this Season model)
|
||||
public static SeasonResource ToResource(this Season model, bool includeImages = false)
|
||||
{
|
||||
if (model == null) return null;
|
||||
|
||||
return new SeasonResource
|
||||
{
|
||||
SeasonNumber = model.SeasonNumber,
|
||||
Monitored = model.Monitored
|
||||
Monitored = model.Monitored,
|
||||
Images = includeImages ? model.Images : null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,13 +33,14 @@ namespace NzbDrone.Api.Series
|
||||
return new Season
|
||||
{
|
||||
SeasonNumber = resource.SeasonNumber,
|
||||
Monitored = resource.Monitored
|
||||
Monitored = resource.Monitored,
|
||||
Images = resource.Images
|
||||
};
|
||||
}
|
||||
|
||||
public static List<SeasonResource> ToResource(this IEnumerable<Season> models)
|
||||
public static List<SeasonResource> ToResource(this IEnumerable<Season> models, bool includeImages = false)
|
||||
{
|
||||
return models.Select(ToResource).ToList();
|
||||
return models.Select(s => ToResource(s, includeImages)).ToList();
|
||||
}
|
||||
|
||||
public static List<Season> ToModel(this IEnumerable<SeasonResource> resources)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.Extensions;
|
||||
@@ -24,7 +24,7 @@ namespace NzbDrone.Api.Series
|
||||
var series = resources.Select(seriesResource => seriesResource.ToModel(_seriesService.GetSeries(seriesResource.Id))).ToList();
|
||||
|
||||
return _seriesService.UpdateSeries(series)
|
||||
.ToResource()
|
||||
.ToResource(false)
|
||||
.AsResponse(HttpStatusCode.Accepted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Datastore.Events;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
@@ -45,6 +46,7 @@ namespace NzbDrone.Api.Series
|
||||
SeriesExistsValidator seriesExistsValidator,
|
||||
DroneFactoryValidator droneFactoryValidator,
|
||||
SeriesAncestorValidator seriesAncestorValidator,
|
||||
SystemFolderValidator systemFolderValidator,
|
||||
ProfileExistsValidator profileExistsValidator
|
||||
)
|
||||
: base(signalRBroadcaster)
|
||||
@@ -71,6 +73,7 @@ namespace NzbDrone.Api.Series
|
||||
.SetValidator(seriesPathValidator)
|
||||
.SetValidator(droneFactoryValidator)
|
||||
.SetValidator(seriesAncestorValidator)
|
||||
.SetValidator(systemFolderValidator)
|
||||
.When(s => !s.Path.IsNullOrWhiteSpace());
|
||||
|
||||
SharedValidator.RuleFor(s => s.ProfileId).SetValidator(profileExistsValidator);
|
||||
@@ -84,26 +87,17 @@ namespace NzbDrone.Api.Series
|
||||
|
||||
private SeriesResource GetSeries(int id)
|
||||
{
|
||||
var includeSeasonImages = Context != null && Request.GetBooleanQueryParameter("includeSeasonImages");
|
||||
|
||||
var series = _seriesService.GetSeries(id);
|
||||
return MapToResource(series);
|
||||
}
|
||||
|
||||
private SeriesResource MapToResource(Core.Tv.Series series)
|
||||
{
|
||||
if (series == null) return null;
|
||||
|
||||
var resource = series.ToResource();
|
||||
MapCoversToLocal(resource);
|
||||
FetchAndLinkSeriesStatistics(resource);
|
||||
PopulateAlternateTitles(resource);
|
||||
|
||||
return resource;
|
||||
return MapToResource(series, includeSeasonImages);
|
||||
}
|
||||
|
||||
private List<SeriesResource> AllSeries()
|
||||
{
|
||||
var includeSeasonImages = Request.GetBooleanQueryParameter("includeSeasonImages");
|
||||
var seriesStats = _seriesStatisticsService.SeriesStatistics();
|
||||
var seriesResources = _seriesService.GetAllSeries().ToResource();
|
||||
var seriesResources = _seriesService.GetAllSeries().Select(s => s.ToResource(includeSeasonImages)).ToList();
|
||||
|
||||
MapCoversToLocal(seriesResources.ToArray());
|
||||
LinkSeriesStatistics(seriesResources, seriesStats);
|
||||
@@ -141,6 +135,18 @@ namespace NzbDrone.Api.Series
|
||||
_seriesService.DeleteSeries(id, deleteFiles);
|
||||
}
|
||||
|
||||
private SeriesResource MapToResource(Core.Tv.Series series, bool includeSeasonImages)
|
||||
{
|
||||
if (series == null) return null;
|
||||
|
||||
var resource = series.ToResource(includeSeasonImages);
|
||||
MapCoversToLocal(resource);
|
||||
FetchAndLinkSeriesStatistics(resource);
|
||||
PopulateAlternateTitles(resource);
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
private void MapCoversToLocal(params SeriesResource[] series)
|
||||
{
|
||||
foreach (var seriesResource in series)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.REST;
|
||||
@@ -97,7 +97,7 @@ namespace NzbDrone.Api.Series
|
||||
|
||||
public static class SeriesResourceMapper
|
||||
{
|
||||
public static SeriesResource ToResource(this Core.Tv.Series model)
|
||||
public static SeriesResource ToResource(this Core.Tv.Series model, bool includeSeasonImages = false)
|
||||
{
|
||||
if (model == null) return null;
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace NzbDrone.Api.Series
|
||||
AirTime = model.AirTime,
|
||||
Images = model.Images,
|
||||
|
||||
Seasons = model.Seasons.ToResource(),
|
||||
Seasons = model.Seasons.ToResource(includeSeasonImages),
|
||||
Year = model.Year,
|
||||
|
||||
Path = model.Path,
|
||||
@@ -214,9 +214,9 @@ namespace NzbDrone.Api.Series
|
||||
return series;
|
||||
}
|
||||
|
||||
public static List<SeriesResource> ToResource(this IEnumerable<Core.Tv.Series> series)
|
||||
public static List<SeriesResource> ToResource(this IEnumerable<Core.Tv.Series> series, bool includeSeasonImages)
|
||||
{
|
||||
return series.Select(ToResource).ToList();
|
||||
return series.Select(s => ToResource(s, includeSeasonImages)).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
<package id="Nancy.Authentication.Basic" version="1.4.1" targetFramework="net40" />
|
||||
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -52,7 +52,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" targetFramework="net40" />
|
||||
<package id="NBuilder" version="4.0.0" targetFramework="net40" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -45,7 +45,7 @@
|
||||
<HintPath>..\packages\FluentAssertions.4.19.0\lib\net40\FluentAssertions.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
<package id="Selenium.Support" version="3.2.0" targetFramework="net40" />
|
||||
<package id="Selenium.WebDriver" version="3.2.0" targetFramework="net40" />
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<packages>
|
||||
<package id="FluentAssertions" version="4.19.0" targetFramework="net40" />
|
||||
<package id="Moq" version="4.0.10827" targetFramework="net40" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -200,6 +200,11 @@ namespace NzbDrone.Common.Disk
|
||||
throw new IOException(string.Format("Source and destination can't be the same {0}", source));
|
||||
}
|
||||
|
||||
CopyFileInternal(source, destination, overwrite);
|
||||
}
|
||||
|
||||
protected virtual void CopyFileInternal(string source, string destination, bool overwrite = false)
|
||||
{
|
||||
File.Copy(source, destination, overwrite);
|
||||
}
|
||||
|
||||
@@ -219,6 +224,11 @@ namespace NzbDrone.Common.Disk
|
||||
}
|
||||
|
||||
RemoveReadOnly(source);
|
||||
MoveFileInternal(source, destination);
|
||||
}
|
||||
|
||||
protected virtual void MoveFileInternal(string source, string destination)
|
||||
{
|
||||
File.Move(source, destination);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Common.Instrumentation.Extensions
|
||||
return logBuilder.LoggerName(logEvent.LoggerName)
|
||||
.TimeStamp(logEvent.TimeStamp)
|
||||
.Message(logEvent.Message, logEvent.Parameters)
|
||||
.Properties((Dictionary<object, object>)logEvent.Properties)
|
||||
.Properties(logEvent.Properties.ToDictionary(v => v.Key, v => v.Value))
|
||||
.Exception(logEvent.Exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace NzbDrone.Common.Instrumentation
|
||||
if (exception is NullReferenceException &&
|
||||
exception.ToString().Contains("Microsoft.AspNet.SignalR.Transports.TransportHeartbeat.ProcessServerCommand"))
|
||||
{
|
||||
Logger.Warn("SignalR Heartbeat interupted");
|
||||
Logger.Warn("SignalR Heartbeat interrupted");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,4 +49,4 @@ namespace NzbDrone.Common.Instrumentation
|
||||
Logger.Fatal(exception, "EPIC FAIL.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Org.Mentalis, Version=1.0.0.1, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNet4.SocksProxy.1.3.4.0\lib\net40\Org.Mentalis.dll</HintPath>
|
||||
@@ -212,6 +212,7 @@
|
||||
<Compile Include="Serializer\IntConverter.cs" />
|
||||
<Compile Include="Serializer\Json.cs" />
|
||||
<Compile Include="Serializer\JsonVisitor.cs" />
|
||||
<Compile Include="Serializer\UnderscoreStringEnumConverter.cs" />
|
||||
<Compile Include="ServiceFactory.cs" />
|
||||
<Compile Include="ServiceProvider.cs" />
|
||||
<Compile Include="Extensions\StringExtensions.cs" />
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NzbDrone.Common.Serializer
|
||||
{
|
||||
public class UnderscoreStringEnumConverter : JsonConverter
|
||||
{
|
||||
public object UnknownValue { get; set; }
|
||||
|
||||
public UnderscoreStringEnumConverter(object unknownValue)
|
||||
{
|
||||
UnknownValue = unknownValue;
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType.IsEnum;
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var enumString = reader.Value.ToString().Replace("_", string.Empty);
|
||||
|
||||
try
|
||||
{
|
||||
return Enum.Parse(objectType, enumString, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (UnknownValue == null)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return UnknownValue;
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var enumText = value.ToString();
|
||||
var builder = new StringBuilder(enumText.Length + 4);
|
||||
builder.Append(char.ToLower(enumText[0]));
|
||||
for (int i = 1; i < enumText.Length; i++)
|
||||
{
|
||||
if (char.IsUpper(enumText[i]))
|
||||
{
|
||||
builder.Append('_');
|
||||
}
|
||||
builder.Append(char.ToLower(enumText[i]));
|
||||
}
|
||||
enumText = builder.ToString();
|
||||
|
||||
writer.WriteValue(enumText);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="DotNet4.SocksProxy" version="1.3.4.0" targetFramework="net40" />
|
||||
<package id="ICSharpCode.SharpZipLib.Patched" version="0.86.5" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="SharpRaven" version="2.2.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -25,7 +25,15 @@ namespace NzbDrone.Console
|
||||
try
|
||||
{
|
||||
var startupArgs = new StartupContext(args);
|
||||
NzbDroneLogger.Register(startupArgs, false, true);
|
||||
try
|
||||
{
|
||||
NzbDroneLogger.Register(startupArgs, false, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Console.WriteLine("NLog Exception: " + ex.ToString());
|
||||
throw;
|
||||
}
|
||||
Bootstrap.Start(startupArgs, new ConsoleAlerts());
|
||||
}
|
||||
catch (SonarrStartupException ex)
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<package id="Microsoft.Owin" version="2.1.0" targetFramework="net40" />
|
||||
<package id="Microsoft.Owin.Hosting" version="2.1.0" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="Owin" version="1.0" targetFramework="net40" />
|
||||
</packages>
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class BooleanIntConverterFixture : CoreTest<Core.Datastore.Converters.BooleanIntConverter>
|
||||
{
|
||||
[TestCase(true, 1)]
|
||||
[TestCase(false, 0)]
|
||||
public void should_return_int_when_saving_boolean_to_db(bool input, int expected)
|
||||
{
|
||||
Subject.ToDB(input).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_null_value_when_saving_to_db()
|
||||
{
|
||||
Subject.ToDB(null).Should().Be(DBNull.Value);
|
||||
}
|
||||
|
||||
[TestCase(1, true)]
|
||||
[TestCase(0, false)]
|
||||
public void should_return_bool_when_getting_int_from_db(int input, bool expected)
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = (long)input
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(DBNull.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_for_non_boolean_equivalent_number_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = (long)2
|
||||
};
|
||||
|
||||
Assert.Throws<ConversionException>(() => Subject.FromDB(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv.Commands;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class CommandConverterFixture : CoreTest<CommandConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_json_string_when_saving_boolean_to_db()
|
||||
{
|
||||
var command = new RefreshSeriesCommand();
|
||||
|
||||
Subject.ToDB(command).Should().BeOfType<string>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_for_null_value_when_saving_to_db()
|
||||
{
|
||||
Subject.ToDB(null).Should().Be(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_db_null_value_when_saving_to_db()
|
||||
{
|
||||
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_command_when_getting_json_from_db()
|
||||
{
|
||||
var dataRecordMock = new Mock<IDataRecord>();
|
||||
dataRecordMock.Setup(s => s.GetOrdinal("Name")).Returns(0);
|
||||
dataRecordMock.Setup(s => s.GetString(0)).Returns("RefreshSeries");
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DataRecord = dataRecordMock.Object,
|
||||
DbValue = new RefreshSeriesCommand().ToJson()
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().BeOfType<RefreshSeriesCommand>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_for_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class DoubleConverterFixture : CoreTest<DoubleConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_double_when_saving_double_to_db()
|
||||
{
|
||||
var input = 10.5D;
|
||||
|
||||
Subject.ToDB(input).Should().Be(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_for_null_value_when_saving_to_db()
|
||||
{
|
||||
Subject.ToDB(null).Should().Be(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_db_null_value_when_saving_to_db()
|
||||
{
|
||||
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_double_when_getting_double_from_db()
|
||||
{
|
||||
var expected = 10.5D;
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = expected
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_double_when_getting_string_from_db()
|
||||
{
|
||||
var expected = 10.5D;
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = $"{expected}"
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_for_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(DBNull.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class EnumIntConverterFixture : CoreTest<Core.Datastore.Converters.EnumIntConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_int_when_saving_enum_to_db()
|
||||
{
|
||||
Subject.ToDB(SeriesTypes.Standard).Should().Be((int)SeriesTypes.Standard);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_null_value_when_saving_to_db()
|
||||
{
|
||||
Subject.ToDB(null).Should().Be(DBNull.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_enum_when_getting_int_from_db()
|
||||
{
|
||||
var mockMemberInfo = new Mock<MemberInfo>();
|
||||
mockMemberInfo.SetupGet(s => s.DeclaringType).Returns(typeof(Series));
|
||||
mockMemberInfo.SetupGet(s => s.Name).Returns("SeriesType");
|
||||
|
||||
var expected = SeriesTypes.Standard;
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
ColumnMap = new ColumnMap(mockMemberInfo.Object) { FieldType = typeof(SeriesTypes) },
|
||||
DbValue = (long)expected
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_for_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class GuidConverterFixture : CoreTest<GuidConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_string_when_saving_guid_to_db()
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
|
||||
Subject.ToDB(guid).Should().Be(guid.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_null_value_when_saving_to_db()
|
||||
{
|
||||
Subject.ToDB(null).Should().Be(DBNull.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_guid_when_getting_string_from_db()
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = guid.ToString()
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(guid);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_empty_guid_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(Guid.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class Int32ConverterFixture : CoreTest<Int32Converter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_int_when_saving_int_to_db()
|
||||
{
|
||||
var i = 5;
|
||||
|
||||
Subject.ToDB(5).Should().Be(5);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_int_when_getting_int_from_db()
|
||||
{
|
||||
var i = 5;
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = i
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(i);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_int_when_getting_string_from_db()
|
||||
{
|
||||
var i = 5;
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = i.ToString()
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(i);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(DBNull.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class OsPathConverterFixture : CoreTest<OsPathConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_string_when_saving_os_path_to_db()
|
||||
{
|
||||
var path = @"C:\Test\TV".AsOsAgnostic();
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
Subject.ToDB(osPath).Should().Be(path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_os_path_when_getting_string_from_db()
|
||||
{
|
||||
var path = @"C:\Test\TV".AsOsAgnostic();
|
||||
var osPath = new OsPath(path);
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = path
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(osPath);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(DBNull.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class QualityIntConverterFixture : CoreTest<QualityIntConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_int_when_saving_quality_to_db()
|
||||
{
|
||||
var quality = Quality.Bluray1080p;
|
||||
|
||||
Subject.ToDB(quality).Should().Be(quality.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_0_when_saving_db_null_to_db()
|
||||
{
|
||||
Subject.ToDB(DBNull.Value).Should().Be(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_when_saving_another_object_to_db()
|
||||
{
|
||||
Assert.Throws<InvalidOperationException>(() => Subject.ToDB("Not a quality"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_quality_when_getting_string_from_db()
|
||||
{
|
||||
var quality = Quality.Bluray1080p;
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = quality.Id
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(quality);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(Quality.Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class TimeSpanConverterFixture : CoreTest<TimeSpanConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_string_when_saving_timespan_to_db()
|
||||
{
|
||||
var timeSpan = TimeSpan.FromMinutes(5);
|
||||
|
||||
Subject.ToDB(timeSpan).Should().Be(timeSpan.ToString("c", CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_null_when_saving_empty_string_to_db()
|
||||
{
|
||||
Subject.ToDB("").Should().Be(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_time_span_when_getting_time_span_from_db()
|
||||
{
|
||||
var timeSpan = TimeSpan.FromMinutes(5);
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = timeSpan
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(timeSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_time_span_when_getting_string_from_db()
|
||||
{
|
||||
var timeSpan = TimeSpan.FromMinutes(5);
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = timeSpan.ToString("c", CultureInfo.InvariantCulture)
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(timeSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_time_span_zero_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(TimeSpan.Zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class UtcConverterFixture : CoreTest<UtcConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_date_time_when_saving_date_time_to_db()
|
||||
{
|
||||
var dateTime = DateTime.Now;
|
||||
|
||||
Subject.ToDB(dateTime).Should().Be(dateTime.ToUniversalTime());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_when_saving_db_null_to_db()
|
||||
{
|
||||
Subject.ToDB(DBNull.Value).Should().Be(DBNull.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_time_span_when_getting_time_span_from_db()
|
||||
{
|
||||
var dateTime = DateTime.Now.ToUniversalTime();
|
||||
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = dateTime
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(dateTime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_db_null_for_db_null_value_when_getting_from_db()
|
||||
{
|
||||
var context = new ConverterContext
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
};
|
||||
|
||||
Subject.FromDB(context).Should().Be(DBNull.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
public class MaximumSizeSpecificationFixture : CoreTest<MaximumSizeSpecification>
|
||||
{
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_remoteEpisode = new RemoteEpisode() { Release = new ReleaseInfo() };
|
||||
}
|
||||
|
||||
private void WithMaximumSize(int size)
|
||||
{
|
||||
Mocker.GetMock<IConfigService>().SetupGet(c => c.MaximumSize).Returns(size);
|
||||
}
|
||||
|
||||
private void WithSize(int size)
|
||||
{
|
||||
_remoteEpisode.Release.Size = size * 1024 * 1024;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_maximum_size_is_set_to_zero()
|
||||
{
|
||||
WithMaximumSize(0);
|
||||
WithSize(1000);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_size_is_smaller_than_maximum_size()
|
||||
{
|
||||
WithMaximumSize(2000);
|
||||
WithSize(1999);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_size_is_equals_to_maximum_size()
|
||||
{
|
||||
WithMaximumSize(2000);
|
||||
WithSize(2000);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_when_size_is_bigger_than_maximum_size()
|
||||
{
|
||||
WithMaximumSize(2000);
|
||||
WithSize(2001);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_size_is_zero()
|
||||
{
|
||||
WithMaximumSize(2000);
|
||||
WithSize(0);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using FluentAssertions;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Download.Clients.DownloadStation;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class DownloadStationsTaskStatusJsonConverterFixture
|
||||
{
|
||||
[TestCase("captcha_needed", DownloadStationTaskStatus.CaptchaNeeded)]
|
||||
[TestCase("filehosting_waiting", DownloadStationTaskStatus.FilehostingWaiting)]
|
||||
[TestCase("hash_checking", DownloadStationTaskStatus.HashChecking)]
|
||||
[TestCase("error", DownloadStationTaskStatus.Error)]
|
||||
[TestCase("downloading", DownloadStationTaskStatus.Downloading)]
|
||||
public void should_parse_enum_correctly(string value, DownloadStationTaskStatus expected)
|
||||
{
|
||||
var task = "{\"Status\": \"" + value + "\"}";
|
||||
|
||||
var item = JsonConvert.DeserializeObject<DownloadStationTask>(task);
|
||||
|
||||
item.Status.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("captcha_needed", DownloadStationTaskStatus.CaptchaNeeded)]
|
||||
[TestCase("filehosting_waiting", DownloadStationTaskStatus.FilehostingWaiting)]
|
||||
[TestCase("hash_checking", DownloadStationTaskStatus.HashChecking)]
|
||||
[TestCase("error", DownloadStationTaskStatus.Error)]
|
||||
[TestCase("downloading", DownloadStationTaskStatus.Downloading)]
|
||||
public void should_serialize_enum_correctly(string expected, DownloadStationTaskStatus value)
|
||||
{
|
||||
var task = new DownloadStationTask { Status = value };
|
||||
|
||||
var item = JsonConvert.SerializeObject(task);
|
||||
|
||||
item.Should().Contain(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_unknown_if_unknown_enum_value()
|
||||
{
|
||||
var task = "{\"Status\": \"some_unknown_value\"}";
|
||||
|
||||
var item = JsonConvert.DeserializeObject<DownloadStationTask>(task);
|
||||
|
||||
item.Status.Should().Be(DownloadStationTaskStatus.Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -605,9 +605,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
[TestCase(DownloadStationTaskStatus.Finished, DownloadItemStatus.Completed)]
|
||||
[TestCase(DownloadStationTaskStatus.Finishing, DownloadItemStatus.Downloading)]
|
||||
[TestCase(DownloadStationTaskStatus.HashChecking, DownloadItemStatus.Downloading)]
|
||||
[TestCase(DownloadStationTaskStatus.CaptchaNeeded, DownloadItemStatus.Downloading)]
|
||||
[TestCase(DownloadStationTaskStatus.Paused, DownloadItemStatus.Paused)]
|
||||
[TestCase(DownloadStationTaskStatus.Seeding, DownloadItemStatus.Completed)]
|
||||
[TestCase(DownloadStationTaskStatus.FilehostingWaiting, DownloadItemStatus.Queued)]
|
||||
[TestCase(DownloadStationTaskStatus.Waiting, DownloadItemStatus.Queued)]
|
||||
[TestCase(DownloadStationTaskStatus.Unknown, DownloadItemStatus.Queued)]
|
||||
public void GetItems_should_return_item_as_downloadItemStatus(DownloadStationTaskStatus apiStatus, DownloadItemStatus expectedItemStatus)
|
||||
{
|
||||
GivenSerialNumber();
|
||||
|
||||
@@ -414,8 +414,12 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.DownloadStationTests
|
||||
[TestCase(DownloadStationTaskStatus.Finished, DownloadItemStatus.Completed)]
|
||||
[TestCase(DownloadStationTaskStatus.Finishing, DownloadItemStatus.Downloading)]
|
||||
[TestCase(DownloadStationTaskStatus.HashChecking, DownloadItemStatus.Downloading)]
|
||||
[TestCase(DownloadStationTaskStatus.CaptchaNeeded, DownloadItemStatus.Downloading)]
|
||||
[TestCase(DownloadStationTaskStatus.Paused, DownloadItemStatus.Paused)]
|
||||
[TestCase(DownloadStationTaskStatus.Seeding, DownloadItemStatus.Completed)]
|
||||
[TestCase(DownloadStationTaskStatus.FilehostingWaiting, DownloadItemStatus.Queued)]
|
||||
[TestCase(DownloadStationTaskStatus.Waiting, DownloadItemStatus.Queued)]
|
||||
[TestCase(DownloadStationTaskStatus.Unknown, DownloadItemStatus.Queued)]
|
||||
public void GetItems_should_return_item_as_downloadItemStatus(DownloadStationTaskStatus apiStatus, DownloadItemStatus expectedItemStatus)
|
||||
{
|
||||
GivenSerialNumber();
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
|
||||
.Returns(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.ParseSpecialEpisodeTitle(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||
.Setup(s => s.ParseSpecialEpisodeTitle(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||
.Returns(remoteEpisode.ParsedEpisodeInfo);
|
||||
|
||||
var client = new DownloadClientDefinition()
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
[TestFixture]
|
||||
public class EpisodeTitleSpecificationFixture : CoreTest<EpisodeTitleSpecification>
|
||||
{
|
||||
private Series _series;
|
||||
private LocalEpisode _localEpisode;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesType = SeriesTypes.Standard)
|
||||
.With(s => s.Path = @"C:\Test\TV\30 Rock".AsOsAgnostic())
|
||||
.Build();
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.AirDateUtc = DateTime.UtcNow)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_localEpisode = new LocalEpisode
|
||||
{
|
||||
Path = @"C:\Test\Unsorted\30 Rock\30.rock.s01e01.avi".AsOsAgnostic(),
|
||||
Episodes = episodes,
|
||||
Series = _series
|
||||
};
|
||||
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.RequiresEpisodeTitle(_series, episodes))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_reject_when_title_is_null()
|
||||
{
|
||||
_localEpisode.Episodes.First().Title = null;
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_reject_when_title_is_TBA()
|
||||
{
|
||||
_localEpisode.Episodes.First().Title = "TBA";
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_accept_when_did_not_air_recently_but_title_is_TBA()
|
||||
{
|
||||
_localEpisode.Episodes.First().AirDateUtc = DateTime.UtcNow.AddDays(-7);
|
||||
_localEpisode.Episodes.First().Title = "TBA";
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_accept_when_episode_title_is_not_required()
|
||||
{
|
||||
_localEpisode.Episodes.First().Title = "TBA";
|
||||
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.Setup(s => s.RequiresEpisodeTitle(_series, _localEpisode.Episodes))
|
||||
.Returns(false);
|
||||
|
||||
Subject.IsSatisfiedBy(_localEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,13 +64,16 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaFileDeletionService
|
||||
public void should_throw_if_root_folder_does_not_exist()
|
||||
{
|
||||
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_should_throw_if_root_folder_is_empty()
|
||||
{
|
||||
GivenRootFolderExists();
|
||||
|
||||
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net40\NLog.dll</HintPath>
|
||||
<HintPath>..\packages\NLog.4.5.0-rc06\lib\net40-client\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.6.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NUnit.3.6.0\lib\net40\nunit.framework.dll</HintPath>
|
||||
@@ -115,6 +115,16 @@
|
||||
<Compile Include="DataAugmentation\Scene\SceneMappingServiceFixture.cs" />
|
||||
<Compile Include="DataAugmentation\DailySeries\DailySeriesDataProxyFixture.cs" />
|
||||
<Compile Include="Datastore\BasicRepositoryFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\UtcConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\TimeSpanConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\QualityIntConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\OsPathConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\Int32ConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\GuidConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\EnumIntConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\DoubleConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\CommandConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\BooleanIntConverterFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\ProviderSettingConverterFixture.cs" />
|
||||
<Compile Include="Datastore\DatabaseFixture.cs" />
|
||||
<Compile Include="Datastore\DatabaseRelationshipFixture.cs" />
|
||||
@@ -146,6 +156,7 @@
|
||||
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\AnimeVersionUpgradeSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\FullSeasonSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\MaximumSizeSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\ProtocolSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\CutoffSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" />
|
||||
@@ -176,6 +187,7 @@
|
||||
<Compile Include="Download\DownloadClientTests\Blackhole\UsenetBlackholeFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\DelugeTests\DelugeFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\DownloadClientFixtureBase.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\DownloadStationTests\DownloadStationsTaskStatusJsonConverterFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\DownloadStationTests\TorrentDownloadStationFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\DownloadStationTests\SerialNumberProviderFixture.cs" />
|
||||
<Compile Include="Download\DownloadClientTests\DownloadStationTests\SharedFolderResolverFixture.cs" />
|
||||
@@ -288,6 +300,7 @@
|
||||
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\DetectSampleFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\EpisodeTitleSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameFileSpecificationFixture.cs" />
|
||||
@@ -308,6 +321,7 @@
|
||||
<Compile Include="MetadataSource\SkyHook\SkyHookProxyFixture.cs" />
|
||||
<Compile Include="NotificationTests\NotificationBaseFixture.cs" />
|
||||
<Compile Include="NotificationTests\SynologyIndexerFixture.cs" />
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\RequiresEpisodeTitleFixture.cs" />
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\EpisodeTitleCollapseFixture.cs" />
|
||||
<Compile Include="OrganizerTests\FileNameBuilderTests\MultiEpisodeFixture.cs" />
|
||||
@@ -402,6 +416,7 @@
|
||||
<Compile Include="TvTests\ShouldRefreshSeriesFixture.cs" />
|
||||
<Compile Include="UpdateTests\UpdatePackageProviderFixture.cs" />
|
||||
<Compile Include="UpdateTests\UpdateServiceFixture.cs" />
|
||||
<Compile Include="ValidationTests\SystemFolderValidatorFixture.cs" />
|
||||
<Compile Include="XbmcVersionTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class RequiresEpisodeTitleFixture : CoreTest<FileNameBuilder>
|
||||
{
|
||||
private Series _series;
|
||||
private Episode _episode;
|
||||
private EpisodeFile _episodeFile;
|
||||
private NamingConfig _namingConfig;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>
|
||||
.CreateNew()
|
||||
.With(s => s.Title = "South Park")
|
||||
.Build();
|
||||
|
||||
_episode = Builder<Episode>.CreateNew()
|
||||
.With(e => e.Title = "City Sushi")
|
||||
.With(e => e.SeasonNumber = 15)
|
||||
.With(e => e.EpisodeNumber = 6)
|
||||
.With(e => e.AbsoluteEpisodeNumber = 100)
|
||||
.Build();
|
||||
|
||||
_namingConfig = NamingConfig.Default;
|
||||
_namingConfig.RenameEpisodes = true;
|
||||
|
||||
Mocker.GetMock<INamingConfigService>()
|
||||
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_when_episode_title_is_not_part_of_the_pattern()
|
||||
{
|
||||
_namingConfig.StandardEpisodeFormat = "{Series Title} S{season:00}E{episode:00}";
|
||||
Subject.RequiresEpisodeTitle(_series, new List<Episode> { _episode }).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_when_episode_title_is_part_of_the_pattern()
|
||||
{
|
||||
Subject.RequiresEpisodeTitle(_series, new List<Episode> { _episode }).Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,5 +39,12 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
Parser.Parser.ParseTitle(title).IsPossibleSpecialEpisode.Should().BeTrue();
|
||||
}
|
||||
|
||||
|
||||
[TestCase("Dr.S11E00.A.Christmas.Carol.Special.720p.HDTV-FieldOfView")]
|
||||
public void IsPossibleSpecialEpisode_should_be_true_if_e00_special(string title)
|
||||
{
|
||||
Parser.Parser.ParseTitle(title).IsPossibleSpecialEpisode.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Series Title.S6.E1-E2-E3.Episode Name.1080p.WEB-DL", "Series Title", 6, new[] { 1, 2, 3 })]
|
||||
[TestCase("Mad.Men.S05E01-E02.720p.5.1Ch.BluRay", "Mad Men", 5, new[] { 1, 2 })]
|
||||
[TestCase("Mad.Men.S05E01-02.720p.5.1Ch.BluRay", "Mad Men", 5, new[] { 1, 2 })]
|
||||
[TestCase("S01E01-E03 - Episode Title.HDTV-720p", "", 1, new [] { 1, 2, 3 })]
|
||||
[TestCase("1x01-x03 - Episode Title.HDTV-720p", "", 1, new [] { 1, 2, 3 })]
|
||||
//[TestCase("", "", , new [] { })]
|
||||
public void should_parse_multiple_episodes(string postTitle, string title, int season, int[] episodes)
|
||||
{
|
||||
|
||||
@@ -152,6 +152,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Incorporated.S01E08.Das.geloeschte.Ich.German.DD51.Dubbed.DL.720p.AmazonHD.x264-TVS", false)]
|
||||
[TestCase("Marco.Polo.S01E11.One.Hundred.Eyes.2015.German.DD51.DL.720p.NetflixUHD.x264.NewUp.by.Wunschtante", false)]
|
||||
[TestCase("Hush 2016 German DD51 DL 720p NetflixHD x264-TVS", false)]
|
||||
[TestCase("Community.6x10.Basic.RV.Repair.and.Palmistry.ITA.ENG.720p.WEB-DLMux.H.264-GiuseppeTnT", false)]
|
||||
[TestCase("Community.6x11.Modern.Espionage.ITA.ENG.720p.WEB.DLMux.H.264-GiuseppeTnT", false)]
|
||||
public void should_parse_webdl720p_quality(string title, bool proper)
|
||||
{
|
||||
ParseAndVerifyQuality(title, Quality.WEBDL720p, proper);
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("[ www.Torrenting.com ] - Revenge.S03E14.720p.HDTV.X264-DIMENSION", "DIMENSION")]
|
||||
[TestCase("Seed S02E09 HDTV x264-2HD [eztv]-[rarbg.com]", "2HD")]
|
||||
[TestCase("7s-atlantis-s02e01-720p.mkv", null)]
|
||||
[TestCase("The.Middle.S09E13.720p.HEVC.x265-MeGusta-Pre", "MeGusta")]
|
||||
[TestCase("Ghosted.S01E08.Haunted.Hayride.720p.AMZN.WEBRip.DDP5.1.x264-NTb-postbot", "NTb")]
|
||||
[TestCase("Ghosted.S01E08.Haunted.Hayride.720p.AMZN.WEBRip.DDP5.1.x264-NTb-xpost", "NTb")]
|
||||
//[TestCase("", "")]
|
||||
public void should_parse_release_group(string title, string expected)
|
||||
{
|
||||
|
||||
@@ -41,15 +41,20 @@ namespace NzbDrone.Core.Test.Profiles
|
||||
[Test]
|
||||
public void should_not_be_able_to_delete_profile_if_assigned_to_series()
|
||||
{
|
||||
var profile = Builder<Profile>.CreateNew()
|
||||
.With(p => p.Id = 2)
|
||||
.Build();
|
||||
|
||||
var seriesList = Builder<Series>.CreateListOfSize(3)
|
||||
.Random(1)
|
||||
.With(c => c.ProfileId = 2)
|
||||
.With(c => c.ProfileId = profile.Id)
|
||||
.Build().ToList();
|
||||
|
||||
|
||||
Mocker.GetMock<ISeriesService>().Setup(c => c.GetAllSeries()).Returns(seriesList);
|
||||
Mocker.GetMock<IProfileRepository>().Setup(c => c.Get(profile.Id)).Returns(profile);
|
||||
|
||||
Assert.Throws<ProfileInUseException>(() => Subject.Delete(2));
|
||||
Assert.Throws<ProfileInUseException>(() => Subject.Delete(profile.Id));
|
||||
|
||||
Mocker.GetMock<IProfileRepository>().Verify(c => c.Delete(It.IsAny<int>()), Times.Never());
|
||||
|
||||
@@ -72,4 +77,4 @@ namespace NzbDrone.Core.Test.Profiles
|
||||
Mocker.GetMock<IProfileRepository>().Verify(c => c.Delete(1), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,5 +67,31 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeServiceTests
|
||||
.Should()
|
||||
.BeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_handle_e00_specials()
|
||||
{
|
||||
const string expectedTitle = "Inside The Walking Dead: Walker University";
|
||||
GivenEpisodesWithTitles("Inside The Walking Dead", expectedTitle, "Inside The Walking Dead Walker University 2");
|
||||
|
||||
Subject.FindEpisodeByTitle(1, 1, "The.Walking.Dead.S04E00.Inside.The.Walking.Dead.Walker.University.720p.HDTV.x264-W4F")
|
||||
.Title
|
||||
.Should()
|
||||
.Be(expectedTitle);
|
||||
}
|
||||
|
||||
[TestCase("Dead.Man.Walking.S04E00.Inside.The.Walking.Dead.Walker.University.720p.HDTV.x264-W4F", "Inside The Walking Dead: Walker University", new[] { "Inside The Walking Dead", "Inside The Walking Dead Walker University 2" })]
|
||||
[TestCase("Who.1999.S11E00.Twice.Upon.A.Time.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTb", "Twice Upon A Time", new[] { "Last Christmas" })]
|
||||
[TestCase("Who.1999.S11E00.Twice.Upon.A.Time.Christmas.Special.720p.HDTV.x264-FoV", "Twice Upon A Time", new[] { "Last Christmas" })]
|
||||
[TestCase("Who.1999.S10E00.Christmas.Special.The.Return.Of.Doctor.Mysterio.1080p.BluRay.x264-OUIJA", "The Return Of Doctor Mysterio", new[] { "Doctor Mysterio" })]
|
||||
public void should_handle_special(string releaseTitle, string expectedTitle, string[] rejectedTitles)
|
||||
{
|
||||
GivenEpisodesWithTitles(rejectedTitles.Concat(new[] { expectedTitle }).ToArray());
|
||||
|
||||
var episode = Subject.FindEpisodeByTitle(1, 0, releaseTitle);
|
||||
|
||||
episode.Should().NotBeNull();
|
||||
episode.Title.Should().Be(expectedTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.TvTests
|
||||
|
||||
private void GivenSeriesLastRefreshedRecently()
|
||||
{
|
||||
_series.LastInfoSync = DateTime.UtcNow.AddHours(-1);
|
||||
_series.LastInfoSync = DateTime.UtcNow.AddMinutes(-30);
|
||||
}
|
||||
|
||||
private void GivenRecentlyAired()
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ValidationTests
|
||||
{
|
||||
public class SystemFolderValidatorFixture : CoreTest<SystemFolderValidator>
|
||||
{
|
||||
private TestValidator<Series> _validator;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_validator = new TestValidator<Series>
|
||||
{
|
||||
v => v.RuleFor(s => s.Path).SetValidator(Subject)
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_valid_if_set_to_windows_folder()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = Environment.GetFolderPath(Environment.SpecialFolder.Windows))
|
||||
.Build();
|
||||
|
||||
_validator.Validate(series).IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_valid_if_child_of_windows_folder()
|
||||
{
|
||||
WindowsOnly();
|
||||
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Test"))
|
||||
.Build();
|
||||
|
||||
_validator.Validate(series).IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_valid_if_set_to_bin_folder()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = "/bin")
|
||||
.Build();
|
||||
|
||||
_validator.Validate(series).IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_valid_if_child_of_bin_folder()
|
||||
{
|
||||
MonoOnly();
|
||||
|
||||
var series = Builder<Series>.CreateNew()
|
||||
.With(s => s.Path = "/bin/test")
|
||||
.Build();
|
||||
|
||||
_validator.Validate(series).IsValid.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<package id="Moq" version="4.0.10827" targetFramework="net40" />
|
||||
<package id="NBuilder" version="4.0.0" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net40" />
|
||||
<package id="NLog" version="4.5.0-rc06" targetFramework="net40" />
|
||||
<package id="NUnit" version="3.6.0" targetFramework="net40" />
|
||||
<package id="Prowlin" version="0.9.4456.26422" targetFramework="net40" />
|
||||
<package id="Unity" version="2.1.505.2" targetFramework="net40" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -106,6 +106,12 @@ namespace NzbDrone.Core.Configuration
|
||||
set { SetValue("RssSyncInterval", value); }
|
||||
}
|
||||
|
||||
public int MaximumSize
|
||||
{
|
||||
get { return GetValueInt("MaximumSize", 0); }
|
||||
set { SetValue("MaximumSize", value); }
|
||||
}
|
||||
|
||||
public int MinimumAge
|
||||
{
|
||||
get { return GetValueInt("MinimumAge", 0); }
|
||||
@@ -155,6 +161,13 @@ namespace NzbDrone.Core.Configuration
|
||||
set { SetValue("CreateEmptySeriesFolders", value); }
|
||||
}
|
||||
|
||||
public bool DeleteEmptyFolders
|
||||
{
|
||||
get { return GetValueBoolean("DeleteEmptyFolders", false); }
|
||||
|
||||
set { SetValue("DeleteEmptyFolders", value); }
|
||||
}
|
||||
|
||||
public FileDateType FileDate
|
||||
{
|
||||
get { return GetValueEnum("FileDate", FileDateType.None); }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Common.Http.Proxy;
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace NzbDrone.Core.Configuration
|
||||
string RecycleBin { get; set; }
|
||||
bool AutoDownloadPropers { get; set; }
|
||||
bool CreateEmptySeriesFolders { get; set; }
|
||||
bool DeleteEmptyFolders { get; set; }
|
||||
FileDateType FileDate { get; set; }
|
||||
bool SkipFreeSpaceCheckWhenImporting { get; set; }
|
||||
bool CopyUsingHardlinks { get; set; }
|
||||
@@ -45,6 +46,7 @@ namespace NzbDrone.Core.Configuration
|
||||
//Indexers
|
||||
int Retention { get; set; }
|
||||
int RssSyncInterval { get; set; }
|
||||
int MaximumSize { get; set; }
|
||||
int MinimumAge { get; set; }
|
||||
|
||||
//UI
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
|
||||
@@ -25,6 +25,11 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
if (clrValue == null)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
var value = clrValue;
|
||||
|
||||
return value.ToString();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
|
||||
@@ -23,17 +23,7 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
if (dbValue is int)
|
||||
{
|
||||
return dbValue;
|
||||
}
|
||||
|
||||
return Convert.ToInt32(dbValue);
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
@@ -43,4 +33,4 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
|
||||
public Type DbType { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
using NzbDrone.Core.Qualities;
|
||||
@@ -27,9 +27,9 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
if(clrValue == DBNull.Value) return 0;
|
||||
if (clrValue == DBNull.Value) return 0;
|
||||
|
||||
if(clrValue as Quality == null)
|
||||
if (clrValue as Quality == null)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to save a quality that isn't really a quality");
|
||||
}
|
||||
@@ -56,4 +56,4 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
writer.WriteValue(ToDB(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Marr.Data.Converters;
|
||||
using Marr.Data.Mapping;
|
||||
@@ -15,22 +15,17 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
return TimeSpan.Parse(context.DbValue.ToString());
|
||||
if (context.DbValue is TimeSpan)
|
||||
{
|
||||
return context.DbValue;
|
||||
}
|
||||
|
||||
return TimeSpan.Parse(context.DbValue.ToString(), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
if (dbValue is TimeSpan)
|
||||
{
|
||||
return dbValue;
|
||||
}
|
||||
|
||||
return TimeSpan.Parse(dbValue.ToString(), CultureInfo.InvariantCulture);
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
@@ -45,4 +40,4 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
|
||||
public Type DbType { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Marr.Data;
|
||||
using Marr.Data.Mapping;
|
||||
@@ -46,7 +46,11 @@ namespace NzbDrone.Core.Datastore
|
||||
RegisterMappers();
|
||||
|
||||
Mapper.Entity<Config>().RegisterModel("Config");
|
||||
Mapper.Entity<RootFolder>().RegisterModel("RootFolders").Ignore(r => r.FreeSpace);
|
||||
|
||||
Mapper.Entity<RootFolder>().RegisterModel("RootFolders")
|
||||
.Ignore(r => r.FreeSpace)
|
||||
.Ignore(r => r.TotalSpace);
|
||||
|
||||
Mapper.Entity<ScheduledTask>().RegisterModel("ScheduledTasks");
|
||||
|
||||
Mapper.Entity<IndexerDefinition>().RegisterDefinition("Indexers")
|
||||
|
||||
@@ -58,6 +58,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
{
|
||||
DownloadDecision decision = null;
|
||||
_logger.ProgressTrace("Processing release {0}/{1}", reportNumber, reports.Count);
|
||||
_logger.Debug("Processing release '{0}' from '{1}'", report.Title, report.Indexer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -65,7 +66,7 @@ namespace NzbDrone.Core.DecisionEngine
|
||||
|
||||
if (parsedEpisodeInfo == null || parsedEpisodeInfo.IsPossibleSpecialEpisode)
|
||||
{
|
||||
var specialEpisodeInfo = _parsingService.ParseSpecialEpisodeTitle(report.Title, report.TvdbId, report.TvRageId, searchCriteria);
|
||||
var specialEpisodeInfo = _parsingService.ParseSpecialEpisodeTitle(parsedEpisodeInfo, report.Title, report.TvdbId, report.TvRageId, searchCriteria);
|
||||
|
||||
if (specialEpisodeInfo != null)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
{
|
||||
public class MaximumSizeSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MaximumSizeSpecification(IConfigService configService, Logger logger)
|
||||
{
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public SpecificationPriority Priority => SpecificationPriority.Default;
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var size = subject.Release.Size;
|
||||
var maximumSize = _configService.MaximumSize.Megabytes();
|
||||
|
||||
if (maximumSize == 0)
|
||||
{
|
||||
_logger.Debug("Maximum size is not set.");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
_logger.Debug("Release has unknown size, skipping size check.");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
_logger.Debug("Checking if release meets maximum size requirements. {0}", size.SizeSuffix());
|
||||
|
||||
if (size > maximumSize)
|
||||
{
|
||||
var message = $"{size.SizeSuffix()} is too big, maximum size is {maximumSize.SizeSuffix()}";
|
||||
|
||||
_logger.Debug(message);
|
||||
return Decision.Reject(message);
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
{
|
||||
public class DailyEpisodeMatchSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
|
||||
public DailyEpisodeMatchSpecification(Logger logger, IEpisodeService episodeService)
|
||||
{
|
||||
_logger = logger;
|
||||
_episodeService = episodeService;
|
||||
}
|
||||
|
||||
public SpecificationPriority Priority => SpecificationPriority.Database;
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
if (searchCriteria == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var dailySearchSpec = searchCriteria as DailyEpisodeSearchCriteria;
|
||||
|
||||
if (dailySearchSpec == null) return Decision.Accept();
|
||||
|
||||
var episode = _episodeService.GetEpisode(dailySearchSpec.Series.Id, dailySearchSpec.AirDate.ToString(Episode.AIR_DATE_FORMAT));
|
||||
|
||||
if (!remoteEpisode.ParsedEpisodeInfo.IsDaily || remoteEpisode.ParsedEpisodeInfo.AirDate != episode.AirDate)
|
||||
{
|
||||
_logger.Debug("Episode AirDate does not match searched episode number, skipping.");
|
||||
return Decision.Reject("Episode does not match");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,6 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||
}
|
||||
|
||||
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||
@@ -67,8 +65,6 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
_proxy.SetLabel(actualHash, Settings.TvCategory, Settings);
|
||||
}
|
||||
|
||||
_proxy.SetTorrentConfiguration(actualHash, "remove_at_ratio", false, Settings);
|
||||
|
||||
var isRecentEpisode = remoteEpisode.IsRecentEpisode();
|
||||
|
||||
if (isRecentEpisode && Settings.RecentTvPriority == (int)DelugePriority.First ||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -84,21 +84,33 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
|
||||
public string AddTorrentFromMagnet(string magnetLink, DelugeSettings settings)
|
||||
{
|
||||
var response = ProcessRequest<string>(settings, "core.add_torrent_magnet", magnetLink, new JObject());
|
||||
var options = new
|
||||
{
|
||||
add_paused = settings.AddPaused,
|
||||
remove_at_ratio = false
|
||||
};
|
||||
|
||||
var response = ProcessRequest<string>(settings, "core.add_torrent_magnet", magnetLink, options);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public string AddTorrentFromFile(string filename, byte[] fileContent, DelugeSettings settings)
|
||||
{
|
||||
var response = ProcessRequest<string>(settings, "core.add_torrent_file", filename, fileContent, new JObject());
|
||||
var options = new
|
||||
{
|
||||
add_paused = settings.AddPaused,
|
||||
remove_at_ratio = false
|
||||
};
|
||||
|
||||
var response = ProcessRequest<string>(settings, "core.add_torrent_file", filename, fileContent, options);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public bool RemoveTorrent(string hashString, bool removeData, DelugeSettings settings)
|
||||
public bool RemoveTorrent(string hash, bool removeData, DelugeSettings settings)
|
||||
{
|
||||
var response = ProcessRequest<bool>(settings, "core.remove_torrent", hashString, removeData);
|
||||
var response = ProcessRequest<bool>(settings, "core.remove_torrent", hash, removeData);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -49,7 +49,10 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
|
||||
public int OlderTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(7, Label = "Add Paused", Type = FieldType.Checkbox)]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
{
|
||||
@@ -23,7 +22,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
[JsonProperty(PropertyName = "status_extra")]
|
||||
public Dictionary<string, string> StatusExtra { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonConverter(typeof(UnderscoreStringEnumConverter), DownloadStationTaskStatus.Unknown)]
|
||||
public DownloadStationTaskStatus Status { get; set; }
|
||||
|
||||
public DownloadStationTaskAdditional Additional { get; set; }
|
||||
@@ -41,6 +40,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
|
||||
public enum DownloadStationTaskStatus
|
||||
{
|
||||
Unknown,
|
||||
Waiting,
|
||||
Downloading,
|
||||
Paused,
|
||||
@@ -48,9 +48,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
Finished,
|
||||
HashChecking,
|
||||
Seeding,
|
||||
FileHostingWaiting,
|
||||
FilehostingWaiting,
|
||||
Extracting,
|
||||
Error
|
||||
Error,
|
||||
CaptchaNeeded
|
||||
}
|
||||
|
||||
public enum DownloadStationPriority
|
||||
|
||||
@@ -227,7 +227,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
{
|
||||
switch (torrent.Status)
|
||||
{
|
||||
case DownloadStationTaskStatus.Unknown:
|
||||
case DownloadStationTaskStatus.Waiting:
|
||||
case DownloadStationTaskStatus.FilehostingWaiting:
|
||||
return torrent.Size == 0 || GetRemainingSize(torrent) > 0 ? DownloadItemStatus.Queued : DownloadItemStatus.Completed;
|
||||
case DownloadStationTaskStatus.Paused:
|
||||
return DownloadItemStatus.Paused;
|
||||
|
||||
@@ -315,7 +315,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
{
|
||||
switch (task.Status)
|
||||
{
|
||||
case DownloadStationTaskStatus.Unknown:
|
||||
case DownloadStationTaskStatus.Waiting:
|
||||
case DownloadStationTaskStatus.FilehostingWaiting:
|
||||
return task.Size == 0 || GetRemainingSize(task) > 0 ? DownloadItemStatus.Queued : DownloadItemStatus.Completed;
|
||||
case DownloadStationTaskStatus.Paused:
|
||||
return DownloadItemStatus.Paused;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -52,12 +52,12 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
[FieldDefinition(6, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
|
||||
public int OlderTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NzbGet version 16.0")]
|
||||
[FieldDefinition(7, Label = "Add Paused", Type = FieldType.Checkbox, HelpText = "This option requires at least NzbGet version 16.0")]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -75,6 +75,11 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
.Post()
|
||||
.AddFormParameter("urls", torrentUrl);
|
||||
|
||||
if (settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.AddFormParameter("category", settings.TvCategory);
|
||||
}
|
||||
|
||||
var result = ProcessRequest(request, settings);
|
||||
|
||||
// Note: Older qbit versions returned nothing, so we can't do != "Ok." here.
|
||||
@@ -105,6 +110,11 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
.Post()
|
||||
.AddFormParameter("hashes", hash);
|
||||
|
||||
if (settings.TvCategory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.AddFormParameter("category", settings.TvCategory);
|
||||
}
|
||||
|
||||
ProcessRequest(request, settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -51,6 +51,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
{
|
||||
var arguments = new Dictionary<string, object>();
|
||||
arguments.Add("filename", torrentUrl);
|
||||
arguments.Add("paused", settings.AddPaused);
|
||||
|
||||
if (!downloadDirectory.IsNullOrWhiteSpace())
|
||||
{
|
||||
@@ -64,6 +65,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
{
|
||||
var arguments = new Dictionary<string, object>();
|
||||
arguments.Add("metainfo", Convert.ToBase64String(torrentData));
|
||||
arguments.Add("paused", settings.AddPaused);
|
||||
|
||||
if (!downloadDirectory.IsNullOrWhiteSpace())
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
@@ -62,7 +62,10 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
[FieldDefinition(8, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing episodes that aired over 14 days ago")]
|
||||
public int OlderTvPriority { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
[FieldDefinition(9, Label = "Add Paused", Type = FieldType.Checkbox)]
|
||||
public bool AddPaused { get; set; }
|
||||
|
||||
[FieldDefinition(10, Label = "Use SSL", Type = FieldType.Checkbox)]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -20,10 +20,5 @@ namespace NzbDrone.Core.Download
|
||||
public InvalidNzbException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public InvalidNzbException(string message, string nzbName)
|
||||
: base($"{message} [{0}]", nzbName)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
if (nzb == null)
|
||||
{
|
||||
throw new InvalidNzbException("No Root element", filename);
|
||||
throw new InvalidNzbException("Invalid NZB: No Root element [{0}]", filename);
|
||||
}
|
||||
|
||||
if (!nzb.Name.LocalName.Equals("nzb"))
|
||||
{
|
||||
throw new InvalidNzbException("Invalid root element", filename);
|
||||
throw new InvalidNzbException("Invalid NZB: Unexpected root element. Expected 'nzb' found '{0}' [{1}]", nzb.Name.LocalName, filename);
|
||||
}
|
||||
|
||||
var ns = nzb.Name.Namespace;
|
||||
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
if (files.Empty())
|
||||
{
|
||||
throw new InvalidNzbException("No files", filename);
|
||||
throw new InvalidNzbException("Invalid NZB: No files [{0}]", filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||
{
|
||||
// Try parsing the original source title and if that fails, try parsing it as a special
|
||||
// TODO: Pass the TVDB ID and TVRage IDs in as well so we have a better chance for finding the item
|
||||
parsedEpisodeInfo = Parser.Parser.ParseTitle(firstHistoryItem.SourceTitle) ?? _parsingService.ParseSpecialEpisodeTitle(firstHistoryItem.SourceTitle, 0, 0);
|
||||
parsedEpisodeInfo = Parser.Parser.ParseTitle(firstHistoryItem.SourceTitle) ?? _parsingService.ParseSpecialEpisodeTitle(parsedEpisodeInfo, firstHistoryItem.SourceTitle, 0, 0);
|
||||
|
||||
if (parsedEpisodeInfo != null)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Extras.Files
|
||||
|
||||
public abstract class ExtraFileService<TExtraFile> : IExtraFileService<TExtraFile>,
|
||||
IHandleAsync<SeriesDeletedEvent>,
|
||||
IHandleAsync<EpisodeFileDeletedEvent>
|
||||
IHandle<EpisodeFileDeletedEvent>
|
||||
where TExtraFile : ExtraFile, new()
|
||||
{
|
||||
private readonly IExtraFileRepository<TExtraFile> _repository;
|
||||
@@ -103,7 +103,7 @@ namespace NzbDrone.Core.Extras.Files
|
||||
_repository.DeleteForSeries(message.Series.Id);
|
||||
}
|
||||
|
||||
public void HandleAsync(EpisodeFileDeletedEvent message)
|
||||
public void Handle(EpisodeFileDeletedEvent message)
|
||||
{
|
||||
var episodeFile = message.EpisodeFile;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
@@ -7,10 +6,19 @@ namespace NzbDrone.Core.HealthCheck
|
||||
public class CheckOnAttribute: Attribute
|
||||
{
|
||||
public Type EventType { get; set; }
|
||||
public CheckOnCondition Condition { get; set; }
|
||||
|
||||
public CheckOnAttribute(Type eventType)
|
||||
public CheckOnAttribute(Type eventType, CheckOnCondition condition = CheckOnCondition.Always)
|
||||
{
|
||||
EventType = eventType;
|
||||
Condition = condition;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CheckOnCondition
|
||||
{
|
||||
Always,
|
||||
FailedOnly,
|
||||
SuccessfulOnly
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
|
||||
@@ -7,6 +8,8 @@ namespace NzbDrone.Core.HealthCheck.Checks
|
||||
{
|
||||
[CheckOn(typeof(SeriesDeletedEvent))]
|
||||
[CheckOn(typeof(SeriesMovedEvent))]
|
||||
[CheckOn(typeof(EpisodeImportedEvent), CheckOnCondition.FailedOnly)]
|
||||
[CheckOn(typeof(EpisodeImportFailedEvent), CheckOnCondition.SuccessfulOnly)]
|
||||
public class RootFolderCheck : HealthCheckBase
|
||||
{
|
||||
private readonly ISeriesService _seriesService;
|
||||
|
||||
14
src/NzbDrone.Core/HealthCheck/EventDrivenHealthCheck.cs
Normal file
14
src/NzbDrone.Core/HealthCheck/EventDrivenHealthCheck.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace NzbDrone.Core.HealthCheck
|
||||
{
|
||||
public class EventDrivenHealthCheck
|
||||
{
|
||||
public IProvideHealthCheck HealthCheck { get; set; }
|
||||
public CheckOnCondition Condition { get; set; }
|
||||
|
||||
public EventDrivenHealthCheck(IProvideHealthCheck healthCheck, CheckOnCondition condition)
|
||||
{
|
||||
HealthCheck = healthCheck;
|
||||
Condition = condition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace NzbDrone.Core.HealthCheck
|
||||
private readonly IProvideHealthCheck[] _healthChecks;
|
||||
private readonly IProvideHealthCheck[] _startupHealthChecks;
|
||||
private readonly IProvideHealthCheck[] _scheduledHealthChecks;
|
||||
private readonly Dictionary<Type, IProvideHealthCheck[]> _eventDrivenHealthChecks;
|
||||
private readonly Dictionary<Type, EventDrivenHealthCheck[]> _eventDrivenHealthChecks;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly Logger _logger;
|
||||
@@ -58,10 +58,10 @@ namespace NzbDrone.Core.HealthCheck
|
||||
return _healthCheckResults.Values.ToList();
|
||||
}
|
||||
|
||||
private Dictionary<Type, IProvideHealthCheck[]> GetEventDrivenHealthChecks()
|
||||
private Dictionary<Type, EventDrivenHealthCheck[]> GetEventDrivenHealthChecks()
|
||||
{
|
||||
return _healthChecks
|
||||
.SelectMany(h => h.GetType().GetAttributes<CheckOnAttribute>().Select(a => Tuple.Create(a.EventType, h)))
|
||||
.SelectMany(h => h.GetType().GetAttributes<CheckOnAttribute>().Select(a => Tuple.Create(a.EventType, new EventDrivenHealthCheck(h, a.Condition))))
|
||||
.GroupBy(t => t.Item1, t => t.Item2)
|
||||
.ToDictionary(g => g.Key, g => g.ToArray());
|
||||
}
|
||||
@@ -111,15 +111,43 @@ namespace NzbDrone.Core.HealthCheck
|
||||
return;
|
||||
}
|
||||
|
||||
IProvideHealthCheck[] checks;
|
||||
EventDrivenHealthCheck[] checks;
|
||||
if (!_eventDrivenHealthChecks.TryGetValue(message.GetType(), out checks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var filteredChecks = new List<IProvideHealthCheck>();
|
||||
var healthCheckResults = _healthCheckResults.Values.ToList();
|
||||
|
||||
foreach (var eventDrivenHealthCheck in checks)
|
||||
{
|
||||
if (eventDrivenHealthCheck.Condition == CheckOnCondition.Always)
|
||||
{
|
||||
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
|
||||
continue;
|
||||
}
|
||||
|
||||
var healthCheckType = eventDrivenHealthCheck.HealthCheck.GetType();
|
||||
|
||||
if (eventDrivenHealthCheck.Condition == CheckOnCondition.FailedOnly &&
|
||||
healthCheckResults.Any(r => r.Source == healthCheckType))
|
||||
{
|
||||
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (eventDrivenHealthCheck.Condition == CheckOnCondition.SuccessfulOnly &&
|
||||
healthCheckResults.None(r => r.Source == healthCheckType))
|
||||
{
|
||||
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Add debounce
|
||||
|
||||
PerformHealthCheck(checks);
|
||||
PerformHealthCheck(filteredChecks.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
@@ -52,6 +52,8 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
lock (result)
|
||||
{
|
||||
_logger.Debug("Found {0} from {1}", indexerReports.Count, indexer.Name);
|
||||
|
||||
result.AddRange(indexerReports);
|
||||
}
|
||||
}
|
||||
@@ -71,4 +73,4 @@ namespace NzbDrone.Core.Indexers
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,10 @@ namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
_logger.Debug("Series folder doesn't exist: {0}", series.Path);
|
||||
}
|
||||
|
||||
CleanMediaFiles(series, new List<string>());
|
||||
CompletedScanning(series);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,6 +115,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_logger.Trace("Import decisions complete for: {0} [{1}]", series, decisionsStopwatch.Elapsed);
|
||||
_importApprovedEpisodes.Import(decisions, false);
|
||||
|
||||
RemoveEmptySeriesFolder(series.Path);
|
||||
CompletedScanning(series);
|
||||
}
|
||||
|
||||
@@ -186,6 +189,21 @@ namespace NzbDrone.Core.MediaFiles
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveEmptySeriesFolder(string path)
|
||||
{
|
||||
if (_configService.DeleteEmptyFolders)
|
||||
{
|
||||
if (_diskProvider.GetFiles(path, SearchOption.AllDirectories).Empty())
|
||||
{
|
||||
_diskProvider.DeleteFolder(path, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_diskProvider.RemoveEmptySubfolders(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(SeriesUpdatedEvent message)
|
||||
{
|
||||
Scan(message.Series);
|
||||
|
||||
@@ -12,7 +12,7 @@ using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
@@ -158,7 +158,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
|
||||
if (!_diskProvider.FolderExists(rootFolder))
|
||||
{
|
||||
throw new EpisodeImport.RootFolderNotFoundException(string.Format("Root folder '{0}' was not found.", rootFolder));
|
||||
throw new RootFolderNotFoundException(string.Format("Root folder '{0}' was not found.", rootFolder));
|
||||
}
|
||||
|
||||
var changed = false;
|
||||
|
||||
@@ -125,6 +125,8 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
catch (RootFolderNotFoundException e)
|
||||
{
|
||||
_logger.Warn(e, "Couldn't import episode " + localEpisode);
|
||||
_eventAggregator.PublishEvent(new EpisodeImportFailedEvent(e, localEpisode, newDownload, downloadClientItem));
|
||||
|
||||
importResults.Add(new ImportResult(importDecision, "Failed to import episode, Root folder missing."));
|
||||
}
|
||||
catch (DestinationAlreadyExistsException e)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -205,10 +205,23 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
|
||||
item.Series = decision.LocalEpisode.Series;
|
||||
}
|
||||
|
||||
if (decision.LocalEpisode.Episodes.Any())
|
||||
if (decision.LocalEpisode.Episodes.Any() && decision.LocalEpisode.Episodes.Select(c => c.SeasonNumber).Distinct().Count() == 1)
|
||||
{
|
||||
item.SeasonNumber = decision.LocalEpisode.SeasonNumber;
|
||||
item.Episodes = decision.LocalEpisode.Episodes;
|
||||
var seasons = decision.LocalEpisode.Episodes.Select(c => c.SeasonNumber).Distinct().ToList();
|
||||
|
||||
if (seasons.Empty())
|
||||
{
|
||||
_logger.Warn("Expected one season, but found none for: {0}", decision.LocalEpisode.Path);
|
||||
}
|
||||
else if (seasons.Count > 1)
|
||||
{
|
||||
_logger.Warn("Expected one season, but found {0} ({1}) for: {2}", seasons.Count, string.Join(", ", seasons), decision.LocalEpisode.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SeasonNumber = decision.LocalEpisode.SeasonNumber;
|
||||
item.Episodes = decision.LocalEpisode.Episodes;
|
||||
}
|
||||
}
|
||||
|
||||
item.Quality = decision.LocalEpisode.Quality;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Organizer;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
{
|
||||
public class EpisodeTitleSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly IBuildFileNames _buildFileNames;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public EpisodeTitleSpecification(IBuildFileNames buildFileNames, Logger logger)
|
||||
{
|
||||
_buildFileNames = buildFileNames;
|
||||
_logger = logger;
|
||||
}
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
if (!_buildFileNames.RequiresEpisodeTitle(localEpisode.Series, localEpisode.Episodes))
|
||||
{
|
||||
_logger.Debug("File name format does not require episode title, skipping check");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
foreach (var episode in localEpisode.Episodes)
|
||||
{
|
||||
var airDateUtc = episode.AirDateUtc;
|
||||
var title = episode.Title;
|
||||
|
||||
if (airDateUtc.HasValue && airDateUtc.Value.Before(DateTime.UtcNow.AddDays(-1)))
|
||||
{
|
||||
_logger.Debug("Episode aired more than 1 day ago");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (title.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Debug("Episode does not have a title and recently aired");
|
||||
|
||||
return Decision.Reject("Episode does not have a title and recently aired");
|
||||
}
|
||||
|
||||
if (title.Equals("TBA"))
|
||||
{
|
||||
_logger.Debug("Episode has a TBA title and recently aired");
|
||||
|
||||
return Decision.Reject("Episode has a TBA title and recently aired");
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
@@ -10,10 +11,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
public class MatchesFolderSpecification : IImportDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private readonly IParsingService _parsingService;
|
||||
|
||||
public MatchesFolderSpecification(Logger logger)
|
||||
public MatchesFolderSpecification(IParsingService parsingService, Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_parsingService = parsingService;
|
||||
}
|
||||
public Decision IsSatisfiedBy(LocalEpisode localEpisode, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
@@ -31,6 +34,11 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle(dirInfo.Name);
|
||||
|
||||
if (folderInfo != null && folderInfo.IsPossibleSceneSeasonSpecial)
|
||||
{
|
||||
folderInfo = _parsingService.ParseSpecialEpisodeTitle(folderInfo, dirInfo.Name, localEpisode.Series.TvdbId, 0);
|
||||
}
|
||||
|
||||
if (folderInfo == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
@@ -25,18 +27,23 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var sample = _detectSample.IsSample(localEpisode.Series,
|
||||
localEpisode.Path,
|
||||
localEpisode.IsSpecial);
|
||||
|
||||
if (sample == DetectSampleResult.Sample)
|
||||
try
|
||||
{
|
||||
return Decision.Reject("Sample");
|
||||
var sample = _detectSample.IsSample(localEpisode.Series, localEpisode.Path, localEpisode.IsSpecial);
|
||||
|
||||
if (sample == DetectSampleResult.Sample)
|
||||
{
|
||||
return Decision.Reject("Sample");
|
||||
}
|
||||
|
||||
else if (sample == DetectSampleResult.Indeterminate)
|
||||
{
|
||||
return Decision.Reject("Unable to determine if file is a sample");
|
||||
}
|
||||
}
|
||||
|
||||
else if (sample == DetectSampleResult.Indeterminate)
|
||||
catch (InvalidSeasonException e)
|
||||
{
|
||||
return Decision.Reject("Unable to determine if file is a sample");
|
||||
_logger.Warn(e, "Invalid season detected during sample check");
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.Events
|
||||
{
|
||||
public class EpisodeImportFailedEvent : IEvent
|
||||
{
|
||||
public Exception Exception { get; set; }
|
||||
public LocalEpisode EpisodeInfo { get; }
|
||||
public bool NewDownload { get; }
|
||||
public string DownloadClient { get; }
|
||||
public string DownloadId { get; }
|
||||
|
||||
public EpisodeImportFailedEvent(Exception exception, LocalEpisode episodeInfo, bool newDownload, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
Exception = exception;
|
||||
EpisodeInfo = episodeInfo;
|
||||
NewDownload = newDownload;
|
||||
|
||||
if (downloadClientItem != null)
|
||||
{
|
||||
DownloadClient = downloadClientItem.DownloadClient;
|
||||
DownloadId = downloadClientItem.DownloadId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,10 @@ using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Tv.Events;
|
||||
@@ -16,21 +19,29 @@ namespace NzbDrone.Core.MediaFiles
|
||||
void DeleteEpisodeFile(Series series, EpisodeFile episodeFile);
|
||||
}
|
||||
|
||||
public class MediaFileDeletionService : IDeleteMediaFiles, IHandleAsync<SeriesDeletedEvent>
|
||||
public class MediaFileDeletionService : IDeleteMediaFiles,
|
||||
IHandleAsync<SeriesDeletedEvent>,
|
||||
IHandle<EpisodeFileDeletedEvent>
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public MediaFileDeletionService(IDiskProvider diskProvider,
|
||||
IRecycleBinProvider recycleBinProvider,
|
||||
IMediaFileService mediaFileService,
|
||||
ISeriesService seriesService,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
_mediaFileService = mediaFileService;
|
||||
_seriesService = seriesService;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -76,11 +87,55 @@ namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
if (message.DeleteFiles)
|
||||
{
|
||||
var series = message.Series;
|
||||
var allSeries = _seriesService.GetAllSeries();
|
||||
|
||||
foreach (var s in allSeries)
|
||||
{
|
||||
if (s.Id == series.Id) continue;
|
||||
|
||||
if (series.Path.IsParentPath(s.Path))
|
||||
{
|
||||
_logger.Error("Series path: '{0}' is a parent of another series, not deleting files.", series.Path);
|
||||
return;
|
||||
}
|
||||
|
||||
if (series.Path.PathEquals(s.Path))
|
||||
{
|
||||
_logger.Error("Series path: '{0}' is the same as another series, not deleting files.", series.Path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_diskProvider.FolderExists(message.Series.Path))
|
||||
{
|
||||
_recycleBinProvider.DeleteFolder(message.Series.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[EventHandleOrder(EventHandleOrder.Last)]
|
||||
public void Handle(EpisodeFileDeletedEvent message)
|
||||
{
|
||||
if (message.Reason == DeleteMediaFileReason.Upgrade)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_configService.DeleteEmptyFolders)
|
||||
{
|
||||
var series = message.EpisodeFile.Series.Value;
|
||||
var seasonFolder = message.EpisodeFile.Path.GetParentPath();
|
||||
|
||||
if (_diskProvider.GetFiles(series.Path, SearchOption.AllDirectories).Empty())
|
||||
{
|
||||
_diskProvider.DeleteFolder(series.Path, true);
|
||||
}
|
||||
else if (_diskProvider.GetFiles(seasonFolder, SearchOption.AllDirectories).Empty())
|
||||
{
|
||||
_diskProvider.RemoveEmptySubfolders(seasonFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
@@ -25,6 +26,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IEpisodeService _episodeService;
|
||||
private readonly Logger _logger;
|
||||
private static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public UpdateEpisodeFileService(IDiskProvider diskProvider,
|
||||
IConfigService configService,
|
||||
@@ -77,6 +79,75 @@ namespace NzbDrone.Core.MediaFiles
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ChangeFileDateToLocalAirDate(string filePath, string fileDate, string fileTime)
|
||||
{
|
||||
DateTime airDate;
|
||||
|
||||
if (DateTime.TryParse(fileDate + ' ' + fileTime, out airDate))
|
||||
{
|
||||
// avoiding false +ve checks and set date skewing by not using UTC (Windows)
|
||||
DateTime oldDateTime = _diskProvider.FileGetLastWrite(filePath);
|
||||
|
||||
if (OsInfo.IsNotWindows && airDate < EpochTime)
|
||||
{
|
||||
_logger.Debug("Setting date of file to 1970-01-01 as actual airdate is before that time and will not be set properly");
|
||||
airDate = EpochTime;
|
||||
}
|
||||
|
||||
if (!DateTime.Equals(airDate, oldDateTime))
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.FileSetLastWriteTime(filePath, airDate);
|
||||
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldDateTime, airDate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to set date of file [" + filePath + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_logger.Debug("Could not create valid date to change file [{0}]", filePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ChangeFileDateToUtcAirDate(string filePath, DateTime airDateUtc)
|
||||
{
|
||||
DateTime oldLastWrite = _diskProvider.FileGetLastWrite(filePath);
|
||||
|
||||
if (OsInfo.IsNotWindows && airDateUtc < EpochTime)
|
||||
{
|
||||
_logger.Debug("Setting date of file to 1970-01-01 as actual airdate is before that time and will not be set properly");
|
||||
airDateUtc = EpochTime;
|
||||
}
|
||||
|
||||
if (!DateTime.Equals(airDateUtc, oldLastWrite))
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.FileSetLastWriteTime(filePath, airDateUtc);
|
||||
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldLastWrite, airDateUtc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to set date of file [" + filePath + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Handle(SeriesScannedEvent message)
|
||||
{
|
||||
if (_configService.FileDate == FileDateType.None)
|
||||
@@ -112,62 +183,5 @@ namespace NzbDrone.Core.MediaFiles
|
||||
_logger.ProgressDebug("No file dates changed for {0}", message.Series.Title);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ChangeFileDateToLocalAirDate(string filePath, string fileDate, string fileTime)
|
||||
{
|
||||
DateTime airDate;
|
||||
|
||||
if (DateTime.TryParse(fileDate + ' ' + fileTime, out airDate))
|
||||
{
|
||||
// avoiding false +ve checks and set date skewing by not using UTC (Windows)
|
||||
DateTime oldDateTime = _diskProvider.FileGetLastWrite(filePath);
|
||||
|
||||
if (!DateTime.Equals(airDate, oldDateTime))
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.FileSetLastWriteTime(filePath, airDate);
|
||||
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldDateTime, airDate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to set date of file [" + filePath + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_logger.Debug("Could not create valid date to change file [{0}]", filePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ChangeFileDateToUtcAirDate(string filePath, DateTime airDateUtc)
|
||||
{
|
||||
DateTime oldLastWrite = _diskProvider.FileGetLastWrite(filePath);
|
||||
|
||||
if (!DateTime.Equals(airDateUtc, oldLastWrite))
|
||||
{
|
||||
try
|
||||
{
|
||||
_diskProvider.FileSetLastWriteTime(filePath, airDateUtc);
|
||||
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", filePath, oldLastWrite, airDateUtc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to set date of file [" + filePath + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user