1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-12-12 11:15:43 +02:00

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.
This commit is contained in:
Mark McDowall 2011-04-04 19:48:46 -07:00
parent 148fd044ef
commit 460e48d6a1
8 changed files with 33 additions and 697 deletions

View File

@ -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>

View File

@ -14,6 +14,7 @@ public class ParserTest
{ {
[Test] [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.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.113.720p.HDTV.X264-DIMENSION", 1, 13)]
[Row("Two.and.a.Half.Me.1013.720p.HDTV.X264-DIMENSION", 10, 13)] [Row("Two.and.a.Half.Me.1013.720p.HDTV.X264-DIMENSION", 10, 13)]
@ -34,18 +35,6 @@ public void episode_parse(string path, int season, int episode)
Assert.AreEqual(episode, result.Episodes[0]); 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] [Test]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", QualityTypes.BDRip)] [Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", QualityTypes.BDRip)]
[Row("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", QualityTypes.BDRip)] [Row("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", QualityTypes.BDRip)]

View File

@ -9,64 +9,6 @@
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository.Quality; 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 namespace NzbDrone.Core
{ {
internal static class Parser internal static class Parser
@ -315,471 +257,5 @@ public static string NormalizePath(string path)
} }
}
}
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('/', '\\', ' ');
}
}
}
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('/', '\\', ' ');
}
} }
} }

View File

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

View File

@ -52,6 +52,23 @@ public TvdbSearchResult GetSeries(string title)
return null; 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) public TvdbSeries GetSeries(int id, bool loadEpisodes)
{ {
Logger.Debug("Fetching SeriesId'{0}' from tvdb", id); Logger.Debug("Fetching SeriesId'{0}' from tvdb", id);

View File

@ -80,12 +80,14 @@ public ActionResult RenderPartial(string path)
ViewData["path"] = path; ViewData["path"] = path;
ViewData["javaPath"] = path.Replace(Path.DirectorySeparatorChar, '|').Replace(Path.VolumeSeparatorChar, '^'); ViewData["javaPath"] = path.Replace(Path.DirectorySeparatorChar, '|').Replace(Path.VolumeSeparatorChar, '^');
var defaultQuality = _configProvider.DefaultQualityProfile;
var qualityProfiles = _qualityProvider.GetAllProfiles(); var qualityProfiles = _qualityProvider.GetAllProfiles();
ViewData["quality"] = new SelectList( ViewData["quality"] = new SelectList(
qualityProfiles, qualityProfiles,
"QualityProfileId", "QualityProfileId",
"Name", "Name",
"HD"); defaultQuality); ;
return PartialView("AddSeriesItem", suggestions); return PartialView("AddSeriesItem", suggestions);
@ -117,8 +119,9 @@ public ActionResult _textLookUp(string text, int? filterMode)
public SelectList GetSuggestionList(string searchString) public SelectList GetSuggestionList(string searchString)
{ {
var dataVal = _tvDbProvider.SearchSeries(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);
} }
} }

View File

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

View File

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