parent
635e93b2db
commit
07c4b48578
@ -0,0 +1,46 @@
|
||||
using System.IO.Abstractions;
|
||||
using Common.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Serilog;
|
||||
|
||||
namespace TrashLib.Services.Common.QualityDefinition;
|
||||
|
||||
internal class QualityGuideParser<T> where T : class
|
||||
{
|
||||
private readonly ILogger _log;
|
||||
|
||||
public QualityGuideParser(ILogger log)
|
||||
{
|
||||
_log = log;
|
||||
}
|
||||
|
||||
public ICollection<T> GetQualities(IEnumerable<IDirectoryInfo> jsonDirectories)
|
||||
{
|
||||
return jsonDirectories
|
||||
.SelectMany(x => x.GetFiles("*.json"))
|
||||
.Select(ParseQuality)
|
||||
.NotNull()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private T? ParseQuality(IFileInfo jsonFile)
|
||||
{
|
||||
var serializer = JsonSerializer.Create(new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new DefaultContractResolver
|
||||
{
|
||||
NamingStrategy = new SnakeCaseNamingStrategy()
|
||||
}
|
||||
});
|
||||
|
||||
using var json = new JsonTextReader(jsonFile.OpenText());
|
||||
var quality = serializer.Deserialize<T>(json);
|
||||
if (quality is null)
|
||||
{
|
||||
_log.Debug("Failed to parse quality definition JSON file: {Filename}", jsonFile.FullName);
|
||||
}
|
||||
|
||||
return quality;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace TrashLib.Services.Common.QualityDefinition;
|
||||
|
||||
public class QualityItem
|
||||
{
|
||||
public QualityItem(string quality, decimal min, decimal max)
|
||||
{
|
||||
Quality = quality;
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public const decimal MaxUnlimitedThreshold = 400;
|
||||
|
||||
public string Quality { get; }
|
||||
public decimal Min { get; }
|
||||
public decimal Max { get; }
|
||||
|
||||
public decimal? MaxForApi => Max < MaxUnlimitedThreshold ? Max : null;
|
||||
public decimal MinForApi => Min;
|
||||
|
||||
public string AnnotatedMin => Min.ToString(CultureInfo.InvariantCulture);
|
||||
public string AnnotatedMax => AnnotatedValue(Max, MaxUnlimitedThreshold);
|
||||
|
||||
protected static string AnnotatedValue(decimal value, decimal threshold)
|
||||
{
|
||||
var builder = new StringBuilder(value.ToString(CultureInfo.InvariantCulture));
|
||||
if (value >= threshold)
|
||||
{
|
||||
builder.Append(" (Unlimited)");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public bool IsMinDifferent(decimal serviceValue) => serviceValue != Min;
|
||||
|
||||
public bool IsMaxDifferent(decimal? serviceValue)
|
||||
{
|
||||
return serviceValue == null
|
||||
? MaxUnlimitedThreshold != Max
|
||||
: serviceValue != Max || MaxUnlimitedThreshold == Max;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||
|
||||
public interface IRadarrQualityDefinitionGuideParser
|
||||
{
|
||||
Task<string> GetMarkdownData();
|
||||
IDictionary<RadarrQualityDefinitionType, List<RadarrQualityData>> ParseMarkdown(string markdown);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||
|
||||
public interface IRadarrQualityGuideParser
|
||||
{
|
||||
ICollection<RadarrQualityData> GetQualities();
|
||||
}
|
@ -1,25 +1,7 @@
|
||||
using TrashLib.Services.Sonarr.QualityDefinition;
|
||||
|
||||
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||
|
||||
public class RadarrQualityData : SonarrQualityData
|
||||
{
|
||||
public const decimal PreferredUnlimitedThreshold = 395;
|
||||
|
||||
public decimal Preferred { get; set; }
|
||||
public decimal? PreferredForApi => Preferred < PreferredUnlimitedThreshold ? Preferred : null;
|
||||
public string AnnotatedPreferred => AnnotatedValue(Preferred, PreferredUnlimitedThreshold);
|
||||
|
||||
public decimal InterpolatedPreferred(decimal ratio)
|
||||
{
|
||||
var cappedMax = Math.Min(Max, PreferredUnlimitedThreshold);
|
||||
return Math.Round(Min + (cappedMax - Min) * ratio, 1);
|
||||
}
|
||||
|
||||
public bool IsPreferredDifferent(decimal? serviceValue)
|
||||
{
|
||||
return serviceValue == null
|
||||
? PreferredUnlimitedThreshold != Preferred
|
||||
: serviceValue != Preferred || PreferredUnlimitedThreshold == Preferred;
|
||||
}
|
||||
}
|
||||
public record RadarrQualityData(
|
||||
string TrashId,
|
||||
string Type,
|
||||
IReadOnlyCollection<RadarrQualityItem> Qualities
|
||||
);
|
||||
|
@ -1,78 +0,0 @@
|
||||
using System.IO.Abstractions;
|
||||
using System.Text.RegularExpressions;
|
||||
using Common.Extensions;
|
||||
using TrashLib.Startup;
|
||||
|
||||
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||
|
||||
internal class RadarrQualityDefinitionGuideParser : IRadarrQualityDefinitionGuideParser
|
||||
{
|
||||
private readonly IAppPaths _paths;
|
||||
private readonly Regex _regexHeader = new(@"^#+", RegexOptions.Compiled);
|
||||
|
||||
private readonly Regex _regexTableRow =
|
||||
new(@"\| *(.*?) *\| *([\d.]+) *\| *([\d.]+) *\|", RegexOptions.Compiled);
|
||||
|
||||
public RadarrQualityDefinitionGuideParser(IAppPaths paths)
|
||||
{
|
||||
_paths = paths;
|
||||
}
|
||||
|
||||
public async Task<string> GetMarkdownData()
|
||||
{
|
||||
var repoDir = _paths.RepoDirectory;
|
||||
var file = repoDir
|
||||
.SubDirectory("docs")
|
||||
.SubDirectory("Radarr")
|
||||
.File("Radarr-Quality-Settings-File-Size.md").OpenText();
|
||||
|
||||
return await file.ReadToEndAsync();
|
||||
}
|
||||
|
||||
public IDictionary<RadarrQualityDefinitionType, List<RadarrQualityData>> ParseMarkdown(string markdown)
|
||||
{
|
||||
var results = new Dictionary<RadarrQualityDefinitionType, List<RadarrQualityData>>();
|
||||
List<RadarrQualityData>? table = null;
|
||||
|
||||
var reader = new StringReader(markdown);
|
||||
for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var match = _regexHeader.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
// todo: hard-coded for now since there's only one supported right now.
|
||||
var type = RadarrQualityDefinitionType.Movie;
|
||||
table = results.GetOrCreate(type);
|
||||
|
||||
// If we grab a table that isn't empty, that means for whatever reason *another* table
|
||||
// in the markdown is trying to modify a previous table's data. For example, maybe there
|
||||
// are two "Series" quality tables. That would be a weird edge case, but handle that
|
||||
// here just in case.
|
||||
if (table.Count > 0)
|
||||
{
|
||||
table = null;
|
||||
}
|
||||
}
|
||||
else if (table != null)
|
||||
{
|
||||
match = _regexTableRow.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
table.Add(new RadarrQualityData
|
||||
{
|
||||
Name = match.Groups[1].Value,
|
||||
Min = match.Groups[2].Value.ToDecimal(),
|
||||
Max = match.Groups[3].Value.ToDecimal()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||
|
||||
public enum RadarrQualityDefinitionType
|
||||
{
|
||||
Movie
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
using Serilog;
|
||||
using TrashLib.Repo;
|
||||
using TrashLib.Services.Common.QualityDefinition;
|
||||
|
||||
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||
|
||||
internal class RadarrQualityGuideParser : IRadarrQualityGuideParser
|
||||
{
|
||||
private readonly QualityGuideParser<RadarrQualityData> _parser;
|
||||
private readonly IRepoPathsFactory _pathFactory;
|
||||
|
||||
public RadarrQualityGuideParser(ILogger log, IRepoPathsFactory pathFactory)
|
||||
{
|
||||
_parser = new QualityGuideParser<RadarrQualityData>(log);
|
||||
_pathFactory = pathFactory;
|
||||
}
|
||||
|
||||
public ICollection<RadarrQualityData> GetQualities()
|
||||
=> _parser.GetQualities(_pathFactory.Create().RadarrQualityPaths);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
using TrashLib.Services.Common.QualityDefinition;
|
||||
|
||||
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||
|
||||
public class RadarrQualityItem : QualityItem
|
||||
{
|
||||
public RadarrQualityItem(string quality, decimal min, decimal max, decimal preferred)
|
||||
: base(quality, min, max)
|
||||
{
|
||||
Preferred = preferred;
|
||||
}
|
||||
|
||||
public const decimal PreferredUnlimitedThreshold = 395;
|
||||
|
||||
public decimal Preferred { get; set; }
|
||||
public decimal? PreferredForApi => Preferred < PreferredUnlimitedThreshold ? Preferred : null;
|
||||
public string AnnotatedPreferred => AnnotatedValue(Preferred, PreferredUnlimitedThreshold);
|
||||
|
||||
public decimal InterpolatedPreferred(decimal ratio)
|
||||
{
|
||||
var cappedMax = Math.Min(Max, PreferredUnlimitedThreshold);
|
||||
return Math.Round(Min + (cappedMax - Min) * ratio, 1);
|
||||
}
|
||||
|
||||
public bool IsPreferredDifferent(decimal? serviceValue)
|
||||
{
|
||||
return serviceValue == null
|
||||
? PreferredUnlimitedThreshold != Preferred
|
||||
: serviceValue != Preferred || PreferredUnlimitedThreshold == Preferred;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
||||
|
||||
public interface ISonarrQualityDefinitionGuideParser
|
||||
{
|
||||
Task<string> GetMarkdownData();
|
||||
IDictionary<SonarrQualityDefinitionType, List<SonarrQualityData>> ParseMarkdown(string markdown);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
||||
|
||||
public interface ISonarrQualityGuideParser
|
||||
{
|
||||
ICollection<SonarrQualityData> GetQualities();
|
||||
}
|
@ -1,39 +1,9 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using TrashLib.Services.Common.QualityDefinition;
|
||||
|
||||
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
||||
|
||||
public class SonarrQualityData
|
||||
{
|
||||
public const decimal MaxUnlimitedThreshold = 400;
|
||||
|
||||
public string Name { get; set; } = "";
|
||||
public decimal Min { get; set; }
|
||||
public decimal Max { get; set; }
|
||||
|
||||
public decimal? MaxForApi => Max < MaxUnlimitedThreshold ? Max : null;
|
||||
public decimal MinForApi => Min;
|
||||
|
||||
public string AnnotatedMin => Min.ToString(CultureInfo.InvariantCulture);
|
||||
public string AnnotatedMax => AnnotatedValue(Max, MaxUnlimitedThreshold);
|
||||
|
||||
protected static string AnnotatedValue(decimal value, decimal threshold)
|
||||
{
|
||||
var builder = new StringBuilder(value.ToString(CultureInfo.InvariantCulture));
|
||||
if (value >= threshold)
|
||||
{
|
||||
builder.Append(" (Unlimited)");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public bool IsMinDifferent(decimal serviceValue) => serviceValue != Min;
|
||||
|
||||
public bool IsMaxDifferent(decimal? serviceValue)
|
||||
{
|
||||
return serviceValue == null
|
||||
? MaxUnlimitedThreshold != Max
|
||||
: serviceValue != Max || MaxUnlimitedThreshold == Max;
|
||||
}
|
||||
}
|
||||
public record SonarrQualityData(
|
||||
string TrashId,
|
||||
string Type,
|
||||
IReadOnlyCollection<QualityItem> Qualities
|
||||
);
|
||||
|
@ -1,81 +0,0 @@
|
||||
using System.IO.Abstractions;
|
||||
using System.Text.RegularExpressions;
|
||||
using Common.Extensions;
|
||||
using TrashLib.Startup;
|
||||
|
||||
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
||||
|
||||
internal class SonarrQualityDefinitionGuideParser : ISonarrQualityDefinitionGuideParser
|
||||
{
|
||||
private readonly Regex _regexHeader = new(@"^#+", RegexOptions.Compiled);
|
||||
|
||||
private readonly Regex _regexTableRow =
|
||||
new(@"\| *(.*?) *\| *([\d.]+) *\| *([\d.]+) *\|", RegexOptions.Compiled);
|
||||
|
||||
private readonly IAppPaths _paths;
|
||||
|
||||
public SonarrQualityDefinitionGuideParser(IAppPaths paths)
|
||||
{
|
||||
_paths = paths;
|
||||
}
|
||||
|
||||
public async Task<string> GetMarkdownData()
|
||||
{
|
||||
var repoDir = _paths.RepoDirectory;
|
||||
var file = repoDir
|
||||
.SubDirectory("docs")
|
||||
.SubDirectory("Sonarr")
|
||||
.File("Sonarr-Quality-Settings-File-Size.md").OpenText();
|
||||
|
||||
return await file.ReadToEndAsync();
|
||||
}
|
||||
|
||||
public IDictionary<SonarrQualityDefinitionType, List<SonarrQualityData>> ParseMarkdown(string markdown)
|
||||
{
|
||||
var results = new Dictionary<SonarrQualityDefinitionType, List<SonarrQualityData>>();
|
||||
List<SonarrQualityData>? table = null;
|
||||
|
||||
var reader = new StringReader(markdown);
|
||||
for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var match = _regexHeader.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
var type = line.ContainsIgnoreCase("anime")
|
||||
? SonarrQualityDefinitionType.Anime
|
||||
: SonarrQualityDefinitionType.Series;
|
||||
|
||||
table = results.GetOrCreate(type);
|
||||
|
||||
// If we grab a table that isn't empty, that means for whatever reason *another* table
|
||||
// in the markdown is trying to modify a previous table's data. For example, maybe there
|
||||
// are two "Series" quality tables. That would be a weird edge case, but handle that
|
||||
// here just in case.
|
||||
if (table.Count > 0)
|
||||
{
|
||||
table = null;
|
||||
}
|
||||
}
|
||||
else if (table != null)
|
||||
{
|
||||
match = _regexTableRow.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
table.Add(new SonarrQualityData
|
||||
{
|
||||
Name = match.Groups[1].Value,
|
||||
Min = match.Groups[2].Value.ToDecimal(),
|
||||
Max = match.Groups[3].Value.ToDecimal()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
||||
|
||||
public enum SonarrQualityDefinitionType
|
||||
{
|
||||
Anime,
|
||||
Series,
|
||||
Hybrid
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
using Serilog;
|
||||
using TrashLib.Repo;
|
||||
using TrashLib.Services.Common.QualityDefinition;
|
||||
|
||||
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
||||
|
||||
internal class SonarrQualityGuideParser : ISonarrQualityGuideParser
|
||||
{
|
||||
private readonly QualityGuideParser<SonarrQualityData> _parser;
|
||||
private readonly IRepoPathsFactory _pathFactory;
|
||||
|
||||
public SonarrQualityGuideParser(ILogger log, IRepoPathsFactory pathFactory)
|
||||
{
|
||||
_parser = new QualityGuideParser<SonarrQualityData>(log);
|
||||
_pathFactory = pathFactory;
|
||||
}
|
||||
|
||||
public ICollection<SonarrQualityData> GetQualities()
|
||||
=> _parser.GetQualities(_pathFactory.Create().SonarrQualityPaths);
|
||||
}
|
Loading…
Reference in new issue