Fix more warnings

This commit is contained in:
Bond-009 2019-05-10 20:37:42 +02:00
parent 2aed2d164b
commit a6f9ceedd8
41 changed files with 356 additions and 254 deletions

View File

@ -33,27 +33,29 @@ namespace Emby.Naming.Audio
// Normalize
// Remove whitespace
filename = filename.Replace("-", " ");
filename = filename.Replace(".", " ");
filename = filename.Replace("(", " ");
filename = filename.Replace(")", " ");
filename = filename.Replace('-', ' ');
filename = filename.Replace('.', ' ');
filename = filename.Replace('(', ' ');
filename = filename.Replace(')', ' ');
filename = Regex.Replace(filename, @"\s+", " ");
filename = filename.TrimStart();
foreach (var prefix in _options.AlbumStackingPrefixes)
{
if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) == 0)
if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) != 0)
{
var tmp = filename.Substring(prefix.Length);
continue;
}
tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty;
var tmp = filename.Substring(prefix.Length);
if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{
result.IsMultiPart = true;
break;
}
tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty;
if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
{
result.IsMultiPart = true;
break;
}
}

View File

@ -7,11 +7,13 @@ namespace Emby.Naming.Audio
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the part.
/// </summary>
/// <value>The part.</value>
public string Part { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is multi part.
/// </summary>

View File

@ -12,35 +12,56 @@ namespace Emby.Naming.AudioBook
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the container.
/// </summary>
/// <value>The container.</value>
public string Container { get; set; }
/// <summary>
/// Gets or sets the part number.
/// </summary>
/// <value>The part number.</value>
public int? PartNumber { get; set; }
/// <summary>
/// Gets or sets the chapter number.
/// </summary>
/// <value>The chapter number.</value>
public int? ChapterNumber { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public bool IsDirectory { get; set; }
/// <inheritdoc/>
public int CompareTo(AudioBookFileInfo other)
{
if (ReferenceEquals(this, other)) return 0;
if (ReferenceEquals(null, other)) return 1;
if (ReferenceEquals(this, other))
{
return 0;
}
if (ReferenceEquals(null, other))
{
return 1;
}
var chapterNumberComparison = Nullable.Compare(ChapterNumber, other.ChapterNumber);
if (chapterNumberComparison != 0) return chapterNumberComparison;
if (chapterNumberComparison != 0)
{
return chapterNumberComparison;
}
var partNumberComparison = Nullable.Compare(PartNumber, other.PartNumber);
if (partNumberComparison != 0) return partNumberComparison;
if (partNumberComparison != 0)
{
return partNumberComparison;
}
return string.Compare(Path, other.Path, StringComparison.Ordinal);
}
}

View File

@ -1,3 +1,4 @@
using System;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
@ -14,14 +15,13 @@ namespace Emby.Naming.AudioBook
_options = options;
}
public AudioBookFilePathParserResult Parse(string path, bool IsDirectory)
public AudioBookFilePathParserResult Parse(string path)
{
var result = Parse(path);
return !result.Success ? new AudioBookFilePathParserResult() : result;
}
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
private AudioBookFilePathParserResult Parse(string path)
{
var result = new AudioBookFilePathParserResult();
var fileName = Path.GetFileNameWithoutExtension(path);
foreach (var expression in _options.AudioBookPartsExpressions)
@ -40,6 +40,7 @@ namespace Emby.Naming.AudioBook
}
}
}
if (!result.PartNumber.HasValue)
{
var value = match.Groups["part"];

View File

@ -3,7 +3,9 @@ namespace Emby.Naming.AudioBook
public class AudioBookFilePathParserResult
{
public int? PartNumber { get; set; }
public int? ChapterNumber { get; set; }
public bool Success { get; set; }
}
}

View File

@ -7,33 +7,40 @@ namespace Emby.Naming.AudioBook
/// </summary>
public class AudioBookInfo
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
public int? Year { get; set; }
/// <summary>
/// Gets or sets the files.
/// </summary>
/// <value>The files.</value>
public List<AudioBookFileInfo> Files { get; set; }
/// <summary>
/// Gets or sets the extras.
/// </summary>
/// <value>The extras.</value>
public List<AudioBookFileInfo> Extras { get; set; }
/// <summary>
/// Gets or sets the alternate versions.
/// </summary>
/// <value>The alternate versions.</value>
public List<AudioBookFileInfo> AlternateVersions { get; set; }
public AudioBookInfo()
{
Files = new List<AudioBookFileInfo>();
Extras = new List<AudioBookFileInfo>();
AlternateVersions = new List<AudioBookFileInfo>();
}
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the year.
/// </summary>
public int? Year { get; set; }
/// <summary>
/// Gets or sets the files.
/// </summary>
/// <value>The files.</value>
public List<AudioBookFileInfo> Files { get; set; }
/// <summary>
/// Gets or sets the extras.
/// </summary>
/// <value>The extras.</value>
public List<AudioBookFileInfo> Extras { get; set; }
/// <summary>
/// Gets or sets the alternate versions.
/// </summary>
/// <value>The alternate versions.</value>
public List<AudioBookFileInfo> AlternateVersions { get; set; }
}
}

View File

@ -15,7 +15,7 @@ namespace Emby.Naming.AudioBook
_options = options;
}
public IEnumerable<AudioBookInfo> Resolve(List<FileSystemMetadata> files)
public IEnumerable<AudioBookInfo> Resolve(IEnumerable<FileSystemMetadata> files)
{
var audioBookResolver = new AudioBookResolver(_options);

View File

@ -24,19 +24,21 @@ namespace Emby.Naming.AudioBook
return Resolve(path, true);
}
public AudioBookFileInfo Resolve(string path, bool IsDirectory = false)
public AudioBookFileInfo Resolve(string path, bool isDirectory = false)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
}
if (IsDirectory) // TODO
// TODO
if (isDirectory)
{
return null;
}
var extension = Path.GetExtension(path);
// Check supported extensions
if (!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
@ -45,8 +47,7 @@ namespace Emby.Naming.AudioBook
var container = extension.TrimStart('.');
var parsingResult = new AudioBookFilePathParser(_options)
.Parse(path, IsDirectory);
var parsingResult = new AudioBookFilePathParser(_options).Parse(path);
return new AudioBookFileInfo
{
@ -54,7 +55,7 @@ namespace Emby.Naming.AudioBook
Container = container,
PartNumber = parsingResult.PartNumber,
ChapterNumber = parsingResult.ChapterNumber,
IsDirectory = IsDirectory
IsDirectory = isDirectory
};
}
}

View File

@ -6,17 +6,28 @@ namespace Emby.Naming.Common
public class EpisodeExpression
{
private string _expression;
public string Expression { get => _expression;
set { _expression = value; _regex = null; } }
private Regex _regex;
public string Expression
{
get => _expression;
set
{
_expression = value;
_regex = null;
}
}
public bool IsByDate { get; set; }
public bool IsOptimistic { get; set; }
public bool IsNamed { get; set; }
public bool SupportsAbsoluteEpisodeNumbers { get; set; }
public string[] DateTimeFormats { get; set; }
private Regex _regex;
public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled));
public EpisodeExpression(string expression, bool byDate)

View File

@ -6,10 +6,12 @@ namespace Emby.Naming.Common
/// The audio
/// </summary>
Audio = 0,
/// <summary>
/// The photo
/// </summary>
Photo = 1,
/// <summary>
/// The video
/// </summary>

View File

@ -8,19 +8,25 @@ namespace Emby.Naming.Common
public class NamingOptions
{
public string[] AudioFileExtensions { get; set; }
public string[] AlbumStackingPrefixes { get; set; }
public string[] SubtitleFileExtensions { get; set; }
public char[] SubtitleFlagDelimiters { get; set; }
public string[] SubtitleForcedFlags { get; set; }
public string[] SubtitleDefaultFlags { get; set; }
public EpisodeExpression[] EpisodeExpressions { get; set; }
public string[] EpisodeWithoutSeasonExpressions { get; set; }
public string[] EpisodeMultiPartExpressions { get; set; }
public string[] VideoFileExtensions { get; set; }
public string[] StubFileExtensions { get; set; }
public string[] AudioBookPartsExpressions { get; set; }
@ -28,12 +34,14 @@ namespace Emby.Naming.Common
public StubTypeRule[] StubTypes { get; set; }
public char[] VideoFlagDelimiters { get; set; }
public Format3DRule[] Format3DRules { get; set; }
public string[] VideoFileStackingExpressions { get; set; }
public string[] CleanDateTimes { get; set; }
public string[] CleanStrings { get; set; }
public string[] CleanDateTimes { get; set; }
public string[] CleanStrings { get; set; }
public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
@ -41,7 +49,7 @@ namespace Emby.Naming.Common
public NamingOptions()
{
VideoFileExtensions = new string[]
VideoFileExtensions = new[]
{
".m4v",
".3gp",
@ -106,53 +114,53 @@ namespace Emby.Naming.Common
{
new StubTypeRule
{
StubType = "dvd",
Token = "dvd"
StubType = "dvd",
Token = "dvd"
},
new StubTypeRule
{
StubType = "hddvd",
Token = "hddvd"
StubType = "hddvd",
Token = "hddvd"
},
new StubTypeRule
{
StubType = "bluray",
Token = "bluray"
StubType = "bluray",
Token = "bluray"
},
new StubTypeRule
{
StubType = "bluray",
Token = "brrip"
StubType = "bluray",
Token = "brrip"
},
new StubTypeRule
{
StubType = "bluray",
Token = "bd25"
StubType = "bluray",
Token = "bd25"
},
new StubTypeRule
{
StubType = "bluray",
Token = "bd50"
StubType = "bluray",
Token = "bd50"
},
new StubTypeRule
{
StubType = "vhs",
Token = "vhs"
StubType = "vhs",
Token = "vhs"
},
new StubTypeRule
{
StubType = "tv",
Token = "HDTV"
StubType = "tv",
Token = "HDTV"
},
new StubTypeRule
{
StubType = "tv",
Token = "PDTV"
StubType = "tv",
Token = "PDTV"
},
new StubTypeRule
{
StubType = "tv",
Token = "DSR"
StubType = "tv",
Token = "DSR"
}
};
@ -286,7 +294,7 @@ namespace Emby.Naming.Common
new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"),
new EpisodeExpression("([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})", true)
{
DateTimeFormats = new []
DateTimeFormats = new[]
{
"yyyy.MM.dd",
"yyyy-MM-dd",
@ -295,7 +303,7 @@ namespace Emby.Naming.Common
},
new EpisodeExpression("([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})", true)
{
DateTimeFormats = new []
DateTimeFormats = new[]
{
"dd.MM.yyyy",
"dd-MM-yyyy",
@ -348,9 +356,7 @@ namespace Emby.Naming.Common
},
// "1-12 episode title"
new EpisodeExpression(@"([0-9]+)-([0-9]+)")
{
},
new EpisodeExpression(@"([0-9]+)-([0-9]+)"),
// "01 - blah.avi", "01-blah.avi"
new EpisodeExpression(@".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\s?-\s?[^\\\/]*$")
@ -427,7 +433,7 @@ namespace Emby.Naming.Common
Token = "_trailer",
MediaType = MediaType.Video
},
new ExtraRule
new ExtraRule
{
ExtraType = "trailer",
RuleType = ExtraRuleType.Suffix,
@ -462,7 +468,7 @@ namespace Emby.Naming.Common
Token = "_sample",
MediaType = MediaType.Video
},
new ExtraRule
new ExtraRule
{
ExtraType = "sample",
RuleType = ExtraRuleType.Suffix,
@ -476,7 +482,6 @@ namespace Emby.Naming.Common
Token = "theme",
MediaType = MediaType.Audio
},
new ExtraRule
{
ExtraType = "scene",
@ -526,8 +531,8 @@ namespace Emby.Naming.Common
Token = "-short",
MediaType = MediaType.Video
}
};
Format3DRules = new[]
{
// Kodi rules:
@ -648,12 +653,10 @@ namespace Emby.Naming.Common
@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$"
}.Select(i => new EpisodeExpression(i)
{
IsNamed = true
}).ToArray();
{
IsNamed = true
}).ToArray();
VideoFileExtensions = extensions
.Distinct(StringComparer.OrdinalIgnoreCase)

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
@ -18,6 +18,22 @@
<PackageId>Jellyfin.Naming</PackageId>
<PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>

View File

@ -5,6 +5,7 @@ namespace Emby.Naming.Extensions
{
public static class StringExtensions
{
// TODO: @bond remove this when moving to netstandard2.1
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
{
var sb = new StringBuilder();

View File

@ -1,30 +0,0 @@
using System;
using System.Text;
namespace Emby.Naming
{
internal static class StringExtensions
{
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
{
var sb = new StringBuilder();
var previousIndex = 0;
var index = str.IndexOf(oldValue, comparison);
while (index != -1)
{
sb.Append(str.Substring(previousIndex, index - previousIndex));
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = str.IndexOf(oldValue, index, comparison);
}
sb.Append(str.Substring(previousIndex));
return sb.ToString();
}
}
}

View File

@ -7,16 +7,19 @@ namespace Emby.Naming.Subtitles
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the language.
/// </summary>
/// <value>The language.</value>
public string Language { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is default.
/// </summary>
/// <value><c>true</c> if this instance is default; otherwise, <c>false</c>.</value>
public bool IsDefault { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is forced.
/// </summary>

View File

@ -7,31 +7,37 @@ namespace Emby.Naming.TV
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the container.
/// </summary>
/// <value>The container.</value>
public string Container { get; set; }
/// <summary>
/// Gets or sets the name of the series.
/// </summary>
/// <value>The name of the series.</value>
public string SeriesName { get; set; }
/// <summary>
/// Gets or sets the format3 d.
/// </summary>
/// <value>The format3 d.</value>
public string Format3D { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [is3 d].
/// </summary>
/// <value><c>true</c> if [is3 d]; otherwise, <c>false</c>.</value>
public bool Is3D { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is stub.
/// </summary>
/// <value><c>true</c> if this instance is stub; otherwise, <c>false</c>.</value>
public bool IsStub { get; set; }
/// <summary>
/// Gets or sets the type of the stub.
/// </summary>
@ -39,12 +45,17 @@ namespace Emby.Naming.TV
public string StubType { get; set; }
public int? SeasonNumber { get; set; }
public int? EpisodeNumber { get; set; }
public int? EndingEpsiodeNumber { get; set; }
public int? Year { get; set; }
public int? Month { get; set; }
public int? Day { get; set; }
public bool IsByDate { get; set; }
}
}

View File

@ -15,12 +15,12 @@ namespace Emby.Naming.TV
_options = options;
}
public EpisodePathParserResult Parse(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true)
public EpisodePathParserResult Parse(string path, bool isDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true)
{
// Added to be able to use regex patterns which require a file extension.
// There were no failed tests without this block, but to be safe, we can keep it until
// the regex which require file extensions are modified so that they don't need them.
if (IsDirectory)
if (isDirectory)
{
path += ".mp4";
}
@ -29,28 +29,20 @@ namespace Emby.Naming.TV
foreach (var expression in _options.EpisodeExpressions)
{
if (supportsAbsoluteNumbers.HasValue)
if (supportsAbsoluteNumbers.HasValue
&& expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value)
{
if (expression.SupportsAbsoluteEpisodeNumbers != supportsAbsoluteNumbers.Value)
{
continue;
}
continue;
}
if (isNamed.HasValue)
if (isNamed.HasValue && expression.IsNamed != isNamed.Value)
{
if (expression.IsNamed != isNamed.Value)
{
continue;
}
continue;
}
if (isOptimistic.HasValue)
if (isOptimistic.HasValue && expression.IsOptimistic != isOptimistic.Value)
{
if (expression.IsOptimistic != isOptimistic.Value)
{
continue;
}
continue;
}
var currentResult = Parse(path, expression);
@ -97,7 +89,8 @@ namespace Emby.Naming.TV
DateTime date;
if (expression.DateTimeFormats.Length > 0)
{
if (DateTime.TryParseExact(match.Groups[0].Value,
if (DateTime.TryParseExact(
match.Groups[0].Value,
expression.DateTimeFormats,
CultureInfo.InvariantCulture,
DateTimeStyles.None,
@ -109,17 +102,15 @@ namespace Emby.Naming.TV
result.Success = true;
}
}
else
else if (DateTime.TryParse(match.Groups[0].Value, out date))
{
if (DateTime.TryParse(match.Groups[0].Value, out date))
{
result.Year = date.Year;
result.Month = date.Month;
result.Day = date.Day;
result.Success = true;
}
result.Year = date.Year;
result.Month = date.Month;
result.Day = date.Day;
result.Success = true;
}
// TODO: Only consider success if date successfully parsed?
result.Success = true;
}
@ -142,7 +133,8 @@ namespace Emby.Naming.TV
// or a 'p' or 'i' as what you would get with a pixel resolution specification.
// It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108
int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length;
if (nextIndex >= name.Length || "0123456789iIpP".IndexOf(name[nextIndex]) == -1)
if (nextIndex >= name.Length
|| "0123456789iIpP".IndexOf(name[nextIndex]) == -1)
{
if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{
@ -160,6 +152,7 @@ namespace Emby.Naming.TV
{
result.SeasonNumber = num;
}
if (int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num))
{
result.EpisodeNumber = num;
@ -171,8 +164,11 @@ namespace Emby.Naming.TV
// Invalidate match when the season is 200 through 1927 or above 2500
// because it is an error unless the TV show is intentionally using false season numbers.
// It avoids erroneous parsing of something like "Series Special (1920x1080).mkv" as being season 1920 episode 1080.
if (result.SeasonNumber >= 200 && result.SeasonNumber < 1928 || result.SeasonNumber > 2500)
if ((result.SeasonNumber >= 200 && result.SeasonNumber < 1928)
|| result.SeasonNumber > 2500)
{
result.Success = false;
}
result.IsByDate = expression.IsByDate;
}

View File

@ -3,14 +3,21 @@ namespace Emby.Naming.TV
public class EpisodePathParserResult
{
public int? SeasonNumber { get; set; }
public int? EpisodeNumber { get; set; }
public int? EndingEpsiodeNumber { get; set; }
public string SeriesName { get; set; }
public bool Success { get; set; }
public bool IsByDate { get; set; }
public int? Year { get; set; }
public int? Month { get; set; }
public int? Day { get; set; }
}
}

View File

@ -15,7 +15,13 @@ namespace Emby.Naming.TV
_options = options;
}
public EpisodeInfo Resolve(string path, bool IsDirectory, bool? isNamed = null, bool? isOptimistic = null, bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true)
public EpisodeInfo Resolve(
string path,
bool isDirectory,
bool? isNamed = null,
bool? isOptimistic = null,
bool? supportsAbsoluteNumbers = null,
bool fillExtendedInfo = true)
{
if (string.IsNullOrEmpty(path))
{
@ -26,7 +32,7 @@ namespace Emby.Naming.TV
string container = null;
string stubType = null;
if (!IsDirectory)
if (!isDirectory)
{
var extension = Path.GetExtension(path);
// Check supported extensions
@ -52,7 +58,7 @@ namespace Emby.Naming.TV
var format3DResult = new Format3DParser(_options).Parse(flags);
var parsingResult = new EpisodePathParser(_options)
.Parse(path, IsDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo);
.Parse(path, isDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo);
return new EpisodeInfo
{

View File

@ -3,30 +3,24 @@ using System.Globalization;
using System.IO;
using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.Extensions;
namespace Emby.Naming.TV
{
public class SeasonPathParser
{
private readonly NamingOptions _options;
public SeasonPathParser(NamingOptions options)
{
_options = options;
}
public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
{
var result = new SeasonPathParserResult();
var seasonNumberInfo = GetSeasonNumberFromPath(path, supportSpecialAliases, supportNumericSeasonFolders);
result.SeasonNumber = seasonNumberInfo.Item1;
result.SeasonNumber = seasonNumberInfo.seasonNumber;
if (result.SeasonNumber.HasValue)
{
result.Success = true;
result.IsSeasonFolder = seasonNumberInfo.Item2;
result.IsSeasonFolder = seasonNumberInfo.isSeasonFolder;
}
return result;
@ -35,7 +29,7 @@ namespace Emby.Naming.TV
/// <summary>
/// A season folder must contain one of these somewhere in the name
/// </summary>
private static readonly string[] SeasonFolderNames =
private static readonly string[] _seasonFolderNames =
{
"season",
"sæson",
@ -54,19 +48,23 @@ namespace Emby.Naming.TV
/// <param name="supportSpecialAliases">if set to <c>true</c> [support special aliases].</param>
/// <param name="supportNumericSeasonFolders">if set to <c>true</c> [support numeric season folders].</param>
/// <returns>System.Nullable{System.Int32}.</returns>
private Tuple<int?, bool> GetSeasonNumberFromPath(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders)
private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPath(
string path,
bool supportSpecialAliases,
bool supportNumericSeasonFolders)
{
var filename = Path.GetFileName(path);
var filename = Path.GetFileName(path) ?? string.Empty;
if (supportSpecialAliases)
{
if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase))
{
return new Tuple<int?, bool>(0, true);
return (0, true);
}
if (string.Equals(filename, "extras", StringComparison.OrdinalIgnoreCase))
{
return new Tuple<int?, bool>(0, true);
return (0, true);
}
}
@ -74,7 +72,7 @@ namespace Emby.Naming.TV
{
if (int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{
return new Tuple<int?, bool>(val, true);
return (val, true);
}
}
@ -84,12 +82,12 @@ namespace Emby.Naming.TV
if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{
return new Tuple<int?, bool>(val, true);
return (val, true);
}
}
// Look for one of the season folder names
foreach (var name in SeasonFolderNames)
foreach (var name in _seasonFolderNames)
{
var index = filename.IndexOf(name, StringComparison.OrdinalIgnoreCase);
@ -107,10 +105,10 @@ namespace Emby.Naming.TV
var parts = filename.Split(new[] { '.', '_', ' ', '-' }, StringSplitOptions.RemoveEmptyEntries);
var resultNumber = parts.Select(GetSeasonNumberFromPart).FirstOrDefault(i => i.HasValue);
return new Tuple<int?, bool>(resultNumber, true);
return (resultNumber, true);
}
private int? GetSeasonNumberFromPart(string part)
private static int? GetSeasonNumberFromPart(string part)
{
if (part.Length < 2 || !part.StartsWith("s", StringComparison.OrdinalIgnoreCase))
{
@ -132,7 +130,7 @@ namespace Emby.Naming.TV
/// </summary>
/// <param name="path">The path.</param>
/// <returns>System.Nullable{System.Int32}.</returns>
private Tuple<int?, bool> GetSeasonNumberFromPathSubstring(string path)
private static (int? seasonNumber, bool isSeasonFolder) GetSeasonNumberFromPathSubstring(string path)
{
var numericStart = -1;
var length = 0;
@ -174,10 +172,10 @@ namespace Emby.Naming.TV
if (numericStart == -1)
{
return new Tuple<int?, bool>(null, isSeasonFolder);
return (null, isSeasonFolder);
}
return new Tuple<int?, bool>(int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder);
return (int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture), isSeasonFolder);
}
}
}

View File

@ -7,11 +7,13 @@ namespace Emby.Naming.TV
/// </summary>
/// <value>The season number.</value>
public int? SeasonNumber { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="SeasonPathParserResult"/> is success.
/// </summary>
/// <value><c>true</c> if success; otherwise, <c>false</c>.</value>
public bool Success { get; set; }
public bool IsSeasonFolder { get; set; }
}
}

View File

@ -27,8 +27,8 @@ namespace Emby.Naming.Video
{
var extension = Path.GetExtension(name) ?? string.Empty;
// Check supported extensions
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) &&
!_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)
&& !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
// Dummy up a file extension because the expressions will fail without one
// This is tricky because we can't just check Path.GetExtension for empty
@ -38,7 +38,6 @@ namespace Emby.Naming.Video
}
catch (ArgumentException)
{
}
var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i))
@ -69,14 +68,15 @@ namespace Emby.Naming.Video
var match = expression.Match(name);
if (match.Success && match.Groups.Count == 4)
if (match.Success
&& match.Groups.Count == 4
&& match.Groups[1].Success
&& match.Groups[2].Success
&& int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
{
if (match.Groups[1].Success && match.Groups[2].Success && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
{
name = match.Groups[1].Value;
result.Year = year;
result.HasChanged = true;
}
name = match.Groups[1].Value;
result.Year = year;
result.HasChanged = true;
}
result.Name = name;

View File

@ -56,7 +56,6 @@ namespace Emby.Naming.Video
result.Rule = rule;
}
}
else if (rule.RuleType == ExtraRuleType.Suffix)
{
var filename = Path.GetFileNameWithoutExtension(path);
@ -67,7 +66,6 @@ namespace Emby.Naming.Video
result.Rule = rule;
}
}
else if (rule.RuleType == ExtraRuleType.Regex)
{
var filename = Path.GetFileName(path);

View File

@ -15,9 +15,9 @@ namespace Emby.Naming.Video
Files = new List<string>();
}
public bool ContainsFile(string file, bool IsDirectory)
public bool ContainsFile(string file, bool isDirectory)
{
if (IsDirectoryStack == IsDirectory)
if (IsDirectoryStack == isDirectory)
{
return Files.Contains(file, StringComparer.OrdinalIgnoreCase);
}

View File

@ -15,10 +15,12 @@ namespace Emby.Naming.Video
public Format3DResult Parse(string path)
{
var delimeters = _options.VideoFlagDelimiters.ToList();
delimeters.Add(' ');
int oldLen = _options.VideoFlagDelimiters.Length;
var delimeters = new char[oldLen + 1];
_options.VideoFlagDelimiters.CopyTo(delimeters, 0);
delimeters[oldLen] = ' ';
return Parse(new FlagParser(_options).GetFlags(path, delimeters.ToArray()));
return Parse(new FlagParser(_options).GetFlags(path, delimeters));
}
internal Format3DResult Parse(string[] videoFlags)
@ -66,8 +68,10 @@ namespace Emby.Naming.Video
format = flag;
result.Tokens.Add(rule.Token);
}
break;
}
foundPrefix = string.Equals(flag, rule.PreceedingToken, StringComparison.OrdinalIgnoreCase);
}

View File

@ -4,25 +4,27 @@ namespace Emby.Naming.Video
{
public class Format3DResult
{
public Format3DResult()
{
Tokens = new List<string>();
}
/// <summary>
/// Gets or sets a value indicating whether [is3 d].
/// </summary>
/// <value><c>true</c> if [is3 d]; otherwise, <c>false</c>.</value>
public bool Is3D { get; set; }
/// <summary>
/// Gets or sets the format3 d.
/// </summary>
/// <value>The format3 d.</value>
public string Format3D { get; set; }
/// <summary>
/// Gets or sets the tokens.
/// </summary>
/// <value>The tokens.</value>
public List<string> Tokens { get; set; }
public Format3DResult()
{
Tokens = new List<string>();
}
}
}

View File

@ -40,17 +40,24 @@ namespace Emby.Naming.Video
var result = new StackResult();
foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName)))
{
var stack = new FileStack();
stack.Name = Path.GetFileName(directory.Key);
stack.IsDirectoryStack = false;
var stack = new FileStack()
{
Name = Path.GetFileName(directory.Key),
IsDirectoryStack = false
};
foreach (var file in directory)
{
if (file.IsDirectory)
{
continue;
}
stack.Files.Add(file.FullName);
}
result.Stacks.Add(stack);
}
return result;
}
@ -114,16 +121,16 @@ namespace Emby.Naming.Video
{
if (!string.Equals(volume1, volume2, StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase) &&
string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase))
if (string.Equals(ignore1, ignore2, StringComparison.OrdinalIgnoreCase)
&& string.Equals(extension1, extension2, StringComparison.OrdinalIgnoreCase))
{
if (stack.Files.Count == 0)
{
stack.Name = title1 + ignore1;
stack.IsDirectoryStack = file1.IsDirectory;
//stack.Name = title1 + ignore1 + extension1;
stack.Files.Add(file1.FullName);
}
stack.Files.Add(file2.FullName);
}
else

View File

@ -9,24 +9,32 @@ namespace Emby.Naming.Video
{
public static StubResult ResolveFile(string path, NamingOptions options)
{
var result = new StubResult();
var extension = Path.GetExtension(path) ?? string.Empty;
if (options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
if (path == null)
{
result.IsStub = true;
return default(StubResult);
}
path = Path.GetFileNameWithoutExtension(path);
var extension = Path.GetExtension(path);
var token = (Path.GetExtension(path) ?? string.Empty).TrimStart('.');
if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
return default(StubResult);
}
foreach (var rule in options.StubTypes)
var result = new StubResult()
{
IsStub = true
};
path = Path.GetFileNameWithoutExtension(path);
var token = Path.GetExtension(path).TrimStart('.');
foreach (var rule in options.StubTypes)
{
if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase))
{
result.StubType = rule.StubType;
break;
}
result.StubType = rule.StubType;
break;
}
}

View File

@ -7,6 +7,7 @@ namespace Emby.Naming.Video
/// </summary>
/// <value><c>true</c> if this instance is stub; otherwise, <c>false</c>.</value>
public bool IsStub { get; set; }
/// <summary>
/// Gets or sets the type of the stub.
/// </summary>

View File

@ -7,6 +7,7 @@ namespace Emby.Naming.Video
/// </summary>
/// <value>The token.</value>
public string Token { get; set; }
/// <summary>
/// Gets or sets the type of the stub.
/// </summary>

View File

@ -1,4 +1,3 @@
namespace Emby.Naming.Video
{
/// <summary>
@ -11,56 +10,67 @@ namespace Emby.Naming.Video
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the container.
/// </summary>
/// <value>The container.</value>
public string Container { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the year.
/// </summary>
/// <value>The year.</value>
public int? Year { get; set; }
/// <summary>
/// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc.
/// </summary>
/// <value>The type of the extra.</value>
public string ExtraType { get; set; }
/// <summary>
/// Gets or sets the extra rule.
/// </summary>
/// <value>The extra rule.</value>
public ExtraRule ExtraRule { get; set; }
/// <summary>
/// Gets or sets the format3 d.
/// </summary>
/// <value>The format3 d.</value>
public string Format3D { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [is3 d].
/// </summary>
/// <value><c>true</c> if [is3 d]; otherwise, <c>false</c>.</value>
public bool Is3D { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is stub.
/// </summary>
/// <value><c>true</c> if this instance is stub; otherwise, <c>false</c>.</value>
public bool IsStub { get; set; }
/// <summary>
/// Gets or sets the type of the stub.
/// </summary>
/// <value>The type of the stub.</value>
public string StubType { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public bool IsDirectory { get; set; }
/// <summary>
/// Gets the file name without extension.
/// </summary>

View File

@ -12,21 +12,25 @@ namespace Emby.Naming.Video
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the year.
/// </summary>
/// <value>The year.</value>
public int? Year { get; set; }
/// <summary>
/// Gets or sets the files.
/// </summary>
/// <value>The files.</value>
public List<VideoFileInfo> Files { get; set; }
/// <summary>
/// Gets or sets the extras.
/// </summary>
/// <value>The extras.</value>
public List<VideoFileInfo> Extras { get; set; }
/// <summary>
/// Gets or sets the alternate versions.
/// </summary>

View File

@ -53,7 +53,7 @@ namespace Emby.Naming.Video
Name = stack.Name
};
info.Year = info.Files.First().Year;
info.Year = info.Files[0].Year;
var extraBaseNames = new List<string>
{
@ -87,7 +87,7 @@ namespace Emby.Naming.Video
Name = media.Name
};
info.Year = info.Files.First().Year;
info.Year = info.Files[0].Year;
var extras = GetExtras(remainingFiles, new List<string> { media.FileNameWithoutExtension });
@ -115,7 +115,7 @@ namespace Emby.Naming.Video
if (!string.IsNullOrEmpty(parentPath))
{
var folderName = Path.GetFileName(Path.GetDirectoryName(videoPath));
var folderName = Path.GetFileName(parentPath);
if (!string.IsNullOrEmpty(folderName))
{
var extras = GetExtras(remainingFiles, new List<string> { folderName });
@ -163,9 +163,7 @@ namespace Emby.Naming.Video
Year = i.Year
}));
var orderedList = list.OrderBy(i => i.Name);
return orderedList;
return list.OrderBy(i => i.Name);
}
private IEnumerable<VideoInfo> GetVideosGroupedByVersion(List<VideoInfo> videos)
@ -179,23 +177,21 @@ namespace Emby.Naming.Video
var folderName = Path.GetFileName(Path.GetDirectoryName(videos[0].Files[0].Path));
if (!string.IsNullOrEmpty(folderName) && folderName.Length > 1)
if (!string.IsNullOrEmpty(folderName)
&& folderName.Length > 1
&& videos.All(i => i.Files.Count == 1
&& IsEligibleForMultiVersion(folderName, i.Files[0].Path))
&& HaveSameYear(videos))
{
if (videos.All(i => i.Files.Count == 1 && IsEligibleForMultiVersion(folderName, i.Files[0].Path)))
{
if (HaveSameYear(videos))
{
var ordered = videos.OrderBy(i => i.Name).ToList();
var ordered = videos.OrderBy(i => i.Name).ToList();
list.Add(ordered[0]);
list.Add(ordered[0]);
list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList();
list[0].Name = folderName;
list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras));
list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList();
list[0].Name = folderName;
list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras));
return list;
}
}
return list;
}
return videos;
@ -213,9 +209,9 @@ namespace Emby.Naming.Video
if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
{
testFilename = testFilename.Substring(folderName.Length).Trim();
return string.IsNullOrEmpty(testFilename) ||
testFilename.StartsWith("-") ||
string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty)) ;
return string.IsNullOrEmpty(testFilename)
|| testFilename[0] == '-'
|| string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty));
}
return false;

View File

@ -38,10 +38,11 @@ namespace Emby.Naming.Video
/// Resolves the specified path.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="IsDirectory">if set to <c>true</c> [is folder].</param>
/// <param name="isDirectory">if set to <c>true</c> [is folder].</param>
/// <param name="parseName">Whether or not the name should be parsed for info</param>
/// <returns>VideoFileInfo.</returns>
/// <exception cref="ArgumentNullException">path</exception>
public VideoFileInfo Resolve(string path, bool IsDirectory, bool parseName = true)
public VideoFileInfo Resolve(string path, bool isDirectory, bool parseName = true)
{
if (string.IsNullOrEmpty(path))
{
@ -52,9 +53,10 @@ namespace Emby.Naming.Video
string container = null;
string stubType = null;
if (!IsDirectory)
if (!isDirectory)
{
var extension = Path.GetExtension(path);
// Check supported extensions
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
@ -79,7 +81,7 @@ namespace Emby.Naming.Video
var extraResult = new ExtraResolver(_options).GetExtraInfo(path);
var name = IsDirectory
var name = isDirectory
? Path.GetFileName(path)
: Path.GetFileNameWithoutExtension(path);
@ -108,7 +110,7 @@ namespace Emby.Naming.Video
Is3D = format3DResult.Is3D,
Format3D = format3DResult.Format3D,
ExtraType = extraResult.ExtraType,
IsDirectory = IsDirectory,
IsDirectory = isDirectory,
ExtraRule = extraResult.Rule
};
}

View File

@ -52,8 +52,8 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>

View File

@ -2368,7 +2368,7 @@ namespace Emby.Server.Implementations.Library
public int? GetSeasonNumberFromPath(string path)
{
return new SeasonPathParser(GetNamingOptions()).Parse(path, true, true).SeasonNumber;
return new SeasonPathParser().Parse(path, true, true).SeasonNumber;
}
public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh)

View File

@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
var path = args.Path;
var seasonParserResult = new SeasonPathParser(namingOptions).Parse(path, true, true);
var seasonParserResult = new SeasonPathParser().Parse(path, true, true);
var season = new Season
{

View File

@ -194,9 +194,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager)
{
var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
var seasonNumber = new SeasonPathParser(namingOptions).Parse(path, isTvContentType, isTvContentType).SeasonNumber;
var seasonNumber = new SeasonPathParser().Parse(path, isTvContentType, isTvContentType).SeasonNumber;
return seasonNumber.HasValue;
}

View File

@ -12,7 +12,7 @@
<!-- We need C# 7.1 for async main-->
<LangVersion>latest</LangVersion>
<!-- Disable documentation warnings (for now) -->
<NoWarn>SA1600;SA1601;CS1591</NoWarn>
<NoWarn>SA1600;SA1601;SA1629;CS1591</NoWarn>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
@ -26,8 +26,8 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.2" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>

View File

@ -122,8 +122,12 @@ namespace Jellyfin.Server
// The default connection limit is 10 for ASP.NET hosted applications and 2 for all others.
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
// CA5359: Do Not Disable Certificate Validation
#pragma warning disable CA5359
// Allow all https requests
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
#pragma warning restore CA5359
var fileSystem = new ManagedFileSystem(_loggerFactory, appPaths);
@ -368,7 +372,7 @@ namespace Jellyfin.Server
}
catch (Exception ex)
{
_logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder. {0}");
_logger.LogInformation(ex, "Skia not available. Will fallback to NullIMageEncoder.");
}
return new NullImageEncoder();

View File

@ -14,12 +14,17 @@
<Rule Id="SA1200" Action="None" />
<!-- disable warning SA1309: Fields must not begin with an underscore -->
<Rule Id="SA1309" Action="None" />
<!-- disable warning SA1413: Use trailing comma in multi-line initializers -->
<Rule Id="SA1413" Action="None" />
<!-- disable warning SA1512: Single-line comments must not be followed by blank line -->
<Rule Id="SA1512" Action="None" />
<!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
<Rule Id="SA1633" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
<!-- disable warning CA1031: Do not catch general exception types -->
<Rule Id="CA1031" Action="Info" />
<!-- disable warning CA1822: Member does not access instance data and can be marked as static -->
<Rule Id="CA1822" Action="Info" />