mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-20 13:33:34 -07:00
Refactor and Enable Renaming for Album and Artist Files (#61)
Refactor and Enable Renaming for Album and Artist Files
This commit is contained in:
parent
1c3cfad23f
commit
8569084255
70 changed files with 1526 additions and 3133 deletions
|
@ -34,12 +34,7 @@ namespace NzbDrone.Api.Config
|
||||||
Get["/samples"] = x => GetExamples(this.Bind<NamingConfigResource>());
|
Get["/samples"] = x => GetExamples(this.Bind<NamingConfigResource>());
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.MultiEpisodeStyle).InclusiveBetween(0, 5);
|
SharedValidator.RuleFor(c => c.MultiEpisodeStyle).InclusiveBetween(0, 5);
|
||||||
SharedValidator.RuleFor(c => c.StandardEpisodeFormat).ValidEpisodeFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.StandardTrackFormat).ValidTrackFormat();
|
SharedValidator.RuleFor(c => c.StandardTrackFormat).ValidTrackFormat();
|
||||||
SharedValidator.RuleFor(c => c.DailyEpisodeFormat).ValidDailyEpisodeFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.AnimeEpisodeFormat).ValidAnimeEpisodeFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.SeriesFolderFormat).ValidSeriesFolderFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.SeasonFolderFormat).ValidSeasonFolderFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
|
SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
|
||||||
SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat();
|
SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat();
|
||||||
}
|
}
|
||||||
|
@ -57,7 +52,7 @@ namespace NzbDrone.Api.Config
|
||||||
var nameSpec = _namingConfigService.GetConfig();
|
var nameSpec = _namingConfigService.GetConfig();
|
||||||
var resource = nameSpec.ToResource();
|
var resource = nameSpec.ToResource();
|
||||||
|
|
||||||
if (resource.StandardEpisodeFormat.IsNotNullOrWhiteSpace())
|
if (resource.StandardTrackFormat.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
|
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
|
||||||
basicConfig.AddToResource(resource);
|
basicConfig.AddToResource(resource);
|
||||||
|
@ -76,45 +71,13 @@ namespace NzbDrone.Api.Config
|
||||||
var nameSpec = config.ToModel();
|
var nameSpec = config.ToModel();
|
||||||
var sampleResource = new NamingSampleResource();
|
var sampleResource = new NamingSampleResource();
|
||||||
|
|
||||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
|
||||||
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
|
|
||||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
|
||||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
|
||||||
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
|
||||||
var animeMultiEpisodeSampleResult = _filenameSampleService.GetAnimeMultiEpisodeSample(nameSpec);
|
|
||||||
|
|
||||||
sampleResource.SingleEpisodeExample = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult) != null
|
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
|
||||||
? "Invalid format"
|
|
||||||
: singleEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
|
sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
|
||||||
? "Invalid format"
|
? "Invalid format"
|
||||||
: singleTrackSampleResult.FileName;
|
: singleTrackSampleResult.FileName;
|
||||||
|
|
||||||
sampleResource.MultiEpisodeExample = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: multiEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.DailyEpisodeExample = _filenameValidationService.ValidateDailyFilename(dailyEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: dailyEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.AnimeEpisodeExample = _filenameValidationService.ValidateAnimeFilename(animeEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: animeEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.AnimeMultiEpisodeExample = _filenameValidationService.ValidateAnimeFilename(animeMultiEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: animeMultiEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.SeriesFolderExample = nameSpec.SeriesFolderFormat.IsNullOrWhiteSpace()
|
|
||||||
? "Invalid format"
|
|
||||||
: _filenameSampleService.GetSeriesFolderSample(nameSpec);
|
|
||||||
|
|
||||||
sampleResource.SeasonFolderExample = nameSpec.SeasonFolderFormat.IsNullOrWhiteSpace()
|
|
||||||
? "Invalid format"
|
|
||||||
: _filenameSampleService.GetSeasonFolderSample(nameSpec);
|
|
||||||
|
|
||||||
sampleResource.ArtistFolderExample = nameSpec.ArtistFolderFormat.IsNullOrWhiteSpace()
|
sampleResource.ArtistFolderExample = nameSpec.ArtistFolderFormat.IsNullOrWhiteSpace()
|
||||||
? "Invalid format"
|
? "Invalid format"
|
||||||
: _filenameSampleService.GetArtistFolderSample(nameSpec);
|
: _filenameSampleService.GetArtistFolderSample(nameSpec);
|
||||||
|
@ -128,28 +91,15 @@ namespace NzbDrone.Api.Config
|
||||||
|
|
||||||
private void ValidateFormatResult(NamingConfig nameSpec)
|
private void ValidateFormatResult(NamingConfig nameSpec)
|
||||||
{
|
{
|
||||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
|
||||||
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
|
|
||||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
|
||||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
|
||||||
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
|
||||||
var animeMultiEpisodeSampleResult = _filenameSampleService.GetAnimeMultiEpisodeSample(nameSpec);
|
|
||||||
|
|
||||||
var singleEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult);
|
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
|
||||||
|
|
||||||
var singleTrackValidationResult = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult);
|
var singleTrackValidationResult = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult);
|
||||||
var multiEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult);
|
|
||||||
var dailyEpisodeValidationResult = _filenameValidationService.ValidateDailyFilename(dailyEpisodeSampleResult);
|
|
||||||
var animeEpisodeValidationResult = _filenameValidationService.ValidateAnimeFilename(animeEpisodeSampleResult);
|
|
||||||
var animeMultiEpisodeValidationResult = _filenameValidationService.ValidateAnimeFilename(animeMultiEpisodeSampleResult);
|
|
||||||
|
|
||||||
var validationFailures = new List<ValidationFailure>();
|
var validationFailures = new List<ValidationFailure>();
|
||||||
|
|
||||||
validationFailures.AddIfNotNull(singleEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(singleTrackValidationResult);
|
validationFailures.AddIfNotNull(singleTrackValidationResult);
|
||||||
validationFailures.AddIfNotNull(multiEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(dailyEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(animeEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(animeMultiEpisodeValidationResult);
|
|
||||||
|
|
||||||
if (validationFailures.Any())
|
if (validationFailures.Any())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
using NzbDrone.Api.REST;
|
using NzbDrone.Api.REST;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
public class NamingConfigResource : RestResource
|
public class NamingConfigResource : RestResource
|
||||||
{
|
{
|
||||||
public bool RenameEpisodes { get; set; }
|
|
||||||
public bool RenameTracks { get; set; }
|
public bool RenameTracks { get; set; }
|
||||||
public bool ReplaceIllegalCharacters { get; set; }
|
public bool ReplaceIllegalCharacters { get; set; }
|
||||||
public int MultiEpisodeStyle { get; set; }
|
public int MultiEpisodeStyle { get; set; }
|
||||||
public string StandardEpisodeFormat { get; set; }
|
|
||||||
public string StandardTrackFormat { get; set; }
|
public string StandardTrackFormat { get; set; }
|
||||||
public string DailyEpisodeFormat { get; set; }
|
|
||||||
public string AnimeEpisodeFormat { get; set; }
|
|
||||||
public string SeriesFolderFormat { get; set; }
|
|
||||||
public string SeasonFolderFormat { get; set; }
|
|
||||||
public string ArtistFolderFormat { get; set; }
|
public string ArtistFolderFormat { get; set; }
|
||||||
public string AlbumFolderFormat { get; set; }
|
public string AlbumFolderFormat { get; set; }
|
||||||
public bool IncludeSeriesTitle { get; set; }
|
public bool IncludeArtistName { get; set; }
|
||||||
public bool IncludeEpisodeTitle { get; set; }
|
public bool IncludeAlbumTitle { get; set; }
|
||||||
public bool IncludeQuality { get; set; }
|
public bool IncludeQuality { get; set; }
|
||||||
public bool ReplaceSpaces { get; set; }
|
public bool ReplaceSpaces { get; set; }
|
||||||
public string Separator { get; set; }
|
public string Separator { get; set; }
|
||||||
|
@ -33,16 +27,9 @@ namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
|
|
||||||
RenameEpisodes = model.RenameEpisodes,
|
|
||||||
RenameTracks = model.RenameTracks,
|
RenameTracks = model.RenameTracks,
|
||||||
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
||||||
MultiEpisodeStyle = model.MultiEpisodeStyle,
|
|
||||||
StandardEpisodeFormat = model.StandardEpisodeFormat,
|
|
||||||
StandardTrackFormat = model.StandardTrackFormat,
|
StandardTrackFormat = model.StandardTrackFormat,
|
||||||
DailyEpisodeFormat = model.DailyEpisodeFormat,
|
|
||||||
AnimeEpisodeFormat = model.AnimeEpisodeFormat,
|
|
||||||
SeriesFolderFormat = model.SeriesFolderFormat,
|
|
||||||
SeasonFolderFormat = model.SeasonFolderFormat,
|
|
||||||
ArtistFolderFormat = model.ArtistFolderFormat,
|
ArtistFolderFormat = model.ArtistFolderFormat,
|
||||||
AlbumFolderFormat = model.AlbumFolderFormat
|
AlbumFolderFormat = model.AlbumFolderFormat
|
||||||
//IncludeSeriesTitle
|
//IncludeSeriesTitle
|
||||||
|
@ -56,8 +43,8 @@ namespace NzbDrone.Api.Config
|
||||||
|
|
||||||
public static void AddToResource(this BasicNamingConfig basicNamingConfig, NamingConfigResource resource)
|
public static void AddToResource(this BasicNamingConfig basicNamingConfig, NamingConfigResource resource)
|
||||||
{
|
{
|
||||||
resource.IncludeSeriesTitle = basicNamingConfig.IncludeSeriesTitle;
|
resource.IncludeArtistName = basicNamingConfig.IncludeArtistName;
|
||||||
resource.IncludeEpisodeTitle = basicNamingConfig.IncludeEpisodeTitle;
|
resource.IncludeAlbumTitle = basicNamingConfig.IncludeAlbumTitle;
|
||||||
resource.IncludeQuality = basicNamingConfig.IncludeQuality;
|
resource.IncludeQuality = basicNamingConfig.IncludeQuality;
|
||||||
resource.ReplaceSpaces = basicNamingConfig.ReplaceSpaces;
|
resource.ReplaceSpaces = basicNamingConfig.ReplaceSpaces;
|
||||||
resource.Separator = basicNamingConfig.Separator;
|
resource.Separator = basicNamingConfig.Separator;
|
||||||
|
@ -70,16 +57,9 @@ namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
Id = resource.Id,
|
Id = resource.Id,
|
||||||
|
|
||||||
RenameEpisodes = resource.RenameEpisodes,
|
|
||||||
RenameTracks = resource.RenameTracks,
|
RenameTracks = resource.RenameTracks,
|
||||||
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
|
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
|
||||||
MultiEpisodeStyle = resource.MultiEpisodeStyle,
|
|
||||||
StandardEpisodeFormat = resource.StandardEpisodeFormat,
|
|
||||||
StandardTrackFormat = resource.StandardTrackFormat,
|
StandardTrackFormat = resource.StandardTrackFormat,
|
||||||
DailyEpisodeFormat = resource.DailyEpisodeFormat,
|
|
||||||
AnimeEpisodeFormat = resource.AnimeEpisodeFormat,
|
|
||||||
SeriesFolderFormat = resource.SeriesFolderFormat,
|
|
||||||
SeasonFolderFormat = resource.SeasonFolderFormat,
|
|
||||||
ArtistFolderFormat = resource.ArtistFolderFormat,
|
ArtistFolderFormat = resource.ArtistFolderFormat,
|
||||||
AlbumFolderFormat = resource.AlbumFolderFormat
|
AlbumFolderFormat = resource.AlbumFolderFormat
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Api.REST;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
|
||||||
{
|
|
||||||
public class RenameEpisodeModule : NzbDroneRestModule<RenameEpisodeResource>
|
|
||||||
{
|
|
||||||
private readonly IRenameEpisodeFileService _renameEpisodeFileService;
|
|
||||||
|
|
||||||
public RenameEpisodeModule(IRenameEpisodeFileService renameEpisodeFileService)
|
|
||||||
: base("rename")
|
|
||||||
{
|
|
||||||
_renameEpisodeFileService = renameEpisodeFileService;
|
|
||||||
|
|
||||||
GetResourceAll = GetEpisodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RenameEpisodeResource> GetEpisodes()
|
|
||||||
{
|
|
||||||
if (!Request.Query.SeriesId.HasValue)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("seriesId is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
var seriesId = (int)Request.Query.SeriesId;
|
|
||||||
|
|
||||||
if (Request.Query.SeasonNumber.HasValue)
|
|
||||||
{
|
|
||||||
var seasonNumber = (int)Request.Query.SeasonNumber;
|
|
||||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Api.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
|
||||||
{
|
|
||||||
public class RenameEpisodeResource : RestResource
|
|
||||||
{
|
|
||||||
public int SeriesId { get; set; }
|
|
||||||
public int SeasonNumber { get; set; }
|
|
||||||
public List<int> EpisodeNumbers { get; set; }
|
|
||||||
public int EpisodeFileId { get; set; }
|
|
||||||
public string ExistingPath { get; set; }
|
|
||||||
public string NewPath { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RenameEpisodeResourceMapper
|
|
||||||
{
|
|
||||||
public static RenameEpisodeResource ToResource(this Core.MediaFiles.RenameEpisodeFilePreview model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new RenameEpisodeResource
|
|
||||||
{
|
|
||||||
SeriesId = model.SeriesId,
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
EpisodeNumbers = model.EpisodeNumbers.ToList(),
|
|
||||||
EpisodeFileId = model.EpisodeFileId,
|
|
||||||
ExistingPath = model.ExistingPath,
|
|
||||||
NewPath = model.NewPath
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<RenameEpisodeResource> ToResource(this IEnumerable<Core.MediaFiles.RenameEpisodeFilePreview> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -164,8 +164,6 @@
|
||||||
<Compile Include="Episodes\EpisodeModule.cs" />
|
<Compile Include="Episodes\EpisodeModule.cs" />
|
||||||
<Compile Include="Episodes\EpisodeModuleWithSignalR.cs" />
|
<Compile Include="Episodes\EpisodeModuleWithSignalR.cs" />
|
||||||
<Compile Include="Episodes\EpisodeResource.cs" />
|
<Compile Include="Episodes\EpisodeResource.cs" />
|
||||||
<Compile Include="Episodes\RenameEpisodeModule.cs" />
|
|
||||||
<Compile Include="Episodes\RenameEpisodeResource.cs" />
|
|
||||||
<Compile Include="ErrorManagement\ApiException.cs" />
|
<Compile Include="ErrorManagement\ApiException.cs" />
|
||||||
<Compile Include="ErrorManagement\ErrorHandler.cs" />
|
<Compile Include="ErrorManagement\ErrorHandler.cs" />
|
||||||
<Compile Include="ErrorManagement\ErrorModel.cs" />
|
<Compile Include="ErrorManagement\ErrorModel.cs" />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -78,6 +78,16 @@ namespace NzbDrone.Common.Extensions
|
||||||
return !string.IsNullOrWhiteSpace(text);
|
return !string.IsNullOrWhiteSpace(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool StartsWithIgnoreCase(this string text, string startsWith)
|
||||||
|
{
|
||||||
|
return text.StartsWith(startsWith, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool EqualsIgnoreCase(this string text, string equals)
|
||||||
|
{
|
||||||
|
return text.Equals(equals, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
public static bool ContainsIgnoreCase(this string text, string contains)
|
public static bool ContainsIgnoreCase(this string text, string contains)
|
||||||
{
|
{
|
||||||
return text.IndexOf(contains, StringComparison.InvariantCultureIgnoreCase) > -1;
|
return text.IndexOf(contains, StringComparison.InvariantCultureIgnoreCase) > -1;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Instrumentation.Extensions
|
namespace NzbDrone.Common.Instrumentation.Extensions
|
||||||
{
|
{
|
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Instrumentation.Extensions
|
||||||
|
{
|
||||||
|
public static class SentryLoggerExtensions
|
||||||
|
{
|
||||||
|
public static readonly Logger SentryLogger = LogManager.GetLogger("Sentry");
|
||||||
|
|
||||||
|
public static LogBuilder SentryFingerprint(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
|
{
|
||||||
|
return logBuilder.Property("Sentry", fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogBuilder WriteSentryDebug(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
|
{
|
||||||
|
return LogSentryMessage(logBuilder, LogLevel.Debug, fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogBuilder WriteSentryInfo(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
|
{
|
||||||
|
return LogSentryMessage(logBuilder, LogLevel.Info, fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogBuilder WriteSentryWarn(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
|
{
|
||||||
|
return LogSentryMessage(logBuilder, LogLevel.Warn, fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogBuilder WriteSentryError(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
|
{
|
||||||
|
return LogSentryMessage(logBuilder, LogLevel.Error, fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogBuilder LogSentryMessage(LogBuilder logBuilder, LogLevel level, string[] fingerprint)
|
||||||
|
{
|
||||||
|
SentryLogger.Log(level)
|
||||||
|
.CopyLogEvent(logBuilder.LogEventInfo)
|
||||||
|
.SentryFingerprint(fingerprint)
|
||||||
|
.Write();
|
||||||
|
|
||||||
|
return logBuilder.Property("Sentry", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LogBuilder CopyLogEvent(this LogBuilder logBuilder, LogEventInfo logEvent)
|
||||||
|
{
|
||||||
|
return logBuilder.LoggerName(logEvent.LoggerName)
|
||||||
|
.TimeStamp(logEvent.TimeStamp)
|
||||||
|
.Message(logEvent.Message, logEvent.Parameters)
|
||||||
|
.Properties((Dictionary<object, object>)logEvent.Properties)
|
||||||
|
.Exception(logEvent.Exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -89,32 +89,38 @@ namespace NzbDrone.Common.Instrumentation
|
||||||
|
|
||||||
private static void RegisterSentry(bool updateClient)
|
private static void RegisterSentry(bool updateClient)
|
||||||
{
|
{
|
||||||
// string dsn;
|
// TODO Enable above when we recieve sentry service account.
|
||||||
|
|
||||||
// if (updateClient)
|
string dsn;
|
||||||
// {
|
|
||||||
// dsn = RuntimeInfo.IsProduction
|
|
||||||
// ? "https://b85aa82c65b84b0e99e3b7c281438357:392b5bc007974147a922c5d841c47cf9@sentry.lidarr.audio/11"
|
|
||||||
// : "https://6168f0946aba4e60ac23e469ac08eac5:bd59e8454ccc454ea27a90cff1f814ca@sentry.lidarr.audio/9";
|
|
||||||
|
|
||||||
// }
|
if (updateClient)
|
||||||
// else
|
{
|
||||||
// {
|
dsn = RuntimeInfo.IsProduction
|
||||||
// dsn = RuntimeInfo.IsProduction
|
? "https://b85aa82c65b84b0e99e3b7c281438357:392b5bc007974147a922c5d841c47cf9@sentry.lidarr.audio/11"
|
||||||
// ? "https://3e8a38b1a4df4de8b0453a724f5a1139:5a708dd75c724b32ae5128b6a895650f@sentry.lidarr.audio/8"
|
: "https://6168f0946aba4e60ac23e469ac08eac5:bd59e8454ccc454ea27a90cff1f814ca@sentry.lidarr.audio/9";
|
||||||
// : "https://4ee3580e01d8407c96a7430fbc953512:5f2d07227a0b4fde99dea07041a3ff93@sentry.lidarr.audio/10";
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var target = new SentryTarget(dsn)
|
}
|
||||||
// {
|
else
|
||||||
// Name = "sentryTarget",
|
{
|
||||||
// Layout = "${message}"
|
dsn = RuntimeInfo.IsProduction
|
||||||
// };
|
? "https://3e8a38b1a4df4de8b0453a724f5a1139:5a708dd75c724b32ae5128b6a895650f@sentry.lidarr.audio/8"
|
||||||
|
: "https://4ee3580e01d8407c96a7430fbc953512:5f2d07227a0b4fde99dea07041a3ff93@sentry.lidarr.audio/10";
|
||||||
|
}
|
||||||
|
|
||||||
// var loggingRule = new LoggingRule("*", updateClient ? LogLevel.Trace : LogLevel.Error, target);
|
var target = new SentryTarget(dsn)
|
||||||
// LogManager.Configuration.AddTarget("sentryTarget", target);
|
{
|
||||||
// LogManager.Configuration.LoggingRules.Add(loggingRule);
|
Name = "sentryTarget",
|
||||||
}
|
Layout = "${message}"
|
||||||
|
};
|
||||||
|
|
||||||
|
var loggingRule = new LoggingRule("*", updateClient ? LogLevel.Trace : LogLevel.Warn, target);
|
||||||
|
LogManager.Configuration.AddTarget("sentryTarget", target);
|
||||||
|
LogManager.Configuration.LoggingRules.Add(loggingRule);
|
||||||
|
|
||||||
|
// Events logged to Sentry go only to Sentry.
|
||||||
|
var loggingRuleSentry = new LoggingRule("Sentry", LogLevel.Debug, target) { Final = true };
|
||||||
|
LogManager.Configuration.LoggingRules.Insert(0, loggingRuleSentry);
|
||||||
|
}
|
||||||
|
|
||||||
private static void RegisterDebugger()
|
private static void RegisterDebugger()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
@ -71,6 +72,11 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
|
|
||||||
private static List<string> GetFingerPrint(LogEventInfo logEvent)
|
private static List<string> GetFingerPrint(LogEventInfo logEvent)
|
||||||
{
|
{
|
||||||
|
if (logEvent.Properties.ContainsKey("Sentry"))
|
||||||
|
{
|
||||||
|
return ((string[])logEvent.Properties["Sentry"]).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
var fingerPrint = new List<string>
|
var fingerPrint = new List<string>
|
||||||
{
|
{
|
||||||
logEvent.Level.Ordinal.ToString(),
|
logEvent.Level.Ordinal.ToString(),
|
||||||
|
@ -94,13 +100,33 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
return fingerPrint;
|
return fingerPrint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsSentryMessage(LogEventInfo logEvent)
|
||||||
|
{
|
||||||
|
if (logEvent.Properties.ContainsKey("Sentry"))
|
||||||
|
{
|
||||||
|
return logEvent.Properties["Sentry"] != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logEvent.Level >= LogLevel.Error && logEvent.Exception != null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void Write(LogEventInfo logEvent)
|
protected override void Write(LogEventInfo logEvent)
|
||||||
{
|
{
|
||||||
|
if (_unauthorized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// don't report non-critical events without exceptions
|
// don't report non-critical events without exceptions
|
||||||
if (logEvent.Exception == null || _unauthorized)
|
if (!IsSentryMessage(logEvent))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -112,8 +138,16 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
}
|
}
|
||||||
|
|
||||||
var extras = logEvent.Properties.ToDictionary(x => x.Key.ToString(), x => x.Value.ToString());
|
var extras = logEvent.Properties.ToDictionary(x => x.Key.ToString(), x => x.Value.ToString());
|
||||||
|
extras.Remove("Sentry");
|
||||||
_client.Logger = logEvent.LoggerName;
|
_client.Logger = logEvent.LoggerName;
|
||||||
|
|
||||||
|
if (logEvent.Exception != null)
|
||||||
|
{
|
||||||
|
foreach (DictionaryEntry data in logEvent.Exception.Data)
|
||||||
|
{
|
||||||
|
extras.Add(data.Key.ToString(), data.Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var sentryMessage = new SentryMessage(logEvent.Message, logEvent.Parameters);
|
var sentryMessage = new SentryMessage(logEvent.Message, logEvent.Parameters);
|
||||||
|
|
||||||
|
@ -135,11 +169,16 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
sentryEvent.Fingerprint.Add(logEvent.Exception.GetType().FullName);
|
sentryEvent.Fingerprint.Add(logEvent.Exception.GetType().FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (logEvent.Properties.ContainsKey("Sentry"))
|
||||||
|
{
|
||||||
|
sentryEvent.Fingerprint.Clear();
|
||||||
|
Array.ForEach((string[])logEvent.Properties["Sentry"], sentryEvent.Fingerprint.Add);
|
||||||
|
}
|
||||||
|
|
||||||
var osName = Environment.GetEnvironmentVariable("OS_NAME");
|
var osName = Environment.GetEnvironmentVariable("OS_NAME");
|
||||||
var osVersion = Environment.GetEnvironmentVariable("OS_VERSION");
|
var osVersion = Environment.GetEnvironmentVariable("OS_VERSION");
|
||||||
var runTimeVersion = Environment.GetEnvironmentVariable("RUNTIME_VERSION");
|
var runTimeVersion = Environment.GetEnvironmentVariable("RUNTIME_VERSION");
|
||||||
|
|
||||||
|
|
||||||
sentryEvent.Tags.Add("os_name", osName);
|
sentryEvent.Tags.Add("os_name", osName);
|
||||||
sentryEvent.Tags.Add("os_version", $"{osName} {osVersion}");
|
sentryEvent.Tags.Add("os_version", $"{osName} {osVersion}");
|
||||||
sentryEvent.Tags.Add("runtime_version", $"{PlatformInfo.PlatformName} {runTimeVersion}");
|
sentryEvent.Tags.Add("runtime_version", $"{PlatformInfo.PlatformName} {runTimeVersion}");
|
||||||
|
|
|
@ -175,7 +175,8 @@
|
||||||
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
<Compile Include="Extensions\IEnumerableExtensions.cs" />
|
||||||
<Compile Include="Http\UserAgentBuilder.cs" />
|
<Compile Include="Http\UserAgentBuilder.cs" />
|
||||||
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
|
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
|
||||||
<Compile Include="Instrumentation\Extensions\LoggerProgressExtensions.cs" />
|
<Compile Include="Instrumentation\Extensions\SentryLoggerExtensions.cs" />
|
||||||
|
<Compile Include="Instrumentation\Extensions\LoggerExtensions.cs" />
|
||||||
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
|
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
|
||||||
<Compile Include="Instrumentation\LogEventExtensions.cs" />
|
<Compile Include="Instrumentation\LogEventExtensions.cs" />
|
||||||
<Compile Include="Instrumentation\NzbDroneFileTarget.cs" />
|
<Compile Include="Instrumentation\NzbDroneFileTarget.cs" />
|
||||||
|
|
|
@ -1,124 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using FizzWare.NBuilder;
|
|
||||||
using Moq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
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.EpisodeFileMovingServiceTests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class MoveEpisodeFileFixture : CoreTest<EpisodeFileMovingService>
|
|
||||||
{
|
|
||||||
private Series _series;
|
|
||||||
private EpisodeFile _episodeFile;
|
|
||||||
private LocalEpisode _localEpisode;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
_series = Builder<Series>.CreateNew()
|
|
||||||
.With(s => s.Path = @"C:\Test\TV\Series".AsOsAgnostic())
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_episodeFile = Builder<EpisodeFile>.CreateNew()
|
|
||||||
.With(f => f.Path = null)
|
|
||||||
.With(f => f.RelativePath = @"Season 1\File.avi")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_localEpisode = Builder<LocalEpisode>.CreateNew()
|
|
||||||
.With(l => l.Series = _series)
|
|
||||||
.With(l => l.Episodes = Builder<Episode>.CreateListOfSize(1).Build().ToList())
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
|
||||||
.Setup(s => s.BuildFileName(It.IsAny<List<Episode>>(), It.IsAny<Series>(), It.IsAny<EpisodeFile>(), null))
|
|
||||||
.Returns("File Name");
|
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
|
||||||
.Setup(s => s.BuildFilePath(It.IsAny<Series>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
|
|
||||||
.Returns(@"C:\Test\TV\Series\Season 01\File Name.avi".AsOsAgnostic());
|
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
|
||||||
.Setup(s => s.BuildSeasonPath(It.IsAny<Series>(), It.IsAny<int>()))
|
|
||||||
.Returns(@"C:\Test\TV\Series\Season 01".AsOsAgnostic());
|
|
||||||
|
|
||||||
var rootFolder = @"C:\Test\TV\".AsOsAgnostic();
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
|
||||||
.Setup(s => s.FolderExists(rootFolder))
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
|
||||||
.Setup(s => s.FileExists(It.IsAny<string>()))
|
|
||||||
.Returns(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_catch_UnauthorizedAccessException_during_folder_inheritance()
|
|
||||||
{
|
|
||||||
WindowsOnly();
|
|
||||||
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
|
||||||
.Setup(s => s.InheritFolderPermissions(It.IsAny<string>()))
|
|
||||||
.Throws<UnauthorizedAccessException>();
|
|
||||||
|
|
||||||
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_catch_InvalidOperationException_during_folder_inheritance()
|
|
||||||
{
|
|
||||||
WindowsOnly();
|
|
||||||
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
|
||||||
.Setup(s => s.InheritFolderPermissions(It.IsAny<string>()))
|
|
||||||
.Throws<InvalidOperationException>();
|
|
||||||
|
|
||||||
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_notify_on_series_folder_creation()
|
|
||||||
{
|
|
||||||
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
|
|
||||||
|
|
||||||
Mocker.GetMock<IEventAggregator>()
|
|
||||||
.Verify(s => s.PublishEvent<EpisodeFolderCreatedEvent>(It.Is<EpisodeFolderCreatedEvent>(p =>
|
|
||||||
p.SeriesFolder.IsNotNullOrWhiteSpace())), Times.Once());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_notify_on_season_folder_creation()
|
|
||||||
{
|
|
||||||
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
|
|
||||||
|
|
||||||
Mocker.GetMock<IEventAggregator>()
|
|
||||||
.Verify(s => s.PublishEvent<EpisodeFolderCreatedEvent>(It.Is<EpisodeFolderCreatedEvent>(p =>
|
|
||||||
p.SeasonFolder.IsNotNullOrWhiteSpace())), Times.Once());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_not_notify_if_series_folder_already_exists()
|
|
||||||
{
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
|
||||||
.Setup(s => s.FolderExists(_series.Path))
|
|
||||||
.Returns(true);
|
|
||||||
|
|
||||||
Subject.MoveEpisodeFile(_episodeFile, _localEpisode);
|
|
||||||
|
|
||||||
Mocker.GetMock<IEventAggregator>()
|
|
||||||
.Verify(s => s.PublishEvent<EpisodeFolderCreatedEvent>(It.Is<EpisodeFolderCreatedEvent>(p =>
|
|
||||||
p.SeriesFolder.IsNotNullOrWhiteSpace())), Times.Never());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
using FluentAssertions;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class FormattedAudioChannelsFixture
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void should_subtract_one_from_AudioChannels_as_total_channels_if_LFE_in_AudioChannelPositionsText()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 6,
|
|
||||||
AudioChannelPositions = null,
|
|
||||||
AudioChannelPositionsText = "Front: L C R, Side: L R, LFE"
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_use_AudioChannels_as_total_channels_if_LFE_not_in_AudioChannelPositionsText()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 2,
|
|
||||||
AudioChannelPositions = null,
|
|
||||||
AudioChannelPositionsText = "Front: L R"
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_0_if_schema_revision_is_less_than_3_and_other_properties_are_null()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 2,
|
|
||||||
AudioChannelPositions = null,
|
|
||||||
AudioChannelPositionsText = null,
|
|
||||||
SchemaRevision = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_use_AudioChannels_if_schema_revision_is_3_and_other_properties_are_null()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 2,
|
|
||||||
AudioChannelPositions = null,
|
|
||||||
AudioChannelPositionsText = null,
|
|
||||||
SchemaRevision = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_sum_AudioChannelPositions()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 2,
|
|
||||||
AudioChannelPositions = "2/0/0",
|
|
||||||
AudioChannelPositionsText = null,
|
|
||||||
SchemaRevision = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_sum_AudioChannelPositions_including_decimal()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 2,
|
|
||||||
AudioChannelPositions = "3/2/0.1",
|
|
||||||
AudioChannelPositionsText = null,
|
|
||||||
SchemaRevision = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_cleanup_extraneous_text_from_AudioChannelPositions()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 2,
|
|
||||||
AudioChannelPositions = "Object Based / 3/2/2.1",
|
|
||||||
AudioChannelPositionsText = null,
|
|
||||||
SchemaRevision = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_sum_first_series_of_numbers_from_AudioChannelPositions()
|
|
||||||
{
|
|
||||||
var mediaInfoModel = new MediaInfoModel
|
|
||||||
{
|
|
||||||
AudioChannels = 2,
|
|
||||||
AudioChannelPositions = "3/2/2.1 / 3/2/2.1",
|
|
||||||
AudioChannelPositionsText = null,
|
|
||||||
SchemaRevision = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class FormatAudioCodecFixture : TestBase
|
||||||
|
{
|
||||||
|
[TestCase("AC-3", "AC3")]
|
||||||
|
[TestCase("E-AC-3", "EAC3")]
|
||||||
|
[TestCase("MPEG Audio", "MPEG Audio")]
|
||||||
|
[TestCase("DTS", "DTS")]
|
||||||
|
public void should_format_audio_format(string audioFormat, string expectedFormat)
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioFormat = audioFormat
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(expectedFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_MP3_for_MPEG_Audio_with_Layer_3_for_the_profile()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioFormat = "MPEG Audio",
|
||||||
|
AudioProfile = "Layer 3"
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be("MP3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_AudioFormat_by_default()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioFormat = "Other Audio Format",
|
||||||
|
AudioCodecID = "Other Audio Codec"
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(mediaInfoModel.AudioFormat);
|
||||||
|
ExceptionVerification.ExpectedWarns(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
@ -23,10 +22,10 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_artist = new Artist
|
_artist = new Artist
|
||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
Path = @"C:\artist".AsOsAgnostic()
|
Path = @"C:\artist".AsOsAgnostic()
|
||||||
};
|
};
|
||||||
|
|
||||||
Mocker.GetMock<IConfigService>()
|
Mocker.GetMock<IConfigService>()
|
||||||
.SetupGet(s => s.EnableMediaInfo)
|
.SetupGet(s => s.EnableMediaInfo)
|
||||||
|
@ -59,9 +58,9 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
{
|
{
|
||||||
var trackFiles = Builder<TrackFile>.CreateListOfSize(3)
|
var trackFiles = Builder<TrackFile>.CreateListOfSize(3)
|
||||||
.All()
|
.All()
|
||||||
.With(v => v.RelativePath = "media.mkv")
|
.With(v => v.RelativePath = "media.flac")
|
||||||
.TheFirst(1)
|
.TheFirst(1)
|
||||||
.With(v => v.MediaInfo = new MediaInfoModel { SchemaRevision = 3 })
|
.With(v => v.MediaInfo = new MediaInfoModel { SchemaRevision = UpdateMediaInfoService.CURRENT_MEDIA_INFO_SCHEMA_REVISION })
|
||||||
.BuildList();
|
.BuildList();
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
|
@ -74,7 +73,33 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
Subject.Handle(new ArtistScannedEvent(_artist));
|
Subject.Handle(new ArtistScannedEvent(_artist));
|
||||||
|
|
||||||
Mocker.GetMock<IVideoFileInfoReader>()
|
Mocker.GetMock<IVideoFileInfoReader>()
|
||||||
.Verify(v => v.GetMediaInfo(Path.Combine(_artist.Path, "media.mkv")), Times.Exactly(2));
|
.Verify(v => v.GetMediaInfo(Path.Combine(_artist.Path, "media.flac")), Times.Exactly(2));
|
||||||
|
|
||||||
|
Mocker.GetMock<IMediaFileService>()
|
||||||
|
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Exactly(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_skip_not_yet_date_media_info()
|
||||||
|
{
|
||||||
|
var trackFiles = Builder<TrackFile>.CreateListOfSize(3)
|
||||||
|
.All()
|
||||||
|
.With(v => v.RelativePath = "media.flac")
|
||||||
|
.TheFirst(1)
|
||||||
|
.With(v => v.MediaInfo = new MediaInfoModel { SchemaRevision = UpdateMediaInfoService.MINIMUM_MEDIA_INFO_SCHEMA_REVISION })
|
||||||
|
.BuildList();
|
||||||
|
|
||||||
|
Mocker.GetMock<IMediaFileService>()
|
||||||
|
.Setup(v => v.GetFilesByArtist(1))
|
||||||
|
.Returns(trackFiles);
|
||||||
|
|
||||||
|
GivenFileExists();
|
||||||
|
GivenSuccessfulScan();
|
||||||
|
|
||||||
|
Subject.Handle(new ArtistScannedEvent(_artist));
|
||||||
|
|
||||||
|
Mocker.GetMock<IVideoFileInfoReader>()
|
||||||
|
.Verify(v => v.GetMediaInfo(Path.Combine(_artist.Path, "media.flac")), Times.Exactly(2));
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Exactly(2));
|
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Exactly(2));
|
||||||
|
@ -85,7 +110,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
{
|
{
|
||||||
var trackFiles = Builder<TrackFile>.CreateListOfSize(3)
|
var trackFiles = Builder<TrackFile>.CreateListOfSize(3)
|
||||||
.All()
|
.All()
|
||||||
.With(v => v.RelativePath = "media.mkv")
|
.With(v => v.RelativePath = "media.flac")
|
||||||
.TheFirst(1)
|
.TheFirst(1)
|
||||||
.With(v => v.MediaInfo = new MediaInfoModel())
|
.With(v => v.MediaInfo = new MediaInfoModel())
|
||||||
.BuildList();
|
.BuildList();
|
||||||
|
@ -100,7 +125,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
Subject.Handle(new ArtistScannedEvent(_artist));
|
Subject.Handle(new ArtistScannedEvent(_artist));
|
||||||
|
|
||||||
Mocker.GetMock<IVideoFileInfoReader>()
|
Mocker.GetMock<IVideoFileInfoReader>()
|
||||||
.Verify(v => v.GetMediaInfo(Path.Combine(_artist.Path, "media.mkv")), Times.Exactly(3));
|
.Verify(v => v.GetMediaInfo(Path.Combine(_artist.Path, "media.flac")), Times.Exactly(3));
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Exactly(3));
|
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Exactly(3));
|
||||||
|
@ -111,7 +136,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
{
|
{
|
||||||
var trackFiles = Builder<TrackFile>.CreateListOfSize(2)
|
var trackFiles = Builder<TrackFile>.CreateListOfSize(2)
|
||||||
.All()
|
.All()
|
||||||
.With(v => v.RelativePath = "media.mkv")
|
.With(v => v.RelativePath = "media.flac")
|
||||||
.BuildList();
|
.BuildList();
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
|
@ -123,7 +148,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
Subject.Handle(new ArtistScannedEvent(_artist));
|
Subject.Handle(new ArtistScannedEvent(_artist));
|
||||||
|
|
||||||
Mocker.GetMock<IVideoFileInfoReader>()
|
Mocker.GetMock<IVideoFileInfoReader>()
|
||||||
.Verify(v => v.GetMediaInfo("media.mkv"), Times.Never());
|
.Verify(v => v.GetMediaInfo("media.flac"), Times.Never());
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Never());
|
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Never());
|
||||||
|
@ -132,25 +157,25 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||||
[Test]
|
[Test]
|
||||||
public void should_continue_after_failure()
|
public void should_continue_after_failure()
|
||||||
{
|
{
|
||||||
var trackFiles = Builder<TrackFile>.CreateListOfSize(2)
|
var episodeFiles = Builder<TrackFile>.CreateListOfSize(2)
|
||||||
.All()
|
.All()
|
||||||
.With(v => v.RelativePath = "media.mkv")
|
.With(v => v.RelativePath = "media.flac")
|
||||||
.TheFirst(1)
|
.TheFirst(1)
|
||||||
.With(v => v.RelativePath = "media2.mkv")
|
.With(v => v.RelativePath = "media2.flac")
|
||||||
.BuildList();
|
.BuildList();
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
.Setup(v => v.GetFilesByArtist(1))
|
.Setup(v => v.GetFilesByArtist(1))
|
||||||
.Returns(trackFiles);
|
.Returns(episodeFiles);
|
||||||
|
|
||||||
GivenFileExists();
|
GivenFileExists();
|
||||||
GivenSuccessfulScan();
|
GivenSuccessfulScan();
|
||||||
GivenFailedScan(Path.Combine(_artist.Path, "media2.mkv"));
|
GivenFailedScan(Path.Combine(_artist.Path, "media2.flac"));
|
||||||
|
|
||||||
Subject.Handle(new ArtistScannedEvent(_artist));
|
Subject.Handle(new ArtistScannedEvent(_artist));
|
||||||
|
|
||||||
Mocker.GetMock<IVideoFileInfoReader>()
|
Mocker.GetMock<IVideoFileInfoReader>()
|
||||||
.Verify(v => v.GetMediaInfo(Path.Combine(_artist.Path, "media.mkv")), Times.Exactly(1));
|
.Verify(v => v.GetMediaInfo(Path.Combine(_artist.Path, "media.flac")), Times.Exactly(1));
|
||||||
|
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Exactly(1));
|
.Verify(v => v.Update(It.IsAny<TrackFile>()), Times.Exactly(1));
|
||||||
|
|
|
@ -8,12 +8,11 @@ using NzbDrone.Core.MediaFiles.Commands;
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.MediaFiles
|
namespace NzbDrone.Core.Test.MediaFiles
|
||||||
{
|
{
|
||||||
public class RenameEpisodeFileServiceFixture : CoreTest<RenameEpisodeFileService>
|
public class RenameTrackFileServiceFixture : CoreTest<RenameTrackFileService>
|
||||||
{
|
{
|
||||||
private Artist _artist;
|
private Artist _artist;
|
||||||
private List<TrackFile> _trackFiles;
|
private List<TrackFile> _trackFiles;
|
||||||
|
@ -35,14 +34,14 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||||
.Returns(_artist);
|
.Returns(_artist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenNoEpisodeFiles()
|
private void GivenNoTrackFiles()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
.Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
|
.Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
|
||||||
.Returns(new List<TrackFile>());
|
.Returns(new List<TrackFile>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenEpisodeFiles()
|
private void GivenTrackFiles()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IMediaFileService>()
|
Mocker.GetMock<IMediaFileService>()
|
||||||
.Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
|
.Setup(s => s.Get(It.IsAny<IEnumerable<int>>()))
|
||||||
|
@ -58,18 +57,18 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_publish_event_if_no_files_to_rename()
|
public void should_not_publish_event_if_no_files_to_rename()
|
||||||
{
|
{
|
||||||
GivenNoEpisodeFiles();
|
GivenNoTrackFiles();
|
||||||
|
|
||||||
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int>{1}));
|
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int>{1}));
|
||||||
|
|
||||||
Mocker.GetMock<IEventAggregator>()
|
Mocker.GetMock<IEventAggregator>()
|
||||||
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never());
|
.Verify(v => v.PublishEvent(It.IsAny<ArtistRenamedEvent>()), Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_publish_event_if_no_files_are_renamed()
|
public void should_not_publish_event_if_no_files_are_renamed()
|
||||||
{
|
{
|
||||||
GivenEpisodeFiles();
|
GivenTrackFiles();
|
||||||
|
|
||||||
Mocker.GetMock<IMoveTrackFiles>()
|
Mocker.GetMock<IMoveTrackFiles>()
|
||||||
.Setup(s => s.MoveTrackFile(It.IsAny<TrackFile>(), It.IsAny<Artist>()))
|
.Setup(s => s.MoveTrackFile(It.IsAny<TrackFile>(), It.IsAny<Artist>()))
|
||||||
|
@ -78,25 +77,25 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||||
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int> { 1 }));
|
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int> { 1 }));
|
||||||
|
|
||||||
Mocker.GetMock<IEventAggregator>()
|
Mocker.GetMock<IEventAggregator>()
|
||||||
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Never());
|
.Verify(v => v.PublishEvent(It.IsAny<ArtistRenamedEvent>()), Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_publish_event_if_files_are_renamed()
|
public void should_publish_event_if_files_are_renamed()
|
||||||
{
|
{
|
||||||
GivenEpisodeFiles();
|
GivenTrackFiles();
|
||||||
GivenMovedFiles();
|
GivenMovedFiles();
|
||||||
|
|
||||||
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int> { 1 }));
|
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int> { 1 }));
|
||||||
|
|
||||||
Mocker.GetMock<IEventAggregator>()
|
Mocker.GetMock<IEventAggregator>()
|
||||||
.Verify(v => v.PublishEvent(It.IsAny<SeriesRenamedEvent>()), Times.Once());
|
.Verify(v => v.PublishEvent(It.IsAny<ArtistRenamedEvent>()), Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_update_moved_files()
|
public void should_update_moved_files()
|
||||||
{
|
{
|
||||||
GivenEpisodeFiles();
|
GivenTrackFiles();
|
||||||
GivenMovedFiles();
|
GivenMovedFiles();
|
||||||
|
|
||||||
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int> { 1 }));
|
Subject.Execute(new RenameFilesCommand(_artist.Id, new List<int> { 1 }));
|
||||||
|
@ -106,9 +105,9 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_get_episodefiles_by_ids_only()
|
public void should_get_trackfiles_by_ids_only()
|
||||||
{
|
{
|
||||||
GivenEpisodeFiles();
|
GivenTrackFiles();
|
||||||
GivenMovedFiles();
|
GivenMovedFiles();
|
||||||
|
|
||||||
var files = new List<int> { 1 };
|
var files = new List<int> { 1 };
|
|
@ -0,0 +1,124 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.Organizer;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.MediaFiles.TrackFileMovingServiceTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class MoveTrackFileFixture : CoreTest<TrackFileMovingService>
|
||||||
|
{
|
||||||
|
private Artist _artist;
|
||||||
|
private TrackFile _trackFile;
|
||||||
|
private LocalTrack _localtrack;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_artist = Builder<Artist>.CreateNew()
|
||||||
|
.With(s => s.Path = @"C:\Test\Music\Artist".AsOsAgnostic())
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_trackFile = Builder<TrackFile>.CreateNew()
|
||||||
|
.With(f => f.Path = null)
|
||||||
|
.With(f => f.RelativePath = @"Album\File.mp3")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_localtrack = Builder<LocalTrack>.CreateNew()
|
||||||
|
.With(l => l.Artist = _artist)
|
||||||
|
.With(l => l.Tracks = Builder<Track>.CreateListOfSize(1).Build().ToList())
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
|
.Setup(s => s.BuildTrackFileName(It.IsAny<List<Track>>(), It.IsAny<Artist>(), It.IsAny<Album>(), It.IsAny<TrackFile>(), null))
|
||||||
|
.Returns("File Name");
|
||||||
|
|
||||||
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
|
.Setup(s => s.BuildTrackFilePath(It.IsAny<Artist>(), It.IsAny<Album>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||||
|
.Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic());
|
||||||
|
|
||||||
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
|
.Setup(s => s.BuildAlbumPath(It.IsAny<Artist>(), It.IsAny<Album>()))
|
||||||
|
.Returns(@"C:\Test\Music\Artist\Album".AsOsAgnostic());
|
||||||
|
|
||||||
|
var rootFolder = @"C:\Test\Music\".AsOsAgnostic();
|
||||||
|
Mocker.GetMock<IDiskProvider>()
|
||||||
|
.Setup(s => s.FolderExists(rootFolder))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>()
|
||||||
|
.Setup(s => s.FileExists(It.IsAny<string>()))
|
||||||
|
.Returns(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_catch_UnauthorizedAccessException_during_folder_inheritance()
|
||||||
|
{
|
||||||
|
WindowsOnly();
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>()
|
||||||
|
.Setup(s => s.InheritFolderPermissions(It.IsAny<string>()))
|
||||||
|
.Throws<UnauthorizedAccessException>();
|
||||||
|
|
||||||
|
Subject.MoveTrackFile(_trackFile, _localtrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_catch_InvalidOperationException_during_folder_inheritance()
|
||||||
|
{
|
||||||
|
WindowsOnly();
|
||||||
|
|
||||||
|
Mocker.GetMock<IDiskProvider>()
|
||||||
|
.Setup(s => s.InheritFolderPermissions(It.IsAny<string>()))
|
||||||
|
.Throws<InvalidOperationException>();
|
||||||
|
|
||||||
|
Subject.MoveTrackFile(_trackFile, _localtrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_notify_on_artist_folder_creation()
|
||||||
|
{
|
||||||
|
Subject.MoveTrackFile(_trackFile, _localtrack);
|
||||||
|
|
||||||
|
Mocker.GetMock<IEventAggregator>()
|
||||||
|
.Verify(s => s.PublishEvent<TrackFolderCreatedEvent>(It.Is<TrackFolderCreatedEvent>(p =>
|
||||||
|
p.ArtistFolder.IsNotNullOrWhiteSpace())), Times.Once());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_notify_on_album_folder_creation()
|
||||||
|
{
|
||||||
|
Subject.MoveTrackFile(_trackFile, _localtrack);
|
||||||
|
|
||||||
|
Mocker.GetMock<IEventAggregator>()
|
||||||
|
.Verify(s => s.PublishEvent<TrackFolderCreatedEvent>(It.Is<TrackFolderCreatedEvent>(p =>
|
||||||
|
p.AlbumFolder.IsNotNullOrWhiteSpace())), Times.Once());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_notify_if_artist_folder_already_exists()
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IDiskProvider>()
|
||||||
|
.Setup(s => s.FolderExists(_artist.Path))
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
Subject.MoveTrackFile(_trackFile, _localtrack);
|
||||||
|
|
||||||
|
Mocker.GetMock<IEventAggregator>()
|
||||||
|
.Verify(s => s.PublishEvent<TrackFolderCreatedEvent>(It.Is<TrackFolderCreatedEvent>(p =>
|
||||||
|
p.ArtistFolder.IsNotNullOrWhiteSpace())), Times.Never());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,35 +5,35 @@ using NUnit.Framework;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Core.Tv.Commands;
|
using NzbDrone.Core.Music.Commands;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.TvTests
|
namespace NzbDrone.Core.Test.MusicTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class MoveSeriesServiceFixture : CoreTest<MoveSeriesService>
|
public class MoveArtistServiceFixture : CoreTest<MoveArtistService>
|
||||||
{
|
{
|
||||||
private Series _series;
|
private Artist _artist;
|
||||||
private MoveSeriesCommand _command;
|
private MoveArtistCommand _command;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_series = Builder<Series>
|
_artist = Builder<Artist>
|
||||||
.CreateNew()
|
.CreateNew()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_command = new MoveSeriesCommand
|
_command = new MoveArtistCommand
|
||||||
{
|
{
|
||||||
SeriesId = 1,
|
ArtistId = 1,
|
||||||
SourcePath = @"C:\Test\TV\Series".AsOsAgnostic(),
|
SourcePath = @"C:\Test\Music\Artist".AsOsAgnostic(),
|
||||||
DestinationPath = @"C:\Test\TV2\Series".AsOsAgnostic()
|
DestinationPath = @"C:\Test\Music2\Artist".AsOsAgnostic()
|
||||||
};
|
};
|
||||||
|
|
||||||
Mocker.GetMock<ISeriesService>()
|
Mocker.GetMock<IArtistService>()
|
||||||
.Setup(s => s.GetSeries(It.IsAny<int>()))
|
.Setup(s => s.GetArtist(It.IsAny<int>()))
|
||||||
.Returns(_series);
|
.Returns(_artist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenFailedMove()
|
private void GivenFailedMove()
|
||||||
|
@ -54,7 +54,7 @@ namespace NzbDrone.Core.Test.TvTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_no_update_series_path_on_error()
|
public void should_no_update_artist_path_on_error()
|
||||||
{
|
{
|
||||||
GivenFailedMove();
|
GivenFailedMove();
|
||||||
|
|
||||||
|
@ -62,26 +62,26 @@ namespace NzbDrone.Core.Test.TvTests
|
||||||
|
|
||||||
ExceptionVerification.ExpectedErrors(1);
|
ExceptionVerification.ExpectedErrors(1);
|
||||||
|
|
||||||
Mocker.GetMock<ISeriesService>()
|
Mocker.GetMock<IArtistService>()
|
||||||
.Verify(v => v.UpdateSeries(It.IsAny<Series>()), Times.Never());
|
.Verify(v => v.UpdateArtist(It.IsAny<Artist>()), Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_build_new_path_when_root_folder_is_provided()
|
public void should_build_new_path_when_root_folder_is_provided()
|
||||||
{
|
{
|
||||||
_command.DestinationPath = null;
|
_command.DestinationPath = null;
|
||||||
_command.DestinationRootFolder = @"C:\Test\TV3".AsOsAgnostic();
|
_command.DestinationRootFolder = @"C:\Test\Music3".AsOsAgnostic();
|
||||||
|
|
||||||
var expectedPath = @"C:\Test\TV3\Series".AsOsAgnostic();
|
var expectedPath = @"C:\Test\Music3\Artist".AsOsAgnostic();
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
.Setup(s => s.GetSeriesFolder(It.IsAny<Series>(), null))
|
.Setup(s => s.GetArtistFolder(It.IsAny<Artist>(), null))
|
||||||
.Returns("Series");
|
.Returns("Artist");
|
||||||
|
|
||||||
Subject.Execute(_command);
|
Subject.Execute(_command);
|
||||||
|
|
||||||
Mocker.GetMock<ISeriesService>()
|
Mocker.GetMock<IArtistService>()
|
||||||
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Path == expectedPath)), Times.Once());
|
.Verify(v => v.UpdateArtist(It.Is<Artist>(s => s.Path == expectedPath)), Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -89,11 +89,11 @@ namespace NzbDrone.Core.Test.TvTests
|
||||||
{
|
{
|
||||||
Subject.Execute(_command);
|
Subject.Execute(_command);
|
||||||
|
|
||||||
Mocker.GetMock<ISeriesService>()
|
Mocker.GetMock<IArtistService>()
|
||||||
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Path == _command.DestinationPath)), Times.Once());
|
.Verify(v => v.UpdateArtist(It.Is<Artist>(s => s.Path == _command.DestinationPath)), Times.Once());
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
.Verify(v => v.GetSeriesFolder(It.IsAny<Series>(), null), Times.Never());
|
.Verify(v => v.GetArtistFolder(It.IsAny<Artist>(), null), Times.Never());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -283,7 +283,8 @@
|
||||||
<Compile Include="MediaFiles\DiskScanServiceTests\ScanFixture.cs" />
|
<Compile Include="MediaFiles\DiskScanServiceTests\ScanFixture.cs" />
|
||||||
<Compile Include="MediaFiles\DownloadedEpisodesCommandServiceFixture.cs" />
|
<Compile Include="MediaFiles\DownloadedEpisodesCommandServiceFixture.cs" />
|
||||||
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
|
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
|
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioCodecFixture.cs" />
|
||||||
|
<Compile Include="MediaFiles\TrackFileMovingServiceTests\MoveTrackFileFixture.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
|
||||||
|
@ -291,7 +292,6 @@
|
||||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
|
||||||
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
|
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
|
||||||
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
|
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
|
||||||
<Compile Include="MediaFiles\MediaInfo\FormattedAudioChannelsFixture.cs" />
|
|
||||||
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
|
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\SkyHookProxySearchFixture.cs" />
|
<Compile Include="MetadataSource\SkyHook\SkyHookProxySearchFixture.cs" />
|
||||||
<Compile Include="MetadataSource\SearchSeriesComparerFixture.cs" />
|
<Compile Include="MetadataSource\SearchSeriesComparerFixture.cs" />
|
||||||
|
@ -300,8 +300,6 @@
|
||||||
<Compile Include="NotificationTests\NotificationBaseFixture.cs" />
|
<Compile Include="NotificationTests\NotificationBaseFixture.cs" />
|
||||||
<Compile Include="NotificationTests\SynologyIndexerFixture.cs" />
|
<Compile Include="NotificationTests\SynologyIndexerFixture.cs" />
|
||||||
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
|
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
|
||||||
<Compile Include="OrganizerTests\FileNameBuilderTests\EpisodeTitleCollapseFixture.cs" />
|
|
||||||
<Compile Include="OrganizerTests\FileNameBuilderTests\MultiEpisodeFixture.cs" />
|
|
||||||
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
|
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
|
||||||
<Compile Include="ParserTests\MusicParserFixture.cs" />
|
<Compile Include="ParserTests\MusicParserFixture.cs" />
|
||||||
<Compile Include="Qualities\RevisionComparableFixture.cs" />
|
<Compile Include="Qualities\RevisionComparableFixture.cs" />
|
||||||
|
@ -312,7 +310,7 @@
|
||||||
<Compile Include="MediaFiles\MediaFileTableCleanupServiceFixture.cs" />
|
<Compile Include="MediaFiles\MediaFileTableCleanupServiceFixture.cs" />
|
||||||
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoServiceFixture.cs" />
|
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoServiceFixture.cs" />
|
||||||
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReaderFixture.cs" />
|
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReaderFixture.cs" />
|
||||||
<Compile Include="MediaFiles\RenameEpisodeFileServiceFixture.cs" />
|
<Compile Include="MediaFiles\RenameTrackFileServiceFixture.cs" />
|
||||||
<Compile Include="MediaFiles\UpgradeMediaFileServiceFixture.cs" />
|
<Compile Include="MediaFiles\UpgradeMediaFileServiceFixture.cs" />
|
||||||
<Compile Include="Messaging\Commands\CommandEqualityComparerFixture.cs" />
|
<Compile Include="Messaging\Commands\CommandEqualityComparerFixture.cs" />
|
||||||
<Compile Include="Messaging\Commands\CommandExecutorFixture.cs" />
|
<Compile Include="Messaging\Commands\CommandExecutorFixture.cs" />
|
||||||
|
@ -329,9 +327,9 @@
|
||||||
<Compile Include="NotificationTests\Xbmc\Json\UpdateFixture.cs" />
|
<Compile Include="NotificationTests\Xbmc\Json\UpdateFixture.cs" />
|
||||||
<Compile Include="NotificationTests\Xbmc\OnDownloadFixture.cs" />
|
<Compile Include="NotificationTests\Xbmc\OnDownloadFixture.cs" />
|
||||||
<Compile Include="OrganizerTests\BuildFilePathFixture.cs" />
|
<Compile Include="OrganizerTests\BuildFilePathFixture.cs" />
|
||||||
<Compile Include="OrganizerTests\GetSeasonFolderFixture.cs" />
|
<Compile Include="OrganizerTests\GetAlbumFolderFixture.cs" />
|
||||||
<Compile Include="OrganizerTests\FileNameBuilderTests\FileNameBuilderFixture.cs" />
|
<Compile Include="OrganizerTests\FileNameBuilderTests\FileNameBuilderFixture.cs" />
|
||||||
<Compile Include="OrganizerTests\GetSeriesFolderFixture.cs" />
|
<Compile Include="OrganizerTests\GetArtistFolderFixture.cs" />
|
||||||
<Compile Include="ParserTests\AbsoluteEpisodeNumberParserFixture.cs" />
|
<Compile Include="ParserTests\AbsoluteEpisodeNumberParserFixture.cs" />
|
||||||
<Compile Include="ParserTests\AnimeMetadataParserFixture.cs" />
|
<Compile Include="ParserTests\AnimeMetadataParserFixture.cs" />
|
||||||
<Compile Include="ParserTests\ExtendedQualityParserRegex.cs" />
|
<Compile Include="ParserTests\ExtendedQualityParserRegex.cs" />
|
||||||
|
@ -378,7 +376,7 @@
|
||||||
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithFilesFixture.cs" />
|
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithFilesFixture.cs" />
|
||||||
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithoutFilesFixture.cs" />
|
<Compile Include="TvTests\EpisodeRepositoryTests\EpisodesWithoutFilesFixture.cs" />
|
||||||
<Compile Include="TvTests\EpisodeRepositoryTests\FindEpisodeFixture.cs" />
|
<Compile Include="TvTests\EpisodeRepositoryTests\FindEpisodeFixture.cs" />
|
||||||
<Compile Include="TvTests\MoveSeriesServiceFixture.cs" />
|
<Compile Include="MusicTests\MoveArtistServiceFixture.cs" />
|
||||||
<Compile Include="TvTests\RefreshEpisodeServiceFixture.cs" />
|
<Compile Include="TvTests\RefreshEpisodeServiceFixture.cs" />
|
||||||
<Compile Include="TvTests\RefreshSeriesServiceFixture.cs" />
|
<Compile Include="TvTests\RefreshSeriesServiceFixture.cs" />
|
||||||
<Compile Include="TvTests\EpisodeMonitoredServiceTests\SetEpisodeMontitoredFixture.cs" />
|
<Compile Include="TvTests\EpisodeMonitoredServiceTests\SetEpisodeMontitoredFixture.cs" />
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
@ -24,41 +24,25 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[TestCase("30 Rock - S01E05 - Episode Title", 1, true, "Season {season:00}", @"C:\Test\30 Rock\Season 01\30 Rock - S01E05 - Episode Title.mkv")]
|
public void should_clean_album_folder_when_it_contains_illegal_characters_in_album_or_artist_title()
|
||||||
[TestCase("30 Rock - S01E05 - Episode Title", 1, true, "Season {season}", @"C:\Test\30 Rock\Season 1\30 Rock - S01E05 - Episode Title.mkv")]
|
|
||||||
[TestCase("30 Rock - S01E05 - Episode Title", 1, false, "Season {season:00}", @"C:\Test\30 Rock\30 Rock - S01E05 - Episode Title.mkv")]
|
|
||||||
[TestCase("30 Rock - S01E05 - Episode Title", 1, false, "Season {season}", @"C:\Test\30 Rock\30 Rock - S01E05 - Episode Title.mkv")]
|
|
||||||
[TestCase("30 Rock - S01E05 - Episode Title", 1, true, "ReallyUglySeasonFolder {season}", @"C:\Test\30 Rock\ReallyUglySeasonFolder 1\30 Rock - S01E05 - Episode Title.mkv")]
|
|
||||||
[TestCase("30 Rock - S00E05 - Episode Title", 0, true, "Season {season}", @"C:\Test\30 Rock\Specials\30 Rock - S00E05 - Episode Title.mkv")]
|
|
||||||
public void CalculateFilePath_SeasonFolder_SingleNumber(string filename, int seasonNumber, bool useSeasonFolder, string seasonFolderFormat, string expectedPath)
|
|
||||||
{
|
{
|
||||||
var fakeSeries = Builder<Series>.CreateNew()
|
var filename = @"02 - Track Title";
|
||||||
.With(s => s.Title = "30 Rock")
|
var expectedPath = @"C:\Test\Fake- The Artist\Fake- The Artist Fake- Album\02 - Track Title.flac";
|
||||||
.With(s => s.Path = @"C:\Test\30 Rock".AsOsAgnostic())
|
|
||||||
.With(s => s.SeasonFolder = useSeasonFolder)
|
var fakeArtist = Builder<Artist>.CreateNew()
|
||||||
|
.With(s => s.Name = "Fake: The Artist")
|
||||||
|
.With(s => s.Path = @"C:\Test\Fake- The Artist".AsOsAgnostic())
|
||||||
|
.With(s => s.AlbumFolder = true)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
namingConfig.SeasonFolderFormat = seasonFolderFormat;
|
var fakeAlbum = Builder<Album>.CreateNew()
|
||||||
|
.With(s => s.Title = "Fake: Album")
|
||||||
Subject.BuildFilePath(fakeSeries, seasonNumber, filename, ".mkv").Should().Be(expectedPath.AsOsAgnostic());
|
.With(s => s.Path = @"C:\Test\Fake- The Artist\Fake- Album".AsOsAgnostic())
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_clean_season_folder_when_it_contains_illegal_characters_in_series_title()
|
|
||||||
{
|
|
||||||
var filename = @"S01E05 - Episode Title";
|
|
||||||
var seasonNumber = 1;
|
|
||||||
var expectedPath = @"C:\Test\NCIS- Los Angeles\NCIS- Los Angeles Season 1\S01E05 - Episode Title.mkv";
|
|
||||||
|
|
||||||
var fakeSeries = Builder<Series>.CreateNew()
|
|
||||||
.With(s => s.Title = "NCIS: Los Angeles")
|
|
||||||
.With(s => s.Path = @"C:\Test\NCIS- Los Angeles".AsOsAgnostic())
|
|
||||||
.With(s => s.SeasonFolder = true)
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
namingConfig.SeasonFolderFormat = "{Series Title} Season {season:0}";
|
namingConfig.AlbumFolderFormat = "{Artist Name} {Album Title}";
|
||||||
|
|
||||||
Subject.BuildFilePath(fakeSeries, seasonNumber, filename, ".mkv").Should().Be(expectedPath.AsOsAgnostic());
|
Subject.BuildTrackFilePath(fakeArtist, fakeAlbum, filename, ".flac").Should().Be(expectedPath.AsOsAgnostic());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,37 +7,41 @@ using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class CleanTitleFixture : CoreTest<FileNameBuilder>
|
public class CleanTitleFixture : CoreTest<FileNameBuilder>
|
||||||
{
|
{
|
||||||
private Series _series;
|
private Artist _artist;
|
||||||
private Episode _episode;
|
private Album _album;
|
||||||
private EpisodeFile _episodeFile;
|
private Track _track;
|
||||||
|
private TrackFile _trackFile;
|
||||||
private NamingConfig _namingConfig;
|
private NamingConfig _namingConfig;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_series = Builder<Series>
|
_artist = Builder<Artist>
|
||||||
.CreateNew()
|
.CreateNew()
|
||||||
.With(s => s.Title = "South Park")
|
.With(s => s.Name = "Avenged Sevenfold")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_episode = Builder<Episode>.CreateNew()
|
_album = Builder<Album>
|
||||||
.With(e => e.Title = "City Sushi")
|
.CreateNew()
|
||||||
.With(e => e.SeasonNumber = 15)
|
.With(s => s.Title = "Hail to the King")
|
||||||
.With(e => e.EpisodeNumber = 6)
|
.Build();
|
||||||
.With(e => e.AbsoluteEpisodeNumber = 100)
|
|
||||||
|
_track = Builder<Track>.CreateNew()
|
||||||
|
.With(e => e.Title = "Doing Time")
|
||||||
|
.With(e => e.TrackNumber = 3)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.MP3_256), ReleaseGroup = "LidarrTest" };
|
_trackFile = new TrackFile { Quality = new QualityModel(Quality.MP3_256), ReleaseGroup = "LidarrTest" };
|
||||||
|
|
||||||
_namingConfig = NamingConfig.Default;
|
_namingConfig = NamingConfig.Default;
|
||||||
_namingConfig.RenameEpisodes = true;
|
_namingConfig.RenameTracks = true;
|
||||||
|
|
||||||
Mocker.GetMock<INamingConfigService>()
|
Mocker.GetMock<INamingConfigService>()
|
||||||
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
||||||
|
@ -65,20 +69,19 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
[TestCase("[a] title", "a title")]
|
[TestCase("[a] title", "a title")]
|
||||||
[TestCase("backslash \\ backlash", "backslash backlash")]
|
[TestCase("backslash \\ backlash", "backslash backlash")]
|
||||||
[TestCase("I'm the Boss", "Im the Boss")]
|
[TestCase("I'm the Boss", "Im the Boss")]
|
||||||
//[TestCase("", "")]
|
public void should_get_expected_title_back(string name, string expected)
|
||||||
public void should_get_expected_title_back(string title, string expected)
|
|
||||||
{
|
{
|
||||||
_series.Title = title;
|
_artist.Name = name;
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series CleanTitle}";
|
_namingConfig.StandardTrackFormat = "{Artist CleanName}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track }, _artist, _album, _trackFile)
|
||||||
.Should().Be(expected);
|
.Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_use_and_as_separator_for_multiple_episodes()
|
public void should_use_and_as_separator_for_multiple_episodes()
|
||||||
{
|
{
|
||||||
var episodes = Builder<Episode>.CreateListOfSize(2)
|
var tracks = Builder<Track>.CreateListOfSize(2)
|
||||||
.TheFirst(1)
|
.TheFirst(1)
|
||||||
.With(e => e.Title = "Surrender Benson")
|
.With(e => e.Title = "Surrender Benson")
|
||||||
.TheNext(1)
|
.TheNext(1)
|
||||||
|
@ -86,10 +89,10 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
.Build()
|
.Build()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Episode CleanTitle}";
|
_namingConfig.StandardTrackFormat = "{Track CleanTitle}";
|
||||||
|
|
||||||
Subject.BuildFileName(episodes, _series, _episodeFile)
|
Subject.BuildTrackFileName(tracks, _artist, _album, _trackFile)
|
||||||
.Should().Be(episodes.First().Title + " and " + episodes.Last().Title);
|
.Should().Be(tracks.First().Title + " and " + tracks.Last().Title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
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 EpisodeTitleCollapseFixture : CoreTest<FileNameBuilder>
|
|
||||||
{
|
|
||||||
private Series _series;
|
|
||||||
private Episode _episode1;
|
|
||||||
private Episode _episode2;
|
|
||||||
private Episode _episode3;
|
|
||||||
private EpisodeFile _episodeFile;
|
|
||||||
private NamingConfig _namingConfig;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
_series = Builder<Series>
|
|
||||||
.CreateNew()
|
|
||||||
.With(s => s.Title = "South Park")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
_namingConfig = NamingConfig.Default;
|
|
||||||
_namingConfig.RenameEpisodes = true;
|
|
||||||
|
|
||||||
|
|
||||||
Mocker.GetMock<INamingConfigService>()
|
|
||||||
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
|
||||||
|
|
||||||
_episode1 = 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();
|
|
||||||
|
|
||||||
_episode2 = Builder<Episode>.CreateNew()
|
|
||||||
.With(e => e.Title = "City Sushi")
|
|
||||||
.With(e => e.SeasonNumber = 15)
|
|
||||||
.With(e => e.EpisodeNumber = 7)
|
|
||||||
.With(e => e.AbsoluteEpisodeNumber = 101)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_episode3 = Builder<Episode>.CreateNew()
|
|
||||||
.With(e => e.Title = "City Sushi")
|
|
||||||
.With(e => e.SeasonNumber = 15)
|
|
||||||
.With(e => e.EpisodeNumber = 8)
|
|
||||||
.With(e => e.AbsoluteEpisodeNumber = 102)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.MP3_256), ReleaseGroup = "LidarrTest" };
|
|
||||||
|
|
||||||
Mocker.GetMock<IQualityDefinitionService>()
|
|
||||||
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
|
|
||||||
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[TestCase("Hey, Baby, What's Wrong (1)", "Hey, Baby, What's Wrong (2)", "Hey, Baby, What's Wrong")]
|
|
||||||
[TestCase("Meet the Guys and Girls of Cycle 20 Part 1", "Meet the Guys and Girls of Cycle 20 Part 2", "Meet the Guys and Girls of Cycle 20")]
|
|
||||||
[TestCase("Meet the Guys and Girls of Cycle 20 Part1", "Meet the Guys and Girls of Cycle 20 Part2", "Meet the Guys and Girls of Cycle 20")]
|
|
||||||
[TestCase("Meet the Guys and Girls of Cycle 20 Part01", "Meet the Guys and Girls of Cycle 20 Part02", "Meet the Guys and Girls of Cycle 20")]
|
|
||||||
[TestCase("Meet the Guys and Girls of Cycle 20 Part 01", "Meet the Guys and Girls of Cycle 20 Part 02", "Meet the Guys and Girls of Cycle 20")]
|
|
||||||
[TestCase("Meet the Guys and Girls of Cycle 20 part 1", "Meet the Guys and Girls of Cycle 20 part 2", "Meet the Guys and Girls of Cycle 20")]
|
|
||||||
[TestCase("Meet the Guys and Girls of Cycle 20 pt 1", "Meet the Guys and Girls of Cycle 20 pt 2", "Meet the Guys and Girls of Cycle 20")]
|
|
||||||
[TestCase("Meet the Guys and Girls of Cycle 20 pt. 1", "Meet the Guys and Girls of Cycle 20 pt. 2", "Meet the Guys and Girls of Cycle 20")]
|
|
||||||
public void should_collapse_episode_titles_when_episode_titles_are_the_same(string title1, string title2, string expected)
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Episode Title}";
|
|
||||||
|
|
||||||
_episode1.Title = title1;
|
|
||||||
_episode2.Title = title2;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_not_collapse_episode_titles_when_episode_titles_are_not_the_same()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 3;
|
|
||||||
|
|
||||||
_episode1.Title = "Hello";
|
|
||||||
_episode2.Title = "World";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06-E07 - Hello + World");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_not_collaspe_when_result_is_empty()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Episode Title}";
|
|
||||||
|
|
||||||
_episode1.Title = "Part 1";
|
|
||||||
_episode2.Title = "Part 2";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("Part 1 + Part 2");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
|
@ -8,7 +8,7 @@ using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
{
|
{
|
||||||
|
@ -16,35 +16,39 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
|
|
||||||
public class FileNameBuilderFixture : CoreTest<FileNameBuilder>
|
public class FileNameBuilderFixture : CoreTest<FileNameBuilder>
|
||||||
{
|
{
|
||||||
private Series _series;
|
private Artist _artist;
|
||||||
private Episode _episode1;
|
private Album _album;
|
||||||
private EpisodeFile _episodeFile;
|
private Track _track1;
|
||||||
|
private TrackFile _trackFile;
|
||||||
private NamingConfig _namingConfig;
|
private NamingConfig _namingConfig;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_series = Builder<Series>
|
_artist = Builder<Artist>
|
||||||
.CreateNew()
|
.CreateNew()
|
||||||
.With(s => s.Title = "South Park")
|
.With(s => s.Name = "Linkin Park")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_album = Builder<Album>
|
||||||
|
.CreateNew()
|
||||||
|
.With(s => s.Title = "Hybrid Theory")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
_namingConfig = NamingConfig.Default;
|
_namingConfig = NamingConfig.Default;
|
||||||
_namingConfig.RenameEpisodes = true;
|
_namingConfig.RenameTracks = true;
|
||||||
|
|
||||||
|
|
||||||
Mocker.GetMock<INamingConfigService>()
|
Mocker.GetMock<INamingConfigService>()
|
||||||
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
||||||
|
|
||||||
_episode1 = Builder<Episode>.CreateNew()
|
_track1 = Builder<Track>.CreateNew()
|
||||||
.With(e => e.Title = "City Sushi")
|
.With(e => e.Title = "City Sushi")
|
||||||
.With(e => e.SeasonNumber = 15)
|
.With(e => e.TrackNumber = 6)
|
||||||
.With(e => e.EpisodeNumber = 6)
|
|
||||||
.With(e => e.AbsoluteEpisodeNumber = 100)
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.MP3_256), ReleaseGroup = "LidarrTest" };
|
_trackFile = new TrackFile { Quality = new QualityModel(Quality.MP3_256), ReleaseGroup = "LidarrTest" };
|
||||||
|
|
||||||
Mocker.GetMock<IQualityDefinitionService>()
|
Mocker.GetMock<IQualityDefinitionService>()
|
||||||
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
|
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
|
||||||
|
@ -53,592 +57,413 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
|
|
||||||
private void GivenProper()
|
private void GivenProper()
|
||||||
{
|
{
|
||||||
_episodeFile.Quality.Revision.Version = 2;
|
_trackFile.Quality.Revision.Version = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenReal()
|
private void GivenReal()
|
||||||
{
|
{
|
||||||
_episodeFile.Quality.Revision.Real = 1;
|
_trackFile.Quality.Revision.Real = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_Series_space_Title()
|
public void should_replace_Artist_space_Name()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title}";
|
_namingConfig.StandardTrackFormat = "{Artist Name}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("South Park");
|
.Should().Be("Linkin Park");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_Series_underscore_Title()
|
public void should_replace_Artist_underscore_Name()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series_Title}";
|
_namingConfig.StandardTrackFormat = "{Artist_Name}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("South_Park");
|
.Should().Be("Linkin_Park");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_Series_dot_Title()
|
public void should_replace_Artist_dot_Name()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("South.Park");
|
.Should().Be("Linkin.Park");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_Series_dash_Title()
|
public void should_replace_Artist_dash_Name()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series-Title}";
|
_namingConfig.StandardTrackFormat = "{Artist-Name}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("South-Park");
|
.Should().Be("Linkin-Park");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_SERIES_TITLE_with_all_caps()
|
public void should_replace_ARTIST_NAME_with_all_caps()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{SERIES TITLE}";
|
_namingConfig.StandardTrackFormat = "{ARTIST NAME}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("SOUTH PARK");
|
.Should().Be("LINKIN PARK");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_SERIES_TITLE_with_random_casing_should_keep_original_casing()
|
public void should_replace_ARTIST_NAME_with_random_casing_should_keep_original_casing()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{sErIES-tItLE}";
|
_namingConfig.StandardTrackFormat = "{aRtIST-nAmE}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(_series.Title.Replace(' ', '-'));
|
.Should().Be(_artist.Name.Replace(' ', '-'));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_series_title_with_all_lower_case()
|
public void should_replace_artist_name_with_all_lower_case()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{series title}";
|
_namingConfig.StandardTrackFormat = "{artist name}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("south park");
|
.Should().Be("linkin park");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_cleanup_Series_Title()
|
public void should_cleanup_Artist_Name()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.CleanTitle}";
|
_namingConfig.StandardTrackFormat = "{Artist.CleanName}";
|
||||||
_series.Title = "South Park (1997)";
|
_artist.Name = "Linkin Park (1997)";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("South.Park.1997");
|
.Should().Be("Linkin.Park.1997");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_episode_title()
|
public void should_replace_Album_space_Title()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Episode Title}";
|
_namingConfig.StandardTrackFormat = "{Album Title}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid Theory");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_replace_Album_underscore_Title()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{Album_Title}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid_Theory");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_replace_Album_dot_Title()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{Album.Title}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid.Theory");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_replace_Album_dash_Title()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{Album-Title}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid-Theory");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_replace_ALBUM_TITLE_with_all_caps()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{ALBUM TITLE}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("HYBRID THEORY");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_replace_ALBUM_TITLE_with_random_casing_should_keep_original_casing()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{aLbUM-tItLE}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be(_album.Title.Replace(' ', '-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_replace_album_title_with_all_lower_case()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{album title}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("hybrid theory");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_cleanup_Album_Title()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{Artist.CleanName}";
|
||||||
|
_artist.Name = "Hybrid Theory (2000)";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid.Theory.2000");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_replace_track_title()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{Track Title}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("City Sushi");
|
.Should().Be("City Sushi");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_episode_title_if_pattern_has_random_casing()
|
public void should_replace_track_title_if_pattern_has_random_casing()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{ePisOde-TitLe}";
|
_namingConfig.StandardTrackFormat = "{tRaCK-TitLe}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("City-Sushi");
|
.Should().Be("City-Sushi");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_season_number_with_single_digit()
|
public void should_replace_track_number_with_single_digit()
|
||||||
{
|
{
|
||||||
_episode1.SeasonNumber = 1;
|
_track1.TrackNumber = 1;
|
||||||
_namingConfig.StandardEpisodeFormat = "{season}x{episode}";
|
_namingConfig.StandardTrackFormat = "{track}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("1x6");
|
.Should().Be("1");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_season00_number_with_two_digits()
|
public void should_replace_track00_number_with_two_digits()
|
||||||
{
|
{
|
||||||
_episode1.SeasonNumber = 1;
|
_track1.TrackNumber = 1;
|
||||||
_namingConfig.StandardEpisodeFormat = "{season:00}x{episode}";
|
_namingConfig.StandardTrackFormat = "{track:00}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("01x6");
|
.Should().Be("01");
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_episode_number_with_single_digit()
|
|
||||||
{
|
|
||||||
_episode1.SeasonNumber = 1;
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{season}x{episode}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("1x6");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_episode00_number_with_two_digits()
|
|
||||||
{
|
|
||||||
_episode1.SeasonNumber = 1;
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{season}x{episode:00}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("1x06");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_quality_title()
|
public void should_replace_quality_title()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Quality Title}";
|
_namingConfig.StandardTrackFormat = "{Quality Title}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("HDTV-720p");
|
.Should().Be("MP3-256");
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_quality_proper_with_proper()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Quality Proper}";
|
|
||||||
GivenProper();
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("Proper");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_quality_real_with_real()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Quality Real}";
|
|
||||||
GivenReal();
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("REAL");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_all_contents_in_pattern()
|
public void should_replace_all_contents_in_pattern()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} [{Quality Title}]";
|
_namingConfig.StandardTrackFormat = "{Artist Name} - {Album Title} - {track:00} - {Track Title} [{Quality Title}]";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> {_track1}, _artist, _album, _trackFile)
|
||||||
.Should().Be("South Park - S15E06 - City Sushi [HDTV-720p]");
|
.Should().Be("Linkin Park - Hybrid Theory - 06 - City Sushi [MP3-256]");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void use_file_name_when_sceneName_is_null()
|
public void use_file_name_when_sceneName_is_null()
|
||||||
{
|
{
|
||||||
_namingConfig.RenameEpisodes = false;
|
_namingConfig.RenameTracks = false;
|
||||||
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
|
_trackFile.RelativePath = "Linkin Park - 06 - Test";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.RelativePath));
|
.Should().Be(Path.GetFileNameWithoutExtension(_trackFile.RelativePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void use_path_when_sceneName_and_relative_path_are_null()
|
public void use_path_when_sceneName_and_relative_path_are_null()
|
||||||
{
|
{
|
||||||
_namingConfig.RenameEpisodes = false;
|
_namingConfig.RenameTracks = false;
|
||||||
_episodeFile.RelativePath = null;
|
_trackFile.RelativePath = null;
|
||||||
_episodeFile.Path = @"C:\Test\Unsorted\Series - S01E01 - Test";
|
_trackFile.Path = @"C:\Test\Unsorted\Artist - 01 - Test";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.Path));
|
.Should().Be(Path.GetFileNameWithoutExtension(_trackFile.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void use_file_name_when_sceneName_is_not_null()
|
|
||||||
{
|
|
||||||
_namingConfig.RenameEpisodes = false;
|
|
||||||
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
|
||||||
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("30.Rock.S01E01.xvid-LOL");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_use_airDate_if_series_isDaily()
|
public void should_not_clean_track_title_if_there_is_only_one()
|
||||||
{
|
|
||||||
_namingConfig.DailyEpisodeFormat = "{Series Title} - {air-date} - {Episode Title}";
|
|
||||||
|
|
||||||
_series.Title = "The Daily Show with Jon Stewart";
|
|
||||||
_series.SeriesType = SeriesTypes.Daily;
|
|
||||||
|
|
||||||
_episode1.AirDate = "2012-12-13";
|
|
||||||
_episode1.Title = "Kristen Stewart";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("The Daily Show with Jon Stewart - 2012-12-13 - Kristen Stewart");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_set_airdate_to_unknown_if_not_available()
|
|
||||||
{
|
|
||||||
_namingConfig.DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title}";
|
|
||||||
|
|
||||||
_series.Title = "The Daily Show with Jon Stewart";
|
|
||||||
_series.SeriesType = SeriesTypes.Daily;
|
|
||||||
|
|
||||||
_episode1.AirDate = null;
|
|
||||||
_episode1.Title = "Kristen Stewart";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("The Daily Show with Jon Stewart - Unknown - Kristen Stewart");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_not_clean_episode_title_if_there_is_only_one()
|
|
||||||
{
|
{
|
||||||
var title = "City Sushi (1)";
|
var title = "City Sushi (1)";
|
||||||
_episode1.Title = title;
|
_track1.Title = title;
|
||||||
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Episode Title}";
|
_namingConfig.StandardTrackFormat = "{Track Title}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(title);
|
.Should().Be(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_should_replace_release_group()
|
public void should_should_replace_release_group()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Release Group}";
|
_namingConfig.StandardTrackFormat = "{Release Group}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(_episodeFile.ReleaseGroup);
|
.Should().Be(_trackFile.ReleaseGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_use_original_title()
|
public void should_be_able_to_use_original_title()
|
||||||
{
|
{
|
||||||
_series.Title = "30 Rock";
|
_artist.Name = "Linkin Park";
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - {Original Title}";
|
_namingConfig.StandardTrackFormat = "{Artist Name} - {Original Title} - {Track Title}";
|
||||||
|
|
||||||
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
_trackFile.SceneName = "Linkin.Park.Meteora.320-LOL";
|
||||||
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
|
_trackFile.RelativePath = "30 Rock - 01 - Test";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("30 Rock - 30.Rock.S01E01.xvid-LOL");
|
.Should().Be("Linkin Park - Linkin.Park.Meteora.320-LOL - City Sushi");
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_trim_periods_from_end_of_episode_title()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 3;
|
|
||||||
|
|
||||||
var episode = Builder<Episode>.CreateNew()
|
|
||||||
.With(e => e.Title = "Part 1.")
|
|
||||||
.With(e => e.SeasonNumber = 6)
|
|
||||||
.With(e => e.EpisodeNumber = 6)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { episode }, new Series { Title = "30 Rock" }, _episodeFile)
|
|
||||||
.Should().Be("30 Rock - S06E06 - Part 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_trim_question_marks_from_end_of_episode_title()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 3;
|
|
||||||
|
|
||||||
var episode = Builder<Episode>.CreateNew()
|
|
||||||
.With(e => e.Title = "Part 1?")
|
|
||||||
.With(e => e.SeasonNumber = 6)
|
|
||||||
.With(e => e.EpisodeNumber = 6)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { episode }, new Series { Title = "30 Rock" }, _episodeFile)
|
|
||||||
.Should().Be("30 Rock - S06E06 - Part 1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_double_period_with_single_period()
|
public void should_replace_double_period_with_single_period()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{Episode.Title}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}.{track:00}.{Track.Title}";
|
||||||
|
|
||||||
var episode = Builder<Episode>.CreateNew()
|
var track = Builder<Track>.CreateNew()
|
||||||
.With(e => e.Title = "Part 1")
|
.With(e => e.Title = "Part 1")
|
||||||
.With(e => e.SeasonNumber = 6)
|
.With(e => e.TrackNumber = 6)
|
||||||
.With(e => e.EpisodeNumber = 6)
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { episode }, new Series { Title = "Chicago P.D." }, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { track }, new Artist { Name = "In The Woods." }, new Album { Title = "30 Rock" }, _trackFile)
|
||||||
.Should().Be("Chicago.P.D.S06E06.Part.1");
|
.Should().Be("In.The.Woods.06.Part.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_triple_period_with_single_period()
|
public void should_replace_triple_period_with_single_period()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{Episode.Title}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}.{track:00}.{Track.Title}";
|
||||||
|
|
||||||
var episode = Builder<Episode>.CreateNew()
|
var track = Builder<Track>.CreateNew()
|
||||||
.With(e => e.Title = "Part 1")
|
.With(e => e.Title = "Part 1")
|
||||||
.With(e => e.SeasonNumber = 6)
|
.With(e => e.TrackNumber = 6)
|
||||||
.With(e => e.EpisodeNumber = 6)
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { episode }, new Series { Title = "Chicago P.D.." }, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { track }, new Artist { Name = "In The Woods..." }, new Album { Title = "30 Rock" }, _trackFile)
|
||||||
.Should().Be("Chicago.P.D.S06E06.Part.1");
|
.Should().Be("In.The.Woods.06.Part.1");
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_not_replace_absolute_numbering_when_series_is_not_anime()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{absolute:00}.{Episode.Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South.Park.S15E06.City.Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_standard_and_absolute_numbering_when_series_is_anime()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{absolute:00}.{Episode.Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South.Park.S15E06.100.City.Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_standard_numbering_when_series_is_anime()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{Episode.Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South.Park.S15E06.City.Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_absolute_numbering_when_series_is_anime()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series.Title}.{absolute:00}.{Episode.Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South.Park.100.City.Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_duplicate_numbering_individually()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series.Title}.{season}x{episode:00}.{absolute:000}\\{Series.Title}.S{season:00}E{episode:00}.{absolute:00}.{Episode.Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South.Park.15x06.100\\South.Park.S15E06.100.City.Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_individual_season_episode_tokens()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} Season {season:0000} Episode {episode:0000}\\{Series.Title}.S{season:00}E{episode:00}.{absolute:00}.{Episode.Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park Season 0015 Episode 0006\\South.Park.S15E06.100.City.Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_use_standard_naming_when_anime_episode_has_no_absolute_number()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_episode1.AbsoluteEpisodeNumber = null;
|
|
||||||
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - {season:0}x{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 15x06 - City Sushi");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_include_affixes_if_value_not_empty()
|
public void should_include_affixes_if_value_not_empty()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}{_Episode.Title_}{Quality.Title}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}.{track:00}{_Track.Title_}{Quality.Title}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("South.Park.S15E06_City.Sushi_HDTV-720p");
|
.Should().Be("Linkin.Park.06_City.Sushi_MP3-256");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_include_affixes_if_value_empty()
|
public void should_not_include_affixes_if_value_empty()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}{_Episode.Title_}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}.{track:00}{_Track.Title_}";
|
||||||
|
|
||||||
_episode1.Title = "";
|
_track1.Title = "";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("South.Park.S15E06");
|
.Should().Be("Linkin.Park.06");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_format_mediainfo_properly()
|
public void should_format_mediainfo_properly()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{Episode.Title}.{MEDIAINFO.FULL}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}.{track:00}.{Track.Title}.{MEDIAINFO.FULL}";
|
||||||
|
|
||||||
_episodeFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel()
|
_trackFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel()
|
||||||
{
|
{
|
||||||
VideoCodec = "AVC",
|
AudioFormat = "FLAC",
|
||||||
AudioFormat = "DTS",
|
|
||||||
AudioLanguages = "English/Spanish",
|
AudioLanguages = "English/Spanish",
|
||||||
Subtitles = "English/Spanish/Italian"
|
Subtitles = "English/Spanish/Italian"
|
||||||
};
|
};
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("South.Park.S15E06.City.Sushi.X264.DTS[EN+ES].[EN+ES+IT]");
|
.Should().Be("Linkin.Park.06.City.Sushi.FLAC[EN+ES].[EN+ES+IT]");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_exclude_english_in_mediainfo_audio_language()
|
public void should_exclude_english_in_mediainfo_audio_language()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}E{episode:00}.{Episode.Title}.{MEDIAINFO.FULL}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}.{track:00}.{Track.Title}.{MEDIAINFO.FULL}";
|
||||||
|
|
||||||
_episodeFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel()
|
_trackFile.MediaInfo = new Core.MediaFiles.MediaInfo.MediaInfoModel
|
||||||
{
|
{
|
||||||
VideoCodec = "AVC",
|
AudioFormat = "FLAC",
|
||||||
AudioFormat = "DTS",
|
|
||||||
AudioLanguages = "English",
|
AudioLanguages = "English",
|
||||||
Subtitles = "English/Spanish/Italian"
|
Subtitles = "English/Spanish/Italian"
|
||||||
};
|
};
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("South.Park.S15E06.City.Sushi.X264.DTS.[EN+ES+IT]");
|
.Should().Be("Linkin.Park.06.City.Sushi.FLAC.[EN+ES+IT]");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_remove_duplicate_non_word_characters()
|
public void should_remove_duplicate_non_word_characters()
|
||||||
{
|
{
|
||||||
_series.Title = "Venture Bros.";
|
_artist.Name = "Venture Bros.";
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.{season}x{episode:00}";
|
_namingConfig.StandardTrackFormat = "{Artist.Name}.{Album.Title}-{track:00}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("Venture.Bros.15x06");
|
.Should().Be("Venture.Bros.Hybrid.Theory-06");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_use_existing_filename_when_scene_name_is_not_available()
|
public void should_use_existing_filename_when_scene_name_is_not_available()
|
||||||
{
|
{
|
||||||
_namingConfig.RenameEpisodes = true;
|
_namingConfig.RenameTracks = true;
|
||||||
_namingConfig.StandardEpisodeFormat = "{Original Title}";
|
_namingConfig.StandardTrackFormat = "{Original Title}";
|
||||||
|
|
||||||
_episodeFile.SceneName = null;
|
_trackFile.SceneName = null;
|
||||||
_episodeFile.RelativePath = "existing.file.mkv";
|
_trackFile.RelativePath = "existing.file.mkv";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(Path.GetFileNameWithoutExtension(_episodeFile.RelativePath));
|
.Should().Be(Path.GetFileNameWithoutExtension(_trackFile.RelativePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_use_only_original_title()
|
public void should_be_able_to_use_only_original_title()
|
||||||
{
|
{
|
||||||
_series.Title = "30 Rock";
|
_artist.Name = "30 Rock";
|
||||||
_namingConfig.StandardEpisodeFormat = "{Original Title}";
|
_namingConfig.StandardTrackFormat = "{Original Title}";
|
||||||
|
|
||||||
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
_trackFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
||||||
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
|
_trackFile.RelativePath = "30 Rock - S01E01 - Test";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("30.Rock.S01E01.xvid-LOL");
|
.Should().Be("30.Rock.S01E01.xvid-LOL");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_allow_period_between_season_and_episode()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series.Title}.S{season:00}.E{episode:00}.{Episode.Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South.Park.S15.E06.City.Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_allow_space_between_season_and_episode()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00} E{episode:00} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15 E06 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_quality_proper_with_v2_for_anime_v2()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Quality Proper}";
|
|
||||||
|
|
||||||
GivenProper();
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("v2");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_include_quality_proper_when_release_is_not_a_proper()
|
public void should_not_include_quality_proper_when_release_is_not_a_proper()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Quality Title} {Quality Proper}";
|
_namingConfig.StandardTrackFormat = "{Quality Title} {Quality Proper}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("HDTV-720p");
|
.Should().Be("MP3-256");
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_wrap_proper_in_square_brackets()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} [{Quality Title}] {[Quality Proper]}";
|
|
||||||
|
|
||||||
GivenProper();
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06 [HDTV-720p] [Proper]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_wrap_proper_in_square_brackets_when_not_a_proper()
|
public void should_not_wrap_proper_in_square_brackets_when_not_a_proper()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} [{Quality Title}] {[Quality Proper]}";
|
_namingConfig.StandardTrackFormat = "{Artist Name} - {track:00} [{Quality Title}] {[Quality Proper]}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("South Park - S15E06 [HDTV-720p]");
|
.Should().Be("Linkin Park - 06 [MP3-256]");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_replace_quality_full_with_quality_title_only_when_not_a_proper()
|
public void should_replace_quality_full_with_quality_title_only_when_not_a_proper()
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} [{Quality Full}]";
|
_namingConfig.StandardTrackFormat = "{Artist Name} - {track:00} [{Quality Full}]";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("South Park - S15E06 [HDTV-720p]");
|
.Should().Be("Linkin Park - 06 [MP3-256]");
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_quality_full_with_quality_title_and_proper_only_when_a_proper()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} [{Quality Full}]";
|
|
||||||
|
|
||||||
GivenProper();
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06 [HDTV-720p Proper]");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_quality_full_with_quality_title_and_real_when_a_real()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} [{Quality Full}]";
|
|
||||||
GivenReal();
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06 [HDTV-720p REAL]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(' ')]
|
[TestCase(' ')]
|
||||||
|
@ -647,10 +472,10 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
[TestCase('_')]
|
[TestCase('_')]
|
||||||
public void should_trim_extra_separators_from_end_when_quality_proper_is_not_included(char separator)
|
public void should_trim_extra_separators_from_end_when_quality_proper_is_not_included(char separator)
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = string.Format("{{Quality{0}Title}}{0}{{Quality{0}Proper}}", separator);
|
_namingConfig.StandardTrackFormat = string.Format("{{Quality{0}Title}}{0}{{Quality{0}Proper}}", separator);
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("HDTV-720p");
|
.Should().Be("MP3-256");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(' ')]
|
[TestCase(' ')]
|
||||||
|
@ -659,67 +484,59 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
[TestCase('_')]
|
[TestCase('_')]
|
||||||
public void should_trim_extra_separators_from_middle_when_quality_proper_is_not_included(char separator)
|
public void should_trim_extra_separators_from_middle_when_quality_proper_is_not_included(char separator)
|
||||||
{
|
{
|
||||||
_namingConfig.StandardEpisodeFormat = string.Format("{{Quality{0}Title}}{0}{{Quality{0}Proper}}{0}{{Episode{0}Title}}", separator);
|
_namingConfig.StandardTrackFormat = string.Format("{{Quality{0}Title}}{0}{{Quality{0}Proper}}{0}{{Track{0}Title}}", separator);
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(string.Format("HDTV-720p{0}City{0}Sushi", separator));
|
.Should().Be(string.Format("MP3-256{0}City{0}Sushi", separator));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_not_require_a_separator_between_tokens()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "[{Release Group}]{Series.CleanTitle}.{absolute:000}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("[LidarrTest]South.Park.100");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_use_original_filename()
|
public void should_be_able_to_use_original_filename()
|
||||||
{
|
{
|
||||||
_series.Title = "30 Rock";
|
_artist.Name = "30 Rock";
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - {Original Filename}";
|
_namingConfig.StandardTrackFormat = "{Artist Name} - {Original Filename}";
|
||||||
|
|
||||||
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
_trackFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
||||||
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
|
_trackFile.RelativePath = "30 Rock - S01E01 - Test";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("30 Rock - 30 Rock - S01E01 - Test");
|
.Should().Be("30 Rock - 30 Rock - S01E01 - Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_use_original_filename_only()
|
public void should_be_able_to_use_original_filename_only()
|
||||||
{
|
{
|
||||||
_series.Title = "30 Rock";
|
_artist.Name = "30 Rock";
|
||||||
_namingConfig.StandardEpisodeFormat = "{Original Filename}";
|
_namingConfig.StandardTrackFormat = "{Original Filename}";
|
||||||
|
|
||||||
_episodeFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
_trackFile.SceneName = "30.Rock.S01E01.xvid-LOL";
|
||||||
_episodeFile.RelativePath = "30 Rock - S01E01 - Test";
|
_trackFile.RelativePath = "30 Rock - S01E01 - Test";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("30 Rock - S01E01 - Test");
|
.Should().Be("30 Rock - S01E01 - Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_use_Lidarr_as_release_group_when_not_available()
|
public void should_use_Lidarr_as_release_group_when_not_available()
|
||||||
{
|
{
|
||||||
_episodeFile.ReleaseGroup = null;
|
_trackFile.ReleaseGroup = null;
|
||||||
_namingConfig.StandardEpisodeFormat = "{Release Group}";
|
_namingConfig.StandardTrackFormat = "{Release Group}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be("Lidarr");
|
.Should().Be("Lidarr");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase("{Episode Title}{-Release Group}", "City Sushi")]
|
[TestCase("{Track Title}{-Release Group}", "City Sushi")]
|
||||||
[TestCase("{Episode Title}{ Release Group}", "City Sushi")]
|
[TestCase("{Track Title}{ Release Group}", "City Sushi")]
|
||||||
[TestCase("{Episode Title}{ [Release Group]}", "City Sushi")]
|
[TestCase("{Track Title}{ [Release Group]}", "City Sushi")]
|
||||||
public void should_not_use_Lidarr_as_release_group_if_pattern_has_separator(string pattern, string expectedFileName)
|
public void should_not_use_Lidarr_as_release_group_if_pattern_has_separator(string pattern, string expectedFileName)
|
||||||
{
|
{
|
||||||
_episodeFile.ReleaseGroup = null;
|
_trackFile.ReleaseGroup = null;
|
||||||
_namingConfig.StandardEpisodeFormat = pattern;
|
_namingConfig.StandardTrackFormat = pattern;
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(expectedFileName);
|
.Should().Be(expectedFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,10 +545,10 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
[TestCase("IMMERSE")]
|
[TestCase("IMMERSE")]
|
||||||
public void should_use_existing_casing_for_release_group(string releaseGroup)
|
public void should_use_existing_casing_for_release_group(string releaseGroup)
|
||||||
{
|
{
|
||||||
_episodeFile.ReleaseGroup = releaseGroup;
|
_trackFile.ReleaseGroup = releaseGroup;
|
||||||
_namingConfig.StandardEpisodeFormat = "{Release Group}";
|
_namingConfig.StandardTrackFormat = "{Release Group}";
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
.Should().Be(releaseGroup);
|
.Should().Be(releaseGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,271 +0,0 @@
|
||||||
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 MultiEpisodeFixture : CoreTest<FileNameBuilder>
|
|
||||||
{
|
|
||||||
private Series _series;
|
|
||||||
private Episode _episode1;
|
|
||||||
private Episode _episode2;
|
|
||||||
private Episode _episode3;
|
|
||||||
private EpisodeFile _episodeFile;
|
|
||||||
private NamingConfig _namingConfig;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
_series = Builder<Series>
|
|
||||||
.CreateNew()
|
|
||||||
.With(s => s.Title = "South Park")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
_namingConfig = NamingConfig.Default;
|
|
||||||
_namingConfig.RenameEpisodes = true;
|
|
||||||
|
|
||||||
|
|
||||||
Mocker.GetMock<INamingConfigService>()
|
|
||||||
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
|
||||||
|
|
||||||
_episode1 = 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();
|
|
||||||
|
|
||||||
_episode2 = Builder<Episode>.CreateNew()
|
|
||||||
.With(e => e.Title = "City Sushi")
|
|
||||||
.With(e => e.SeasonNumber = 15)
|
|
||||||
.With(e => e.EpisodeNumber = 7)
|
|
||||||
.With(e => e.AbsoluteEpisodeNumber = 101)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_episode3 = Builder<Episode>.CreateNew()
|
|
||||||
.With(e => e.Title = "City Sushi")
|
|
||||||
.With(e => e.SeasonNumber = 15)
|
|
||||||
.With(e => e.EpisodeNumber = 8)
|
|
||||||
.With(e => e.AbsoluteEpisodeNumber = 102)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.MP3_256), ReleaseGroup = "LidarrTest" };
|
|
||||||
|
|
||||||
Mocker.GetMock<IQualityDefinitionService>()
|
|
||||||
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
|
|
||||||
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GivenProper()
|
|
||||||
{
|
|
||||||
_episodeFile.Quality.Revision.Version = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_replace_Series_space_Title()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1}, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_extend_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 0;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> {_episode1, _episode2}, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06-07 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_duplicate_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 1;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06 - S15E07 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_repeat_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 2;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06E07 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_scene_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 3;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06-E07 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_use_dash_as_separator_when_multi_episode_style_is_extend_for_anime()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 100-101 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_duplicate_absolute_pattern_when_multi_episode_style_is_duplicate()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.MultiEpisodeStyle = (int)MultiEpisodeStyle.Duplicate;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 100 - 101 - 102 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_get_proper_filename_when_multi_episode_is_duplicated_and_bracket_follows_pattern()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat =
|
|
||||||
"{Series Title} - S{season:00}E{episode:00} - ({Quality Title}, {MediaInfo Full}, {Release Group}) - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = (int) MultiEpisodeStyle.Duplicate;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06 - S15E07 - (HDTV-720p, , LidarrTest) - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_range_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 4;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06-08 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_range_multi_episode_anime_properly()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.MultiEpisodeStyle = 4;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 100-102 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_repeat_multi_episode_anime_properly()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.MultiEpisodeStyle = 2;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 100-101-102 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_single_episode_with_range_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 4;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_single_anime_episode_with_range_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.MultiEpisodeStyle = 4;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 100 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_default_to_dash_when_serparator_is_not_set_for_absolute_number()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.MultiEpisodeStyle = (int)MultiEpisodeStyle.Duplicate;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - [{absolute:000}] - {Episode Title} - {Quality Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 15x06 - 15x07 - [100-101] - City Sushi - HDTV-720p");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_prefixed_range_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 5;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06-E08 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_prefixed_range_multi_episode_anime_properly()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.MultiEpisodeStyle = 5;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 100-102 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_single_episode_with_prefixed_range_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 5;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - S15E06 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_single_anime_episode_with_prefixed_range_multi_episode_properly()
|
|
||||||
{
|
|
||||||
_series.SeriesType = SeriesTypes.Anime;
|
|
||||||
_namingConfig.MultiEpisodeStyle = 5;
|
|
||||||
_namingConfig.AnimeEpisodeFormat = "{Series Title} - {absolute:000} - {Episode Title}";
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 100 - City Sushi");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_format_prefixed_range_multi_episode_using_episode_separator()
|
|
||||||
{
|
|
||||||
_namingConfig.StandardEpisodeFormat = "{Series Title} - {season:0}x{episode:00} - {Episode Title}";
|
|
||||||
_namingConfig.MultiEpisodeStyle = 5;
|
|
||||||
|
|
||||||
Subject.BuildFileName(new List<Episode> { _episode1, _episode2, _episode3 }, _series, _episodeFile)
|
|
||||||
.Should().Be("South Park - 15x06-x08 - City Sushi");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Organizer;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.OrganizerTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class GetAlbumFolderFixture : CoreTest<FileNameBuilder>
|
||||||
|
{
|
||||||
|
private NamingConfig namingConfig;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
namingConfig = NamingConfig.Default;
|
||||||
|
|
||||||
|
Mocker.GetMock<INamingConfigService>()
|
||||||
|
.Setup(c => c.GetConfig()).Returns(namingConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Venture Bros.", "Today", "{Artist.Name}.{Album.Title}", "Venture.Bros.Today")]
|
||||||
|
[TestCase("Venture Bros.", "Today", "{Artist Name} {Album Title}", "Venture Bros. Today")]
|
||||||
|
public void should_use_albumFolderFormat_to_build_folder_name(string artistName, string albumTitle, string format, string expected)
|
||||||
|
{
|
||||||
|
namingConfig.AlbumFolderFormat = format;
|
||||||
|
|
||||||
|
var artist = new Artist { Name = artistName };
|
||||||
|
var album = new Album { Title = albumTitle };
|
||||||
|
|
||||||
|
Subject.GetAlbumFolder(artist, album, namingConfig).Should().Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Organizer;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.OrganizerTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
|
||||||
|
public class GetArtistFolderFixture : CoreTest<FileNameBuilder>
|
||||||
|
{
|
||||||
|
private NamingConfig namingConfig;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
namingConfig = NamingConfig.Default;
|
||||||
|
|
||||||
|
Mocker.GetMock<INamingConfigService>()
|
||||||
|
.Setup(c => c.GetConfig()).Returns(namingConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("Avenged Sevenfold", "{Artist Name}", "Avenged Sevenfold")]
|
||||||
|
[TestCase("Avenged Sevenfold", "{Artist.Name}", "Avenged.Sevenfold")]
|
||||||
|
[TestCase("AC/DC", "{Artist Name}", "AC+DC")]
|
||||||
|
[TestCase("In the Woods...", "{Artist.Name}", "In.the.Woods")]
|
||||||
|
[TestCase("3OH!3", "{Artist.Name}", "3OH!3")]
|
||||||
|
[TestCase("Avenged Sevenfold", ".{Artist.Name}.", "Avenged.Sevenfold")]
|
||||||
|
public void should_use_artistFolderFormat_to_build_folder_name(string artistName, string format, string expected)
|
||||||
|
{
|
||||||
|
namingConfig.ArtistFolderFormat = format;
|
||||||
|
|
||||||
|
var artist = new Artist { Name = artistName };
|
||||||
|
|
||||||
|
Subject.GetArtistFolder(artist).Should().Be(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +0,0 @@
|
||||||
using FluentAssertions;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
using NzbDrone.Core.Test.Framework;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.OrganizerTests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class GetSeasonFolderFixture : CoreTest<FileNameBuilder>
|
|
||||||
{
|
|
||||||
private NamingConfig namingConfig;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
namingConfig = NamingConfig.Default;
|
|
||||||
|
|
||||||
Mocker.GetMock<INamingConfigService>()
|
|
||||||
.Setup(c => c.GetConfig()).Returns(namingConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase("Venture Bros.", 1, "{Series.Title}.{season:00}", "Venture.Bros.01")]
|
|
||||||
[TestCase("Venture Bros.", 1, "{Series Title} Season {season:00}", "Venture Bros. Season 01")]
|
|
||||||
public void should_use_seriesFolderFormat_to_build_folder_name(string seriesTitle, int seasonNumber, string format, string expected)
|
|
||||||
{
|
|
||||||
namingConfig.SeasonFolderFormat = format;
|
|
||||||
|
|
||||||
var series = new Series { Title = seriesTitle };
|
|
||||||
|
|
||||||
Subject.GetSeasonFolder(series, seasonNumber, namingConfig).Should().Be(expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
using FluentAssertions;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
using NzbDrone.Core.Test.Framework;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.OrganizerTests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
|
|
||||||
public class GetSeriesFolderFixture : CoreTest<FileNameBuilder>
|
|
||||||
{
|
|
||||||
private NamingConfig namingConfig;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
namingConfig = NamingConfig.Default;
|
|
||||||
|
|
||||||
Mocker.GetMock<INamingConfigService>()
|
|
||||||
.Setup(c => c.GetConfig()).Returns(namingConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase("30 Rock", "{Series Title}", "30 Rock")]
|
|
||||||
[TestCase("30 Rock", "{Series.Title}", "30.Rock")]
|
|
||||||
[TestCase("24/7 Road to the NHL Winter Classic", "{Series Title}", "24+7 Road to the NHL Winter Classic")]
|
|
||||||
[TestCase("Venture Bros.", "{Series.Title}", "Venture.Bros")]
|
|
||||||
[TestCase(".hack", "{Series.Title}", "hack")]
|
|
||||||
[TestCase("30 Rock", ".{Series.Title}.", "30.Rock")]
|
|
||||||
public void should_use_seriesFolderFormat_to_build_folder_name(string seriesTitle, string format, string expected)
|
|
||||||
{
|
|
||||||
namingConfig.SeriesFolderFormat = format;
|
|
||||||
|
|
||||||
var series = new Series { Title = seriesTitle };
|
|
||||||
|
|
||||||
Subject.GetSeriesFolder(series).Should().Be(expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(114)]
|
||||||
|
public class remove_tv_naming : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Delete.Column("RenameEpisodes").FromTable("NamingConfig");
|
||||||
|
Delete.Column("StandardEpisodeFormat").FromTable("NamingConfig");
|
||||||
|
Delete.Column("DailyEpisodeFormat").FromTable("NamingConfig");
|
||||||
|
Delete.Column("AnimeEpisodeFormat").FromTable("NamingConfig");
|
||||||
|
Delete.Column("SeasonFolderFormat").FromTable("NamingConfig");
|
||||||
|
Delete.Column("SeriesFolderFormat").FromTable("NamingConfig");
|
||||||
|
Delete.Column("MultiEpisodeStyle").FromTable("NamingConfig");
|
||||||
|
|
||||||
|
Execute.Sql("DELETE FROM Config WHERE [Key] = 'filedate'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles.Commands
|
namespace NzbDrone.Core.MediaFiles.Commands
|
||||||
{
|
{
|
||||||
public class RenameFilesCommand : Command
|
public class RenameFilesCommand : Command
|
||||||
{
|
{
|
||||||
public int SeriesId { get; set; }
|
public int ArtistId { get; set; }
|
||||||
public List<int> Files { get; set; }
|
public List<int> Files { get; set; }
|
||||||
|
|
||||||
public override bool SendUpdatesToClient => true;
|
public override bool SendUpdatesToClient => true;
|
||||||
|
@ -14,9 +14,9 @@ namespace NzbDrone.Core.MediaFiles.Commands
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenameFilesCommand(int seriesId, List<int> files)
|
public RenameFilesCommand(int artistId, List<int> files)
|
||||||
{
|
{
|
||||||
SeriesId = seriesId;
|
ArtistId = artistId;
|
||||||
Files = files;
|
Files = files;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
|
||||||
{
|
|
||||||
public class EpisodeFileMoveResult
|
|
||||||
{
|
|
||||||
public EpisodeFileMoveResult()
|
|
||||||
{
|
|
||||||
OldFiles = new List<EpisodeFile>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public EpisodeFile EpisodeFile { get; set; }
|
|
||||||
public List<EpisodeFile> OldFiles { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,215 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.EnsureThat;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
|
||||||
{
|
|
||||||
public interface IMoveEpisodeFiles
|
|
||||||
{
|
|
||||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series);
|
|
||||||
EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
|
|
||||||
EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EpisodeFileMovingService : IMoveEpisodeFiles
|
|
||||||
{
|
|
||||||
private readonly IEpisodeService _episodeService;
|
|
||||||
private readonly IUpdateEpisodeFileService _updateEpisodeFileService;
|
|
||||||
private readonly IBuildFileNames _buildFileNames;
|
|
||||||
private readonly IDiskTransferService _diskTransferService;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly IMediaFileAttributeService _mediaFileAttributeService;
|
|
||||||
private readonly IEventAggregator _eventAggregator;
|
|
||||||
private readonly IConfigService _configService;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public EpisodeFileMovingService(IEpisodeService episodeService,
|
|
||||||
IUpdateEpisodeFileService updateEpisodeFileService,
|
|
||||||
IBuildFileNames buildFileNames,
|
|
||||||
IDiskTransferService diskTransferService,
|
|
||||||
IDiskProvider diskProvider,
|
|
||||||
IMediaFileAttributeService mediaFileAttributeService,
|
|
||||||
IEventAggregator eventAggregator,
|
|
||||||
IConfigService configService,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_updateEpisodeFileService = updateEpisodeFileService;
|
|
||||||
_buildFileNames = buildFileNames;
|
|
||||||
_diskTransferService = diskTransferService;
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_mediaFileAttributeService = mediaFileAttributeService;
|
|
||||||
_eventAggregator = eventAggregator;
|
|
||||||
_configService = configService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, Series series)
|
|
||||||
{
|
|
||||||
var episodes = _episodeService.GetEpisodesByFileId(episodeFile.Id);
|
|
||||||
var newFileName = _buildFileNames.BuildFileName(episodes, series, episodeFile);
|
|
||||||
var filePath = _buildFileNames.BuildFilePath(series, episodes.First().SeasonNumber, newFileName, Path.GetExtension(episodeFile.RelativePath));
|
|
||||||
|
|
||||||
EnsureEpisodeFolder(episodeFile, series, episodes.Select(v => v.SeasonNumber).First(), filePath);
|
|
||||||
|
|
||||||
_logger.Debug("Renaming episode file: {0} to {1}", episodeFile, filePath);
|
|
||||||
|
|
||||||
return TransferFile(episodeFile, series, episodes, filePath, TransferMode.Move);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EpisodeFile MoveEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
|
||||||
{
|
|
||||||
var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
|
|
||||||
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
|
|
||||||
|
|
||||||
EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
|
|
||||||
|
|
||||||
_logger.Debug("Moving episode file: {0} to {1}", episodeFile.Path, filePath);
|
|
||||||
|
|
||||||
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Move);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EpisodeFile CopyEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode)
|
|
||||||
{
|
|
||||||
var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
|
|
||||||
var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
|
|
||||||
|
|
||||||
EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
|
|
||||||
|
|
||||||
if (_configService.CopyUsingHardlinks)
|
|
||||||
{
|
|
||||||
_logger.Debug("Hardlinking episode file: {0} to {1}", episodeFile.Path, filePath);
|
|
||||||
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.HardLinkOrCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Debug("Copying episode file: {0} to {1}", episodeFile.Path, filePath);
|
|
||||||
return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Episode> episodes, string destinationFilePath, TransferMode mode)
|
|
||||||
{
|
|
||||||
Ensure.That(episodeFile, () => episodeFile).IsNotNull();
|
|
||||||
Ensure.That(series, () => series).IsNotNull();
|
|
||||||
Ensure.That(destinationFilePath, () => destinationFilePath).IsValidPath();
|
|
||||||
|
|
||||||
var episodeFilePath = episodeFile.Path ?? Path.Combine(series.Path, episodeFile.RelativePath);
|
|
||||||
|
|
||||||
if (!_diskProvider.FileExists(episodeFilePath))
|
|
||||||
{
|
|
||||||
throw new FileNotFoundException("Episode file path does not exist", episodeFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (episodeFilePath == destinationFilePath)
|
|
||||||
{
|
|
||||||
throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
_diskTransferService.TransferFile(episodeFilePath, destinationFilePath, mode);
|
|
||||||
|
|
||||||
episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilePath);
|
|
||||||
|
|
||||||
_updateEpisodeFileService.ChangeFileDateForFile(episodeFile, series, episodes);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_mediaFileAttributeService.SetFolderLastWriteTime(series.Path, episodeFile.DateAdded);
|
|
||||||
|
|
||||||
if (series.SeasonFolder)
|
|
||||||
{
|
|
||||||
var seasonFolder = Path.GetDirectoryName(destinationFilePath);
|
|
||||||
|
|
||||||
_mediaFileAttributeService.SetFolderLastWriteTime(seasonFolder, episodeFile.DateAdded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Warn(ex, "Unable to set last write time");
|
|
||||||
}
|
|
||||||
|
|
||||||
_mediaFileAttributeService.SetFilePermissions(destinationFilePath);
|
|
||||||
|
|
||||||
return episodeFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureEpisodeFolder(EpisodeFile episodeFile, LocalEpisode localEpisode, string filePath)
|
|
||||||
{
|
|
||||||
EnsureEpisodeFolder(episodeFile, localEpisode.Series, localEpisode.SeasonNumber, filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureEpisodeFolder(EpisodeFile episodeFile, Series series, int seasonNumber, string filePath)
|
|
||||||
{
|
|
||||||
var episodeFolder = Path.GetDirectoryName(filePath);
|
|
||||||
var seasonFolder = _buildFileNames.BuildSeasonPath(series, seasonNumber);
|
|
||||||
var seriesFolder = series.Path;
|
|
||||||
var rootFolder = new OsPath(seriesFolder).Directory.FullPath;
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(rootFolder))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotFoundException(string.Format("Root folder '{0}' was not found.", rootFolder));
|
|
||||||
}
|
|
||||||
|
|
||||||
var changed = false;
|
|
||||||
var newEvent = new EpisodeFolderCreatedEvent(series, episodeFile);
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(seriesFolder))
|
|
||||||
{
|
|
||||||
CreateFolder(seriesFolder);
|
|
||||||
newEvent.SeriesFolder = seriesFolder;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seriesFolder != seasonFolder && !_diskProvider.FolderExists(seasonFolder))
|
|
||||||
{
|
|
||||||
CreateFolder(seasonFolder);
|
|
||||||
newEvent.SeasonFolder = seasonFolder;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seasonFolder != episodeFolder && !_diskProvider.FolderExists(episodeFolder))
|
|
||||||
{
|
|
||||||
CreateFolder(episodeFolder);
|
|
||||||
newEvent.EpisodeFolder = episodeFolder;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed)
|
|
||||||
{
|
|
||||||
_eventAggregator.PublishEvent(newEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateFolder(string directoryName)
|
|
||||||
{
|
|
||||||
Ensure.That(directoryName, () => directoryName).IsNotNullOrWhiteSpace();
|
|
||||||
|
|
||||||
var parentFolder = new OsPath(directoryName).Directory.FullPath;
|
|
||||||
if (!_diskProvider.FolderExists(parentFolder))
|
|
||||||
{
|
|
||||||
CreateFolder(parentFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_diskProvider.CreateFolder(directoryName);
|
|
||||||
}
|
|
||||||
catch (IOException ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Unable to create directory: {0}", directoryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
_mediaFileAttributeService.SetFolderPermissions(directoryName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
using NzbDrone.Common.Messaging;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles.Events
|
||||||
|
{
|
||||||
|
public class TrackFolderCreatedEvent : IEvent
|
||||||
|
{
|
||||||
|
public Artist Artist { get; private set; }
|
||||||
|
public TrackFile TrackFile { get; private set; }
|
||||||
|
public string ArtistFolder { get; set; }
|
||||||
|
public string AlbumFolder { get; set; }
|
||||||
|
public string TrackFolder { get; set; }
|
||||||
|
|
||||||
|
public TrackFolderCreatedEvent(Artist artist, TrackFile trackFile)
|
||||||
|
{
|
||||||
|
Artist = artist;
|
||||||
|
TrackFile = trackFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
namespace NzbDrone.Core.MediaFiles
|
namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
public enum FileDateType
|
public enum FileDateType
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
LocalAirDate = 1,
|
AlbumReleaseDate = 1
|
||||||
UtcAirDate = 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
public interface IMediaFileRepository : IBasicRepository<TrackFile>
|
public interface IMediaFileRepository : IBasicRepository<TrackFile>
|
||||||
{
|
{
|
||||||
List<TrackFile> GetFilesByArtist(int artistId);
|
List<TrackFile> GetFilesByArtist(int artistId);
|
||||||
|
List<TrackFile> GetFilesByAlbum(int albumId);
|
||||||
List<TrackFile> GetFilesWithoutMediaInfo();
|
List<TrackFile> GetFilesWithoutMediaInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,5 +30,10 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
return Query.Where(c => c.ArtistId == artistId).ToList();
|
return Query.Where(c => c.ArtistId == artistId).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TrackFile> GetFilesByAlbum(int albumId)
|
||||||
|
{
|
||||||
|
return Query.Where(c => c.AlbumId == albumId).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,8 +4,6 @@ using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.Tv.Events;
|
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
using System;
|
using System;
|
||||||
|
@ -100,7 +98,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
|
|
||||||
public List<TrackFile> GetFilesByAlbum(int artistId, int albumId)
|
public List<TrackFile> GetFilesByAlbum(int artistId, int albumId)
|
||||||
{
|
{
|
||||||
return _mediaFileRepository.GetFilesByArtist(artistId);
|
return _mediaFileRepository.GetFilesByAlbum(albumId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
200
src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs
Normal file
200
src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
|
{
|
||||||
|
public static class MediaInfoFormatter
|
||||||
|
{
|
||||||
|
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(MediaInfoFormatter));
|
||||||
|
|
||||||
|
public static decimal FormatAudioChannels(MediaInfoModel mediaInfo)
|
||||||
|
{
|
||||||
|
var audioChannelPositions = mediaInfo.AudioChannelPositions;
|
||||||
|
var audioChannelPositionsText = mediaInfo.AudioChannelPositionsText;
|
||||||
|
var audioChannels = mediaInfo.AudioChannels;
|
||||||
|
|
||||||
|
if (audioChannelPositions.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
if (audioChannelPositionsText.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
if (mediaInfo.SchemaRevision >= 3)
|
||||||
|
{
|
||||||
|
return audioChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mediaInfo.AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? audioChannels - 1 + 0.1m : audioChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioChannelPositions.Replace("Object Based / ", "")
|
||||||
|
.Split(new [] { " / " }, StringSplitOptions.None)
|
||||||
|
.First()
|
||||||
|
.Split('/')
|
||||||
|
.Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatAudioCodec(MediaInfoModel mediaInfo)
|
||||||
|
{
|
||||||
|
if (mediaInfo.AudioCodecID == null)
|
||||||
|
{
|
||||||
|
return FormatAudioCodecLegacy(mediaInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
var audioFormat = mediaInfo.AudioFormat;
|
||||||
|
var audioCodecID = mediaInfo.AudioCodecID ?? string.Empty;
|
||||||
|
var audioProfile = mediaInfo.AudioProfile ?? string.Empty;
|
||||||
|
var audioCodecLibrary = mediaInfo.AudioCodecLibrary ?? string.Empty;
|
||||||
|
|
||||||
|
if (audioFormat.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AC-3"))
|
||||||
|
{
|
||||||
|
return "AC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("E-AC-3"))
|
||||||
|
{
|
||||||
|
return "EAC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AAC"))
|
||||||
|
{
|
||||||
|
if (audioCodecID == "A_AAC/MPEG4/LC/SBR")
|
||||||
|
{
|
||||||
|
return "HE-AAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "AAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("DTS"))
|
||||||
|
{
|
||||||
|
return "DTS";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("FLAC"))
|
||||||
|
{
|
||||||
|
return "FLAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.Trim().EqualsIgnoreCase("mp3"))
|
||||||
|
{
|
||||||
|
return "MP3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("MPEG Audio"))
|
||||||
|
{
|
||||||
|
if (mediaInfo.AudioCodecID == "55" || mediaInfo.AudioCodecID == "A_MPEG/L3" || mediaInfo.AudioProfile == "Layer 3")
|
||||||
|
{
|
||||||
|
return "MP3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mediaInfo.AudioCodecID == "A_MPEG/L2" || mediaInfo.AudioProfile == "Layer 2")
|
||||||
|
{
|
||||||
|
return "MP2";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Opus"))
|
||||||
|
{
|
||||||
|
return "Opus";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("PCM"))
|
||||||
|
{
|
||||||
|
return "PCM";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("TrueHD"))
|
||||||
|
{
|
||||||
|
return "TrueHD";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Vorbis"))
|
||||||
|
{
|
||||||
|
return "Vorbis";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat == "WMA")
|
||||||
|
{
|
||||||
|
return "WMA";
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug()
|
||||||
|
.Message("Unknown audio format: '{0}' in '{1}'.", string.Join(", ", audioFormat, audioCodecID, audioProfile, audioCodecLibrary))
|
||||||
|
.WriteSentryWarn("UnknownAudioFormat", mediaInfo.ContainerFormat, audioFormat, audioCodecID)
|
||||||
|
.Write();
|
||||||
|
|
||||||
|
return audioFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatAudioCodecLegacy(MediaInfoModel mediaInfo)
|
||||||
|
{
|
||||||
|
var audioFormat = mediaInfo.AudioFormat;
|
||||||
|
|
||||||
|
if (audioFormat.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return audioFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AC-3"))
|
||||||
|
{
|
||||||
|
return "AC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("E-AC-3"))
|
||||||
|
{
|
||||||
|
return "EAC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AAC"))
|
||||||
|
{
|
||||||
|
return "AAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("MPEG Audio") && mediaInfo.AudioProfile == "Layer 3")
|
||||||
|
{
|
||||||
|
return "MP3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("DTS"))
|
||||||
|
{
|
||||||
|
return "DTS";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("TrueHD"))
|
||||||
|
{
|
||||||
|
return "TrueHD";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("FLAC"))
|
||||||
|
{
|
||||||
|
return "FLAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Vorbis"))
|
||||||
|
{
|
||||||
|
return "Vorbis";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Opus"))
|
||||||
|
{
|
||||||
|
return "Opus";
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioFormat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,12 +9,19 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
{
|
{
|
||||||
public class MediaInfoModel : IEmbeddedDocument
|
public class MediaInfoModel : IEmbeddedDocument
|
||||||
{
|
{
|
||||||
|
public string ContainerFormat { get; set; }
|
||||||
public string VideoCodec { get; set; }
|
public string VideoCodec { get; set; }
|
||||||
|
public string VideoFormat { get; set; }
|
||||||
|
public string VideoCodecID { get; set; }
|
||||||
|
public string VideoProfile { get; set; }
|
||||||
|
public string VideoCodecLibrary { get; set; }
|
||||||
public int VideoBitrate { get; set; }
|
public int VideoBitrate { get; set; }
|
||||||
public int VideoBitDepth { get; set; }
|
public int VideoBitDepth { get; set; }
|
||||||
public int Width { get; set; }
|
public int Width { get; set; }
|
||||||
public int Height { get; set; }
|
public int Height { get; set; }
|
||||||
public string AudioFormat { get; set; }
|
public string AudioFormat { get; set; }
|
||||||
|
public string AudioCodecID { get; set; }
|
||||||
|
public string AudioCodecLibrary { get; set; }
|
||||||
public int AudioBitrate { get; set; }
|
public int AudioBitrate { get; set; }
|
||||||
public string AudioBitrateMode { get; set; }
|
public string AudioBitrateMode { get; set; }
|
||||||
public TimeSpan RunTime { get; set; }
|
public TimeSpan RunTime { get; set; }
|
||||||
|
@ -29,32 +36,5 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
public string ScanType { get; set; }
|
public string ScanType { get; set; }
|
||||||
public int SchemaRevision { get; set; }
|
public int SchemaRevision { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public decimal FormattedAudioChannels
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (AudioChannelPositions.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
if (AudioChannelPositionsText.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
if (SchemaRevision >= 3)
|
|
||||||
{
|
|
||||||
return AudioChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? AudioChannels - 1 + 0.1m : AudioChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AudioChannelPositions.Replace("Object Based / ", "")
|
|
||||||
.Split(new string[] { " / " }, StringSplitOptions.None)
|
|
||||||
.First()
|
|
||||||
.Split('/')
|
|
||||||
.Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,10 @@ using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Music;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles.MediaInfo
|
namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
{
|
{
|
||||||
|
@ -19,7 +18,8 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 3;
|
public const int MINIMUM_MEDIA_INFO_SCHEMA_REVISION = 3;
|
||||||
|
public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 4;
|
||||||
|
|
||||||
public UpdateMediaInfoService(IDiskProvider diskProvider,
|
public UpdateMediaInfoService(IDiskProvider diskProvider,
|
||||||
IMediaFileService mediaFileService,
|
IMediaFileService mediaFileService,
|
||||||
|
@ -66,7 +66,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
var allMediaFiles = _mediaFileService.GetFilesByArtist(message.Artist.Id);
|
var allMediaFiles = _mediaFileService.GetFilesByArtist(message.Artist.Id);
|
||||||
var filteredMediaFiles = allMediaFiles.Where(c => c.MediaInfo == null || c.MediaInfo.SchemaRevision < CURRENT_MEDIA_INFO_SCHEMA_REVISION).ToList();
|
var filteredMediaFiles = allMediaFiles.Where(c => c.MediaInfo == null || c.MediaInfo.SchemaRevision < MINIMUM_MEDIA_INFO_SCHEMA_REVISION).ToList();
|
||||||
|
|
||||||
UpdateMediaInfo(message.Artist, filteredMediaFiles);
|
UpdateMediaInfo(message.Artist, filteredMediaFiles);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
@ -105,47 +105,37 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
int.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out generalRuntime);
|
int.TryParse(mediaInfo.Get(StreamKind.General, 0, "PlayTime"), out generalRuntime);
|
||||||
|
|
||||||
string audioBitRateMode = mediaInfo.Get(StreamKind.Audio, 0, "BitRate_Mode");
|
string audioBitRateMode = mediaInfo.Get(StreamKind.Audio, 0, "BitRate_Mode");
|
||||||
string aBitRate = mediaInfo.Get(StreamKind.Audio, 0, "BitRate");
|
string aBitRate = mediaInfo.Get(StreamKind.Audio, 0, "BitRate").Split(new [] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||||
int aBindex = aBitRate.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
if (aBindex > 0)
|
|
||||||
{
|
|
||||||
aBitRate = aBitRate.Remove(aBindex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int.TryParse(aBitRate, out audioBitRate);
|
int.TryParse(aBitRate, out audioBitRate);
|
||||||
int.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "StreamCount"), out streamCount);
|
int.TryParse(mediaInfo.Get(StreamKind.Audio, 0, "StreamCount"), out streamCount);
|
||||||
|
|
||||||
|
|
||||||
string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)");
|
string audioChannelsStr = mediaInfo.Get(StreamKind.Audio, 0, "Channel(s)").Split(new [] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||||
int aCindex = audioChannelsStr.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
|
|
||||||
if (aCindex > 0)
|
|
||||||
{
|
|
||||||
audioChannelsStr = audioChannelsStr.Remove(aCindex);
|
|
||||||
}
|
|
||||||
|
|
||||||
var audioChannelPositions = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions/String2");
|
var audioChannelPositions = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions/String2");
|
||||||
var audioChannelPositionsText = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions");
|
var audioChannelPositionsText = mediaInfo.Get(StreamKind.Audio, 0, "ChannelPositions");
|
||||||
|
|
||||||
string audioLanguages = mediaInfo.Get(StreamKind.General, 0, "Audio_Language_List");
|
string audioLanguages = mediaInfo.Get(StreamKind.General, 0, "Audio_Language_List");
|
||||||
string audioProfile = mediaInfo.Get(StreamKind.Audio, 0, "Format_Profile");
|
string videoProfile = mediaInfo.Get(StreamKind.Video, 0, "Format_Profile").Split(new [] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||||
|
string audioProfile = mediaInfo.Get(StreamKind.Audio, 0, "Format_Profile").Split(new [] { " /" }, StringSplitOptions.None)[0].Trim();
|
||||||
int aPindex = audioProfile.IndexOf(" /", StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
|
|
||||||
if (aPindex > 0)
|
|
||||||
{
|
|
||||||
audioProfile = audioProfile.Remove(aPindex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int.TryParse(audioChannelsStr, out audioChannels);
|
int.TryParse(audioChannelsStr, out audioChannels);
|
||||||
var mediaInfoModel = new MediaInfoModel
|
var mediaInfoModel = new MediaInfoModel
|
||||||
{
|
{
|
||||||
VideoCodec = mediaInfo.Get(StreamKind.Video, 0, "Codec/String"),
|
ContainerFormat = mediaInfo.Get(StreamKind.General, 0, "Format"),
|
||||||
|
VideoFormat = mediaInfo.Get(StreamKind.Video, 0, "Format"),
|
||||||
|
VideoCodecID = mediaInfo.Get(StreamKind.Video, 0, "CodecID"),
|
||||||
|
VideoProfile = videoProfile,
|
||||||
|
VideoCodecLibrary = mediaInfo.Get(StreamKind.Video, 0, "Encoded_Library"),
|
||||||
VideoBitrate = videoBitRate,
|
VideoBitrate = videoBitRate,
|
||||||
VideoBitDepth = videoBitDepth,
|
VideoBitDepth = videoBitDepth,
|
||||||
Height = height,
|
Height = height,
|
||||||
Width = width,
|
Width = width,
|
||||||
AudioFormat = mediaInfo.Get(StreamKind.Audio, 0, "Format"),
|
AudioFormat = mediaInfo.Get(StreamKind.Audio, 0, "Format"),
|
||||||
|
AudioCodecID = mediaInfo.Get(StreamKind.Audio, 0, "CodecID"),
|
||||||
|
AudioProfile = audioProfile,
|
||||||
|
AudioCodecLibrary = mediaInfo.Get(StreamKind.Audio, 0, "Encoded_Library"),
|
||||||
AudioBitrateMode = audioBitRateMode,
|
AudioBitrateMode = audioBitRateMode,
|
||||||
AudioBitrate = audioBitRate,
|
AudioBitrate = audioBitRate,
|
||||||
RunTime = GetBestRuntime(audioRuntime, videoRuntime, generalRuntime),
|
RunTime = GetBestRuntime(audioRuntime, videoRuntime, generalRuntime),
|
||||||
|
@ -153,7 +143,6 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
|
||||||
AudioChannels = audioChannels,
|
AudioChannels = audioChannels,
|
||||||
AudioChannelPositions = audioChannelPositions,
|
AudioChannelPositions = audioChannelPositions,
|
||||||
AudioChannelPositionsText = audioChannelPositionsText,
|
AudioChannelPositionsText = audioChannelPositionsText,
|
||||||
AudioProfile = audioProfile.Trim(),
|
|
||||||
VideoFps = videoFrameRate,
|
VideoFps = videoFrameRate,
|
||||||
AudioLanguages = audioLanguages,
|
AudioLanguages = audioLanguages,
|
||||||
Subtitles = subtitles,
|
Subtitles = subtitles,
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
|
||||||
{
|
|
||||||
public class RenameEpisodeFilePreview
|
|
||||||
{
|
|
||||||
public int SeriesId { get; set; }
|
|
||||||
public int SeasonNumber { get; set; }
|
|
||||||
public List<int> EpisodeNumbers { get; set; }
|
|
||||||
public int EpisodeFileId { get; set; }
|
|
||||||
public string ExistingPath { get; set; }
|
|
||||||
public string NewPath { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,181 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Instrumentation.Extensions;
|
|
||||||
using NzbDrone.Core.MediaFiles.Commands;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
|
||||||
{
|
|
||||||
public interface IRenameEpisodeFileService
|
|
||||||
{
|
|
||||||
List<RenameEpisodeFilePreview> GetRenamePreviews(int seriesId);
|
|
||||||
List<RenameEpisodeFilePreview> GetRenamePreviews(int seriesId, int seasonNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RenameEpisodeFileService : IRenameEpisodeFileService,
|
|
||||||
IExecute<RenameFilesCommand>,
|
|
||||||
IExecute<RenameSeriesCommand>
|
|
||||||
{
|
|
||||||
private readonly ISeriesService _seriesService;
|
|
||||||
private readonly IMediaFileService _mediaFileService;
|
|
||||||
private readonly IMoveEpisodeFiles _episodeFileMover;
|
|
||||||
private readonly IEventAggregator _eventAggregator;
|
|
||||||
private readonly IEpisodeService _episodeService;
|
|
||||||
private readonly IBuildFileNames _filenameBuilder;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public RenameEpisodeFileService(ISeriesService seriesService,
|
|
||||||
IMediaFileService mediaFileService,
|
|
||||||
IMoveEpisodeFiles episodeFileMover,
|
|
||||||
IEventAggregator eventAggregator,
|
|
||||||
IEpisodeService episodeService,
|
|
||||||
IBuildFileNames filenameBuilder,
|
|
||||||
IDiskProvider diskProvider,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_seriesService = seriesService;
|
|
||||||
_mediaFileService = mediaFileService;
|
|
||||||
_episodeFileMover = episodeFileMover;
|
|
||||||
_eventAggregator = eventAggregator;
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_filenameBuilder = filenameBuilder;
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<RenameEpisodeFilePreview> GetRenamePreviews(int seriesId)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//var series = _seriesService.GetSeries(seriesId);
|
|
||||||
//var episodes = _episodeService.GetEpisodeBySeries(seriesId);
|
|
||||||
//var files = _mediaFileService.GetFilesBySeries(seriesId);
|
|
||||||
|
|
||||||
//return GetPreviews(series, episodes, files)
|
|
||||||
// .OrderByDescending(e => e.SeasonNumber)
|
|
||||||
// .ThenByDescending(e => e.EpisodeNumbers.First())
|
|
||||||
// .ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<RenameEpisodeFilePreview> GetRenamePreviews(int seriesId, int seasonNumber)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//var series = _seriesService.GetSeries(seriesId);
|
|
||||||
//var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber);
|
|
||||||
//var files = _mediaFileService.GetFilesBySeason(seriesId, seasonNumber);
|
|
||||||
|
|
||||||
//return GetPreviews(series, episodes, files)
|
|
||||||
// .OrderByDescending(e => e.EpisodeNumbers.First()).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<RenameEpisodeFilePreview> GetPreviews(Series series, List<Episode> episodes, List<EpisodeFile> files)
|
|
||||||
{
|
|
||||||
foreach (var f in files)
|
|
||||||
{
|
|
||||||
var file = f;
|
|
||||||
var episodesInFile = episodes.Where(e => e.EpisodeFileId == file.Id).ToList();
|
|
||||||
var episodeFilePath = Path.Combine(series.Path, file.RelativePath);
|
|
||||||
|
|
||||||
if (!episodesInFile.Any())
|
|
||||||
{
|
|
||||||
_logger.Warn("File ({0}) is not linked to any episodes", episodeFilePath);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seasonNumber = episodesInFile.First().SeasonNumber;
|
|
||||||
var newName = _filenameBuilder.BuildFileName(episodesInFile, series, file);
|
|
||||||
var newPath = _filenameBuilder.BuildFilePath(series, seasonNumber, newName, Path.GetExtension(episodeFilePath));
|
|
||||||
|
|
||||||
if (!episodeFilePath.PathEquals(newPath, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
yield return new RenameEpisodeFilePreview
|
|
||||||
{
|
|
||||||
SeriesId = series.Id,
|
|
||||||
SeasonNumber = seasonNumber,
|
|
||||||
EpisodeNumbers = episodesInFile.Select(e => e.EpisodeNumber).ToList(),
|
|
||||||
EpisodeFileId = file.Id,
|
|
||||||
ExistingPath = file.RelativePath,
|
|
||||||
NewPath = series.Path.GetRelativePath(newPath)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenameFiles(List<EpisodeFile> episodeFiles, Series series)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//var renamed = new List<EpisodeFile>();
|
|
||||||
|
|
||||||
//foreach (var episodeFile in episodeFiles)
|
|
||||||
//{
|
|
||||||
// var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath);
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// _logger.Debug("Renaming episode file: {0}", episodeFile);
|
|
||||||
// _episodeFileMover.MoveEpisodeFile(episodeFile, series);
|
|
||||||
|
|
||||||
// _mediaFileService.Update(episodeFile);
|
|
||||||
// renamed.Add(episodeFile);
|
|
||||||
|
|
||||||
// _logger.Debug("Renamed episode file: {0}", episodeFile);
|
|
||||||
// }
|
|
||||||
// catch (SameFilenameException ex)
|
|
||||||
// {
|
|
||||||
// _logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// _logger.Error(ex, "Failed to rename file {0}", episodeFilePath);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//if (renamed.Any())
|
|
||||||
//{
|
|
||||||
// _diskProvider.RemoveEmptySubfolders(series.Path);
|
|
||||||
|
|
||||||
// _eventAggregator.PublishEvent(new SeriesRenamedEvent(series));
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute(RenameFilesCommand message)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//var series = _seriesService.GetSeries(message.SeriesId);
|
|
||||||
//var episodeFiles = _mediaFileService.Get(message.Files);
|
|
||||||
|
|
||||||
//_logger.ProgressInfo("Renaming {0} files for {1}", episodeFiles.Count, series.Title);
|
|
||||||
//RenameFiles(episodeFiles, series);
|
|
||||||
//_logger.ProgressInfo("Selected episode files renamed for {0}", series.Title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute(RenameSeriesCommand message)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//_logger.Debug("Renaming all files for selected series");
|
|
||||||
//var seriesToRename = _seriesService.GetSeries(message.SeriesIds);
|
|
||||||
|
|
||||||
//foreach (var series in seriesToRename)
|
|
||||||
//{
|
|
||||||
// var episodeFiles = _mediaFileService.GetFilesBySeries(series.Id);
|
|
||||||
// _logger.ProgressInfo("Renaming all files in series: {0}", series.Title);
|
|
||||||
// RenameFiles(episodeFiles, series);
|
|
||||||
// _logger.ProgressInfo("All episode files renamed for {0}", series.Title);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,9 +21,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
List<RenameTrackFilePreview> GetRenamePreviews(int artistId, int albumId);
|
List<RenameTrackFilePreview> GetRenamePreviews(int artistId, int albumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RenameTrackFileService : IRenameTrackFileService,
|
public class RenameTrackFileService : IRenameTrackFileService, IExecute<RenameFilesCommand>, IExecute<RenameArtistCommand>
|
||||||
IExecute<RenameFilesCommand>,
|
|
||||||
IExecute<RenameArtistCommand>
|
|
||||||
{
|
{
|
||||||
private readonly IArtistService _artistService;
|
private readonly IArtistService _artistService;
|
||||||
private readonly IAlbumService _albumService;
|
private readonly IAlbumService _albumService;
|
||||||
|
@ -58,32 +56,29 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
|
|
||||||
public List<RenameTrackFilePreview> GetRenamePreviews(int artistId)
|
public List<RenameTrackFilePreview> GetRenamePreviews(int artistId)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//var artist = _artistService.GetArtist(artistId);
|
|
||||||
//var tracks = _trackService.GetTracksByArtist(artistId);
|
|
||||||
//var files = _mediaFileService.GetFilesByArtist(artistId);
|
|
||||||
|
|
||||||
//return GetPreviews(artist, tracks, files)
|
var artist = _artistService.GetArtist(artistId);
|
||||||
// .OrderByDescending(e => e.SeasonNumber)
|
var tracks = _trackService.GetTracksByArtist(artistId);
|
||||||
// .ThenByDescending(e => e.TrackNumbers.First())
|
var files = _mediaFileService.GetFilesByArtist(artistId);
|
||||||
// .ToList();
|
|
||||||
|
return GetPreviews(artist, tracks, files)
|
||||||
|
.OrderByDescending(e => e.AlbumId)
|
||||||
|
.ThenByDescending(e => e.TrackNumbers.First())
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RenameTrackFilePreview> GetRenamePreviews(int artistId, int albumId)
|
public List<RenameTrackFilePreview> GetRenamePreviews(int artistId, int albumId)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
//throw new NotImplementedException();
|
|
||||||
var artist = _artistService.GetArtist(artistId);
|
var artist = _artistService.GetArtist(artistId);
|
||||||
var album = _albumService.GetAlbum(albumId);
|
|
||||||
var tracks = _trackService.GetTracksByAlbum(artistId, albumId);
|
var tracks = _trackService.GetTracksByAlbum(artistId, albumId);
|
||||||
var files = _mediaFileService.GetFilesByAlbum(artistId, albumId);
|
var files = _mediaFileService.GetFilesByAlbum(artistId, albumId);
|
||||||
|
|
||||||
return GetPreviews(artist, album, tracks, files)
|
return GetPreviews(artist, tracks, files)
|
||||||
.OrderByDescending(e => e.TrackNumbers.First()).ToList();
|
.OrderByDescending(e => e.TrackNumbers.First()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<RenameTrackFilePreview> GetPreviews(Artist artist, Album album, List<Track> tracks, List<TrackFile> files)
|
private IEnumerable<RenameTrackFilePreview> GetPreviews(Artist artist, List<Track> tracks, List<TrackFile> files)
|
||||||
{
|
{
|
||||||
foreach (var f in files)
|
foreach (var f in files)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +92,8 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var albumId = tracksInFile.First().AlbumId;
|
var album = _albumService.GetAlbum(tracksInFile.First().AlbumId);
|
||||||
|
|
||||||
var newName = _filenameBuilder.BuildTrackFileName(tracksInFile, artist, album, file);
|
var newName = _filenameBuilder.BuildTrackFileName(tracksInFile, artist, album, file);
|
||||||
var newPath = _filenameBuilder.BuildTrackFilePath(artist, album, newName, Path.GetExtension(trackFilePath));
|
var newPath = _filenameBuilder.BuildTrackFilePath(artist, album, newName, Path.GetExtension(trackFilePath));
|
||||||
|
|
||||||
|
@ -106,7 +102,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
yield return new RenameTrackFilePreview
|
yield return new RenameTrackFilePreview
|
||||||
{
|
{
|
||||||
ArtistId = artist.Id,
|
ArtistId = artist.Id,
|
||||||
AlbumId = albumId,
|
AlbumId = album.Id,
|
||||||
TrackNumbers = tracksInFile.Select(e => e.TrackNumber).ToList(),
|
TrackNumbers = tracksInFile.Select(e => e.TrackNumber).ToList(),
|
||||||
TrackFileId = file.Id,
|
TrackFileId = file.Id,
|
||||||
ExistingPath = file.RelativePath,
|
ExistingPath = file.RelativePath,
|
||||||
|
@ -118,68 +114,64 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
|
|
||||||
private void RenameFiles(List<TrackFile> trackFiles, Artist artist)
|
private void RenameFiles(List<TrackFile> trackFiles, Artist artist)
|
||||||
{
|
{
|
||||||
// TODO
|
var renamed = new List<TrackFile>();
|
||||||
throw new NotImplementedException();
|
|
||||||
//var renamed = new List<TrackFile>();
|
|
||||||
|
|
||||||
//foreach (var trackFile in trackFiles)
|
foreach (var trackFile in trackFiles)
|
||||||
//{
|
{
|
||||||
// var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath);
|
var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath);
|
||||||
|
|
||||||
// try
|
try
|
||||||
// {
|
{
|
||||||
// _logger.Debug("Renaming track file: {0}", trackFile);
|
_logger.Debug("Renaming track file: {0}", trackFile);
|
||||||
// _trackFileMover.MoveTrackFile(trackFile, artist);
|
_trackFileMover.MoveTrackFile(trackFile, artist);
|
||||||
|
|
||||||
// _mediaFileService.Update(trackFile);
|
_mediaFileService.Update(trackFile);
|
||||||
// renamed.Add(trackFile);
|
renamed.Add(trackFile);
|
||||||
|
|
||||||
// _logger.Debug("Renamed track file: {0}", trackFile);
|
_logger.Debug("Renamed track file: {0}", trackFile);
|
||||||
// }
|
}
|
||||||
// catch (SameFilenameException ex)
|
catch (SameFilenameException ex)
|
||||||
// {
|
{
|
||||||
// _logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);
|
_logger.Debug("File not renamed, source and destination are the same: {0}", ex.Filename);
|
||||||
// }
|
}
|
||||||
// catch (Exception ex)
|
catch (Exception ex)
|
||||||
// {
|
{
|
||||||
// _logger.Error(ex, "Failed to rename file {0}", trackFilePath);
|
_logger.Error(ex, "Failed to rename file {0}", trackFilePath);
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//if (renamed.Any())
|
if (renamed.Any())
|
||||||
//{
|
{
|
||||||
// _diskProvider.RemoveEmptySubfolders(artist.Path);
|
_diskProvider.RemoveEmptySubfolders(artist.Path);
|
||||||
|
|
||||||
// _eventAggregator.PublishEvent(new ArtistRenamedEvent(artist));
|
_eventAggregator.PublishEvent(new ArtistRenamedEvent(artist));
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(RenameFilesCommand message)
|
public void Execute(RenameFilesCommand message)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//var artist = _artistService.GetArtist(message.ArtistId);
|
|
||||||
//var trackFiles = _mediaFileService.Get(message.Files);
|
|
||||||
|
|
||||||
//_logger.ProgressInfo("Renaming {0} files for {1}", trackFiles.Count, artist.Title);
|
var artist = _artistService.GetArtist(message.ArtistId);
|
||||||
//RenameFiles(trackFiles, artist);
|
var trackFiles = _mediaFileService.Get(message.Files);
|
||||||
//_logger.ProgressInfo("Selected track files renamed for {0}", artist.Title);
|
|
||||||
|
_logger.ProgressInfo("Renaming {0} files for {1}", trackFiles.Count, artist.Name);
|
||||||
|
RenameFiles(trackFiles, artist);
|
||||||
|
_logger.ProgressInfo("Selected track files renamed for {0}", artist.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(RenameArtistCommand message)
|
public void Execute(RenameArtistCommand message)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
throw new NotImplementedException();
|
|
||||||
//_logger.Debug("Renaming all files for selected artist");
|
|
||||||
//var artistToRename = _artistService.GetArtist(message.ArtistIds);
|
|
||||||
|
|
||||||
//foreach (var artist in artistToRename)
|
_logger.Debug("Renaming all files for selected artist");
|
||||||
//{
|
var artistToRename = _artistService.GetArtists(message.ArtistIds);
|
||||||
// var trackFiles = _mediaFileService.GetFilesByArtist(artist.Id);
|
|
||||||
// _logger.ProgressInfo("Renaming all files in artist: {0}", artist.Title);
|
foreach (var artist in artistToRename)
|
||||||
// RenameFiles(trackFiles, artist);
|
{
|
||||||
// _logger.ProgressInfo("All track files renamed for {0}", artist.Title);
|
var trackFiles = _mediaFileService.GetFilesByArtist(artist.Id);
|
||||||
//}
|
_logger.ProgressInfo("Renaming all files in artist: {0}", artist.Name);
|
||||||
|
RenameFiles(trackFiles, artist);
|
||||||
|
_logger.ProgressInfo("All track files renamed for {0}", artist.Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnsureThat;
|
using NzbDrone.Common.EnsureThat;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
@ -27,7 +27,8 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
public class TrackFileMovingService : IMoveTrackFiles
|
public class TrackFileMovingService : IMoveTrackFiles
|
||||||
{
|
{
|
||||||
private readonly ITrackService _trackService;
|
private readonly ITrackService _trackService;
|
||||||
//private readonly IUpdateTrackFileService _updateTrackFileService;
|
private readonly IAlbumService _albumService;
|
||||||
|
private readonly IUpdateTrackFileService _updateTrackFileService;
|
||||||
private readonly IBuildFileNames _buildFileNames;
|
private readonly IBuildFileNames _buildFileNames;
|
||||||
private readonly IDiskTransferService _diskTransferService;
|
private readonly IDiskTransferService _diskTransferService;
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
@ -36,8 +37,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public TrackFileMovingService(ITrackService episodeService,
|
public TrackFileMovingService(ITrackService trackService,
|
||||||
//IUpdateEpisodeFileService updateEpisodeFileService,
|
IAlbumService albumService,
|
||||||
|
IUpdateTrackFileService updateTrackFileService,
|
||||||
IBuildFileNames buildFileNames,
|
IBuildFileNames buildFileNames,
|
||||||
IDiskTransferService diskTransferService,
|
IDiskTransferService diskTransferService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
|
@ -46,8 +48,9 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_trackService = episodeService;
|
_trackService = trackService;
|
||||||
//_updateTrackFileService = updateEpisodeFileService;
|
_albumService = albumService;
|
||||||
|
_updateTrackFileService = updateTrackFileService;
|
||||||
_buildFileNames = buildFileNames;
|
_buildFileNames = buildFileNames;
|
||||||
_diskTransferService = diskTransferService;
|
_diskTransferService = diskTransferService;
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
|
@ -59,112 +62,107 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
|
|
||||||
public TrackFile MoveTrackFile(TrackFile trackFile, Artist artist)
|
public TrackFile MoveTrackFile(TrackFile trackFile, Artist artist)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
|
||||||
// TODO
|
|
||||||
//var tracks = _trackService.GetTracksByFileId(trackFile.Id);
|
|
||||||
//var newFileName = _buildFileNames.BuildFileName(tracks, artist, trackFile);
|
|
||||||
//var filePath = _buildFileNames.BuildFilePath(artist, tracks.First(), trackFile.AlbumId, newFileName, Path.GetExtension(trackFile.RelativePath));
|
|
||||||
|
|
||||||
//EnsureAlbumFolder(trackFile, artist, tracks.Select(v => v.Album).First(), filePath);
|
var tracks = _trackService.GetTracksByFileId(trackFile.Id);
|
||||||
|
var album = _albumService.GetAlbum(trackFile.AlbumId);
|
||||||
|
var newFileName = _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile);
|
||||||
|
var filePath = _buildFileNames.BuildTrackFilePath(artist, album, newFileName, Path.GetExtension(trackFile.RelativePath));
|
||||||
|
|
||||||
//_logger.Debug("Renaming track file: {0} to {1}", trackFile, filePath);
|
EnsureTrackFolder(trackFile, artist, album, filePath);
|
||||||
|
|
||||||
//return TransferFile(trackFile, artist, tracks, filePath, TransferMode.Move);
|
_logger.Debug("Renaming track file: {0} to {1}", trackFile, filePath);
|
||||||
|
|
||||||
|
return TransferFile(trackFile, artist, tracks, filePath, TransferMode.Move);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
throw new System.NotImplementedException();
|
|
||||||
//var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
|
|
||||||
//var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
|
|
||||||
|
|
||||||
//EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
|
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile);
|
||||||
|
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, localTrack.Album, newFileName, Path.GetExtension(localTrack.Path));
|
||||||
|
|
||||||
//_logger.Debug("Moving episode file: {0} to {1}", episodeFile.Path, filePath);
|
EnsureTrackFolder(trackFile, localTrack, filePath);
|
||||||
|
|
||||||
//return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Move);
|
_logger.Debug("Moving track file: {0} to {1}", trackFile.Path, filePath);
|
||||||
|
|
||||||
|
return TransferFile(trackFile, localTrack.Artist, localTrack.Tracks, filePath, TransferMode.Move);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
||||||
{
|
{
|
||||||
// TODO
|
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile);
|
||||||
throw new System.NotImplementedException();
|
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, localTrack.Album, newFileName, Path.GetExtension(localTrack.Path));
|
||||||
//var newFileName = _buildFileNames.BuildFileName(localEpisode.Episodes, localEpisode.Series, episodeFile);
|
|
||||||
//var filePath = _buildFileNames.BuildFilePath(localEpisode.Series, localEpisode.SeasonNumber, newFileName, Path.GetExtension(localEpisode.Path));
|
|
||||||
|
|
||||||
//EnsureEpisodeFolder(episodeFile, localEpisode, filePath);
|
EnsureTrackFolder(trackFile, localTrack, filePath);
|
||||||
|
|
||||||
//if (_configService.CopyUsingHardlinks)
|
if (_configService.CopyUsingHardlinks)
|
||||||
//{
|
{
|
||||||
// _logger.Debug("Hardlinking episode file: {0} to {1}", episodeFile.Path, filePath);
|
_logger.Debug("Hardlinking track file: {0} to {1}", trackFile.Path, filePath);
|
||||||
// return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.HardLinkOrCopy);
|
return TransferFile(trackFile, localTrack.Artist, localTrack.Tracks, filePath, TransferMode.HardLinkOrCopy);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//_logger.Debug("Copying episode file: {0} to {1}", episodeFile.Path, filePath);
|
_logger.Debug("Copying track file: {0} to {1}", trackFile.Path, filePath);
|
||||||
//return TransferFile(episodeFile, localEpisode.Series, localEpisode.Episodes, filePath, TransferMode.Copy);
|
return TransferFile(trackFile, localTrack.Artist, localTrack.Tracks, filePath, TransferMode.Copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EpisodeFile TransferFile(EpisodeFile episodeFile, Series series, List<Episode> episodes, string destinationFilePath, TransferMode mode)
|
private TrackFile TransferFile(TrackFile trackFile, Artist artist, List<Track> tracks, string destinationFilePath, TransferMode mode)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
throw new System.NotImplementedException();
|
|
||||||
|
|
||||||
//Ensure.That(episodeFile, () => episodeFile).IsNotNull();
|
Ensure.That(trackFile, () => trackFile).IsNotNull();
|
||||||
//Ensure.That(series, () => series).IsNotNull();
|
Ensure.That(artist, () => artist).IsNotNull();
|
||||||
//Ensure.That(destinationFilePath, () => destinationFilePath).IsValidPath();
|
Ensure.That(destinationFilePath, () => destinationFilePath).IsValidPath();
|
||||||
|
|
||||||
//var episodeFilePath = episodeFile.Path ?? Path.Combine(series.Path, episodeFile.RelativePath);
|
var trackFilePath = trackFile.Path ?? Path.Combine(artist.Path, trackFile.RelativePath);
|
||||||
|
|
||||||
//if (!_diskProvider.FileExists(episodeFilePath))
|
if (!_diskProvider.FileExists(trackFilePath))
|
||||||
//{
|
{
|
||||||
// throw new FileNotFoundException("Episode file path does not exist", episodeFilePath);
|
throw new FileNotFoundException("Track file path does not exist", trackFilePath);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//if (episodeFilePath == destinationFilePath)
|
if (trackFilePath == destinationFilePath)
|
||||||
//{
|
{
|
||||||
// throw new SameFilenameException("File not moved, source and destination are the same", episodeFilePath);
|
throw new SameFilenameException("File not moved, source and destination are the same", trackFilePath);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//_diskTransferService.TransferFile(episodeFilePath, destinationFilePath, mode);
|
_diskTransferService.TransferFile(trackFilePath, destinationFilePath, mode);
|
||||||
|
|
||||||
//episodeFile.RelativePath = series.Path.GetRelativePath(destinationFilePath);
|
trackFile.RelativePath = artist.Path.GetRelativePath(destinationFilePath);
|
||||||
|
|
||||||
//_updateTrackFileService.ChangeFileDateForFile(episodeFile, series, episodes);
|
_updateTrackFileService.ChangeFileDateForFile(trackFile, artist, tracks);
|
||||||
|
|
||||||
//try
|
try
|
||||||
//{
|
{
|
||||||
// _mediaFileAttributeService.SetFolderLastWriteTime(series.Path, episodeFile.DateAdded);
|
_mediaFileAttributeService.SetFolderLastWriteTime(artist.Path, trackFile.DateAdded);
|
||||||
|
|
||||||
// if (series.SeasonFolder)
|
if (artist.AlbumFolder)
|
||||||
// {
|
{
|
||||||
// var seasonFolder = Path.GetDirectoryName(destinationFilePath);
|
var albumFolder = Path.GetDirectoryName(destinationFilePath);
|
||||||
|
|
||||||
// _mediaFileAttributeService.SetFolderLastWriteTime(seasonFolder, episodeFile.DateAdded);
|
_mediaFileAttributeService.SetFolderLastWriteTime(albumFolder, trackFile.DateAdded);
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//catch (Exception ex)
|
catch (Exception ex)
|
||||||
//{
|
{
|
||||||
// _logger.Warn(ex, "Unable to set last write time");
|
_logger.Warn(ex, "Unable to set last write time");
|
||||||
//}
|
}
|
||||||
|
|
||||||
//_mediaFileAttributeService.SetFilePermissions(destinationFilePath);
|
_mediaFileAttributeService.SetFilePermissions(destinationFilePath);
|
||||||
|
|
||||||
//return episodeFile;
|
return trackFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureEpisodeFolder(EpisodeFile episodeFile, LocalEpisode localEpisode, string filePath)
|
private void EnsureTrackFolder(TrackFile trackFile, LocalTrack localTrack, string filePath)
|
||||||
{
|
{
|
||||||
EnsureEpisodeFolder(episodeFile, localEpisode.Series, localEpisode.SeasonNumber, filePath);
|
EnsureTrackFolder(trackFile, localTrack.Artist, localTrack.Album, filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureEpisodeFolder(EpisodeFile episodeFile, Series series, int seasonNumber, string filePath)
|
private void EnsureTrackFolder(TrackFile trackFile, Artist artist, Album album, string filePath)
|
||||||
{
|
{
|
||||||
var episodeFolder = Path.GetDirectoryName(filePath);
|
var trackFolder = Path.GetDirectoryName(filePath);
|
||||||
var seasonFolder = _buildFileNames.BuildSeasonPath(series, seasonNumber);
|
var albumFolder = _buildFileNames.BuildAlbumPath(artist, album);
|
||||||
var seriesFolder = series.Path;
|
var artistFolder = artist.Path;
|
||||||
var rootFolder = new OsPath(seriesFolder).Directory.FullPath;
|
var rootFolder = new OsPath(artistFolder).Directory.FullPath;
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(rootFolder))
|
if (!_diskProvider.FolderExists(rootFolder))
|
||||||
{
|
{
|
||||||
|
@ -172,26 +170,26 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
var changed = false;
|
var changed = false;
|
||||||
var newEvent = new EpisodeFolderCreatedEvent(series, episodeFile);
|
var newEvent = new TrackFolderCreatedEvent(artist, trackFile);
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(seriesFolder))
|
if (!_diskProvider.FolderExists(artistFolder))
|
||||||
{
|
{
|
||||||
CreateFolder(seriesFolder);
|
CreateFolder(artistFolder);
|
||||||
newEvent.SeriesFolder = seriesFolder;
|
newEvent.ArtistFolder = artistFolder;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seriesFolder != seasonFolder && !_diskProvider.FolderExists(seasonFolder))
|
if (artistFolder != albumFolder && !_diskProvider.FolderExists(albumFolder))
|
||||||
{
|
{
|
||||||
CreateFolder(seasonFolder);
|
CreateFolder(albumFolder);
|
||||||
newEvent.SeasonFolder = seasonFolder;
|
newEvent.AlbumFolder = albumFolder;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seasonFolder != episodeFolder && !_diskProvider.FolderExists(episodeFolder))
|
if (albumFolder != trackFolder && !_diskProvider.FolderExists(trackFolder))
|
||||||
{
|
{
|
||||||
CreateFolder(episodeFolder);
|
CreateFolder(trackFolder);
|
||||||
newEvent.EpisodeFolder = episodeFolder;
|
newEvent.TrackFolder = trackFolder;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,173 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Instrumentation.Extensions;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
|
||||||
{
|
|
||||||
public interface IUpdateEpisodeFileService
|
|
||||||
{
|
|
||||||
void ChangeFileDateForFile(EpisodeFile episodeFile, Series series, List<Episode> episodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UpdateEpisodeFileService : IUpdateEpisodeFileService,
|
|
||||||
IHandle<SeriesScannedEvent>
|
|
||||||
{
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly IConfigService _configService;
|
|
||||||
private readonly IEpisodeService _episodeService;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public UpdateEpisodeFileService(IDiskProvider diskProvider,
|
|
||||||
IConfigService configService,
|
|
||||||
IEpisodeService episodeService,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_configService = configService;
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ChangeFileDateForFile(EpisodeFile episodeFile, Series series, List<Episode> episodes)
|
|
||||||
{
|
|
||||||
ChangeFileDate(episodeFile, series, episodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ChangeFileDate(EpisodeFile episodeFile, Series series, List<Episode> episodes)
|
|
||||||
{
|
|
||||||
var episodeFilePath = Path.Combine(series.Path, episodeFile.RelativePath);
|
|
||||||
|
|
||||||
switch (_configService.FileDate)
|
|
||||||
{
|
|
||||||
case FileDateType.LocalAirDate:
|
|
||||||
{
|
|
||||||
var airDate = episodes.First().AirDate;
|
|
||||||
var airTime = series.AirTime;
|
|
||||||
|
|
||||||
if (airDate.IsNullOrWhiteSpace() || airTime.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChangeFileDateToLocalAirDate(episodeFilePath, airDate, airTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
case FileDateType.UtcAirDate:
|
|
||||||
{
|
|
||||||
var airDateUtc = episodes.First().AirDateUtc;
|
|
||||||
|
|
||||||
if (!airDateUtc.HasValue)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChangeFileDateToUtcAirDate(episodeFilePath, airDateUtc.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(SeriesScannedEvent message)
|
|
||||||
{
|
|
||||||
if (_configService.FileDate == FileDateType.None)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var episodes = _episodeService.EpisodesWithFiles(message.Series.Id);
|
|
||||||
|
|
||||||
var episodeFiles = new List<EpisodeFile>();
|
|
||||||
var updated = new List<EpisodeFile>();
|
|
||||||
|
|
||||||
foreach (var group in episodes.GroupBy(e => e.EpisodeFileId))
|
|
||||||
{
|
|
||||||
var episodesInFile = group.Select(e => e).ToList();
|
|
||||||
var episodeFile = episodesInFile.First().EpisodeFile;
|
|
||||||
|
|
||||||
episodeFiles.Add(episodeFile);
|
|
||||||
|
|
||||||
if (ChangeFileDate(episodeFile, message.Series, episodesInFile))
|
|
||||||
{
|
|
||||||
updated.Add(episodeFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updated.Any())
|
|
||||||
{
|
|
||||||
_logger.ProgressDebug("Changed file date for {0} files of {1} in {2}", updated.Count, episodeFiles.Count, message.Series.Title);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
128
src/NzbDrone.Core/MediaFiles/UpdateTrackFileService.cs
Normal file
128
src/NzbDrone.Core/MediaFiles/UpdateTrackFileService.cs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles
|
||||||
|
{
|
||||||
|
public interface IUpdateTrackFileService
|
||||||
|
{
|
||||||
|
void ChangeFileDateForFile(TrackFile trackFile, Artist artist, List<Track> tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateTrackFileService : IUpdateTrackFileService,
|
||||||
|
IHandle<ArtistScannedEvent>
|
||||||
|
{
|
||||||
|
private readonly IDiskProvider _diskProvider;
|
||||||
|
private readonly IAlbumService _albumService;
|
||||||
|
private readonly IConfigService _configService;
|
||||||
|
private readonly ITrackService _trackService;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public UpdateTrackFileService(IDiskProvider diskProvider,
|
||||||
|
IConfigService configService,
|
||||||
|
ITrackService trackService,
|
||||||
|
IAlbumService albumService,
|
||||||
|
Logger logger)
|
||||||
|
{
|
||||||
|
_diskProvider = diskProvider;
|
||||||
|
_configService = configService;
|
||||||
|
_trackService = trackService;
|
||||||
|
_albumService = albumService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChangeFileDateForFile(TrackFile trackFile, Artist artist, List<Track> tracks)
|
||||||
|
{
|
||||||
|
ChangeFileDate(trackFile, artist, tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ChangeFileDate(TrackFile trackFile, Artist artist, List<Track> tracks)
|
||||||
|
{
|
||||||
|
var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath);
|
||||||
|
|
||||||
|
switch (_configService.FileDate)
|
||||||
|
{
|
||||||
|
case FileDateType.AlbumReleaseDate:
|
||||||
|
{
|
||||||
|
var album = _albumService.GetAlbum(trackFile.AlbumId);
|
||||||
|
|
||||||
|
if (!album.ReleaseDate.HasValue)
|
||||||
|
{
|
||||||
|
_logger.Debug("Could not create valid date to change file [{0}]", trackFilePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relDate = album.ReleaseDate.Value;
|
||||||
|
|
||||||
|
// avoiding false +ve checks and set date skewing by not using UTC (Windows)
|
||||||
|
DateTime oldDateTime = _diskProvider.FileGetLastWrite(trackFilePath);
|
||||||
|
|
||||||
|
if (!DateTime.Equals(relDate, oldDateTime))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_diskProvider.FileSetLastWriteTime(trackFilePath, relDate);
|
||||||
|
_logger.Debug("Date of file [{0}] changed from '{1}' to '{2}'", trackFilePath, oldDateTime, relDate);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn(ex, "Unable to set date of file [" + trackFilePath + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ArtistScannedEvent message)
|
||||||
|
{
|
||||||
|
if (_configService.FileDate == FileDateType.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var episodes = _trackService.TracksWithFiles(message.Artist.Id);
|
||||||
|
|
||||||
|
var trackFiles = new List<TrackFile>();
|
||||||
|
var updated = new List<TrackFile>();
|
||||||
|
|
||||||
|
foreach (var group in episodes.GroupBy(e => e.TrackFileId))
|
||||||
|
{
|
||||||
|
var tracksInFile = group.Select(e => e).ToList();
|
||||||
|
var trackFile = tracksInFile.First().TrackFile;
|
||||||
|
|
||||||
|
trackFiles.Add(trackFile);
|
||||||
|
|
||||||
|
if (ChangeFileDate(trackFile, message.Artist, tracksInFile))
|
||||||
|
{
|
||||||
|
updated.Add(trackFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated.Any())
|
||||||
|
{
|
||||||
|
_logger.ProgressDebug("Changed file date for {0} files of {1} in {2}", updated.Count, trackFiles.Count, message.Artist.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.ProgressDebug("No file dates changed for {0}", message.Artist.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
|
@ -9,7 +9,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
public interface IUpgradeMediaFiles
|
public interface IUpgradeMediaFiles
|
||||||
{
|
{
|
||||||
//EpisodeFileMoveResult UpgradeEpisodeFile(EpisodeFile episodeFile, LocalEpisode localEpisode, bool copyOnly = false);
|
|
||||||
TrackFileMoveResult UpgradeTrackFile(TrackFile trackFile, LocalTrack localTrack, bool copyOnly = false);
|
TrackFileMoveResult UpgradeTrackFile(TrackFile trackFile, LocalTrack localTrack, bool copyOnly = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +16,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||||
private readonly IMediaFileService _mediaFileService;
|
private readonly IMediaFileService _mediaFileService;
|
||||||
private readonly IMoveEpisodeFiles _episodeFileMover;
|
|
||||||
private readonly IMoveTrackFiles _trackFileMover;
|
private readonly IMoveTrackFiles _trackFileMover;
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
@ -46,13 +44,13 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
foreach (var existingFile in existingFiles)
|
foreach (var existingFile in existingFiles)
|
||||||
{
|
{
|
||||||
var file = existingFile.First();
|
var file = existingFile.First();
|
||||||
var episodeFilePath = Path.Combine(localTrack.Artist.Path, file.RelativePath);
|
var trackFilePath = Path.Combine(localTrack.Artist.Path, file.RelativePath);
|
||||||
var subfolder = _diskProvider.GetParentFolder(localTrack.Artist.Path).GetRelativePath(_diskProvider.GetParentFolder(episodeFilePath));
|
var subfolder = _diskProvider.GetParentFolder(localTrack.Artist.Path).GetRelativePath(_diskProvider.GetParentFolder(trackFilePath));
|
||||||
|
|
||||||
if (_diskProvider.FileExists(episodeFilePath))
|
if (_diskProvider.FileExists(trackFilePath))
|
||||||
{
|
{
|
||||||
_logger.Debug("Removing existing episode file: {0}", file);
|
_logger.Debug("Removing existing track file: {0}", file);
|
||||||
_recycleBinProvider.DeleteFile(episodeFilePath, subfolder);
|
_recycleBinProvider.DeleteFile(trackFilePath, subfolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
moveFileResult.OldFiles.Add(file);
|
moveFileResult.OldFiles.Add(file);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Tv.Commands
|
namespace NzbDrone.Core.Music.Commands
|
||||||
{
|
{
|
||||||
public class MoveSeriesCommand : Command
|
public class MoveArtistCommand : Command
|
||||||
{
|
{
|
||||||
public int SeriesId { get; set; }
|
public int ArtistId { get; set; }
|
||||||
public string SourcePath { get; set; }
|
public string SourcePath { get; set; }
|
||||||
public string DestinationPath { get; set; }
|
public string DestinationPath { get; set; }
|
||||||
public string DestinationRootFolder { get; set; }
|
public string DestinationRootFolder { get; set; }
|
|
@ -1,16 +1,16 @@
|
||||||
using NzbDrone.Common.Messaging;
|
using NzbDrone.Common.Messaging;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Tv.Events
|
namespace NzbDrone.Core.Music.Events
|
||||||
{
|
{
|
||||||
public class SeriesMovedEvent : IEvent
|
public class ArtistMovedEvent : IEvent
|
||||||
{
|
{
|
||||||
public Series Series { get; set; }
|
public Artist Artist { get; set; }
|
||||||
public string SourcePath { get; set; }
|
public string SourcePath { get; set; }
|
||||||
public string DestinationPath { get; set; }
|
public string DestinationPath { get; set; }
|
||||||
|
|
||||||
public SeriesMovedEvent(Series series, string sourcePath, string destinationPath)
|
public ArtistMovedEvent(Artist artist, string sourcePath, string destinationPath)
|
||||||
{
|
{
|
||||||
Series = series;
|
Artist = artist;
|
||||||
SourcePath = sourcePath;
|
SourcePath = sourcePath;
|
||||||
DestinationPath = destinationPath;
|
DestinationPath = destinationPath;
|
||||||
}
|
}
|
|
@ -6,45 +6,45 @@ using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Organizer;
|
using NzbDrone.Core.Organizer;
|
||||||
using NzbDrone.Core.Tv.Commands;
|
using NzbDrone.Core.Music.Commands;
|
||||||
using NzbDrone.Core.Tv.Events;
|
using NzbDrone.Core.Music.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Tv
|
namespace NzbDrone.Core.Music
|
||||||
{
|
{
|
||||||
public class MoveSeriesService : IExecute<MoveSeriesCommand>
|
public class MoveArtistService : IExecute<MoveArtistCommand>
|
||||||
{
|
{
|
||||||
private readonly ISeriesService _seriesService;
|
private readonly IArtistService _artistService;
|
||||||
private readonly IBuildFileNames _filenameBuilder;
|
private readonly IBuildFileNames _filenameBuilder;
|
||||||
private readonly IDiskTransferService _diskTransferService;
|
private readonly IDiskTransferService _diskTransferService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public MoveSeriesService(ISeriesService seriesService,
|
public MoveArtistService(IArtistService artistService,
|
||||||
IBuildFileNames filenameBuilder,
|
IBuildFileNames filenameBuilder,
|
||||||
IDiskTransferService diskTransferService,
|
IDiskTransferService diskTransferService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_seriesService = seriesService;
|
_artistService = artistService;
|
||||||
_filenameBuilder = filenameBuilder;
|
_filenameBuilder = filenameBuilder;
|
||||||
_diskTransferService = diskTransferService;
|
_diskTransferService = diskTransferService;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(MoveSeriesCommand message)
|
public void Execute(MoveArtistCommand message)
|
||||||
{
|
{
|
||||||
var series = _seriesService.GetSeries(message.SeriesId);
|
var artist = _artistService.GetArtist(message.ArtistId);
|
||||||
var source = message.SourcePath;
|
var source = message.SourcePath;
|
||||||
var destination = message.DestinationPath;
|
var destination = message.DestinationPath;
|
||||||
|
|
||||||
if (!message.DestinationRootFolder.IsNullOrWhiteSpace())
|
if (!message.DestinationRootFolder.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
_logger.Debug("Buiding destination path using root folder: {0} and the series title", message.DestinationRootFolder);
|
_logger.Debug("Buiding destination path using root folder: {0} and the artist name", message.DestinationRootFolder);
|
||||||
destination = Path.Combine(message.DestinationRootFolder, _filenameBuilder.GetSeriesFolder(series));
|
destination = Path.Combine(message.DestinationRootFolder, _filenameBuilder.GetArtistFolder(artist));
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.ProgressInfo("Moving {0} from '{1}' to '{2}'", series.Title, source, destination);
|
_logger.ProgressInfo("Moving {0} from '{1}' to '{2}'", artist.Name, source, destination);
|
||||||
|
|
||||||
//TODO: Move to transactional disk operations
|
//TODO: Move to transactional disk operations
|
||||||
try
|
try
|
||||||
|
@ -53,17 +53,17 @@ namespace NzbDrone.Core.Tv
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "Unable to move series from '{0}' to '{1}'", source, destination);
|
_logger.Error(ex, "Unable to move artist from '{0}' to '{1}'", source, destination);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.ProgressInfo("{0} moved successfully to {1}", series.Title, series.Path);
|
_logger.ProgressInfo("{0} moved successfully to {1}", artist.Name, artist.Path);
|
||||||
|
|
||||||
//Update the series path to the new path
|
//Update the artist path to the new path
|
||||||
series.Path = destination;
|
artist.Path = destination;
|
||||||
series = _seriesService.UpdateSeries(series);
|
artist = _artistService.UpdateArtist(artist);
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new SeriesMovedEvent(series, source, destination));
|
_eventAggregator.PublishEvent(new ArtistMovedEvent(artist, source, destination));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -295,6 +295,7 @@
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Datastore\Migration\105_rename_torrent_downloadstation.cs" />
|
<Compile Include="Datastore\Migration\105_rename_torrent_downloadstation.cs" />
|
||||||
|
<Compile Include="Datastore\Migration\114_remove_tv_naming.cs" />
|
||||||
<Compile Include="Datastore\Migration\113_music_blacklist.cs" />
|
<Compile Include="Datastore\Migration\113_music_blacklist.cs" />
|
||||||
<Compile Include="Datastore\Migration\112_music_history.cs" />
|
<Compile Include="Datastore\Migration\112_music_history.cs" />
|
||||||
<Compile Include="Datastore\Migration\111_setup_music.cs" />
|
<Compile Include="Datastore\Migration\111_setup_music.cs" />
|
||||||
|
@ -731,7 +732,10 @@
|
||||||
<Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" />
|
<Compile Include="MediaFiles\Commands\CleanUpRecycleBinCommand.cs" />
|
||||||
<Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" />
|
<Compile Include="MediaFiles\Commands\DownloadedEpisodesScanCommand.cs" />
|
||||||
<Compile Include="MediaFiles\Commands\RenameArtistCommand.cs" />
|
<Compile Include="MediaFiles\Commands\RenameArtistCommand.cs" />
|
||||||
|
<Compile Include="MediaFiles\Events\EpisodeFolderCreatedEvent.cs" />
|
||||||
|
<Compile Include="MediaFiles\Events\TrackFolderCreatedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\TrackDownloadedEvent.cs" />
|
<Compile Include="MediaFiles\Events\TrackDownloadedEvent.cs" />
|
||||||
|
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatter.cs" />
|
||||||
<Compile Include="MediaFiles\RenameTrackFilePreview.cs" />
|
<Compile Include="MediaFiles\RenameTrackFilePreview.cs" />
|
||||||
<Compile Include="MediaFiles\RenameTrackFileService.cs" />
|
<Compile Include="MediaFiles\RenameTrackFileService.cs" />
|
||||||
<Compile Include="MediaFiles\TrackFileMovingService.cs" />
|
<Compile Include="MediaFiles\TrackFileMovingService.cs" />
|
||||||
|
@ -749,8 +753,6 @@
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="MediaFiles\DownloadedEpisodesCommandService.cs" />
|
<Compile Include="MediaFiles\DownloadedEpisodesCommandService.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeFile.cs" />
|
<Compile Include="MediaFiles\EpisodeFile.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeFileMoveResult.cs" />
|
|
||||||
<Compile Include="MediaFiles\EpisodeFileMovingService.cs" />
|
|
||||||
<Compile Include="MediaFiles\EpisodeImport\IImportDecisionEngineSpecification.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\IImportDecisionEngineSpecification.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeImport\ImportApprovedEpisodes.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\ImportApprovedEpisodes.cs" />
|
||||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMaker.cs" />
|
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMaker.cs" />
|
||||||
|
@ -771,7 +773,6 @@
|
||||||
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
|
<Compile Include="MediaFiles\Events\EpisodeDownloadedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\EpisodeFileAddedEvent.cs" />
|
<Compile Include="MediaFiles\Events\EpisodeFileAddedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\EpisodeFileDeletedEvent.cs" />
|
<Compile Include="MediaFiles\Events\EpisodeFileDeletedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Events\EpisodeFolderCreatedEvent.cs" />
|
|
||||||
<Compile Include="MediaFiles\Events\EpisodeImportedEvent.cs" />
|
<Compile Include="MediaFiles\Events\EpisodeImportedEvent.cs" />
|
||||||
<Compile Include="MediaFiles\Commands\RescanArtistCommand.cs" />
|
<Compile Include="MediaFiles\Commands\RescanArtistCommand.cs" />
|
||||||
<Compile Include="MediaFiles\Events\SeriesRenamedEvent.cs" />
|
<Compile Include="MediaFiles\Events\SeriesRenamedEvent.cs" />
|
||||||
|
@ -793,15 +794,13 @@
|
||||||
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoService.cs" />
|
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoService.cs" />
|
||||||
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
|
<Compile Include="MediaFiles\MediaInfo\VideoFileInfoReader.cs" />
|
||||||
<Compile Include="MediaFiles\RecycleBinProvider.cs" />
|
<Compile Include="MediaFiles\RecycleBinProvider.cs" />
|
||||||
<Compile Include="MediaFiles\RenameEpisodeFilePreview.cs" />
|
|
||||||
<Compile Include="MediaFiles\RenameEpisodeFileService.cs" />
|
|
||||||
<Compile Include="MediaFiles\SameFilenameException.cs" />
|
<Compile Include="MediaFiles\SameFilenameException.cs" />
|
||||||
<Compile Include="MediaFiles\TrackFile.cs" />
|
<Compile Include="MediaFiles\TrackFile.cs" />
|
||||||
<Compile Include="MediaFiles\TrackImport\ImportApprovedTracks.cs" />
|
<Compile Include="MediaFiles\TrackImport\ImportApprovedTracks.cs" />
|
||||||
<Compile Include="MediaFiles\TrackImport\ImportDecision.cs" />
|
<Compile Include="MediaFiles\TrackImport\ImportDecision.cs" />
|
||||||
<Compile Include="MediaFiles\TrackImport\ImportResult.cs" />
|
<Compile Include="MediaFiles\TrackImport\ImportResult.cs" />
|
||||||
<Compile Include="MediaFiles\TrackImport\Specifications\SameTracksImportSpecification.cs" />
|
<Compile Include="MediaFiles\TrackImport\Specifications\SameTracksImportSpecification.cs" />
|
||||||
<Compile Include="MediaFiles\UpdateEpisodeFileService.cs" />
|
<Compile Include="MediaFiles\UpdateTrackFileService.cs" />
|
||||||
<Compile Include="MediaFiles\UpgradeMediaFileService.cs" />
|
<Compile Include="MediaFiles\UpgradeMediaFileService.cs" />
|
||||||
<Compile Include="Messaging\Commands\BackendCommandAttribute.cs" />
|
<Compile Include="Messaging\Commands\BackendCommandAttribute.cs" />
|
||||||
<Compile Include="Messaging\Commands\CleanupCommandMessagingService.cs" />
|
<Compile Include="Messaging\Commands\CleanupCommandMessagingService.cs" />
|
||||||
|
@ -828,6 +827,7 @@
|
||||||
<Compile Include="Messaging\IProcessMessage.cs" />
|
<Compile Include="Messaging\IProcessMessage.cs" />
|
||||||
<Compile Include="MetadataSource\IProvideArtistInfo.cs" />
|
<Compile Include="MetadataSource\IProvideArtistInfo.cs" />
|
||||||
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
||||||
|
<Compile Include="MetadataSource\ISearchForNewArtist.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ActorResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ActorResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\AlbumResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\AlbumResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ArtistInfoResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ArtistInfoResource.cs" />
|
||||||
|
@ -861,9 +861,11 @@
|
||||||
<Compile Include="Extras\Metadata\MetadataRepository.cs" />
|
<Compile Include="Extras\Metadata\MetadataRepository.cs" />
|
||||||
<Compile Include="Extras\Metadata\MetadataService.cs" />
|
<Compile Include="Extras\Metadata\MetadataService.cs" />
|
||||||
<Compile Include="Extras\Metadata\MetadataType.cs" />
|
<Compile Include="Extras\Metadata\MetadataType.cs" />
|
||||||
<Compile Include="MetadataSource\ISearchForNewArtist.cs" />
|
<Compile Include="Music\Commands\MoveArtistCommand.cs" />
|
||||||
|
<Compile Include="Music\Events\ArtistMovedEvent.cs" />
|
||||||
<Compile Include="Music\MonitoringOptions.cs" />
|
<Compile Include="Music\MonitoringOptions.cs" />
|
||||||
<Compile Include="Music\AlbumMonitoredService.cs" />
|
<Compile Include="Music\AlbumMonitoredService.cs" />
|
||||||
|
<Compile Include="Music\MoveArtistService.cs" />
|
||||||
<Compile Include="Music\TrackMonitoredService.cs" />
|
<Compile Include="Music\TrackMonitoredService.cs" />
|
||||||
<Compile Include="Music\Member.cs" />
|
<Compile Include="Music\Member.cs" />
|
||||||
<Compile Include="Music\AddArtistOptions.cs" />
|
<Compile Include="Music\AddArtistOptions.cs" />
|
||||||
|
@ -945,6 +947,8 @@
|
||||||
<Compile Include="Notifications\Webhook\WebhookService.cs" />
|
<Compile Include="Notifications\Webhook\WebhookService.cs" />
|
||||||
<Compile Include="Notifications\Webhook\WebhookSettings.cs" />
|
<Compile Include="Notifications\Webhook\WebhookSettings.cs" />
|
||||||
<Compile Include="Notifications\Webhook\Webhook.cs" />
|
<Compile Include="Notifications\Webhook\Webhook.cs" />
|
||||||
|
<Compile Include="Organizer\AbsoluteTrackFormat.cs" />
|
||||||
|
<Compile Include="Organizer\TrackFormat.cs" />
|
||||||
<Compile Include="Organizer\NamingConfigRepository.cs" />
|
<Compile Include="Organizer\NamingConfigRepository.cs" />
|
||||||
<Compile Include="Notifications\Twitter\Twitter.cs" />
|
<Compile Include="Notifications\Twitter\Twitter.cs" />
|
||||||
<Compile Include="Notifications\Twitter\TwitterService.cs" />
|
<Compile Include="Notifications\Twitter\TwitterService.cs" />
|
||||||
|
@ -1055,9 +1059,7 @@
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Notifications\Xbmc\XbmcSettings.cs" />
|
<Compile Include="Notifications\Xbmc\XbmcSettings.cs" />
|
||||||
<Compile Include="Organizer\AbsoluteEpisodeFormat.cs" />
|
|
||||||
<Compile Include="Organizer\BasicNamingConfig.cs" />
|
<Compile Include="Organizer\BasicNamingConfig.cs" />
|
||||||
<Compile Include="Organizer\EpisodeFormat.cs" />
|
|
||||||
<Compile Include="Organizer\EpisodeSortingType.cs" />
|
<Compile Include="Organizer\EpisodeSortingType.cs" />
|
||||||
<Compile Include="Organizer\Exception.cs" />
|
<Compile Include="Organizer\Exception.cs" />
|
||||||
<Compile Include="Organizer\FileNameBuilder.cs" />
|
<Compile Include="Organizer\FileNameBuilder.cs" />
|
||||||
|
@ -1143,7 +1145,6 @@
|
||||||
<Compile Include="Tv\AddSeriesOptions.cs" />
|
<Compile Include="Tv\AddSeriesOptions.cs" />
|
||||||
<Compile Include="Tv\AddSeriesService.cs" />
|
<Compile Include="Tv\AddSeriesService.cs" />
|
||||||
<Compile Include="Tv\AddSeriesValidator.cs" />
|
<Compile Include="Tv\AddSeriesValidator.cs" />
|
||||||
<Compile Include="Tv\Commands\MoveSeriesCommand.cs" />
|
|
||||||
<Compile Include="Tv\Commands\RefreshSeriesCommand.cs" />
|
<Compile Include="Tv\Commands\RefreshSeriesCommand.cs" />
|
||||||
<Compile Include="Tv\Episode.cs" />
|
<Compile Include="Tv\Episode.cs" />
|
||||||
<Compile Include="Tv\EpisodeAddedService.cs" />
|
<Compile Include="Tv\EpisodeAddedService.cs" />
|
||||||
|
@ -1157,11 +1158,9 @@
|
||||||
<Compile Include="Tv\Events\SeriesAddedEvent.cs" />
|
<Compile Include="Tv\Events\SeriesAddedEvent.cs" />
|
||||||
<Compile Include="Tv\Events\SeriesDeletedEvent.cs" />
|
<Compile Include="Tv\Events\SeriesDeletedEvent.cs" />
|
||||||
<Compile Include="Tv\Events\SeriesEditedEvent.cs" />
|
<Compile Include="Tv\Events\SeriesEditedEvent.cs" />
|
||||||
<Compile Include="Tv\Events\SeriesMovedEvent.cs" />
|
|
||||||
<Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" />
|
<Compile Include="Tv\Events\SeriesRefreshStartingEvent.cs" />
|
||||||
<Compile Include="Tv\Events\SeriesUpdatedEvent.cs" />
|
<Compile Include="Tv\Events\SeriesUpdatedEvent.cs" />
|
||||||
<Compile Include="Tv\MonitoringOptions.cs" />
|
<Compile Include="Tv\MonitoringOptions.cs" />
|
||||||
<Compile Include="Tv\MoveSeriesService.cs" />
|
|
||||||
<Compile Include="Tv\Ratings.cs" />
|
<Compile Include="Tv\Ratings.cs" />
|
||||||
<Compile Include="Tv\RefreshEpisodeService.cs" />
|
<Compile Include="Tv\RefreshEpisodeService.cs" />
|
||||||
<Compile Include="Tv\RefreshSeriesService.cs" />
|
<Compile Include="Tv\RefreshSeriesService.cs" />
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace NzbDrone.Core.Organizer
|
|
||||||
{
|
|
||||||
public class EpisodeFormat
|
|
||||||
{
|
|
||||||
public string Separator { get; set; }
|
|
||||||
public string EpisodePattern { get; set; }
|
|
||||||
public string EpisodeSeparator { get; set; }
|
|
||||||
public string SeasonEpisodePattern { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
9
src/NzbDrone.Core/Organizer/AbsoluteTrackFormat.cs
Normal file
9
src/NzbDrone.Core/Organizer/AbsoluteTrackFormat.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace NzbDrone.Core.Organizer
|
||||||
|
{
|
||||||
|
public class TrackFormat
|
||||||
|
{
|
||||||
|
public string Separator { get; set; }
|
||||||
|
public string TrackPattern { get; set; }
|
||||||
|
public string TrackSeparator { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
{
|
{
|
||||||
public class BasicNamingConfig
|
public class BasicNamingConfig
|
||||||
{
|
{
|
||||||
public bool IncludeSeriesTitle { get; set; }
|
public bool IncludeArtistName { get; set; }
|
||||||
public bool IncludeEpisodeTitle { get; set; }
|
public bool IncludeAlbumTitle { get; set; }
|
||||||
public bool IncludeQuality { get; set; }
|
public bool IncludeQuality { get; set; }
|
||||||
public bool ReplaceSpaces { get; set; }
|
public bool ReplaceSpaces { get; set; }
|
||||||
public string Separator { get; set; }
|
public string Separator { get; set; }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -9,6 +9,7 @@ using NzbDrone.Common.Cache;
|
||||||
using NzbDrone.Common.EnsureThat;
|
using NzbDrone.Common.EnsureThat;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
@ -17,28 +18,20 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
public interface IBuildFileNames
|
public interface IBuildFileNames
|
||||||
{
|
{
|
||||||
string BuildFileName(List<Episode> episodes, Series series, EpisodeFile episodeFile, NamingConfig namingConfig = null);
|
|
||||||
string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null);
|
string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null);
|
||||||
string BuildFilePath(Series series, int seasonNumber, string fileName, string extension);
|
|
||||||
string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension);
|
string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension);
|
||||||
string BuildSeasonPath(Series series, int seasonNumber);
|
|
||||||
string BuildAlbumPath(Artist artist, Album album);
|
string BuildAlbumPath(Artist artist, Album album);
|
||||||
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
||||||
string GetSeriesFolder(Series series, NamingConfig namingConfig = null);
|
|
||||||
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
|
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
|
||||||
string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null);
|
string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null);
|
||||||
string GetSeasonFolder(Series series, int seasonNumber, NamingConfig namingConfig = null);
|
|
||||||
|
|
||||||
// TODO: Implement Music functions
|
|
||||||
//string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FileNameBuilder : IBuildFileNames
|
public class FileNameBuilder : IBuildFileNames
|
||||||
{
|
{
|
||||||
private readonly INamingConfigService _namingConfigService;
|
private readonly INamingConfigService _namingConfigService;
|
||||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
private readonly IQualityDefinitionService _qualityDefinitionService;
|
||||||
private readonly ICached<EpisodeFormat[]> _episodeFormatCache;
|
private readonly ICached<TrackFormat[]> _trackFormatCache;
|
||||||
private readonly ICached<AbsoluteEpisodeFormat[]> _absoluteEpisodeFormatCache;
|
private readonly ICached<AbsoluteTrackFormat[]> _absoluteTrackFormatCache;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private static readonly Regex TitleRegex = new Regex(@"\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9]+))?(?<suffix>[- ._)\]]*)\}",
|
private static readonly Regex TitleRegex = new Regex(@"\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9]+))?(?<suffix>[- ._)\]]*)\}",
|
||||||
|
@ -91,69 +84,11 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
_namingConfigService = namingConfigService;
|
_namingConfigService = namingConfigService;
|
||||||
_qualityDefinitionService = qualityDefinitionService;
|
_qualityDefinitionService = qualityDefinitionService;
|
||||||
_episodeFormatCache = cacheManager.GetCache<EpisodeFormat[]>(GetType(), "episodeFormat");
|
_trackFormatCache = cacheManager.GetCache<TrackFormat[]>(GetType(), "trackFormat");
|
||||||
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
|
_absoluteTrackFormatCache = cacheManager.GetCache<AbsoluteTrackFormat[]>(GetType(), "absoluteTrackFormat");
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BuildFileName(List<Episode> episodes, Series series, EpisodeFile episodeFile, NamingConfig namingConfig = null)
|
|
||||||
{
|
|
||||||
if (namingConfig == null)
|
|
||||||
{
|
|
||||||
namingConfig = _namingConfigService.GetConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!namingConfig.RenameEpisodes)
|
|
||||||
{
|
|
||||||
return GetOriginalTitle(episodeFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (namingConfig.StandardEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Standard)
|
|
||||||
{
|
|
||||||
throw new NamingFormatException("Standard episode format cannot be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (namingConfig.DailyEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Daily)
|
|
||||||
{
|
|
||||||
throw new NamingFormatException("Daily episode format cannot be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (namingConfig.AnimeEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Anime)
|
|
||||||
{
|
|
||||||
throw new NamingFormatException("Anime episode format cannot be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
var pattern = namingConfig.StandardEpisodeFormat;
|
|
||||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
|
||||||
|
|
||||||
episodes = episodes.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber).ToList();
|
|
||||||
|
|
||||||
if (series.SeriesType == SeriesTypes.Daily)
|
|
||||||
{
|
|
||||||
pattern = namingConfig.DailyEpisodeFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (series.SeriesType == SeriesTypes.Anime && episodes.All(e => e.AbsoluteEpisodeNumber.HasValue))
|
|
||||||
{
|
|
||||||
pattern = namingConfig.AnimeEpisodeFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern = AddSeasonEpisodeNumberingTokens(pattern, tokenHandlers, episodes, namingConfig);
|
|
||||||
pattern = AddAbsoluteNumberingTokens(pattern, tokenHandlers, series, episodes, namingConfig);
|
|
||||||
|
|
||||||
AddSeriesTokens(tokenHandlers, series);
|
|
||||||
AddEpisodeTokens(tokenHandlers, episodes);
|
|
||||||
AddEpisodeFileTokens(tokenHandlers, episodeFile);
|
|
||||||
AddQualityTokens(tokenHandlers, series, episodeFile);
|
|
||||||
AddMediaInfoTokens(tokenHandlers, episodeFile);
|
|
||||||
|
|
||||||
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
|
||||||
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
|
||||||
fileName = TrimSeparatorsRegex.Replace(fileName, string.Empty);
|
|
||||||
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null)
|
public string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null)
|
||||||
{
|
{
|
||||||
if (namingConfig == null)
|
if (namingConfig == null)
|
||||||
|
@ -176,8 +111,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
tracks = tracks.OrderBy(e => e.AlbumId).ThenBy(e => e.TrackNumber).ToList();
|
tracks = tracks.OrderBy(e => e.AlbumId).ThenBy(e => e.TrackNumber).ToList();
|
||||||
|
|
||||||
//pattern = AddSeasonEpisodeNumberingTokens(pattern, tokenHandlers, episodes, namingConfig);
|
|
||||||
|
|
||||||
pattern = FormatTrackNumberTokens(pattern, "", tracks);
|
pattern = FormatTrackNumberTokens(pattern, "", tracks);
|
||||||
//pattern = AddAbsoluteNumberingTokens(pattern, tokenHandlers, series, episodes, namingConfig);
|
//pattern = AddAbsoluteNumberingTokens(pattern, tokenHandlers, series, episodes, namingConfig);
|
||||||
|
|
||||||
|
@ -186,7 +119,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
AddTrackTokens(tokenHandlers, tracks);
|
AddTrackTokens(tokenHandlers, tracks);
|
||||||
AddTrackFileTokens(tokenHandlers, trackFile);
|
AddTrackFileTokens(tokenHandlers, trackFile);
|
||||||
AddQualityTokens(tokenHandlers, artist, trackFile);
|
AddQualityTokens(tokenHandlers, artist, trackFile);
|
||||||
//AddMediaInfoTokens(tokenHandlers, trackFile); TODO ReWork MediaInfo for Tracks
|
AddMediaInfoTokens(tokenHandlers, trackFile);
|
||||||
|
|
||||||
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
||||||
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
||||||
|
@ -195,15 +128,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
|
|
||||||
{
|
|
||||||
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
|
||||||
|
|
||||||
var path = BuildSeasonPath(series, seasonNumber);
|
|
||||||
|
|
||||||
return Path.Combine(path, fileName + extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension)
|
public string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension)
|
||||||
{
|
{
|
||||||
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
||||||
|
@ -213,29 +137,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return Path.Combine(path, fileName + extension);
|
return Path.Combine(path, fileName + extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BuildSeasonPath(Series series, int seasonNumber)
|
|
||||||
{
|
|
||||||
var path = series.Path;
|
|
||||||
|
|
||||||
if (series.SeasonFolder)
|
|
||||||
{
|
|
||||||
if (seasonNumber == 0)
|
|
||||||
{
|
|
||||||
path = Path.Combine(path, "Specials");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var seasonFolder = GetSeasonFolder(series, seasonNumber);
|
|
||||||
|
|
||||||
seasonFolder = CleanFileName(seasonFolder);
|
|
||||||
|
|
||||||
path = Path.Combine(path, seasonFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BuildAlbumPath(Artist artist, Album album)
|
public string BuildAlbumPath(Artist artist, Album album)
|
||||||
{
|
{
|
||||||
var path = artist.Path;
|
var path = artist.Path;
|
||||||
|
@ -256,20 +157,19 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)
|
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)
|
||||||
{
|
{
|
||||||
var episodeFormat = GetEpisodeFormat(nameSpec.StandardEpisodeFormat).LastOrDefault();
|
var trackFormat = GetTrackFormat(nameSpec.StandardTrackFormat).LastOrDefault();
|
||||||
|
|
||||||
if (episodeFormat == null)
|
if (trackFormat == null)
|
||||||
{
|
{
|
||||||
return new BasicNamingConfig();
|
return new BasicNamingConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
var basicNamingConfig = new BasicNamingConfig
|
var basicNamingConfig = new BasicNamingConfig
|
||||||
{
|
{
|
||||||
Separator = episodeFormat.Separator,
|
Separator = trackFormat.Separator
|
||||||
NumberStyle = episodeFormat.SeasonEpisodePattern
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var titleTokens = TitleRegex.Matches(nameSpec.StandardEpisodeFormat);
|
var titleTokens = TitleRegex.Matches(nameSpec.StandardTrackFormat);
|
||||||
|
|
||||||
foreach (Match match in titleTokens)
|
foreach (Match match in titleTokens)
|
||||||
{
|
{
|
||||||
|
@ -281,14 +181,14 @@ namespace NzbDrone.Core.Organizer
|
||||||
basicNamingConfig.ReplaceSpaces = true;
|
basicNamingConfig.ReplaceSpaces = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.StartsWith("{Series", StringComparison.InvariantCultureIgnoreCase))
|
if (token.StartsWith("{Artist", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
basicNamingConfig.IncludeSeriesTitle = true;
|
basicNamingConfig.IncludeArtistName = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.StartsWith("{Episode", StringComparison.InvariantCultureIgnoreCase))
|
if (token.StartsWith("{Album", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
basicNamingConfig.IncludeEpisodeTitle = true;
|
basicNamingConfig.IncludeAlbumTitle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.StartsWith("{Quality", StringComparison.InvariantCultureIgnoreCase))
|
if (token.StartsWith("{Quality", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
@ -300,20 +200,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return basicNamingConfig;
|
return basicNamingConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetSeriesFolder(Series series, NamingConfig namingConfig = null)
|
|
||||||
{
|
|
||||||
if (namingConfig == null)
|
|
||||||
{
|
|
||||||
namingConfig = _namingConfigService.GetConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
|
||||||
|
|
||||||
AddSeriesTokens(tokenHandlers, series);
|
|
||||||
|
|
||||||
return CleanFolderName(ReplaceTokens(namingConfig.SeriesFolderFormat, tokenHandlers, namingConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetArtistFolder(Artist artist, NamingConfig namingConfig = null)
|
public string GetArtistFolder(Artist artist, NamingConfig namingConfig = null)
|
||||||
{
|
{
|
||||||
if (namingConfig == null)
|
if (namingConfig == null)
|
||||||
|
@ -328,21 +214,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return CleanFolderName(ReplaceTokens(namingConfig.ArtistFolderFormat, tokenHandlers, namingConfig));
|
return CleanFolderName(ReplaceTokens(namingConfig.ArtistFolderFormat, tokenHandlers, namingConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetSeasonFolder(Series series, int seasonNumber, NamingConfig namingConfig = null)
|
|
||||||
{
|
|
||||||
if (namingConfig == null)
|
|
||||||
{
|
|
||||||
namingConfig = _namingConfigService.GetConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
|
||||||
|
|
||||||
AddSeriesTokens(tokenHandlers, series);
|
|
||||||
AddSeasonTokens(tokenHandlers, seasonNumber);
|
|
||||||
|
|
||||||
return CleanFolderName(ReplaceTokens(namingConfig.SeasonFolderFormat, tokenHandlers, namingConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null)
|
public string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null)
|
||||||
{
|
{
|
||||||
if (namingConfig == null)
|
if (namingConfig == null)
|
||||||
|
@ -387,12 +258,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return name.Trim(' ', '.');
|
return name.Trim(' ', '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddSeriesTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series)
|
|
||||||
{
|
|
||||||
tokenHandlers["{Series Title}"] = m => series.Title;
|
|
||||||
tokenHandlers["{Series CleanTitle}"] = m => CleanTitle(series.Title);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddArtistTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Artist artist)
|
private void AddArtistTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Artist artist)
|
||||||
{
|
{
|
||||||
tokenHandlers["{Artist Name}"] = m => artist.Name;
|
tokenHandlers["{Artist Name}"] = m => artist.Name;
|
||||||
|
@ -413,163 +278,12 @@ namespace NzbDrone.Core.Organizer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string AddSeasonEpisodeNumberingTokens(string pattern, Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Episode> episodes, NamingConfig namingConfig)
|
|
||||||
{
|
|
||||||
var episodeFormats = GetEpisodeFormat(pattern).DistinctBy(v => v.SeasonEpisodePattern).ToList();
|
|
||||||
|
|
||||||
int index = 1;
|
|
||||||
foreach (var episodeFormat in episodeFormats)
|
|
||||||
{
|
|
||||||
var seasonEpisodePattern = episodeFormat.SeasonEpisodePattern;
|
|
||||||
string formatPattern;
|
|
||||||
|
|
||||||
switch ((MultiEpisodeStyle)namingConfig.MultiEpisodeStyle)
|
|
||||||
{
|
|
||||||
case MultiEpisodeStyle.Duplicate:
|
|
||||||
formatPattern = episodeFormat.Separator + episodeFormat.SeasonEpisodePattern;
|
|
||||||
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Repeat:
|
|
||||||
formatPattern = episodeFormat.EpisodeSeparator + episodeFormat.EpisodePattern;
|
|
||||||
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Scene:
|
|
||||||
formatPattern = "-" + episodeFormat.EpisodeSeparator + episodeFormat.EpisodePattern;
|
|
||||||
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Range:
|
|
||||||
formatPattern = "-" + episodeFormat.EpisodePattern;
|
|
||||||
seasonEpisodePattern = FormatRangeNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.PrefixedRange:
|
|
||||||
formatPattern = "-" + episodeFormat.EpisodeSeparator + episodeFormat.EpisodePattern;
|
|
||||||
seasonEpisodePattern = FormatRangeNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//MultiEpisodeStyle.Extend
|
|
||||||
default:
|
|
||||||
formatPattern = "-" + episodeFormat.EpisodePattern;
|
|
||||||
seasonEpisodePattern = FormatNumberTokens(seasonEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var token = string.Format("{{Season Episode{0}}}", index++);
|
|
||||||
pattern = pattern.Replace(episodeFormat.SeasonEpisodePattern, token);
|
|
||||||
tokenHandlers[token] = m => seasonEpisodePattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddSeasonTokens(tokenHandlers, episodes.First().SeasonNumber);
|
|
||||||
|
|
||||||
if (episodes.Count > 1)
|
|
||||||
{
|
|
||||||
tokenHandlers["{Episode}"] = m => episodes.First().EpisodeNumber.ToString(m.CustomFormat) + "-" + episodes.Last().EpisodeNumber.ToString(m.CustomFormat);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tokenHandlers["{Episode}"] = m => episodes.First().EpisodeNumber.ToString(m.CustomFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string AddAbsoluteNumberingTokens(string pattern, Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series, List<Episode> episodes, NamingConfig namingConfig)
|
|
||||||
{
|
|
||||||
var absoluteEpisodeFormats = GetAbsoluteFormat(pattern).DistinctBy(v => v.AbsoluteEpisodePattern).ToList();
|
|
||||||
|
|
||||||
int index = 1;
|
|
||||||
foreach (var absoluteEpisodeFormat in absoluteEpisodeFormats)
|
|
||||||
{
|
|
||||||
if (series.SeriesType != SeriesTypes.Anime)
|
|
||||||
{
|
|
||||||
pattern = pattern.Replace(absoluteEpisodeFormat.AbsoluteEpisodePattern, "");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var absoluteEpisodePattern = absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
|
||||||
string formatPattern;
|
|
||||||
|
|
||||||
switch ((MultiEpisodeStyle) namingConfig.MultiEpisodeStyle)
|
|
||||||
{
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Duplicate:
|
|
||||||
formatPattern = absoluteEpisodeFormat.Separator + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
|
||||||
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Repeat:
|
|
||||||
var repeatSeparator = absoluteEpisodeFormat.Separator.Trim().IsNullOrWhiteSpace() ? " " : absoluteEpisodeFormat.Separator.Trim();
|
|
||||||
|
|
||||||
formatPattern = repeatSeparator + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
|
||||||
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Scene:
|
|
||||||
formatPattern = "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
|
||||||
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MultiEpisodeStyle.Range:
|
|
||||||
case MultiEpisodeStyle.PrefixedRange:
|
|
||||||
formatPattern = "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
|
||||||
var eps = new List<Episode> {episodes.First()};
|
|
||||||
|
|
||||||
if (episodes.Count > 1) eps.Add(episodes.Last());
|
|
||||||
|
|
||||||
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, eps);
|
|
||||||
break;
|
|
||||||
|
|
||||||
//MultiEpisodeStyle.Extend
|
|
||||||
default:
|
|
||||||
formatPattern = "-" + absoluteEpisodeFormat.AbsoluteEpisodePattern;
|
|
||||||
absoluteEpisodePattern = FormatAbsoluteNumberTokens(absoluteEpisodePattern, formatPattern, episodes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var token = string.Format("{{Absolute Pattern{0}}}", index++);
|
|
||||||
pattern = pattern.Replace(absoluteEpisodeFormat.AbsoluteEpisodePattern, token);
|
|
||||||
tokenHandlers[token] = m => absoluteEpisodePattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddSeasonTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, int seasonNumber)
|
|
||||||
{
|
|
||||||
tokenHandlers["{Season}"] = m => seasonNumber.ToString(m.CustomFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddEpisodeTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Episode> episodes)
|
|
||||||
{
|
|
||||||
if (!episodes.First().AirDate.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
tokenHandlers["{Air Date}"] = m => episodes.First().AirDate.Replace('-', ' ');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tokenHandlers["{Air Date}"] = m => "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenHandlers["{Episode Title}"] = m => GetEpisodeTitle(episodes, "+");
|
|
||||||
tokenHandlers["{Episode CleanTitle}"] = m => CleanTitle(GetEpisodeTitle(episodes, "and"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddTrackTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Track> tracks)
|
private void AddTrackTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Track> tracks)
|
||||||
{
|
{
|
||||||
tokenHandlers["{Track Title}"] = m => GetTrackTitle(tracks, "+");
|
tokenHandlers["{Track Title}"] = m => GetTrackTitle(tracks, "+");
|
||||||
tokenHandlers["{Track CleanTitle}"] = m => CleanTitle(GetTrackTitle(tracks, "and"));
|
tokenHandlers["{Track CleanTitle}"] = m => CleanTitle(GetTrackTitle(tracks, "and"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddEpisodeFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, EpisodeFile episodeFile)
|
|
||||||
{
|
|
||||||
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile);
|
|
||||||
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(episodeFile);
|
|
||||||
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Lidarr");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddTrackFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, TrackFile trackFile)
|
private void AddTrackFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, TrackFile trackFile)
|
||||||
{
|
{
|
||||||
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(trackFile);
|
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(trackFile);
|
||||||
|
@ -601,79 +315,20 @@ namespace NzbDrone.Core.Organizer
|
||||||
//tokenHandlers["{Quality Real}"] = m => qualityReal;
|
//tokenHandlers["{Quality Real}"] = m => qualityReal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, EpisodeFile episodeFile)
|
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, TrackFile trackFile)
|
||||||
{
|
{
|
||||||
if (episodeFile.MediaInfo == null) return;
|
if (trackFile.MediaInfo == null)
|
||||||
|
|
||||||
string videoCodec;
|
|
||||||
switch (episodeFile.MediaInfo.VideoCodec)
|
|
||||||
{
|
{
|
||||||
case "AVC":
|
return;
|
||||||
if (episodeFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(episodeFile.SceneName).Contains("h264"))
|
|
||||||
{
|
|
||||||
videoCodec = "h264";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
videoCodec = "x264";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "V_MPEGH/ISO/HEVC":
|
|
||||||
if (episodeFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(episodeFile.SceneName).Contains("h265"))
|
|
||||||
{
|
|
||||||
videoCodec = "h265";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
videoCodec = "x265";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "MPEG-2 Video":
|
|
||||||
videoCodec = "MPEG2";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
videoCodec = episodeFile.MediaInfo.VideoCodec;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string audioCodec;
|
var audioCodec = MediaInfoFormatter.FormatAudioCodec(trackFile.MediaInfo);
|
||||||
switch (episodeFile.MediaInfo.AudioFormat)
|
var audioChannels = MediaInfoFormatter.FormatAudioChannels(trackFile.MediaInfo);
|
||||||
{
|
|
||||||
case "AC-3":
|
|
||||||
audioCodec = "AC3";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "E-AC-3":
|
var mediaInfoAudioLanguages = GetLanguagesToken(trackFile.MediaInfo.AudioLanguages);
|
||||||
audioCodec = "EAC3";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "MPEG Audio":
|
|
||||||
if (episodeFile.MediaInfo.AudioProfile == "Layer 3")
|
|
||||||
{
|
|
||||||
audioCodec = "MP3";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
audioCodec = episodeFile.MediaInfo.AudioFormat;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "DTS":
|
|
||||||
audioCodec = episodeFile.MediaInfo.AudioFormat;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
audioCodec = episodeFile.MediaInfo.AudioFormat;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mediaInfoAudioLanguages = GetLanguagesToken(episodeFile.MediaInfo.AudioLanguages);
|
|
||||||
if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
|
if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
mediaInfoAudioLanguages = string.Format("[{0}]", mediaInfoAudioLanguages);
|
mediaInfoAudioLanguages = $"[{mediaInfoAudioLanguages}]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaInfoAudioLanguages == "[EN]")
|
if (mediaInfoAudioLanguages == "[EN]")
|
||||||
|
@ -681,28 +336,24 @@ namespace NzbDrone.Core.Organizer
|
||||||
mediaInfoAudioLanguages = string.Empty;
|
mediaInfoAudioLanguages = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mediaInfoSubtitleLanguages = GetLanguagesToken(episodeFile.MediaInfo.Subtitles);
|
var mediaInfoSubtitleLanguages = GetLanguagesToken(trackFile.MediaInfo.Subtitles);
|
||||||
if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
|
if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
mediaInfoSubtitleLanguages = string.Format("[{0}]", mediaInfoSubtitleLanguages);
|
mediaInfoSubtitleLanguages = $"[{mediaInfoSubtitleLanguages}]";
|
||||||
}
|
}
|
||||||
|
|
||||||
var videoBitDepth = episodeFile.MediaInfo.VideoBitDepth > 0 ? episodeFile.MediaInfo.VideoBitDepth.ToString() : string.Empty;
|
var videoBitDepth = trackFile.MediaInfo.VideoBitDepth > 0 ? trackFile.MediaInfo.VideoBitDepth.ToString() : string.Empty;
|
||||||
var audioChannels = episodeFile.MediaInfo.FormattedAudioChannels > 0 ?
|
var audioChannelsFormatted = audioChannels > 0 ?
|
||||||
episodeFile.MediaInfo.FormattedAudioChannels.ToString("F1", CultureInfo.InvariantCulture) :
|
audioChannels.ToString("F1", CultureInfo.InvariantCulture) :
|
||||||
string.Empty;
|
string.Empty;
|
||||||
|
|
||||||
tokenHandlers["{MediaInfo Video}"] = m => videoCodec;
|
|
||||||
tokenHandlers["{MediaInfo VideoCodec}"] = m => videoCodec;
|
|
||||||
tokenHandlers["{MediaInfo VideoBitDepth}"] = m => videoBitDepth;
|
|
||||||
|
|
||||||
tokenHandlers["{MediaInfo Audio}"] = m => audioCodec;
|
tokenHandlers["{MediaInfo Audio}"] = m => audioCodec;
|
||||||
tokenHandlers["{MediaInfo AudioCodec}"] = m => audioCodec;
|
tokenHandlers["{MediaInfo AudioCodec}"] = m => audioCodec;
|
||||||
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannels;
|
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannelsFormatted;
|
||||||
|
|
||||||
tokenHandlers["{MediaInfo Simple}"] = m => string.Format("{0} {1}", videoCodec, audioCodec);
|
tokenHandlers["{MediaInfo Simple}"] = m => $"{audioCodec}";
|
||||||
|
|
||||||
tokenHandlers["{MediaInfo Full}"] = m => string.Format("{0} {1}{2} {3}", videoCodec, audioCodec, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
|
tokenHandlers["{MediaInfo Full}"] = m => $"{audioCodec}{mediaInfoAudioLanguages} {mediaInfoSubtitleLanguages}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetLanguagesToken(string mediaInfoLanguages)
|
private string GetLanguagesToken(string mediaInfoLanguages)
|
||||||
|
@ -782,20 +433,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return replacementText;
|
return replacementText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatNumberTokens(string basePattern, string formatPattern, List<Episode> episodes)
|
|
||||||
{
|
|
||||||
var pattern = string.Empty;
|
|
||||||
|
|
||||||
for (int i = 0; i < episodes.Count; i++)
|
|
||||||
{
|
|
||||||
var patternToReplace = i == 0 ? basePattern : formatPattern;
|
|
||||||
|
|
||||||
pattern += EpisodeRegex.Replace(patternToReplace, match => ReplaceNumberToken(match.Groups["episode"].Value, episodes[i].EpisodeNumber));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReplaceSeasonTokens(pattern, episodes.First().SeasonNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string FormatTrackNumberTokens(string basePattern, string formatPattern, List<Track> tracks)
|
private string FormatTrackNumberTokens(string basePattern, string formatPattern, List<Track> tracks)
|
||||||
{
|
{
|
||||||
var pattern = string.Empty;
|
var pattern = string.Empty;
|
||||||
|
@ -810,34 +447,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatAbsoluteNumberTokens(string basePattern, string formatPattern, List<Episode> episodes)
|
|
||||||
{
|
|
||||||
var pattern = string.Empty;
|
|
||||||
|
|
||||||
for (int i = 0; i < episodes.Count; i++)
|
|
||||||
{
|
|
||||||
var patternToReplace = i == 0 ? basePattern : formatPattern;
|
|
||||||
|
|
||||||
pattern += AbsoluteEpisodeRegex.Replace(patternToReplace, match => ReplaceNumberToken(match.Groups["absolute"].Value, episodes[i].AbsoluteEpisodeNumber.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReplaceSeasonTokens(pattern, episodes.First().SeasonNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string FormatRangeNumberTokens(string seasonEpisodePattern, string formatPattern, List<Episode> episodes)
|
|
||||||
{
|
|
||||||
var eps = new List<Episode> { episodes.First() };
|
|
||||||
|
|
||||||
if (episodes.Count > 1) eps.Add(episodes.Last());
|
|
||||||
|
|
||||||
return FormatNumberTokens(seasonEpisodePattern, formatPattern, eps);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ReplaceSeasonTokens(string pattern, int seasonNumber)
|
|
||||||
{
|
|
||||||
return SeasonRegex.Replace(pattern, match => ReplaceNumberToken(match.Groups["season"].Value, seasonNumber));
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ReplaceNumberToken(string token, int value)
|
private string ReplaceNumberToken(string token, int value)
|
||||||
{
|
{
|
||||||
var split = token.Trim('{', '}').Split(':');
|
var split = token.Trim('{', '}').Split(':');
|
||||||
|
@ -846,52 +455,27 @@ namespace NzbDrone.Core.Organizer
|
||||||
return value.ToString(split[1]);
|
return value.ToString(split[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EpisodeFormat[] GetEpisodeFormat(string pattern)
|
private TrackFormat[] GetTrackFormat(string pattern)
|
||||||
{
|
{
|
||||||
return _episodeFormatCache.Get(pattern, () => SeasonEpisodePatternRegex.Matches(pattern).OfType<Match>()
|
return _trackFormatCache.Get(pattern, () => SeasonEpisodePatternRegex.Matches(pattern).OfType<Match>()
|
||||||
.Select(match => new EpisodeFormat
|
.Select(match => new TrackFormat
|
||||||
{
|
{
|
||||||
EpisodeSeparator = match.Groups["episodeSeparator"].Value,
|
TrackSeparator = match.Groups["episodeSeparator"].Value,
|
||||||
Separator = match.Groups["separator"].Value,
|
Separator = match.Groups["separator"].Value,
|
||||||
EpisodePattern = match.Groups["episode"].Value,
|
TrackPattern = match.Groups["episode"].Value,
|
||||||
SeasonEpisodePattern = match.Groups["seasonEpisode"].Value,
|
|
||||||
}).ToArray());
|
}).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private AbsoluteEpisodeFormat[] GetAbsoluteFormat(string pattern)
|
private AbsoluteTrackFormat[] GetAbsoluteFormat(string pattern)
|
||||||
{
|
{
|
||||||
return _absoluteEpisodeFormatCache.Get(pattern, () => AbsoluteEpisodePatternRegex.Matches(pattern).OfType<Match>()
|
return _absoluteTrackFormatCache.Get(pattern, () => AbsoluteEpisodePatternRegex.Matches(pattern).OfType<Match>()
|
||||||
.Select(match => new AbsoluteEpisodeFormat
|
.Select(match => new AbsoluteTrackFormat
|
||||||
{
|
{
|
||||||
Separator = match.Groups["separator"].Value.IsNotNullOrWhiteSpace() ? match.Groups["separator"].Value : "-",
|
Separator = match.Groups["separator"].Value.IsNotNullOrWhiteSpace() ? match.Groups["separator"].Value : "-",
|
||||||
AbsoluteEpisodePattern = match.Groups["absolute"].Value
|
AbsoluteTrackPattern = match.Groups["absolute"].Value
|
||||||
}).ToArray());
|
}).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetEpisodeTitle(List<Episode> episodes, string separator)
|
|
||||||
{
|
|
||||||
separator = string.Format(" {0} ", separator.Trim());
|
|
||||||
|
|
||||||
if (episodes.Count == 1)
|
|
||||||
{
|
|
||||||
return episodes.First().Title.TrimEnd(EpisodeTitleTrimCharacters);
|
|
||||||
}
|
|
||||||
|
|
||||||
var titles = episodes.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters))
|
|
||||||
.Select(CleanupEpisodeTitle)
|
|
||||||
.Distinct()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (titles.All(t => t.IsNullOrWhiteSpace()))
|
|
||||||
{
|
|
||||||
titles = episodes.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters))
|
|
||||||
.Distinct()
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Join(separator, titles);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetTrackTitle(List<Track> tracks, string separator)
|
private string GetTrackTitle(List<Track> tracks, string separator)
|
||||||
{
|
{
|
||||||
separator = string.Format(" {0} ", separator.Trim());
|
separator = string.Format(" {0} ", separator.Trim());
|
||||||
|
@ -902,7 +486,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
}
|
}
|
||||||
|
|
||||||
var titles = tracks.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters))
|
var titles = tracks.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters))
|
||||||
.Select(CleanupEpisodeTitle)
|
.Select(CleanupTrackTitle)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
@ -916,7 +500,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
return string.Join(separator, titles);
|
return string.Join(separator, titles);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CleanupEpisodeTitle(string title)
|
private string CleanupTrackTitle(string title)
|
||||||
{
|
{
|
||||||
//this will remove (1),(2) from the end of multi part episodes.
|
//this will remove (1),(2) from the end of multi part episodes.
|
||||||
return MultiPartCleanupRegex.Replace(title, string.Empty).Trim();
|
return MultiPartCleanupRegex.Replace(title, string.Empty).Trim();
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
|
|
||||||
|
@ -9,14 +8,8 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
public interface IFilenameSampleService
|
public interface IFilenameSampleService
|
||||||
{
|
{
|
||||||
SampleResult GetStandardSample(NamingConfig nameSpec);
|
|
||||||
SampleResult GetStandardTrackSample(NamingConfig nameSpec);
|
SampleResult GetStandardTrackSample(NamingConfig nameSpec);
|
||||||
SampleResult GetMultiEpisodeSample(NamingConfig nameSpec);
|
|
||||||
SampleResult GetDailySample(NamingConfig nameSpec);
|
|
||||||
SampleResult GetAnimeSample(NamingConfig nameSpec);
|
|
||||||
SampleResult GetAnimeMultiEpisodeSample(NamingConfig nameSpec);
|
|
||||||
string GetSeriesFolderSample(NamingConfig nameSpec);
|
|
||||||
string GetSeasonFolderSample(NamingConfig nameSpec);
|
|
||||||
string GetArtistFolderSample(NamingConfig nameSpec);
|
string GetArtistFolderSample(NamingConfig nameSpec);
|
||||||
string GetAlbumFolderSample(NamingConfig nameSpec);
|
string GetAlbumFolderSample(NamingConfig nameSpec);
|
||||||
}
|
}
|
||||||
|
@ -24,35 +17,17 @@ namespace NzbDrone.Core.Organizer
|
||||||
public class FileNameSampleService : IFilenameSampleService
|
public class FileNameSampleService : IFilenameSampleService
|
||||||
{
|
{
|
||||||
private readonly IBuildFileNames _buildFileNames;
|
private readonly IBuildFileNames _buildFileNames;
|
||||||
private static Series _standardSeries;
|
|
||||||
private static Artist _standardArtist;
|
private static Artist _standardArtist;
|
||||||
private static Album _standardAlbum;
|
private static Album _standardAlbum;
|
||||||
private static Track _track1;
|
private static Track _track1;
|
||||||
private static Series _dailySeries;
|
|
||||||
private static Series _animeSeries;
|
|
||||||
private static Episode _episode1;
|
|
||||||
private static Episode _episode2;
|
|
||||||
private static Episode _episode3;
|
|
||||||
private static List<Episode> _singleEpisode;
|
|
||||||
private static List<Track> _singleTrack;
|
private static List<Track> _singleTrack;
|
||||||
private static List<Episode> _multiEpisodes;
|
|
||||||
private static EpisodeFile _singleEpisodeFile;
|
|
||||||
private static TrackFile _singleTrackFile;
|
private static TrackFile _singleTrackFile;
|
||||||
private static EpisodeFile _multiEpisodeFile;
|
|
||||||
private static EpisodeFile _dailyEpisodeFile;
|
|
||||||
private static EpisodeFile _animeEpisodeFile;
|
|
||||||
private static EpisodeFile _animeMultiEpisodeFile;
|
|
||||||
|
|
||||||
public FileNameSampleService(IBuildFileNames buildFileNames)
|
public FileNameSampleService(IBuildFileNames buildFileNames)
|
||||||
{
|
{
|
||||||
_buildFileNames = buildFileNames;
|
_buildFileNames = buildFileNames;
|
||||||
|
|
||||||
_standardSeries = new Series
|
|
||||||
{
|
|
||||||
SeriesType = SeriesTypes.Standard,
|
|
||||||
Title = "Series Title (2010)"
|
|
||||||
};
|
|
||||||
|
|
||||||
_standardArtist = new Artist
|
_standardArtist = new Artist
|
||||||
{
|
{
|
||||||
Name = "Artist Name"
|
Name = "Artist Name"
|
||||||
|
@ -64,18 +39,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
ReleaseDate = System.DateTime.Today
|
ReleaseDate = System.DateTime.Today
|
||||||
};
|
};
|
||||||
|
|
||||||
_dailySeries = new Series
|
|
||||||
{
|
|
||||||
SeriesType = SeriesTypes.Daily,
|
|
||||||
Title = "Series Title (2010)"
|
|
||||||
};
|
|
||||||
|
|
||||||
_animeSeries = new Series
|
|
||||||
{
|
|
||||||
SeriesType = SeriesTypes.Anime,
|
|
||||||
Title = "Series Title (2010)"
|
|
||||||
};
|
|
||||||
|
|
||||||
_track1 = new Track
|
_track1 = new Track
|
||||||
{
|
{
|
||||||
TrackNumber = 3,
|
TrackNumber = 3,
|
||||||
|
@ -84,66 +47,19 @@ namespace NzbDrone.Core.Organizer
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_episode1 = new Episode
|
|
||||||
{
|
|
||||||
SeasonNumber = 1,
|
|
||||||
EpisodeNumber = 1,
|
|
||||||
Title = "Episode Title (1)",
|
|
||||||
AirDate = "2013-10-30",
|
|
||||||
AbsoluteEpisodeNumber = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
_episode2 = new Episode
|
|
||||||
{
|
|
||||||
SeasonNumber = 1,
|
|
||||||
EpisodeNumber = 2,
|
|
||||||
Title = "Episode Title (2)",
|
|
||||||
AbsoluteEpisodeNumber = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
_episode3 = new Episode
|
|
||||||
{
|
|
||||||
SeasonNumber = 1,
|
|
||||||
EpisodeNumber = 3,
|
|
||||||
Title = "Episode Title (3)",
|
|
||||||
AbsoluteEpisodeNumber = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
_singleEpisode = new List<Episode> { _episode1 };
|
|
||||||
_singleTrack = new List<Track> { _track1 };
|
_singleTrack = new List<Track> { _track1 };
|
||||||
_multiEpisodes = new List<Episode> { _episode1, _episode2, _episode3 };
|
|
||||||
|
|
||||||
var mediaInfo = new MediaInfoModel()
|
var mediaInfo = new MediaInfoModel()
|
||||||
{
|
{
|
||||||
VideoCodec = "AVC",
|
VideoCodec = "AVC",
|
||||||
VideoBitDepth = 8,
|
VideoBitDepth = 8,
|
||||||
AudioFormat = "DTS",
|
AudioFormat = "FLAC",
|
||||||
AudioChannels = 6,
|
AudioChannels = 6,
|
||||||
AudioChannelPositions = "3/2/0.1",
|
AudioChannelPositions = "3/2/0.1",
|
||||||
AudioLanguages = "English",
|
AudioLanguages = "English",
|
||||||
Subtitles = "English/German"
|
Subtitles = "English/German"
|
||||||
};
|
};
|
||||||
|
|
||||||
var mediaInfoAnime = new MediaInfoModel()
|
|
||||||
{
|
|
||||||
VideoCodec = "AVC",
|
|
||||||
VideoBitDepth = 8,
|
|
||||||
AudioFormat = "DTS",
|
|
||||||
AudioChannels = 6,
|
|
||||||
AudioChannelPositions = "3/2/0.1",
|
|
||||||
AudioLanguages = "Japanese",
|
|
||||||
Subtitles = "Japanese/English"
|
|
||||||
};
|
|
||||||
|
|
||||||
_singleEpisodeFile = new EpisodeFile
|
|
||||||
{
|
|
||||||
Quality = new QualityModel(Quality.MP3_256, new Revision(2)),
|
|
||||||
RelativePath = "Series.Title.S01E01.720p.HDTV.x264-EVOLVE.mkv",
|
|
||||||
SceneName = "Series.Title.S01E01.720p.HDTV.x264-EVOLVE",
|
|
||||||
ReleaseGroup = "RlsGrp",
|
|
||||||
MediaInfo = mediaInfo
|
|
||||||
};
|
|
||||||
|
|
||||||
_singleTrackFile = new TrackFile
|
_singleTrackFile = new TrackFile
|
||||||
{
|
{
|
||||||
Quality = new QualityModel(Quality.MP3_256, new Revision(2)),
|
Quality = new QualityModel(Quality.MP3_256, new Revision(2)),
|
||||||
|
@ -153,54 +69,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
MediaInfo = mediaInfo
|
MediaInfo = mediaInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
_multiEpisodeFile = new EpisodeFile
|
|
||||||
{
|
|
||||||
Quality = new QualityModel(Quality.MP3_256, new Revision(2)),
|
|
||||||
RelativePath = "Series.Title.S01E01-E03.720p.HDTV.x264-EVOLVE.mkv",
|
|
||||||
SceneName = "Series.Title.S01E01-E03.720p.HDTV.x264-EVOLVE",
|
|
||||||
ReleaseGroup = "RlsGrp",
|
|
||||||
MediaInfo = mediaInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
_dailyEpisodeFile = new EpisodeFile
|
|
||||||
{
|
|
||||||
Quality = new QualityModel(Quality.MP3_256, new Revision(2)),
|
|
||||||
RelativePath = "Series.Title.2013.10.30.HDTV.x264-EVOLVE.mkv",
|
|
||||||
SceneName = "Series.Title.2013.10.30.HDTV.x264-EVOLVE",
|
|
||||||
ReleaseGroup = "RlsGrp",
|
|
||||||
MediaInfo = mediaInfo
|
|
||||||
};
|
|
||||||
|
|
||||||
_animeEpisodeFile = new EpisodeFile
|
|
||||||
{
|
|
||||||
Quality = new QualityModel(Quality.MP3_256, new Revision(2)),
|
|
||||||
RelativePath = "[RlsGroup] Series Title - 001 [720p].mkv",
|
|
||||||
SceneName = "[RlsGroup] Series Title - 001 [720p]",
|
|
||||||
ReleaseGroup = "RlsGrp",
|
|
||||||
MediaInfo = mediaInfoAnime
|
|
||||||
};
|
|
||||||
|
|
||||||
_animeMultiEpisodeFile = new EpisodeFile
|
|
||||||
{
|
|
||||||
Quality = new QualityModel(Quality.MP3_256, new Revision(2)),
|
|
||||||
RelativePath = "[RlsGroup] Series Title - 001 - 103 [720p].mkv",
|
|
||||||
SceneName = "[RlsGroup] Series Title - 001 - 103 [720p]",
|
|
||||||
ReleaseGroup = "RlsGrp",
|
|
||||||
MediaInfo = mediaInfoAnime
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public SampleResult GetStandardSample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
var result = new SampleResult
|
|
||||||
{
|
|
||||||
FileName = BuildSample(_singleEpisode, _standardSeries, _singleEpisodeFile, nameSpec),
|
|
||||||
Series = _standardSeries,
|
|
||||||
Episodes = _singleEpisode,
|
|
||||||
EpisodeFile = _singleEpisodeFile
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SampleResult GetStandardTrackSample(NamingConfig nameSpec)
|
public SampleResult GetStandardTrackSample(NamingConfig nameSpec)
|
||||||
|
@ -217,68 +85,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SampleResult GetMultiEpisodeSample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
var result = new SampleResult
|
|
||||||
{
|
|
||||||
FileName = BuildSample(_multiEpisodes, _standardSeries, _multiEpisodeFile, nameSpec),
|
|
||||||
Series = _standardSeries,
|
|
||||||
Episodes = _multiEpisodes,
|
|
||||||
EpisodeFile = _multiEpisodeFile
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SampleResult GetDailySample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
var result = new SampleResult
|
|
||||||
{
|
|
||||||
FileName = BuildSample(_singleEpisode, _dailySeries, _dailyEpisodeFile, nameSpec),
|
|
||||||
Series = _dailySeries,
|
|
||||||
Episodes = _singleEpisode,
|
|
||||||
EpisodeFile = _dailyEpisodeFile
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SampleResult GetAnimeSample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
var result = new SampleResult
|
|
||||||
{
|
|
||||||
FileName = BuildSample(_singleEpisode, _animeSeries, _animeEpisodeFile, nameSpec),
|
|
||||||
Series = _animeSeries,
|
|
||||||
Episodes = _singleEpisode,
|
|
||||||
EpisodeFile = _animeEpisodeFile
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SampleResult GetAnimeMultiEpisodeSample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
var result = new SampleResult
|
|
||||||
{
|
|
||||||
FileName = BuildSample(_multiEpisodes, _animeSeries, _animeMultiEpisodeFile, nameSpec),
|
|
||||||
Series = _animeSeries,
|
|
||||||
Episodes = _multiEpisodes,
|
|
||||||
EpisodeFile = _animeMultiEpisodeFile
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetSeriesFolderSample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
return _buildFileNames.GetSeriesFolder(_standardSeries, nameSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetSeasonFolderSample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
return _buildFileNames.GetSeasonFolder(_standardSeries, _episode1.SeasonNumber, nameSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetArtistFolderSample(NamingConfig nameSpec)
|
public string GetArtistFolderSample(NamingConfig nameSpec)
|
||||||
{
|
{
|
||||||
return _buildFileNames.GetArtistFolder(_standardArtist, nameSpec);
|
return _buildFileNames.GetArtistFolder(_standardArtist, nameSpec);
|
||||||
|
@ -289,18 +95,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return _buildFileNames.GetAlbumFolder(_standardArtist, _standardAlbum, nameSpec);
|
return _buildFileNames.GetAlbumFolder(_standardArtist, _standardAlbum, nameSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string BuildSample(List<Episode> episodes, Series series, EpisodeFile episodeFile, NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _buildFileNames.BuildFileName(episodes, series, episodeFile, nameSpec);
|
|
||||||
}
|
|
||||||
catch (NamingFormatException)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string BuildTrackSample(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig nameSpec)
|
private string BuildTrackSample(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig nameSpec)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Organizer
|
namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
|
@ -6,30 +6,16 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
public static NamingConfig Default => new NamingConfig
|
public static NamingConfig Default => new NamingConfig
|
||||||
{
|
{
|
||||||
RenameEpisodes = false,
|
|
||||||
RenameTracks = false,
|
RenameTracks = false,
|
||||||
ReplaceIllegalCharacters = true,
|
ReplaceIllegalCharacters = true,
|
||||||
MultiEpisodeStyle = 0,
|
|
||||||
StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}",
|
|
||||||
StandardTrackFormat = "{Artist Name} - {track:00} - {Album Title} - {Track Title}",
|
StandardTrackFormat = "{Artist Name} - {track:00} - {Album Title} - {Track Title}",
|
||||||
DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title} {Quality Full}",
|
|
||||||
AnimeEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}",
|
|
||||||
SeriesFolderFormat = "{Series Title}",
|
|
||||||
SeasonFolderFormat = "Season {season}",
|
|
||||||
ArtistFolderFormat = "{Artist Name}",
|
ArtistFolderFormat = "{Artist Name}",
|
||||||
AlbumFolderFormat = "{Album Title} ({Release Year})"
|
AlbumFolderFormat = "{Album Title} ({Release Year})"
|
||||||
};
|
};
|
||||||
|
|
||||||
public bool RenameEpisodes { get; set; }
|
|
||||||
public bool RenameTracks { get; set; }
|
public bool RenameTracks { get; set; }
|
||||||
public bool ReplaceIllegalCharacters { get; set; }
|
public bool ReplaceIllegalCharacters { get; set; }
|
||||||
public int MultiEpisodeStyle { get; set; }
|
|
||||||
public string StandardEpisodeFormat { get; set; }
|
|
||||||
public string StandardTrackFormat { get; set; }
|
public string StandardTrackFormat { get; set; }
|
||||||
public string DailyEpisodeFormat { get; set; }
|
|
||||||
public string AnimeEpisodeFormat { get; set; }
|
|
||||||
public string SeriesFolderFormat { get; set; }
|
|
||||||
public string SeasonFolderFormat { get; set; }
|
|
||||||
public string ArtistFolderFormat { get; set; }
|
public string ArtistFolderFormat { get; set; }
|
||||||
public string AlbumFolderFormat { get; set; }
|
public string AlbumFolderFormat { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
namespace NzbDrone.Core.Organizer
|
namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
public class AbsoluteEpisodeFormat
|
public class AbsoluteTrackFormat
|
||||||
{
|
{
|
||||||
public string Separator { get; set; }
|
public string Separator { get; set; }
|
||||||
public string AbsoluteEpisodePattern { get; set; }
|
public string AbsoluteTrackPattern { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -47,8 +47,8 @@ namespace NzbDrone.Core.Tv
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(newSeries.Path))
|
if (string.IsNullOrWhiteSpace(newSeries.Path))
|
||||||
{
|
{
|
||||||
var folderName = _fileNameBuilder.GetSeriesFolder(newSeries);
|
//var folderName = _fileNameBuilder.GetSeriesFolder(newSeries);
|
||||||
newSeries.Path = Path.Combine(newSeries.RootFolderPath, folderName);
|
//newSeries.Path = Path.Combine(newSeries.RootFolderPath, folderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
newSeries.CleanTitle = newSeries.Title.CleanSeriesTitle();
|
newSeries.CleanTitle = newSeries.Title.CleanSeriesTitle();
|
||||||
|
|
|
@ -25,100 +25,65 @@ namespace NzbDrone.Integration.Test.ApiTests
|
||||||
public void should_be_able_to_update()
|
public void should_be_able_to_update()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = false;
|
config.RenameTracks = false;
|
||||||
config.StandardEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}";
|
config.StandardTrackFormat = "{Artist Name} - {track:00} - {Album Title}";
|
||||||
config.DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title}";
|
|
||||||
config.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}";
|
|
||||||
|
|
||||||
var result = NamingConfig.Put(config);
|
var result = NamingConfig.Put(config);
|
||||||
result.RenameEpisodes.Should().BeFalse();
|
result.RenameTracks.Should().BeFalse();
|
||||||
result.StandardEpisodeFormat.Should().Be(config.StandardEpisodeFormat);
|
result.StandardTrackFormat.Should().Be(config.StandardTrackFormat);
|
||||||
result.DailyEpisodeFormat.Should().Be(config.DailyEpisodeFormat);
|
|
||||||
result.AnimeEpisodeFormat.Should().Be(config.AnimeEpisodeFormat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_get_bad_request_if_standard_format_is_empty()
|
public void should_get_bad_request_if_standard_format_is_empty()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = true;
|
config.RenameTracks = true;
|
||||||
config.StandardEpisodeFormat = "";
|
config.StandardTrackFormat = "";
|
||||||
config.DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title}";
|
|
||||||
config.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}";
|
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
errors.Should().NotBeNull();
|
errors.Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_get_bad_request_if_standard_format_doesnt_contain_season_and_episode()
|
public void should_get_bad_request_if_standard_format_doesnt_contain_track_number_and_title()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = true;
|
config.RenameTracks = true;
|
||||||
config.StandardEpisodeFormat = "{season}";
|
config.StandardTrackFormat = "{track:00}";
|
||||||
config.DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title}";
|
|
||||||
config.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}";
|
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
errors.Should().NotBeNull();
|
errors.Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_get_bad_request_if_daily_format_doesnt_contain_season_and_episode_or_air_date()
|
public void should_not_require_format_when_rename_tracks_is_false()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = true;
|
config.RenameTracks = false;
|
||||||
config.StandardEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}";
|
config.StandardTrackFormat = "";
|
||||||
config.DailyEpisodeFormat = "{Series Title} - {season} - {Episode Title}";
|
|
||||||
config.AnimeEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}";
|
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
errors.Should().NotBeNull();
|
errors.Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_get_bad_request_if_anime_format_doesnt_contain_season_and_episode_or_absolute()
|
public void should_require_format_when_rename_tracks_is_true()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = false;
|
config.RenameTracks = true;
|
||||||
config.StandardEpisodeFormat = "{Series Title} - {season}x{episode:00} - {Episode Title}";
|
config.StandardTrackFormat = "";
|
||||||
config.DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title}";
|
|
||||||
config.AnimeEpisodeFormat = "{Series Title} - {season} - {Episode Title}";
|
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
errors.Should().NotBeNull();
|
errors.Should().NotBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_require_format_when_rename_episodes_is_false()
|
public void should_get_bad_request_if_artist_folder_format_does_not_contain_artist_name()
|
||||||
{
|
{
|
||||||
var config = NamingConfig.GetSingle();
|
var config = NamingConfig.GetSingle();
|
||||||
config.RenameEpisodes = false;
|
config.RenameTracks = true;
|
||||||
config.StandardEpisodeFormat = "";
|
config.ArtistFolderFormat = "This and That";
|
||||||
config.DailyEpisodeFormat = "";
|
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
|
||||||
errors.Should().NotBeNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_require_format_when_rename_episodes_is_true()
|
|
||||||
{
|
|
||||||
var config = NamingConfig.GetSingle();
|
|
||||||
config.RenameEpisodes = true;
|
|
||||||
config.StandardEpisodeFormat = "";
|
|
||||||
config.DailyEpisodeFormat = "";
|
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
|
||||||
errors.Should().NotBeNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_get_bad_request_if_series_folder_format_does_not_contain_series_title()
|
|
||||||
{
|
|
||||||
var config = NamingConfig.GetSingle();
|
|
||||||
config.RenameEpisodes = true;
|
|
||||||
config.SeriesFolderFormat = "This and That";
|
|
||||||
|
|
||||||
var errors = NamingConfig.InvalidPut(config);
|
var errors = NamingConfig.InvalidPut(config);
|
||||||
errors.Should().NotBeNull();
|
errors.Should().NotBeNull();
|
||||||
|
|
|
@ -161,7 +161,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
command : {
|
command : {
|
||||||
name : 'albumSearch',
|
name : 'albumSearch',
|
||||||
artistId : this.artist.id,
|
artistId : this.artist.id,
|
||||||
albumIds : [this.model.get('id')]
|
albumIds : [this.model.get('id')]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
command : {
|
command : {
|
||||||
name : 'renameFiles',
|
name : 'renameFiles',
|
||||||
artistId : this.artist.id,
|
artistId : this.artist.id,
|
||||||
albumId : this.model.get('id')
|
albumId : this.model.get('id')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -86,8 +86,8 @@ module.exports = Marionette.Layout.extend({
|
||||||
element : this.ui.rename,
|
element : this.ui.rename,
|
||||||
command : {
|
command : {
|
||||||
name : 'renameFiles',
|
name : 'renameFiles',
|
||||||
seriesId : this.model.id,
|
artistId : this.model.id,
|
||||||
seasonNumber : -1
|
albumId : -1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,8 +14,8 @@ module.exports = Marionette.ItemView.extend({
|
||||||
initialize : function(options) {
|
initialize : function(options) {
|
||||||
this.artist = options.artist;
|
this.artist = options.artist;
|
||||||
this.templateHelpers = {
|
this.templateHelpers = {
|
||||||
numberOfArtist : this.artist.length,
|
numberOfArtists : this.artist.length,
|
||||||
artist : new Backbone.Collection(this.artist).toJSON()
|
artist : new Backbone.Collection(this.artist).toJSON()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h3>Organize of Selected Series</h3>
|
<h3>Organize of Selected Artist(s)</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body update-files-artist-modal">
|
<div class="modal-body update-files-artist-modal">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
|
@ -9,12 +9,12 @@
|
||||||
Tip: To preview a rename... select "Cancel" then any artist title and use the <i data-original-title="" class="icon-lidarr-rename" title=""></i>
|
Tip: To preview a rename... select "Cancel" then any artist title and use the <i data-original-title="" class="icon-lidarr-rename" title=""></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Are you sure you want to update all files in the {{numberOfSeries}} selected artist?
|
Are you sure you want to update all files in the {{numberOfArtists}} selected artist(s)?
|
||||||
|
|
||||||
{{debug}}
|
{{debug}}
|
||||||
<ul class="selected-artist">
|
<ul class="selected-artist">
|
||||||
{{#each series}}
|
{{#each artist}}
|
||||||
<li>{{title}}</li>
|
<li>{{name}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,25 +8,25 @@ module.exports = Backbone.Collection.extend({
|
||||||
originalFetch : Backbone.Collection.prototype.fetch,
|
originalFetch : Backbone.Collection.prototype.fetch,
|
||||||
|
|
||||||
initialize : function(options) {
|
initialize : function(options) {
|
||||||
if (!options.seriesId) {
|
if (!options.artistId) {
|
||||||
throw 'seriesId is required';
|
throw 'artistId is required';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.seriesId = options.seriesId;
|
this.artistId = options.artistId;
|
||||||
this.seasonNumber = options.seasonNumber;
|
this.albumId = options.albumId;
|
||||||
},
|
},
|
||||||
|
|
||||||
fetch : function(options) {
|
fetch : function(options) {
|
||||||
if (!this.seriesId) {
|
if (!this.artistId) {
|
||||||
throw 'seriesId is required';
|
throw 'artistId is required';
|
||||||
}
|
}
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.data = {};
|
options.data = {};
|
||||||
options.data.seriesId = this.seriesId;
|
options.data.artistId = this.artistId;
|
||||||
|
|
||||||
if (this.seasonNumber !== undefined) {
|
if (this.albumId !== undefined) {
|
||||||
options.data.seasonNumber = this.seasonNumber;
|
options.data.albumId = this.albumId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.originalFetch.call(this, options);
|
return this.originalFetch.call(this, options);
|
||||||
|
|
|
@ -7,9 +7,10 @@ module.exports = Marionette.ItemView.extend({
|
||||||
|
|
||||||
templateHelpers : function() {
|
templateHelpers : function() {
|
||||||
var type = this.model.get('seriesType');
|
var type = this.model.get('seriesType');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rename : this.naming.get('renameEpisodes'),
|
rename : this.naming.get('renameTracks'),
|
||||||
format : this.naming.get(type + 'EpisodeFormat')
|
format : this.naming.get('standardTrackFormat')
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,12 @@ module.exports = Marionette.Layout.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize : function(options) {
|
initialize : function(options) {
|
||||||
this.model = options.series;
|
this.model = options.artist;
|
||||||
this.seasonNumber = options.seasonNumber;
|
this.albumId = options.albumId;
|
||||||
|
|
||||||
var viewOptions = {};
|
var viewOptions = {};
|
||||||
viewOptions.seriesId = this.model.id;
|
viewOptions.artistId = this.model.id;
|
||||||
viewOptions.seasonNumber = this.seasonNumber;
|
viewOptions.albumId = this.albumId;
|
||||||
|
|
||||||
this.collection = new RenamePreviewCollection(viewOptions);
|
this.collection = new RenamePreviewCollection(viewOptions);
|
||||||
this.listenTo(this.collection, 'sync', this._showPreviews);
|
this.listenTo(this.collection, 'sync', this._showPreviews);
|
||||||
|
@ -66,7 +66,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
var files = _.map(this.collection.where({ rename : true }), function(model) {
|
var files = _.map(this.collection.where({ rename : true }), function(model) {
|
||||||
return model.get('episodeFileId');
|
return model.get('trackFileId');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
|
@ -74,18 +74,18 @@ module.exports = Marionette.Layout.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.seasonNumber) {
|
if (this.albumId) {
|
||||||
CommandController.Execute('renameFiles', {
|
CommandController.Execute('renameFiles', {
|
||||||
name : 'renameFiles',
|
name : 'renameFiles',
|
||||||
seriesId : this.model.id,
|
artistId : this.model.id,
|
||||||
seasonNumber : this.seasonNumber,
|
albumId : this.albumId,
|
||||||
files : files
|
files : files
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
CommandController.Execute('renameFiles', {
|
CommandController.Execute('renameFiles', {
|
||||||
name : 'renameFiles',
|
name : 'renameFiles',
|
||||||
seriesId : this.model.id,
|
artistId : this.model.id,
|
||||||
seasonNumber : -1,
|
albumId : -1,
|
||||||
files : files
|
files : files
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,11 +74,10 @@
|
||||||
<i class="icon-lidarr-form-info" title="Change file date on import/rescan"/>
|
<i class="icon-lidarr-form-info" title="Change file date on import/rescan"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-2 col-sm-pull-1">
|
<div class="col-sm-3 col-sm-pull-1">
|
||||||
<select class="form-control" name="fileDate">
|
<select class="form-control" name="fileDate">
|
||||||
<option value="none">None</option>
|
<option value="none">None</option>
|
||||||
<option value="localAirDate">Local Air Date</option>
|
<option value="albumReleaseDate">Album Release Date</option>
|
||||||
<option value="utcAirDate">UTC Air Date</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,9 +10,7 @@ var view = Marionette.ItemView.extend({
|
||||||
|
|
||||||
ui : {
|
ui : {
|
||||||
namingOptions : '.x-naming-options',
|
namingOptions : '.x-naming-options',
|
||||||
singleEpisodeExample : '.x-single-episode-example',
|
singleTrackExample : '.x-single-track-example'
|
||||||
multiEpisodeExample : '.x-multi-episode-example',
|
|
||||||
dailyEpisodeExample : '.x-daily-episode-example'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize : function(options) {
|
initialize : function(options) {
|
||||||
|
@ -26,12 +24,12 @@ var view = Marionette.ItemView.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
_parseNamingModel : function() {
|
_parseNamingModel : function() {
|
||||||
var standardFormat = this.namingModel.get('standardEpisodeFormat');
|
var standardFormat = this.namingModel.get('standardTrackFormat');
|
||||||
|
|
||||||
var includeSeriesTitle = standardFormat.match(/\{Series[-_. ]Title\}/i);
|
var includeArtistName = standardFormat.match(/\{Artist[-_. ]Name\}/i);
|
||||||
var includeEpisodeTitle = standardFormat.match(/\{Episode[-_. ]Title\}/i);
|
var includeAlbumTitle = standardFormat.match(/\{Album[-_. ]Title\}/i);
|
||||||
var includeQuality = standardFormat.match(/\{Quality[-_. ]Title\}/i);
|
var includeQuality = standardFormat.match(/\{Quality[-_. ]Title\}/i);
|
||||||
var numberStyle = standardFormat.match(/s?\{season(?:\:0+)?\}[ex]\{episode(?:\:0+)?\}/i);
|
var numberStyle = standardFormat.match(/\{track(?:\:0+)?\}/i);
|
||||||
var replaceSpaces = standardFormat.indexOf(' ') === -1;
|
var replaceSpaces = standardFormat.indexOf(' ') === -1;
|
||||||
var separator = standardFormat.match(/\}( - |\.-\.|\.| )|( - |\.-\.|\.| )\{/i);
|
var separator = standardFormat.match(/\}( - |\.-\.|\.| )|( - |\.-\.|\.| )\{/i);
|
||||||
|
|
||||||
|
@ -42,14 +40,14 @@ var view = Marionette.ItemView.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numberStyle === null) {
|
if (numberStyle === null) {
|
||||||
numberStyle = 'S{season:00}E{episode:00}';
|
numberStyle = '{track:00}';
|
||||||
} else {
|
} else {
|
||||||
numberStyle = numberStyle[0];
|
numberStyle = numberStyle[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.model.set({
|
this.model.set({
|
||||||
includeSeriesTitle : includeSeriesTitle !== null,
|
includeArtistName : includeArtistName !== null,
|
||||||
includeEpisodeTitle : includeEpisodeTitle !== null,
|
includeAlbumTitle : includeAlbumTitle !== null,
|
||||||
includeQuality : includeQuality !== null,
|
includeQuality : includeQuality !== null,
|
||||||
numberStyle : numberStyle,
|
numberStyle : numberStyle,
|
||||||
replaceSpaces : replaceSpaces,
|
replaceSpaces : replaceSpaces,
|
||||||
|
@ -62,56 +60,52 @@ var view = Marionette.ItemView.extend({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var standardEpisodeFormat = '';
|
var standardTrackFormat = '';
|
||||||
var dailyEpisodeFormat = '';
|
|
||||||
|
|
||||||
if (this.model.get('includeSeriesTitle')) {
|
if (this.model.get('includeArtistName')) {
|
||||||
if (this.model.get('replaceSpaces')) {
|
if (this.model.get('replaceSpaces')) {
|
||||||
standardEpisodeFormat += '{Series.Title}';
|
standardTrackFormat += '{Artist.Name}';
|
||||||
dailyEpisodeFormat += '{Series.Title}';
|
|
||||||
} else {
|
} else {
|
||||||
standardEpisodeFormat += '{Series Title}';
|
standardTrackFormat += '{Artist Name}';
|
||||||
dailyEpisodeFormat += '{Series Title}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
standardEpisodeFormat += this.model.get('separator');
|
standardTrackFormat += this.model.get('separator');
|
||||||
dailyEpisodeFormat += this.model.get('separator');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
standardEpisodeFormat += this.model.get('numberStyle');
|
if (this.model.get('includeAlbumTitle')) {
|
||||||
dailyEpisodeFormat += '{Air-Date}';
|
|
||||||
|
|
||||||
if (this.model.get('includeEpisodeTitle')) {
|
|
||||||
standardEpisodeFormat += this.model.get('separator');
|
|
||||||
dailyEpisodeFormat += this.model.get('separator');
|
|
||||||
|
|
||||||
if (this.model.get('replaceSpaces')) {
|
if (this.model.get('replaceSpaces')) {
|
||||||
standardEpisodeFormat += '{Episode.Title}';
|
standardTrackFormat += '{Album.Title}';
|
||||||
dailyEpisodeFormat += '{Episode.Title}';
|
|
||||||
} else {
|
} else {
|
||||||
standardEpisodeFormat += '{Episode Title}';
|
standardTrackFormat += '{Album Title}';
|
||||||
dailyEpisodeFormat += '{Episode Title}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
standardTrackFormat += this.model.get('separator');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
standardTrackFormat += this.model.get('numberStyle');
|
||||||
|
|
||||||
|
standardTrackFormat += this.model.get('separator');
|
||||||
|
|
||||||
|
if (this.model.get('replaceSpaces')) {
|
||||||
|
standardTrackFormat += '{Track.Title}';
|
||||||
|
} else {
|
||||||
|
standardTrackFormat += '{Track Title}';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.model.get('includeQuality')) {
|
if (this.model.get('includeQuality')) {
|
||||||
if (this.model.get('replaceSpaces')) {
|
if (this.model.get('replaceSpaces')) {
|
||||||
standardEpisodeFormat += ' {Quality.Title}';
|
standardTrackFormat += ' {Quality.Title}';
|
||||||
dailyEpisodeFormat += ' {Quality.Title}';
|
|
||||||
} else {
|
} else {
|
||||||
standardEpisodeFormat += ' {Quality Title}';
|
standardTrackFormat += ' {Quality Title}';
|
||||||
dailyEpisodeFormat += ' {Quality Title}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.model.get('replaceSpaces')) {
|
if (this.model.get('replaceSpaces')) {
|
||||||
standardEpisodeFormat = standardEpisodeFormat.replace(/\s/g, '.');
|
standardTrackFormat = standardTrackFormat.replace(/\s/g, '.');
|
||||||
dailyEpisodeFormat = dailyEpisodeFormat.replace(/\s/g, '.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.namingModel.set('standardEpisodeFormat', standardEpisodeFormat);
|
this.namingModel.set('standardTrackFormat', standardTrackFormat);
|
||||||
this.namingModel.set('dailyEpisodeFormat', dailyEpisodeFormat);
|
|
||||||
this.namingModel.set('animeEpisodeFormat', standardEpisodeFormat);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">Include Series Title</label>
|
<label class="col-sm-3 control-label">Include Artist Name</label>
|
||||||
|
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label class="checkbox toggle well">
|
<label class="checkbox toggle well">
|
||||||
<input type="checkbox" name="includeSeriesTitle"/>
|
<input type="checkbox" name="includeArtistName"/>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span>Yes</span>
|
<span>Yes</span>
|
||||||
|
@ -17,15 +17,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">Include Episode Title</label>
|
<label class="col-sm-3 control-label">Include Album Title</label>
|
||||||
|
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label class="checkbox toggle well">
|
<label class="checkbox toggle well">
|
||||||
<input type="checkbox" name="includeEpisodeTitle"/>
|
<input type="checkbox" name="includeAlbumTitle"/>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<span>Yes</span>
|
<span>Yes</span>
|
||||||
|
@ -93,10 +91,8 @@
|
||||||
|
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select class="form-control" name="numberStyle">
|
<select class="form-control" name="numberStyle">
|
||||||
<option value="{season}x{episode:00}">1x05</option>
|
<option value="{track}">1</option>
|
||||||
<option value="{season:00}x{episode:00}">01x05</option>
|
<option value="{track:00}">01</option>
|
||||||
<option value="S{season:00}E{episode:00}">S01E05</option>
|
|
||||||
<option value="s{season:00}e{episode:00}">s01e05</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue