using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Linq; using NLog; using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers { public abstract class RssParserBase : IParseFeed { private Logger _logger; protected virtual ReleaseInfo CreateNewReleaseInfo() { return new ReleaseInfo(); } protected RssParserBase() { _logger = NzbDroneLogger.GetLogger(this); } public IEnumerable Process(string xml, string url) { using (var xmlTextReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { ProhibitDtd = false, IgnoreComments = true })) { var document = XDocument.Load(xmlTextReader); var items = document.Descendants("item"); var result = new List(); foreach (var item in items) { try { var reportInfo = ParseFeedItem(item.StripNameSpace(), url); if (reportInfo != null) { reportInfo.DownloadUrl = GetNzbUrl(item); reportInfo.InfoUrl = GetNzbInfoUrl(item); result.Add(reportInfo); } } catch (Exception itemEx) { itemEx.Data.Add("Item", item.Title()); _logger.ErrorException("An error occurred while processing feed item from " + url, itemEx); } } return result; } } private ReleaseInfo ParseFeedItem(XElement item, string url) { var title = GetTitle(item); var reportInfo = CreateNewReleaseInfo(); reportInfo.Title = title; reportInfo.PublishDate = item.PublishDate(); reportInfo.ReleaseGroup = ParseReleaseGroup(title); reportInfo.DownloadUrl = GetNzbUrl(item); reportInfo.InfoUrl = GetNzbInfoUrl(item); try { reportInfo.Size = GetSize(item); } catch (Exception) { throw new SizeParsingException("Unable to parse size from: {0} [{1}]", reportInfo.Title, url); } _logger.Trace("Parsed: {0} from: {1}", reportInfo, item.Title()); return PostProcessor(item, reportInfo); } protected virtual string GetTitle(XElement item) { return item.Title(); } protected virtual string GetNzbUrl(XElement item) { return item.Links().First(); } protected virtual string GetNzbInfoUrl(XElement item) { return String.Empty; } protected abstract long GetSize(XElement item); protected virtual ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult) { return currentResult; } public static string ParseReleaseGroup(string title) { title = title.Trim(); var index = title.LastIndexOf('-'); if (index < 0) index = title.LastIndexOf(' '); if (index < 0) return String.Empty; var group = title.Substring(index + 1); if (@group.Length == title.Length) return String.Empty; return @group.Trim('-', ' ', '[', ']'); } private static readonly Regex ReportSizeRegex = new Regex(@"(?\d+\.\d{1,2}|\d+\,\d+\.\d{1,2}|\d+)\W?(?GB|MB|GiB|MiB)", RegexOptions.IgnoreCase | RegexOptions.Compiled); public static long ParseSize(string sizeString) { var match = ReportSizeRegex.Matches(sizeString); if (match.Count != 0) { var cultureInfo = new CultureInfo("en-US"); var value = Decimal.Parse(Regex.Replace(match[0].Groups["value"].Value, "\\,", ""), cultureInfo); var unit = match[0].Groups["unit"].Value; if (unit.Equals("MB", StringComparison.InvariantCultureIgnoreCase) || unit.Equals("MiB", StringComparison.InvariantCultureIgnoreCase)) { return ConvertToBytes(Convert.ToDouble(value), 2); } if (unit.Equals("GB", StringComparison.InvariantCultureIgnoreCase) || unit.Equals("GiB", StringComparison.InvariantCultureIgnoreCase)) { return ConvertToBytes(Convert.ToDouble(value), 3); } } return 0; } private static long ConvertToBytes(double value, int power) { var multiplier = Math.Pow(1024, power); var result = value * multiplier; return Convert.ToInt64(result); } } }