Add GetBestMatch to TvDbProvider (Provides a way to get the best result from a list of results)

Default value for Quality Dropbox in AddExisting is now the default value from Settings.
pull/3113/head
Mark McDowall 13 years ago
parent 148fd044ef
commit 460e48d6a1

@ -1,153 +0,0 @@
<Configuration>
<CodeStyleSettings>
<ExternalPath IsNull="False">
</ExternalPath>
<Sharing>SOLUTION</Sharing>
<CSharp>
<FormatSettings>
<MODIFIERS_ORDER IsNull="False">
<Item>public</Item>
<Item>protected</Item>
<Item>internal</Item>
<Item>private</Item>
<Item>new</Item>
<Item>abstract</Item>
<Item>virtual</Item>
<Item>override</Item>
<Item>sealed</Item>
<Item>static</Item>
<Item>readonly</Item>
<Item>extern</Item>
<Item>unsafe</Item>
<Item>volatile</Item>
</MODIFIERS_ORDER>
</FormatSettings>
<UsingsSettings />
<Naming2>
<EventHandlerPatternLong>$object$_On$event$</EventHandlerPatternLong>
<EventHandlerPatternShort>$event$Handler</EventHandlerPatternShort>
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="TypesAndNamespaces" />
<PredefinedRule Inspect="True" Prefix="I" Suffix="" Style="AaBb" ElementKind="Interfaces" />
<PredefinedRule Inspect="True" Prefix="T" Suffix="" Style="AaBb" ElementKind="TypeParameters" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="MethodPropertyEvent" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="Locals" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="LocalConstants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="Parameters" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="PublicFields" />
<PredefinedRule Inspect="True" Prefix="_" Suffix="" Style="aaBb" ElementKind="PrivateInstanceFields" />
<PredefinedRule Inspect="True" Prefix="_" Suffix="" Style="aaBb" ElementKind="PrivateStaticFields" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="Constants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="PrivateConstants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="StaticReadonly" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="PrivateStaticReadonly" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="EnumMember" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="Other" />
</Naming2>
</CSharp>
<VB>
<FormatSettings>
<BLANK_LINES_AROUND_NAMESPACE>2</BLANK_LINES_AROUND_NAMESPACE>
<KEEP_USER_LINEBREAKS>False</KEEP_USER_LINEBREAKS>
<SPACE_AFTER_ATTRIBUTE_TARGET_COLON>False</SPACE_AFTER_ATTRIBUTE_TARGET_COLON>
<SPACE_BEFORE_INVOCATION_PARENTHESES>False</SPACE_BEFORE_INVOCATION_PARENTHESES>
<SPACE_BEFORE_METHOD_PARENTHESES>False</SPACE_BEFORE_METHOD_PARENTHESES>
<SPACE_BEFORE_TYPE_PARAMETER_PARENTHESES>False</SPACE_BEFORE_TYPE_PARAMETER_PARENTHESES>
<WRAP_ARGUMENTS_STYLE>CHOP_IF_LONG</WRAP_ARGUMENTS_STYLE>
<WRAP_EXTENDS_LIST_STYLE>CHOP_IF_LONG</WRAP_EXTENDS_LIST_STYLE>
<WRAP_LIMIT>200</WRAP_LIMIT>
<WRAP_PARAMETERS_STYLE>CHOP_IF_LONG</WRAP_PARAMETERS_STYLE>
</FormatSettings>
<ImportsSettings>
<AllowAlias>False</AllowAlias>
</ImportsSettings>
<Naming2>
<EventHandlerPatternLong>$object$_On$event$</EventHandlerPatternLong>
<EventHandlerPatternShort>$event$Handler</EventHandlerPatternShort>
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="TypesAndNamespaces" />
<PredefinedRule Inspect="True" Prefix="I" Suffix="" Style="AaBb" ElementKind="Interfaces" />
<PredefinedRule Inspect="True" Prefix="T" Suffix="" Style="AaBb" ElementKind="TypeParameters" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="MethodPropertyEvent" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="Locals" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="LocalConstants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="Parameters" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="PublicFields" />
<PredefinedRule Inspect="True" Prefix="_" Suffix="" Style="aaBb" ElementKind="PrivateInstanceFields" />
<PredefinedRule Inspect="True" Prefix="_" Suffix="" Style="aaBb" ElementKind="PrivateStaticFields" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="Constants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="PrivateConstants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="StaticReadonly" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="PrivateStaticReadonly" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="EnumMember" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="Other" />
</Naming2>
</VB>
<Web>
<Naming2>
<UserRule Name="ASP_TAG_NAME" Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<UserRule Name="ASP_TAG_PREFIX" Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<UserRule Name="ASP_FIELD" Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<UserRule Name="ASP_HTML_CONTROL" Inspect="True" Prefix="" Suffix="" Style="AaBb" />
</Naming2>
</Web>
<Xaml>
<Naming2>
<UserRule Name="XAML_FIELD" Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
<UserRule Name="NAMESPACE_ALIAS" Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<UserRule Name="XAML_RESOURCE" Inspect="True" Prefix="" Suffix="" Style="AaBb" />
</Naming2>
</Xaml>
<XML>
<FormatSettings />
</XML>
<FileHeader><![CDATA['
' DotNetNuke® - http://www.dotnetnuke.com
' Copyright (c) 2002-2010
' by DotNetNuke Corporation
'
' Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
' documentation files (the "Software"), to deal in the Software without restriction, including without limitation
' the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
' to permit persons to whom the Software is furnished to do so, subject to the following conditions:
'
' The above copyright notice and this permission notice shall be included in all copies or substantial portions
' of the Software.
'
' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
' TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
' THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
' CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
' DEALINGS IN THE SOFTWARE.
']]></FileHeader>
<GenerateMemberBody />
<Naming2>
<EventHandlerPatternLong>$object$_On$event$</EventHandlerPatternLong>
<EventHandlerPatternShort>$event$Handler</EventHandlerPatternShort>
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="TypesAndNamespaces" />
<PredefinedRule Inspect="True" Prefix="I" Suffix="" Style="AaBb" ElementKind="Interfaces" />
<PredefinedRule Inspect="True" Prefix="T" Suffix="" Style="AaBb" ElementKind="TypeParameters" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="MethodPropertyEvent" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="Locals" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="LocalConstants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="aaBb" ElementKind="Parameters" />
<PredefinedRule Inspect="True" Prefix="_" Suffix="" Style="aaBb" ElementKind="PublicFields" />
<PredefinedRule Inspect="True" Prefix="_" Suffix="" Style="aaBb" ElementKind="PrivateInstanceFields" />
<PredefinedRule Inspect="True" Prefix="_" Suffix="" Style="aaBb" ElementKind="PrivateStaticFields" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AA_BB" ElementKind="Constants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AA_BB" ElementKind="PrivateConstants" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="StaticReadonly" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="PrivateStaticReadonly" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="EnumMember" />
<PredefinedRule Inspect="True" Prefix="" Suffix="" Style="AaBb" ElementKind="Other" />
<Abbreviation Text="IIS" />
<Abbreviation Text="ID" />
<Abbreviation Text="SHA" />
<Abbreviation Text="WCF" />
<Abbreviation Text="XML" />
<Abbreviation Text="XSL" />
<Abbreviation Text="AJAX" />
<Abbreviation Text="GUID" />
<Abbreviation Text="URL" />
<Abbreviation Text="DNN" />
</Naming2>
</CodeStyleSettings>
</Configuration>

@ -13,7 +13,8 @@ namespace NzbDrone.Core.Test
public class ParserTest
{
[Test]
[Row("Sonny.With.a.Chance.S02E15", 2,15)]
[Row("Sonny.With.a.Chance.S02E15", 2, 15)]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", 3, 1)]
[Row("Two.and.a.Half.Me.103.720p.HDTV.X264-DIMENSION", 1, 3)]
[Row("Two.and.a.Half.Me.113.720p.HDTV.X264-DIMENSION", 1, 13)]
[Row("Two.and.a.Half.Me.1013.720p.HDTV.X264-DIMENSION", 10, 13)]
@ -34,18 +35,6 @@ namespace NzbDrone.Core.Test
Assert.AreEqual(episode, result.Episodes[0]);
}
[Test]
[Row("The.Office.US.S03E01E02.DUAL.BDRip.XviD.AC3.-HELLYWOOD", 3, 1, 2)]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", 3, 1, 6)]
public void episode_parse_multi(string path, int season, int episodeOne, int episodeTwo)
{
var result = Parser.ParseEpisodeInfo(path);
Assert.Count(2, result);
Assert.AreEqual(season, result[0].SeasonNumber);
Assert.AreEqual(episodeOne, result[0].EpisodeNumber);
Assert.AreEqual(episodeTwo, result[1].EpisodeNumber);
}
[Test]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", QualityTypes.BDRip)]
[Row("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", QualityTypes.BDRip)]
@ -84,4 +73,4 @@ namespace NzbDrone.Core.Test
Assert.AreEqual(clean, result);
}
}
}
}

@ -9,325 +9,6 @@ using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core
{
internal static class Parser
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly Regex[] ReportTitleRegex = new[]
{
new Regex(@"(?<title>.+?)?\W?(?<year>\d+?)?\WS?(?<season>\d+)((?:\-|\.|[a-z])(?<episode>\d+))+\W(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
new Regex(@"(?<title>.+?)?\W?(?<year>\d+?)?\WS?(?<season>\d+)(?<episode>\d{2})\W(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled) //Supports 103/113 naming
};
private static readonly Regex[] SeasonReportTitleRegex = new[]
{
new Regex(@"(?<title>.+?)?\W?(?<year>\d{4}?)?\W(?:S|Season)?\W?(?<season>\d+)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
};
private static readonly Regex NormalizeRegex = new Regex(@"((\s|^)the(\s|$))|((\s|^)and(\s|$))|[^a-z]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Parses a post title into list of episodes it contains
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>List of episodes contained to the post</returns>
internal static EpisodeParseResult ParseEpisodeInfo(string title)
{
Logger.Trace("Parsing string '{0}'", title);
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(title);
if (match.Count != 0)
{
var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
var year = 0;
Int32.TryParse(match[0].Groups["year"].Value, out year);
if (year < 1900 || year > DateTime.Now.Year + 1)
{
year = 0;
}
var seasonNumber = Convert.ToInt32(matchGroup.Groups["season"].Value);
foreach (Capture episode in matchGroup.Groups["episode"].Captures)
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core
{
internal static class Parser
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly Regex[] ReportTitleRegex = new[]
{
new Regex(@"(?<title>.+?)?\W?(?<year>\d+?)?\WS?(?<season>\d+)(?:\-|\.|[a-z])(?<episode>\d+)\W(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
new Regex(@"(?<title>.+?)?\W?(?<year>\d+?)?\WS?(?<season>\d+)(?<episode>\d{2})\W(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled) //Supports 103/113 naming
};
private static readonly Regex[] SeasonReportTitleRegex = new[]
{
new Regex(@"(?<title>.+?)?\W?(?<year>\d{4}?)?\W(?:S|Season)?\W?(?<season>\d+)(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled),
};
private static readonly Regex NormalizeRegex = new Regex(@"((\s|^)the(\s|$))|((\s|^)and(\s|$))|[^a-z]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Parses a post title into list of episodes it contains
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>List of episodes contained to the post</returns>
internal static EpisodeParseResult ParseEpisodeInfo(string title)
{
Logger.Trace("Parsing string '{0}'", title);
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(title);
if (match.Count != 0)
{
var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
var year = 0;
Int32.TryParse(match[0].Groups["year"].Value, out year);
if (year < 1900 || year > DateTime.Now.Year + 1)
{
year = 0;
}
var parsedEpisode = new EpisodeParseResult
{
Proper = title.ToLower().Contains("proper"),
SeriesTitle = seriesName,
SeasonNumber = Convert.ToInt32(match[0].Groups["season"].Value),
Year = year,
Episodes = new List<int>()
};
foreach (Match matchGroup in match)
{
parsedEpisode.Episodes.Add(Convert.ToInt32(matchGroup.Groups["episode"].Value));
}
parsedEpisode.Quality = ParseQuality(title);
Logger.Trace("Episode Parsed. {0}", parsedEpisode);
return parsedEpisode;
}
}
return null;
}
/// <summary>
/// Parses a post title into season it contains
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>Season information contained in the post</returns>
internal static SeasonParseResult ParseSeasonInfo(string title)
{
Logger.Trace("Parsing string '{0}'", title);
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(title);
if (match.Count != 0)
{
var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
var year = 0;
Int32.TryParse(match[0].Groups["year"].Value, out year);
if (year < 1900 || year > DateTime.Now.Year + 1)
{
year = 0;
}
var seasonNumber = Convert.ToInt32(match[0].Groups["season"].Value);
var result = new SeasonParseResult
{
SeriesTitle = seriesName,
SeasonNumber = seasonNumber,
Year = year
};
result.Quality = ParseQuality(title);
Logger.Trace("Season Parsed. {0}", result);
return result;
}
}
return null; //Return null
}
/// <summary>
/// Parses a post title to find the series that relates to it
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>Normalized Series Name</returns>
internal static string ParseSeriesName(string title)
{
Logger.Trace("Parsing string '{0}'", title);
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(title);
if (match.Count != 0)
{
var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
var year = 0;
Int32.TryParse(match[0].Groups["year"].Value, out year);
if (year < 1900 || year > DateTime.Now.Year + 1)
{
year = 0;
}
Logger.Trace("Series Parsed. {0}", seriesName);
return seriesName;
}
}
return String.Empty;
}
/// <summary>
/// Parses proper status out of a report title
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns></returns>
internal static bool ParseProper(string title)
{
return title.ToLower().Contains("proper");
}
private static QualityTypes ParseQuality(string name)
{
Logger.Trace("Trying to parse quality for {0}", name);
var result = QualityTypes.Unknown;
name = name.ToLowerInvariant();
if (name.Contains("dvd"))
return QualityTypes.DVD;
if (name.Contains("bdrip") || name.Contains("brrip"))
{
return QualityTypes.BDRip;
}
if (name.Contains("xvid") || name.Contains("divx"))
{
if (name.Contains("bluray"))
{
return QualityTypes.BDRip;
}
return QualityTypes.TV;
}
if (name.Contains("bluray"))
{
if (name.Contains("720p"))
return QualityTypes.Bluray720;
if (name.Contains("1080p"))
return QualityTypes.Bluray1080;
return QualityTypes.Bluray720;
}
if (name.Contains("web-dl"))
return QualityTypes.WEBDL;
if (name.Contains("x264") || name.Contains("h264") || name.Contains("720p"))
return QualityTypes.HDTV;
//Based on extension
if (result == QualityTypes.Unknown)
{
switch (new FileInfo(name).Extension.ToLower())
{
case ".avi":
case ".xvid":
case ".wmv":
{
result = QualityTypes.TV;
break;
}
case ".mkv":
{
result = QualityTypes.HDTV;
break;
}
}
}
Logger.Trace("Quality Parsed:{0} Title:", result, name);
return result;
}
/// <summary>
/// Normalizes the title. removing all non-word characters as well as common tokens
/// such as 'the' and 'and'
/// </summary>
/// <param name="title">title</param>
/// <returns></returns>
internal static string NormalizeTitle(string title)
{
return NormalizeRegex.Replace(title, String.Empty).ToLower();
}
//Note: changing case on path is a problem for running on mono/*nix
//Not going to change the casing any more... Looks Ugly in UI anyways :P
public static string NormalizePath(string path)
{
if (String.IsNullOrEmpty(path))
throw new ArgumentException("Path can not be null or empty");
var info = new FileInfo(path);
if (info.FullName.StartsWith(@"\\")) //UNC
{
return info.FullName.TrimEnd('/', '\\', ' ');
}
return info.FullName.Trim('/', '\\', ' ');
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core
{
internal static class Parser
@ -575,211 +256,6 @@ namespace NzbDrone.Core
return info.FullName.Trim('/', '\\', ' ');
}
}
}
Proper = title.ToLower().Contains("proper"),
SeriesTitle = seriesName,
SeasonNumber = Convert.ToInt32(match[0].Groups["season"].Value),
Year = year,
Episodes = new List<int>()
};
foreach (Match matchGroup in match)
{
parsedEpisode.Episodes.Add(Convert.ToInt32(matchGroup.Groups["episode"].Value));
}
parsedEpisode.Quality = ParseQuality(title);
Logger.Trace("Episode Parsed. {0}", parsedEpisode);
return parsedEpisode;
}
}
return null;
}
/// <summary>
/// Parses a post title into season it contains
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>Season information contained in the post</returns>
internal static SeasonParseResult ParseSeasonInfo(string title)
{
Logger.Trace("Parsing string '{0}'", title);
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(title);
if (match.Count != 0)
{
var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
var year = 0;
Int32.TryParse(match[0].Groups["year"].Value, out year);
if (year < 1900 || year > DateTime.Now.Year + 1)
{
year = 0;
}
var seasonNumber = Convert.ToInt32(match[0].Groups["season"].Value);
var result = new SeasonParseResult
{
SeriesTitle = seriesName,
SeasonNumber = seasonNumber,
Year = year
};
result.Quality = ParseQuality(title);
Logger.Trace("Season Parsed. {0}", result);
return result;
}
}
return null; //Return null
}
/// <summary>
/// Parses a post title to find the series that relates to it
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>Normalized Series Name</returns>
internal static string ParseSeriesName(string title)
{
Logger.Trace("Parsing string '{0}'", title);
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(title);
if (match.Count != 0)
{
var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
var year = 0;
Int32.TryParse(match[0].Groups["year"].Value, out year);
if (year < 1900 || year > DateTime.Now.Year + 1)
{
year = 0;
}
Logger.Trace("Series Parsed. {0}", seriesName);
return seriesName;
}
}
return String.Empty;
}
/// <summary>
/// Parses proper status out of a report title
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns></returns>
internal static bool ParseProper(string title)
{
return title.ToLower().Contains("proper");
}
private static QualityTypes ParseQuality(string name)
{
Logger.Trace("Trying to parse quality for {0}", name);
var result = QualityTypes.Unknown;
name = name.ToLowerInvariant();
if (name.Contains("dvd"))
return QualityTypes.DVD;
if (name.Contains("bdrip") || name.Contains("brrip"))
{
return QualityTypes.BDRip;
}
if (name.Contains("xvid") || name.Contains("divx"))
{
if (name.Contains("bluray"))
{
return QualityTypes.BDRip;
}
return QualityTypes.TV;
}
if (name.Contains("bluray"))
{
if (name.Contains("720p"))
return QualityTypes.Bluray720;
if (name.Contains("1080p"))
return QualityTypes.Bluray1080;
return QualityTypes.Bluray720;
}
if (name.Contains("web-dl"))
return QualityTypes.WEBDL;
if (name.Contains("x264") || name.Contains("h264") || name.Contains("720p"))
return QualityTypes.HDTV;
//Based on extension
if (result == QualityTypes.Unknown)
{
switch (new FileInfo(name).Extension.ToLower())
{
case ".avi":
case ".xvid":
case ".wmv":
{
result = QualityTypes.TV;
break;
}
case ".mkv":
{
result = QualityTypes.HDTV;
break;
}
}
}
Logger.Trace("Quality Parsed:{0} Title:", result, name);
return result;
}
/// <summary>
/// Normalizes the title. removing all non-word characters as well as common tokens
/// such as 'the' and 'and'
/// </summary>
/// <param name="title">title</param>
/// <returns></returns>
internal static string NormalizeTitle(string title)
{
return NormalizeRegex.Replace(title, String.Empty).ToLower();
}
//Note: changing case on path is a problem for running on mono/*nix
//Not going to change the casing any more... Looks Ugly in UI anyways :P
public static string NormalizePath(string path)
{
if (String.IsNullOrEmpty(path))
throw new ArgumentException("Path can not be null or empty");
var info = new FileInfo(path);
if (info.FullName.StartsWith(@"\\")) //UNC
{
return info.FullName.TrimEnd('/', '\\', ' ');
}
return info.FullName.Trim('/', '\\', ' ');
}
}
}
}

@ -6,6 +6,7 @@ namespace NzbDrone.Core.Providers
public interface ITvDbProvider
{
IList<TvdbSearchResult> SearchSeries(string name);
int GetBestMatch(List<TvdbSearchResult> searchResults, string searchString);
TvdbSearchResult GetSeries(string title);
TvdbSeries GetSeries(int id, bool loadEpisodes);
}

@ -52,6 +52,23 @@ namespace NzbDrone.Core.Providers
return null;
}
public int GetBestMatch(List<TvdbSearchResult> searchResults, string title)
{
if (searchResults.Count == 0)
return 0;
foreach (var tvdbSearchResult in searchResults)
{
if (IsTitleMatch(tvdbSearchResult.SeriesName, title))
{
Logger.Debug("Search for '{0}' was successful", title);
return tvdbSearchResult.Id;
}
}
return searchResults[0].Id;
}
public TvdbSeries GetSeries(int id, bool loadEpisodes)
{
Logger.Debug("Fetching SeriesId'{0}' from tvdb", id);

@ -80,12 +80,14 @@ namespace NzbDrone.Web.Controllers
ViewData["path"] = path;
ViewData["javaPath"] = path.Replace(Path.DirectorySeparatorChar, '|').Replace(Path.VolumeSeparatorChar, '^');
var defaultQuality = _configProvider.DefaultQualityProfile;
var qualityProfiles = _qualityProvider.GetAllProfiles();
ViewData["quality"] = new SelectList(
qualityProfiles,
"QualityProfileId",
"Name",
"HD");
defaultQuality); ;
return PartialView("AddSeriesItem", suggestions);
@ -117,8 +119,9 @@ namespace NzbDrone.Web.Controllers
public SelectList GetSuggestionList(string searchString)
{
var dataVal = _tvDbProvider.SearchSeries(searchString);
var bestResult = _tvDbProvider.GetBestMatch(dataVal.ToList(), searchString);
return new SelectList(dataVal, "Id", "SeriesName");
return new SelectList(dataVal, "Id", "SeriesName", bestResult);
}
}

@ -7,6 +7,9 @@
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%
if (Model.Count() == 0)
Html.DisplayText("No Series to Add");
foreach (var path in Model)
{
Html.RenderAction("RenderPartial", "AddSeries", new { path });

@ -1,6 +1,6 @@
@using NzbDrone.Core.Repository.Quality
@model SelectList
<div padding: 10px" id="div_@(ViewData["guid"])">
<div style="padding:3px" id="div_@(ViewData["guid"])">
<fieldset>
<legend>@ViewData["path"].ToString()</legend>
<div>
@ -11,7 +11,7 @@
.Filterable(f => f.FilterMode(AutoCompleteFilterMode.Contains))
.HighlightFirstMatch(true)
.HtmlAttributes(new { style = "width: 300px;" })
.SelectedIndex(0).Render();}
.Render();}
@Html.Telerik().DropDownList().Name("qualityList_" + ViewData["guid"].ToString()).BindTo((SelectList)ViewData["quality"]).HtmlAttributes(new { style = "width: 100px;" })
<button class="listButton" onclick="addSeries('@ViewData["guid"]','@ViewData["javaPath"].ToString()' )">
Add</button>

Loading…
Cancel
Save