mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-26 23:06:43 -04:00
New Indexer: TorrentDay
This commit is contained in:
@@ -10,6 +10,7 @@ using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
@@ -608,10 +609,10 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
||||
data = data.ToUpper();
|
||||
break;
|
||||
case "urldecode":
|
||||
data = WebUtilityHelpers.UrlDecode(data, _encoding);
|
||||
data = data.UrlDecode(_encoding);
|
||||
break;
|
||||
case "urlencode":
|
||||
data = WebUtilityHelpers.UrlEncode(data, _encoding);
|
||||
data = data.UrlEncode(_encoding);
|
||||
break;
|
||||
case "timeago":
|
||||
case "reltime":
|
||||
|
||||
@@ -8,6 +8,7 @@ using AngleSharp.Html.Parser;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Definitions.Cardigann;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
public static class DateTimeUtil
|
||||
{
|
||||
public const string Rfc1123ZPattern = "ddd, dd MMM yyyy HH':'mm':'ss z";
|
||||
|
||||
private static readonly Regex _TimeAgoRegexp = new Regex(@"(?i)\bago", RegexOptions.Compiled);
|
||||
private static readonly Regex _TodayRegexp = new Regex(@"(?i)\btoday(?:[\s,]+(?:at){0,1}\s*|[\s,]*|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _TomorrowRegexp = new Regex(@"(?i)\btomorrow(?:[\s,]+(?:at){0,1}\s*|[\s,]*|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _YesterdayRegexp = new Regex(@"(?i)\byesterday(?:[\s,]+(?:at){0,1}\s*|[\s,]*|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _DaysOfWeekRegexp = new Regex(@"(?i)\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\s+at\s+", RegexOptions.Compiled);
|
||||
private static readonly Regex _MissingYearRegexp = new Regex(@"^(\d{1,2}-\d{1,2})(\s|$)", RegexOptions.Compiled);
|
||||
private static readonly Regex _MissingYearRegexp2 = new Regex(@"^(\d{1,2}\s+\w{3})\s+(\d{1,2}\:\d{1,2}.*)$", RegexOptions.Compiled); // 1 Jan 10:30
|
||||
|
||||
public static DateTime UnixTimestampToDateTime(long unixTime)
|
||||
{
|
||||
var dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
dt = dt.AddSeconds(unixTime).ToLocalTime();
|
||||
return dt;
|
||||
}
|
||||
|
||||
public static DateTime UnixTimestampToDateTime(double unixTime)
|
||||
{
|
||||
var unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
var unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond);
|
||||
return new DateTime(unixStart.Ticks + unixTimeStampInTicks);
|
||||
}
|
||||
|
||||
public static double DateTimeToUnixTimestamp(DateTime dt)
|
||||
{
|
||||
var date = dt.ToUniversalTime();
|
||||
var ticks = date.Ticks - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Ticks;
|
||||
var ts = ticks / TimeSpan.TicksPerSecond;
|
||||
return ts;
|
||||
}
|
||||
|
||||
// ex: "2 hours 1 day"
|
||||
public static DateTime FromTimeAgo(string str)
|
||||
{
|
||||
str = str.ToLowerInvariant();
|
||||
if (str.Contains("now"))
|
||||
{
|
||||
return DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
|
||||
}
|
||||
|
||||
str = str.Replace(",", "");
|
||||
str = str.Replace("ago", "");
|
||||
str = str.Replace("and", "");
|
||||
|
||||
var timeAgo = TimeSpan.Zero;
|
||||
var timeagoRegex = new Regex(@"\s*?([\d\.]+)\s*?([^\d\s\.]+)\s*?");
|
||||
var timeagoMatches = timeagoRegex.Match(str);
|
||||
|
||||
while (timeagoMatches.Success)
|
||||
{
|
||||
var val = ParseUtil.CoerceFloat(timeagoMatches.Groups[1].Value);
|
||||
var unit = timeagoMatches.Groups[2].Value;
|
||||
timeagoMatches = timeagoMatches.NextMatch();
|
||||
|
||||
if (unit.Contains("sec") || unit == "s")
|
||||
{
|
||||
timeAgo += TimeSpan.FromSeconds(val);
|
||||
}
|
||||
else if (unit.Contains("min") || unit == "m")
|
||||
{
|
||||
timeAgo += TimeSpan.FromMinutes(val);
|
||||
}
|
||||
else if (unit.Contains("hour") || unit.Contains("hr") || unit == "h")
|
||||
{
|
||||
timeAgo += TimeSpan.FromHours(val);
|
||||
}
|
||||
else if (unit.Contains("day") || unit == "d")
|
||||
{
|
||||
timeAgo += TimeSpan.FromDays(val);
|
||||
}
|
||||
else if (unit.Contains("week") || unit.Contains("wk") || unit == "w")
|
||||
{
|
||||
timeAgo += TimeSpan.FromDays(val * 7);
|
||||
}
|
||||
else if (unit.Contains("month") || unit == "mo")
|
||||
{
|
||||
timeAgo += TimeSpan.FromDays(val * 30);
|
||||
}
|
||||
else if (unit.Contains("year") || unit == "y")
|
||||
{
|
||||
timeAgo += TimeSpan.FromDays(val * 365);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("TimeAgo parsing failed, unknown unit: " + unit);
|
||||
}
|
||||
}
|
||||
|
||||
return DateTime.SpecifyKind(DateTime.Now - timeAgo, DateTimeKind.Local);
|
||||
}
|
||||
|
||||
// Uses the DateTimeRoutines library to parse the date
|
||||
// http://www.codeproject.com/Articles/33298/C-Date-Time-Parser
|
||||
public static DateTime FromFuzzyTime(string str, string format = null)
|
||||
{
|
||||
//var dtFormat = format == "UK" ?
|
||||
// DateTimeRoutines.DateTimeRoutines.DateTimeFormat.UkDate :
|
||||
// DateTimeRoutines.DateTimeRoutines.DateTimeFormat.UsaDate;
|
||||
|
||||
//if (DateTimeRoutines.DateTimeRoutines.TryParseDateOrTime(
|
||||
// str, dtFormat, out DateTimeRoutines.DateTimeRoutines.ParsedDateTime dt))
|
||||
// return dt.DateTime;
|
||||
if (DateTime.TryParse(str, out var dateTimeParsed))
|
||||
{
|
||||
return dateTimeParsed;
|
||||
}
|
||||
|
||||
throw new Exception("FromFuzzyTime parsing failed");
|
||||
}
|
||||
|
||||
public static DateTime FromUnknown(string str, string format = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
str = ParseUtil.NormalizeSpace(str);
|
||||
if (str.ToLower().Contains("now"))
|
||||
{
|
||||
return DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// ... ago
|
||||
var match = _TimeAgoRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var timeago = str;
|
||||
return FromTimeAgo(timeago);
|
||||
}
|
||||
|
||||
// Today ...
|
||||
match = _TodayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
return dt;
|
||||
}
|
||||
|
||||
// Yesterday ...
|
||||
match = _YesterdayRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
dt -= TimeSpan.FromDays(1);
|
||||
return dt;
|
||||
}
|
||||
|
||||
// Tomorrow ...
|
||||
match = _TomorrowRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
dt += TimeSpan.FromDays(1);
|
||||
return dt;
|
||||
}
|
||||
|
||||
// [day of the week] at ... (eg: Saturday at 14:22)
|
||||
match = _DaysOfWeekRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var time = str.Replace(match.Groups[0].Value, "");
|
||||
var dt = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
|
||||
dt += ParseTimeSpan(time);
|
||||
|
||||
DayOfWeek dow;
|
||||
var groupMatchLower = match.Groups[1].Value.ToLower();
|
||||
if (groupMatchLower.StartsWith("monday"))
|
||||
{
|
||||
dow = DayOfWeek.Monday;
|
||||
}
|
||||
else if (groupMatchLower.StartsWith("tuesday"))
|
||||
{
|
||||
dow = DayOfWeek.Tuesday;
|
||||
}
|
||||
else if (groupMatchLower.StartsWith("wednesday"))
|
||||
{
|
||||
dow = DayOfWeek.Wednesday;
|
||||
}
|
||||
else if (groupMatchLower.StartsWith("thursday"))
|
||||
{
|
||||
dow = DayOfWeek.Thursday;
|
||||
}
|
||||
else if (groupMatchLower.StartsWith("friday"))
|
||||
{
|
||||
dow = DayOfWeek.Friday;
|
||||
}
|
||||
else if (groupMatchLower.StartsWith("saturday"))
|
||||
{
|
||||
dow = DayOfWeek.Saturday;
|
||||
}
|
||||
else
|
||||
{
|
||||
dow = DayOfWeek.Sunday;
|
||||
}
|
||||
|
||||
while (dt.DayOfWeek != dow)
|
||||
{
|
||||
dt = dt.AddDays(-1);
|
||||
}
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// try parsing the str as an unix timestamp
|
||||
var unixTimeStamp = long.Parse(str);
|
||||
return UnixTimestampToDateTime(unixTimeStamp);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// it wasn't a timestamp, continue....
|
||||
}
|
||||
|
||||
// add missing year
|
||||
match = _MissingYearRegexp.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var date = match.Groups[1].Value;
|
||||
var newDate = DateTime.Now.Year + "-" + date;
|
||||
str = str.Replace(date, newDate);
|
||||
}
|
||||
|
||||
// add missing year 2
|
||||
match = _MissingYearRegexp2.Match(str);
|
||||
if (match.Success)
|
||||
{
|
||||
var date = match.Groups[1].Value;
|
||||
var time = match.Groups[2].Value;
|
||||
str = date + " " + DateTime.Now.Year + " " + time;
|
||||
}
|
||||
|
||||
return FromFuzzyTime(str, format);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"DateTime parsing failed for \"{str}\": {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
// converts a date/time string to a DateTime object using a GoLang layout
|
||||
public static DateTime ParseDateTimeGoLang(string date, string layout)
|
||||
{
|
||||
date = ParseUtil.NormalizeSpace(date);
|
||||
var pattern = layout;
|
||||
|
||||
// year
|
||||
pattern = pattern.Replace("2006", "yyyy");
|
||||
pattern = pattern.Replace("06", "yy");
|
||||
|
||||
// month
|
||||
pattern = pattern.Replace("January", "MMMM");
|
||||
pattern = pattern.Replace("Jan", "MMM");
|
||||
pattern = pattern.Replace("01", "MM");
|
||||
|
||||
// day
|
||||
pattern = pattern.Replace("Monday", "dddd");
|
||||
pattern = pattern.Replace("Mon", "ddd");
|
||||
pattern = pattern.Replace("02", "dd");
|
||||
pattern = pattern.Replace("2", "d");
|
||||
|
||||
// hours/minutes/seconds
|
||||
pattern = pattern.Replace("05", "ss");
|
||||
|
||||
pattern = pattern.Replace("15", "HH");
|
||||
pattern = pattern.Replace("03", "hh");
|
||||
pattern = pattern.Replace("3", "h");
|
||||
|
||||
pattern = pattern.Replace("04", "mm");
|
||||
pattern = pattern.Replace("4", "m");
|
||||
|
||||
pattern = pattern.Replace("5", "s");
|
||||
|
||||
// month again
|
||||
pattern = pattern.Replace("1", "M");
|
||||
|
||||
// fractional seconds
|
||||
pattern = pattern.Replace(".0000", "ffff");
|
||||
pattern = pattern.Replace(".000", "fff");
|
||||
pattern = pattern.Replace(".00", "ff");
|
||||
pattern = pattern.Replace(".0", "f");
|
||||
|
||||
pattern = pattern.Replace(".9999", "FFFF");
|
||||
pattern = pattern.Replace(".999", "FFF");
|
||||
pattern = pattern.Replace(".99", "FF");
|
||||
pattern = pattern.Replace(".9", "F");
|
||||
|
||||
// AM/PM
|
||||
pattern = pattern.Replace("PM", "tt");
|
||||
pattern = pattern.Replace("pm", "tt"); // not sure if this works
|
||||
|
||||
// timezones
|
||||
// these might need further tuning
|
||||
pattern = pattern.Replace("Z07:00", "'Z'zzz");
|
||||
pattern = pattern.Replace("Z07", "'Z'zz");
|
||||
pattern = pattern.Replace("Z07:00", "'Z'zzz");
|
||||
pattern = pattern.Replace("Z07", "'Z'zz");
|
||||
pattern = pattern.Replace("-07:00", "zzz");
|
||||
pattern = pattern.Replace("-07", "zz");
|
||||
|
||||
try
|
||||
{
|
||||
return DateTime.ParseExact(date, pattern, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
throw new FormatException($"Error while parsing DateTime \"{date}\", using layout \"{layout}\" ({pattern}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeSpan ParseTimeSpan(string time) =>
|
||||
string.IsNullOrWhiteSpace(time)
|
||||
? TimeSpan.Zero
|
||||
: DateTime.Parse(time).TimeOfDay;
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
public static class ParseUtil
|
||||
{
|
||||
private static readonly Regex InvalidXmlChars =
|
||||
new Regex(
|
||||
@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]",
|
||||
RegexOptions.Compiled);
|
||||
private static readonly Regex ImdbId = new Regex(@"^(?:tt)?(\d{1,8})$", RegexOptions.Compiled);
|
||||
|
||||
public static string NormalizeSpace(string s) => s.Trim();
|
||||
|
||||
public static string NormalizeMultiSpaces(string s) =>
|
||||
new Regex(@"\s+").Replace(NormalizeSpace(s), " ");
|
||||
|
||||
public static string NormalizeNumber(string s) =>
|
||||
NormalizeSpace(s)
|
||||
.Replace("-", "0")
|
||||
.Replace(",", "");
|
||||
|
||||
public static string RemoveInvalidXmlChars(string text) => string.IsNullOrEmpty(text) ? "" : InvalidXmlChars.Replace(text, "");
|
||||
|
||||
public static double CoerceDouble(string str) => double.Parse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
|
||||
public static float CoerceFloat(string str) => float.Parse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
|
||||
public static int CoerceInt(string str) => int.Parse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
|
||||
public static long CoerceLong(string str) => long.Parse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
|
||||
public static bool TryCoerceDouble(string str, out double result) => double.TryParse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||
|
||||
public static bool TryCoerceFloat(string str, out float result) => float.TryParse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||
|
||||
public static bool TryCoerceInt(string str, out int result) => int.TryParse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||
|
||||
public static bool TryCoerceLong(string str, out long result) => long.TryParse(NormalizeNumber(str), NumberStyles.Any, CultureInfo.InvariantCulture, out result);
|
||||
|
||||
/*
|
||||
public static string GetArgumentFromQueryString(string url, string argument)
|
||||
{
|
||||
if (url == null || argument == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var qsStr = url.Split(new char[] { '?' }, 2)[1];
|
||||
qsStr = qsStr.Split(new char[] { '#' }, 2)[0];
|
||||
var qs = QueryHelpers.ParseQuery(qsStr);
|
||||
return qs[argument].FirstOrDefault();
|
||||
}*/
|
||||
|
||||
public static long? GetLongFromString(string str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var idRegEx = new Regex(@"(\d+)", RegexOptions.Compiled);
|
||||
var idMatch = idRegEx.Match(str);
|
||||
if (!idMatch.Success)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var id = idMatch.Groups[1].Value;
|
||||
return CoerceLong(id);
|
||||
}
|
||||
|
||||
public static int? GetImdbID(string imdbstr)
|
||||
{
|
||||
if (imdbstr == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var match = ImdbId.Match(imdbstr);
|
||||
if (!match.Success)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(match.Groups[1].Value, NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static string GetFullImdbID(string imdbstr)
|
||||
{
|
||||
var imdbid = GetImdbID(imdbstr);
|
||||
if (imdbid == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return "tt" + ((int)imdbid).ToString("D7");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using AngleSharp.Dom;
|
||||
using AngleSharp.Html;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
public static class StringUtil
|
||||
{
|
||||
/*
|
||||
public static string StripNonAlphaNumeric(this string str, string replacement = "") =>
|
||||
StripRegex(str, "[^a-zA-Z0-9 -]", replacement);
|
||||
|
||||
public static string StripRegex(string str, string regex, string replacement = "")
|
||||
{
|
||||
var rgx = new Regex(regex);
|
||||
str = rgx.Replace(str, replacement);
|
||||
return str;
|
||||
}
|
||||
|
||||
// replaces culture specific characters with the corresponding base characters (e.g. è becomes e).
|
||||
public static string RemoveDiacritics(string s)
|
||||
{
|
||||
var normalizedString = s.Normalize(NormalizationForm.FormD);
|
||||
var stringBuilder = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < normalizedString.Length; i++)
|
||||
{
|
||||
var c = normalizedString[i];
|
||||
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string FromBase64(string str) =>
|
||||
Encoding.UTF8.GetString(Convert.FromBase64String(str));
|
||||
|
||||
/// <summary>
|
||||
/// Convert an array of bytes to a string of hex digits
|
||||
/// </summary>
|
||||
/// <param name="bytes">array of bytes</param>
|
||||
/// <returns>String of hex digits</returns>
|
||||
public static string HexStringFromBytes(byte[] bytes) =>
|
||||
string.Join("", bytes.Select(b => b.ToString("X2")));
|
||||
|
||||
/// <summary>
|
||||
/// Compute hash for string encoded as UTF8
|
||||
/// </summary>
|
||||
/// <param name="s">String to be hashed</param>
|
||||
/// <returns>40-character hex string</returns>
|
||||
public static string HashSHA1(string s)
|
||||
{
|
||||
var sha1 = SHA1.Create();
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(s);
|
||||
var hashBytes = sha1.ComputeHash(bytes);
|
||||
|
||||
return HexStringFromBytes(hashBytes);
|
||||
}
|
||||
|
||||
public static string Hash(string s)
|
||||
{
|
||||
// Use input string to calculate MD5 hash
|
||||
var md5 = System.Security.Cryptography.MD5.Create();
|
||||
|
||||
var inputBytes = System.Text.Encoding.ASCII.GetBytes(s);
|
||||
var hashBytes = md5.ComputeHash(inputBytes);
|
||||
|
||||
return HexStringFromBytes(hashBytes);
|
||||
}
|
||||
|
||||
// Is never used
|
||||
// remove in favor of Exception.ToString() ?
|
||||
public static string GetExceptionDetails(this Exception exception)
|
||||
{
|
||||
var properties = exception.GetType()
|
||||
.GetProperties();
|
||||
var fields = properties
|
||||
.Select(property => new
|
||||
{
|
||||
Name = property.Name,
|
||||
Value = property.GetValue(exception, null)
|
||||
})
|
||||
.Select(x => string.Format(
|
||||
"{0} = {1}",
|
||||
x.Name,
|
||||
x.Value != null ? x.Value.ToString() : string.Empty
|
||||
));
|
||||
return string.Join("\n", fields);
|
||||
}
|
||||
*/
|
||||
|
||||
private static char[] MakeValidFileName_invalids;
|
||||
|
||||
/// <summary>Replaces characters in <c>text</c> that are not allowed in
|
||||
/// file names with the specified replacement character.</summary>
|
||||
/// <param name="text">Text to make into a valid filename. The same string is returned if it is valid already.</param>
|
||||
/// <param name="replacement">Replacement character, or null to simply remove bad characters.</param>
|
||||
/// <param name="fancy">Whether to replace quotes and slashes with the non-ASCII characters ” and ⁄.</param>
|
||||
/// <returns>A string that can be used as a filename. If the output string would otherwise be empty, returns "_".</returns>
|
||||
public static string MakeValidFileName(string text, char? replacement = '_', bool fancy = true)
|
||||
{
|
||||
var sb = new StringBuilder(text.Length);
|
||||
var invalids = MakeValidFileName_invalids ?? (MakeValidFileName_invalids = Path.GetInvalidFileNameChars());
|
||||
var changed = false;
|
||||
for (var i = 0; i < text.Length; i++)
|
||||
{
|
||||
var c = text[i];
|
||||
if (invalids.Contains(c))
|
||||
{
|
||||
changed = true;
|
||||
var repl = replacement ?? '\0';
|
||||
if (fancy)
|
||||
{
|
||||
if (c == '"')
|
||||
{
|
||||
repl = '”'; // U+201D right double quotation mark
|
||||
}
|
||||
else if (c == '\'')
|
||||
{
|
||||
repl = '’'; // U+2019 right single quotation mark
|
||||
}
|
||||
else if (c == '/')
|
||||
{
|
||||
repl = '⁄'; // U+2044 fraction slash
|
||||
}
|
||||
}
|
||||
|
||||
if (repl != '\0')
|
||||
{
|
||||
sb.Append(repl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.Length == 0)
|
||||
{
|
||||
return "_";
|
||||
}
|
||||
|
||||
return changed ? sb.ToString() : text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a NameValueCollection to an appropriately formatted query string.
|
||||
/// Duplicate keys are allowed in a NameValueCollection, but are stored as a csv string in Value.
|
||||
/// This function handles leaving the values together in the csv string or splitting the value into separate keys
|
||||
/// </summary>
|
||||
/// <param name="collection">The NameValueCollection being converted</param>
|
||||
/// <param name="encoding">The Encoding to use in url encoding Value</param>
|
||||
/// <param name="duplicateKeysIfMulti">Duplicate keys are handled as true => {"Key=Val1", "Key=Val2} or false => {"Key=Val1,Val2"}</param>
|
||||
/// <param name="separator">The string used to separate each query value</param>
|
||||
/// <returns>A web encoded string of key=value parameters separated by the separator</returns>
|
||||
public static string GetQueryString(this NameValueCollection collection,
|
||||
Encoding encoding = null,
|
||||
bool duplicateKeysIfMulti = false,
|
||||
string separator = "&") =>
|
||||
collection.ToEnumerable(duplicateKeysIfMulti).GetQueryString(encoding, separator);
|
||||
|
||||
public static string GetQueryString(this IEnumerable<KeyValuePair<string, string>> collection,
|
||||
Encoding encoding = null,
|
||||
string separator = "&") =>
|
||||
string.Join(separator,
|
||||
collection.Select(a => $"{a.Key}={WebUtilityHelpers.UrlEncode(a.Value, encoding ?? Encoding.UTF8)}"));
|
||||
|
||||
public static void Add(this ICollection<KeyValuePair<string, string>> collection, string key, string value) => collection.Add(new KeyValuePair<string, string>(key, value));
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, string>> ToEnumerable(
|
||||
this NameValueCollection collection, bool duplicateKeysIfMulti = false)
|
||||
{
|
||||
foreach (string key in collection.Keys)
|
||||
{
|
||||
var value = collection[key];
|
||||
if (duplicateKeysIfMulti)
|
||||
{
|
||||
foreach (var val in value.Split(','))
|
||||
{
|
||||
yield return new KeyValuePair<string, string>(key, val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new KeyValuePair<string, string>(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToHtmlPretty(this IElement element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
return "<NULL>";
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
var sw = new StringWriter(sb);
|
||||
var formatter = new PrettyMarkupFormatter();
|
||||
element.ToHtml(sw, formatter);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GenerateRandom(int length)
|
||||
{
|
||||
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var randBytes = new byte[length];
|
||||
using (var rngCsp = new RNGCryptoServiceProvider())
|
||||
{
|
||||
rngCsp.GetBytes(randBytes);
|
||||
var key = "";
|
||||
foreach (var b in randBytes)
|
||||
{
|
||||
key += chars[b % chars.Length];
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
{
|
||||
public static class WebUtilityHelpers
|
||||
{
|
||||
public static string UrlEncode(string searchString, Encoding encoding)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchString))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var bytes = encoding.GetBytes(searchString);
|
||||
return encoding.GetString(WebUtility.UrlEncodeToBytes(bytes, 0, bytes.Length));
|
||||
}
|
||||
|
||||
public static string UrlDecode(string searchString, Encoding encoding)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchString))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var inputBytes = encoding.GetBytes(searchString);
|
||||
return encoding.GetString(WebUtility.UrlDecodeToBytes(inputBytes, 0, inputBytes.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,7 +226,6 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var descriptions = new List<string>();
|
||||
var tags = new List<string>();
|
||||
|
||||
|
||||
release.MinimumRatio = 1.1;
|
||||
release.MinimumSeedTime = 432000; // 120 hours
|
||||
release.Title = row.name;
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using FluentValidation;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public class TorrentDay : HttpIndexerBase<TorrentDaySettings>
|
||||
{
|
||||
public override string Name => "TorrentDay";
|
||||
|
||||
public override string BaseUrl => "https://torrentday.cool/";
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
public override IndexerCapabilities Capabilities => SetCapabilities();
|
||||
|
||||
public TorrentDay(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, Logger logger)
|
||||
: base(httpClient, indexerStatusService, configService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new TorrentDayRequestGenerator() { Settings = Settings, Capabilities = Capabilities, BaseUrl = BaseUrl };
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new TorrentDayParser(Settings, Capabilities.Categories, BaseUrl);
|
||||
}
|
||||
|
||||
private IndexerCapabilities SetCapabilities()
|
||||
{
|
||||
var caps = new IndexerCapabilities
|
||||
{
|
||||
TvSearchParams = new List<TvSearchParam>
|
||||
{
|
||||
TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep, TvSearchParam.ImdbId
|
||||
},
|
||||
MovieSearchParams = new List<MovieSearchParam>
|
||||
{
|
||||
MovieSearchParam.Q, MovieSearchParam.ImdbId
|
||||
},
|
||||
MusicSearchParams = new List<MusicSearchParam>
|
||||
{
|
||||
MusicSearchParam.Q
|
||||
},
|
||||
BookSearchParams = new List<BookSearchParam>
|
||||
{
|
||||
BookSearchParam.Q
|
||||
}
|
||||
};
|
||||
|
||||
caps.Categories.AddCategoryMapping(29, NewznabStandardCategory.TVAnime, "Anime");
|
||||
caps.Categories.AddCategoryMapping(28, NewznabStandardCategory.PC, "Appz/Packs");
|
||||
caps.Categories.AddCategoryMapping(42, NewznabStandardCategory.AudioAudiobook, "Audio Books");
|
||||
caps.Categories.AddCategoryMapping(20, NewznabStandardCategory.Books, "Books");
|
||||
caps.Categories.AddCategoryMapping(30, NewznabStandardCategory.TVDocumentary, "Documentary");
|
||||
caps.Categories.AddCategoryMapping(47, NewznabStandardCategory.Other, "Fonts");
|
||||
caps.Categories.AddCategoryMapping(43, NewznabStandardCategory.PCMac, "Mac");
|
||||
|
||||
caps.Categories.AddCategoryMapping(96, NewznabStandardCategory.MoviesUHD, "Movie/4K");
|
||||
caps.Categories.AddCategoryMapping(25, NewznabStandardCategory.MoviesSD, "Movies/480p");
|
||||
caps.Categories.AddCategoryMapping(11, NewznabStandardCategory.MoviesBluRay, "Movies/Bluray");
|
||||
caps.Categories.AddCategoryMapping(5, NewznabStandardCategory.MoviesBluRay, "Movies/Bluray-Full");
|
||||
caps.Categories.AddCategoryMapping(3, NewznabStandardCategory.MoviesDVD, "Movies/DVD-R");
|
||||
caps.Categories.AddCategoryMapping(21, NewznabStandardCategory.MoviesSD, "Movies/MP4");
|
||||
caps.Categories.AddCategoryMapping(22, NewznabStandardCategory.MoviesForeign, "Movies/Non-English");
|
||||
caps.Categories.AddCategoryMapping(13, NewznabStandardCategory.Movies, "Movies/Packs");
|
||||
caps.Categories.AddCategoryMapping(44, NewznabStandardCategory.MoviesSD, "Movies/SD/x264");
|
||||
caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.Movies, "Movies/x265");
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.MoviesSD, "Movies/XviD");
|
||||
|
||||
caps.Categories.AddCategoryMapping(17, NewznabStandardCategory.AudioMP3, "Music/Audio");
|
||||
caps.Categories.AddCategoryMapping(23, NewznabStandardCategory.AudioForeign, "Music/Non-English");
|
||||
caps.Categories.AddCategoryMapping(41, NewznabStandardCategory.Audio, "Music/Packs");
|
||||
caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.AudioVideo, "Music/Video");
|
||||
caps.Categories.AddCategoryMapping(27, NewznabStandardCategory.Audio, "Music/Flac");
|
||||
|
||||
caps.Categories.AddCategoryMapping(45, NewznabStandardCategory.AudioOther, "Podcast");
|
||||
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.PCGames, "PC/Games");
|
||||
caps.Categories.AddCategoryMapping(18, NewznabStandardCategory.ConsolePS3, "PS3");
|
||||
caps.Categories.AddCategoryMapping(8, NewznabStandardCategory.ConsolePSP, "PSP");
|
||||
caps.Categories.AddCategoryMapping(10, NewznabStandardCategory.ConsoleWii, "Wii");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.ConsoleXBox360, "Xbox-360");
|
||||
|
||||
caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.TVSD, "TV/480p");
|
||||
caps.Categories.AddCategoryMapping(32, NewznabStandardCategory.TVHD, "TV/Bluray");
|
||||
caps.Categories.AddCategoryMapping(31, NewznabStandardCategory.TVSD, "TV/DVD-R");
|
||||
caps.Categories.AddCategoryMapping(33, NewznabStandardCategory.TVSD, "TV/DVD-Rip");
|
||||
caps.Categories.AddCategoryMapping(46, NewznabStandardCategory.TVSD, "TV/Mobile");
|
||||
caps.Categories.AddCategoryMapping(14, NewznabStandardCategory.TV, "TV/Packs");
|
||||
caps.Categories.AddCategoryMapping(26, NewznabStandardCategory.TVSD, "TV/SD/x264");
|
||||
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.TVHD, "TV/x264");
|
||||
caps.Categories.AddCategoryMapping(34, NewznabStandardCategory.TVUHD, "TV/x265");
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVSD, "TV/XviD");
|
||||
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.XXX, "XXX/Movies");
|
||||
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.XXXPack, "XXX/Packs");
|
||||
|
||||
return caps;
|
||||
}
|
||||
}
|
||||
|
||||
public class TorrentDayRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
public TorrentDaySettings Settings { get; set; }
|
||||
public IndexerCapabilities Capabilities { get; set; }
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
public TorrentDayRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(string term, int[] categories, string imdbId = null)
|
||||
{
|
||||
var searchUrl = BaseUrl + "t.json";
|
||||
|
||||
var cats = Capabilities.Categories.MapTorznabCapsToTrackers(categories);
|
||||
if (cats.Count == 0)
|
||||
{
|
||||
cats = Capabilities.Categories.GetTrackerCategories();
|
||||
}
|
||||
|
||||
var catStr = string.Join(";", cats);
|
||||
searchUrl = searchUrl + "?" + catStr;
|
||||
|
||||
if (imdbId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
searchUrl += ";q=" + imdbId;
|
||||
}
|
||||
else
|
||||
{
|
||||
searchUrl += ";q=" + term.UrlEncode(Encoding.UTF8);
|
||||
}
|
||||
|
||||
var request = new IndexerRequest(searchUrl, HttpAccept.Rss);
|
||||
|
||||
foreach (var cookie in CookieUtil.CookieHeaderToDictionary(Settings.Cookie))
|
||||
{
|
||||
request.HttpRequest.Cookies.Add(cookie.Key, cookie.Value);
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories, searchCriteria.ImdbId));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(string.Format("{0}", searchCriteria.SearchTerm), searchCriteria.Categories));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class TorrentDayParser : IParseIndexerResponse
|
||||
{
|
||||
private readonly TorrentDaySettings _settings;
|
||||
private readonly IndexerCapabilitiesCategories _categories;
|
||||
private readonly string _baseUrl;
|
||||
|
||||
public TorrentDayParser(TorrentDaySettings settings, IndexerCapabilitiesCategories categories, string baseUrl)
|
||||
{
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
_baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public IList<ReleaseInfo> ParseResponse(IndexerResponse indexerResponse)
|
||||
{
|
||||
var torrentInfos = new List<TorrentInfo>();
|
||||
|
||||
var rows = JsonConvert.DeserializeObject<dynamic>(indexerResponse.Content);
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var title = (string)row.name;
|
||||
|
||||
var torrentId = (long)row.t;
|
||||
var details = new Uri(_baseUrl + "details.php?id=" + torrentId);
|
||||
var seeders = (int)row.seeders;
|
||||
var imdbId = (string)row["imdb-id"];
|
||||
var downloadMultiplier = (double?)row["download-multiplier"] ?? 1;
|
||||
var link = new Uri(_baseUrl + "download.php/" + torrentId + "/" + torrentId + ".torrent");
|
||||
var publishDate = DateTimeUtil.UnixTimestampToDateTime((long)row.ctime).ToLocalTime();
|
||||
var imdb = ParseUtil.GetImdbID(imdbId) ?? 0;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
Title = title,
|
||||
Guid = details.AbsoluteUri,
|
||||
DownloadUrl = link.AbsoluteUri,
|
||||
PublishDate = publishDate,
|
||||
Category = _categories.MapTrackerCatToNewznab(row.c.ToString()),
|
||||
Size = (long)row.size,
|
||||
Files = (int)row.files,
|
||||
Grabs = (int)row.completed,
|
||||
Seeders = seeders,
|
||||
Peers = seeders + (int)row.leechers,
|
||||
ImdbId = imdb,
|
||||
DownloadVolumeFactor = downloadMultiplier,
|
||||
UploadVolumeFactor = 1,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 172800 // 48 hours
|
||||
};
|
||||
|
||||
torrentInfos.Add(release);
|
||||
}
|
||||
|
||||
return torrentInfos.ToArray();
|
||||
}
|
||||
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
||||
public class TorrentDaySettingsValidator : AbstractValidator<TorrentDaySettings>
|
||||
{
|
||||
public TorrentDaySettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Cookie).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class TorrentDaySettings : IProviderConfig
|
||||
{
|
||||
private static readonly TorrentDaySettingsValidator Validator = new TorrentDaySettingsValidator();
|
||||
|
||||
public TorrentDaySettings()
|
||||
{
|
||||
Cookie = "";
|
||||
}
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Cookie", HelpText = "Site Cookie")]
|
||||
public string Cookie { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -294,7 +295,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
// freeleech #6579 #6624 #7367
|
||||
string dlMultiplier = row.download_multiplier.ToString();
|
||||
var dlVolumeFactor = dlMultiplier.IsNullOrWhiteSpace() ? 1 : dlMultiplier.CoerceInt();
|
||||
var dlVolumeFactor = dlMultiplier.IsNullOrWhiteSpace() ? 1 : ParseUtil.CoerceInt(dlMultiplier);
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
@@ -345,7 +346,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Username", Advanced = true, HelpText = "Site username")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Site username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Site password", Privacy = PrivacyLevel.Password)]
|
||||
|
||||
Reference in New Issue
Block a user