You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
recyclarr/src/Trash/Radarr/CustomFormat/Processors/GuideSteps/CustomFormatStep.cs

118 lines
5.0 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using Trash.Extensions;
using Trash.Radarr.CustomFormat.Guide;
using Trash.Radarr.CustomFormat.Models;
using Trash.Radarr.CustomFormat.Models.Cache;
namespace Trash.Radarr.CustomFormat.Processors.GuideSteps
{
public class CustomFormatStep : ICustomFormatStep
{
public List<(string, string)> CustomFormatsWithOutdatedNames { get; } = new();
public List<ProcessedCustomFormatData> ProcessedCustomFormats { get; } = new();
public List<TrashIdMapping> DeletedCustomFormatsInCache { get; } = new();
public Dictionary<string, List<ProcessedCustomFormatData>> DuplicatedCustomFormats { get; private set; } =
new();
public void Process(IEnumerable<CustomFormatData> customFormatGuideData, IEnumerable<CustomFormatConfig> config,
CustomFormatCache? cache)
{
var allConfigCfNames = config
.SelectMany(c => c.Names)
.Distinct(StringComparer.CurrentCultureIgnoreCase)
.ToList();
var processedCfs = customFormatGuideData
.Select(cf => ProcessCustomFormatData(cf, cache))
.ToList();
// Perform updates and deletions based on matches in the cache. Matches in the cache are by ID.
foreach (var cf in processedCfs)
{
// Does the name of the CF in the guide match a name in the config? If yes, we keep it.
var configName = allConfigCfNames.FirstOrDefault(n => n.EqualsIgnoreCase(cf.Name));
if (configName != null)
{
if (cf.CacheEntry != null)
{
// The cache entry might be using an old name. This will happen if:
// - A user has synced this CF before, AND
// - The name of the CF in the guide changed, AND
// - The user updated the name in their config to match the name in the guide.
cf.CacheEntry.CustomFormatName = cf.Name;
}
ProcessedCustomFormats.Add(cf);
continue;
}
// Does the name of the CF in the cache match a name in the config? If yes, we keep it.
configName = allConfigCfNames.FirstOrDefault(n => n.EqualsIgnoreCase(cf.CacheEntry?.CustomFormatName));
if (configName != null)
{
// Config name is out of sync with the guide and should be updated
CustomFormatsWithOutdatedNames.Add((configName, cf.Name));
ProcessedCustomFormats.Add(cf);
}
// If we get here, we can't find a match in the config using cache or guide name, so the user must have
// removed it from their config. This will get marked for deletion when we process those later in
// ProcessDeletedCustomFormats().
}
// Orphaned entries in cache represent custom formats we need to delete.
ProcessDeletedCustomFormats(cache);
// Check for multiple custom formats with the same name in the guide data (e.g. "DoVi")
ProcessDuplicates();
}
private void ProcessDuplicates()
{
DuplicatedCustomFormats = ProcessedCustomFormats
.GroupBy(cf => cf.Name)
.Where(grp => grp.Count() > 1)
.ToDictionary(grp => grp.Key, grp => grp.ToList());
ProcessedCustomFormats.RemoveAll(cf => DuplicatedCustomFormats.ContainsKey(cf.Name));
}
private static ProcessedCustomFormatData ProcessCustomFormatData(CustomFormatData guideData,
CustomFormatCache? cache)
{
JObject obj = JObject.Parse(guideData.Json);
var name = obj["name"].Value<string>();
var trashId = obj["trash_id"].Value<string>();
// Remove trash_id, it's metadata that is not meant for Radarr itself
// Radarr supposedly drops this anyway, but I prefer it to be removed by TrashUpdater
obj.Property("trash_id").Remove();
return new ProcessedCustomFormatData(name, trashId, obj)
{
Score = guideData.Score,
CacheEntry = cache?.TrashIdMappings.FirstOrDefault(c => c.TrashId == trashId)
};
}
private void ProcessDeletedCustomFormats(CustomFormatCache? cache)
{
if (cache == null)
{
return;
}
static bool MatchCfInCache(ProcessedCustomFormatData cf, TrashIdMapping c)
=> cf.CacheEntry != null && cf.CacheEntry.TrashId == c.TrashId;
// Delete if CF is in cache and not in the guide or config
DeletedCustomFormatsInCache.AddRange(cache.TrashIdMappings
.Where(c => !ProcessedCustomFormats.Any(cf => MatchCfInCache(cf, c))));
}
}
}