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;
|
namespace TrashLib.Services.Radarr.QualityDefinition;
|
||||||
|
|
||||||
public class RadarrQualityData : SonarrQualityData
|
public record RadarrQualityData(
|
||||||
{
|
string TrashId,
|
||||||
public const decimal PreferredUnlimitedThreshold = 395;
|
string Type,
|
||||||
|
IReadOnlyCollection<RadarrQualityItem> Qualities
|
||||||
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,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 TrashLib.Services.Common.QualityDefinition;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
namespace TrashLib.Services.Sonarr.QualityDefinition;
|
||||||
|
|
||||||
public class SonarrQualityData
|
public record SonarrQualityData(
|
||||||
{
|
string TrashId,
|
||||||
public const decimal MaxUnlimitedThreshold = 400;
|
string Type,
|
||||||
|
IReadOnlyCollection<QualityItem> Qualities
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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