refactor: Include filename in CustomFormatData

gui
Robert Dailey 2 years ago
parent fdd95a619f
commit bd88e162ee

@ -6,39 +6,41 @@ namespace TrashLib.TestLibrary;
public static class NewCf
{
public static CustomFormatData Data(string name, string trashId, int? score = null)
public static CustomFormatData Data(string name, string filename, string trashId, int? score = null)
{
var json = JObject.Parse($"{{'name':'{name}'}}");
return new CustomFormatData(name, trashId, score, new JObject(json));
return new CustomFormatData(name, filename, trashId, score, new JObject(json));
}
public static ProcessedCustomFormatData Processed(string name, string trashId, int? score = null)
public static ProcessedCustomFormatData Processed(string name, string filename, string trashId, int? score = null)
{
return new ProcessedCustomFormatData(Data(name, trashId, score));
return new ProcessedCustomFormatData(Data(name, filename, trashId, score));
}
public static ProcessedCustomFormatData Processed(string name, string trashId, int? score, JObject json)
public static ProcessedCustomFormatData Processed(string name, string filename, string trashId, int? score,
JObject json)
{
return new ProcessedCustomFormatData(new CustomFormatData(name, trashId, score, json));
return new ProcessedCustomFormatData(new CustomFormatData(name, filename, trashId, score, json));
}
public static ProcessedCustomFormatData Processed(string name, string trashId, JObject json)
public static ProcessedCustomFormatData Processed(string name, string filename, string trashId, JObject json)
{
return Processed(name, trashId, null, json);
return Processed(name, filename, trashId, null, json);
}
public static ProcessedCustomFormatData Processed(string name, string trashId, TrashIdMapping cacheEntry)
public static ProcessedCustomFormatData Processed(string name, string filename, string trashId,
TrashIdMapping cacheEntry)
{
return new ProcessedCustomFormatData(Data(name, trashId))
return new ProcessedCustomFormatData(Data(name, filename, trashId))
{
CacheEntry = cacheEntry
};
}
public static ProcessedCustomFormatData Processed(string name, string trashId, JObject json,
public static ProcessedCustomFormatData Processed(string name, string filename, string trashId, JObject json,
TrashIdMapping? cacheEntry)
{
return new ProcessedCustomFormatData(new CustomFormatData(name, trashId, null, json))
return new ProcessedCustomFormatData(new CustomFormatData(name, filename, trashId, null, json))
{
CacheEntry = cacheEntry
};

@ -1,6 +1,5 @@
using System.Collections.ObjectModel;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using NSubstitute;
using NUnit.Framework;
using Serilog;
@ -9,6 +8,7 @@ using TrashLib.Radarr.CustomFormat;
using TrashLib.Radarr.CustomFormat.Models;
using TrashLib.Radarr.CustomFormat.Models.Cache;
using TrashLib.Radarr.CustomFormat.Processors.PersistenceSteps;
using TrashLib.TestLibrary;
namespace TrashLib.Tests.Radarr.CustomFormat;
@ -29,15 +29,6 @@ public class CachePersisterTest
public IServiceCache ServiceCache { get; }
}
private static ProcessedCustomFormatData QuickMakeCf(string cfName, string trashId, int cfId)
{
var cf = new CustomFormatData(cfName, trashId, null, new JObject());
return new ProcessedCustomFormatData(cf)
{
CacheEntry = new TrashIdMapping(trashId, cfName) {CustomFormatId = cfId}
};
}
[TestCase(CustomFormatCache.LatestVersion - 1)]
[TestCase(CustomFormatCache.LatestVersion + 1)]
public void Set_loaded_cache_to_null_if_versions_mismatch(int versionToTest)
@ -124,12 +115,11 @@ public class CachePersisterTest
// Update with new cached items
var results = new CustomFormatTransactionData();
results.NewCustomFormats.Add(QuickMakeCf("cfname", "trashid", 10));
results.NewCustomFormats.Add(NewCf.Processed("cfname", "file", "trashid", 10));
var customFormatData = new List<ProcessedCustomFormatData>
{
new(new CustomFormatData("", "trashid", null, new JObject()))
{CacheEntry = new TrashIdMapping("trashid", "cfname", 10)}
NewCf.Processed("", "file", "trashid", new TrashIdMapping("trashid", "cfname", 10))
};
ctx.Persister.Update(customFormatData);

@ -22,15 +22,15 @@ public class CustomFormatListerTest
{
var testData = new[]
{
NewCf.Data("First", "123"),
NewCf.Data("Second", "456")
NewCf.Data("First", "file1", "123"),
NewCf.Data("Second", "file2", "456")
};
guide.GetCustomFormatData().Returns(testData);
sut.ListCustomFormats();
console.ReadOutputString().Should().ContainAll(
testData.SelectMany(x => new[] {x.Name, x.TrashId}));
console.ReadOutputString().Should()
.ContainAll(testData.SelectMany(x => new[] {x.Name, x.TrashId}));
}
}

@ -24,15 +24,15 @@ public class LocalRepoCustomFormatJsonParserTest
.SubDirectory("json")
.SubDirectory("radarr");
fs.AddFile(jsonDir.File("first.json").FullName, new MockFileData("{'name':'first','trash_id':'1'}"));
fs.AddFile(jsonDir.File("second.json").FullName, new MockFileData("{'name':'second','trash_id':'2'}"));
fs.AddFile(jsonDir.File("first-file.json").FullName, new MockFileData("{'name':'first','trash_id':'1'}"));
fs.AddFile(jsonDir.File("second-file.json").FullName, new MockFileData("{'name':'second','trash_id':'2'}"));
var results = sut.GetCustomFormatData();
results.Should().BeEquivalentTo(new[]
{
NewCf.Data("first", "1"),
NewCf.Data("second", "2")
NewCf.Data("first", "first-file", "1"),
NewCf.Data("second", "second-file", "2")
});
}
}

@ -36,7 +36,7 @@ public class GuideProcessorTest
public ResourceDataReader Data { get; }
public CustomFormatData ReadCustomFormat(string textFile) =>
LocalRepoCustomFormatJsonParser.ParseCustomFormatData(ReadText(textFile));
LocalRepoCustomFormatJsonParser.ParseCustomFormatData(ReadText(textFile), Path.GetFileName(textFile));
public string ReadText(string textFile) => Data.ReadData(textFile);
public JObject ReadJson(string jsonFile) => JObject.Parse(ReadText(jsonFile));
@ -86,11 +86,19 @@ public class GuideProcessorTest
var expectedProcessedCustomFormatData = new List<ProcessedCustomFormatData>
{
NewCf.Processed("Surround Sound", "43bb5f09c79641e7a22e48d440bd8868", 500,
NewCf.Processed("Surround Sound",
"ImportableCustomFormat1_Processed",
"43bb5f09c79641e7a22e48d440bd8868",
500,
ctx.ReadJson("ImportableCustomFormat1_Processed.json")),
NewCf.Processed("DTS-HD/DTS:X", "4eb3c272d48db8ab43c2c85283b69744", 480,
NewCf.Processed("DTS-HD/DTS:X",
"ImportableCustomFormat2_Processed",
"4eb3c272d48db8ab43c2c85283b69744",
480,
ctx.ReadJson("ImportableCustomFormat2_Processed.json")),
NewCf.Processed("No Score", "abc")
NewCf.Processed("No Score", "nofile", "abc")
};
guideProcessor.ProcessedCustomFormats.Should().BeEquivalentTo(expectedProcessedCustomFormatData);

@ -19,8 +19,8 @@ public class ConfigStepTest
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1", 100),
NewCf.Processed("name3", "id3", new TrashIdMapping("id3", "name1"))
NewCf.Processed("name1", "file1", "id1", 100),
NewCf.Processed("name3", "file3", "id3", new TrashIdMapping("id3", "name1"))
};
var testConfig = new CustomFormatConfig[]
@ -51,8 +51,8 @@ public class ConfigStepTest
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", ""),
NewCf.Processed("name2", "")
NewCf.Processed("name1", "", ""),
NewCf.Processed("name2", "", "")
};
var testConfig = new CustomFormatConfig[]
@ -72,7 +72,7 @@ public class ConfigStepTest
{
CustomFormats = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "")
NewCf.Processed("name1", "", "")
}
}
}, op => op
@ -85,8 +85,8 @@ public class ConfigStepTest
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", ""),
NewCf.Processed("name2", "")
NewCf.Processed("name1", "", ""),
NewCf.Processed("name2", "", "")
};
var testConfig = new CustomFormatConfig[]
@ -108,7 +108,7 @@ public class ConfigStepTest
{
CustomFormats = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "")
NewCf.Processed("name1", "", "")
}
}
}, op => op
@ -121,7 +121,7 @@ public class ConfigStepTest
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1")
NewCf.Processed("name1", "file1", "id1")
};
var testConfig = new CustomFormatConfig[]
@ -150,7 +150,7 @@ public class ConfigStepTest
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1")
NewCf.Processed("name1", "file1", "id1")
};
var testConfig = new CustomFormatConfig[]
@ -175,9 +175,9 @@ public class ConfigStepTest
{
var testProcessedCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1", 100),
NewCf.Processed("name3", "id3"),
NewCf.Processed("name4", "id4")
NewCf.Processed("name1", "file1", "id1", 100),
NewCf.Processed("name3", "file3", "id3"),
NewCf.Processed("name4", "file4", "id4")
};
var testConfig = new CustomFormatConfig[]

@ -20,9 +20,9 @@ public class CustomFormatStepTest
{
public List<CustomFormatData> TestGuideData { get; } = new()
{
NewCf.Data("name1", "id1"),
NewCf.Data("name2", "id2"),
NewCf.Data("name3", "id3")
NewCf.Data("name1", "file1", "id1"),
NewCf.Data("name2", "file2", "id2"),
NewCf.Data("name3", "file3", "id3")
};
}
@ -47,7 +47,7 @@ public class CustomFormatStepTest
var testGuideData = new List<CustomFormatData>
{
NewCf.Data(variableCfName, "id1")
NewCf.Data(variableCfName, "", "id1")
};
var processor = new CustomFormatStep();
@ -58,7 +58,7 @@ public class CustomFormatStepTest
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
processor.ProcessedCustomFormats.Should().BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed(variableCfName, "id1", testCache.TrashIdMappings[0])
NewCf.Processed(variableCfName, "", "id1", testCache.TrashIdMappings[0])
});
}
@ -67,7 +67,7 @@ public class CustomFormatStepTest
{
var guideData = new List<CustomFormatData>
{
NewCf.Data("name1", "id1")
NewCf.Data("name1", "file1", "id1")
};
var testConfig = new List<CustomFormatConfig>
@ -91,7 +91,7 @@ public class CustomFormatStepTest
processor.ProcessedCustomFormats.Should()
.BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1")
NewCf.Processed("name1", "file1", "id1")
});
}
@ -112,8 +112,8 @@ public class CustomFormatStepTest
processor.ProcessedCustomFormats.Should()
.BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1"),
NewCf.Processed("name3", "id3")
NewCf.Processed("name1", "file1", "id1"),
NewCf.Processed("name3", "file3", "id3")
});
}
@ -134,9 +134,9 @@ public class CustomFormatStepTest
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
processor.ProcessedCustomFormats.Should().BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1"),
NewCf.Processed("name2", "id2"),
NewCf.Processed("name3", "id3")
NewCf.Processed("name1", "file1", "id1"),
NewCf.Processed("name2", "file2", "id2"),
NewCf.Processed("name3", "file3", "id3")
},
op => op.Using(new JsonEquivalencyStep()));
}
@ -146,7 +146,7 @@ public class CustomFormatStepTest
{
var guideData = new List<CustomFormatData>
{
NewCf.Data("name1", "id1")
NewCf.Data("name1", "file1", "id1")
};
var testConfig = new List<CustomFormatConfig>
@ -167,7 +167,7 @@ public class CustomFormatStepTest
.BeEquivalentTo(new[] {new TrashIdMapping("id1000", "name1")});
processor.ProcessedCustomFormats.Should().BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1")
NewCf.Processed("name1", "file1", "id1")
});
}
@ -181,7 +181,7 @@ public class CustomFormatStepTest
var guideCfs = new List<CustomFormatData>
{
new("3D", "id1", null, new JObject())
new("3D", "file1", "id1", null, new JObject())
};
processor.Process(guideCfs, Array.Empty<CustomFormatConfig>(), cache);
@ -197,7 +197,7 @@ public class CustomFormatStepTest
{
var guideData = new List<CustomFormatData>
{
new("name2", "id1", null, new JObject())
new("name2", "", "id1", null, new JObject())
};
var testConfig = new List<CustomFormatConfig>
@ -225,8 +225,8 @@ public class CustomFormatStepTest
{
var guideData = new List<CustomFormatData>
{
NewCf.Data("name1", "id1"),
NewCf.Data("name1", "id2")
NewCf.Data("name1", "", "id1"),
NewCf.Data("name1", "", "id2")
};
var testConfig = new List<CustomFormatConfig>
@ -241,8 +241,8 @@ public class CustomFormatStepTest
.ContainKey("name1").WhoseValue.Should()
.BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1"),
NewCf.Processed("name1", "id2")
NewCf.Processed("name1", "", "id1"),
NewCf.Processed("name1", "", "id2")
});
processor.CustomFormatsWithOutdatedNames.Should().BeEmpty();
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
@ -265,7 +265,7 @@ public class CustomFormatStepTest
processor.DeletedCustomFormatsInCache.Should().BeEmpty();
processor.ProcessedCustomFormats.Should().BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1")
NewCf.Processed("name1", "", "id1")
},
op => op.Using(new JsonEquivalencyStep()));
}
@ -275,8 +275,8 @@ public class CustomFormatStepTest
{
var guideData = new List<CustomFormatData>
{
NewCf.Data("name1", "id1"),
NewCf.Data("name2", "id2")
NewCf.Data("name1", "", "id1"),
NewCf.Data("name2", "", "id2")
};
var testConfig = new List<CustomFormatConfig>
@ -292,7 +292,7 @@ public class CustomFormatStepTest
processor.ProcessedCustomFormats.Should()
.BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name2", "id2")
NewCf.Processed("name2", "", "id2")
});
}
@ -318,7 +318,7 @@ public class CustomFormatStepTest
{
var guideData = new List<CustomFormatData>
{
NewCf.Data("name1", "id1", 100)
NewCf.Data("name1", "", "id1", 100)
};
var testConfig = new List<CustomFormatConfig>
@ -341,7 +341,7 @@ public class CustomFormatStepTest
processor.ProcessedCustomFormats.Should()
.BeEquivalentTo(new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1", 100)
NewCf.Processed("name1", "", "id1", 100)
},
op => op.Using(new JsonEquivalencyStep()));
}

@ -20,7 +20,7 @@ public class QualityProfileStepTest
{
CustomFormats = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1")
NewCf.Processed("name1", "file1", "id1")
},
QualityProfiles = new List<QualityProfileConfig>
{
@ -45,7 +45,7 @@ public class QualityProfileStepTest
{
CustomFormats = new List<ProcessedCustomFormatData>
{
NewCf.Processed("", "id1", 100)
NewCf.Processed("", "", "id1", 100)
},
QualityProfiles = new List<QualityProfileConfig>
{
@ -74,7 +74,7 @@ public class QualityProfileStepTest
{
CustomFormats = new List<ProcessedCustomFormatData>
{
NewCf.Processed("", "id1", 100)
NewCf.Processed("", "", "id1", 100)
},
QualityProfiles = new List<QualityProfileConfig>
{
@ -109,7 +109,7 @@ public class QualityProfileStepTest
{
CustomFormats = new List<ProcessedCustomFormatData>
{
NewCf.Processed("name1", "id1", 0)
NewCf.Processed("name1", "file1", "id1", 0)
},
QualityProfiles = new List<QualityProfileConfig>
{

@ -1,7 +1,6 @@
using NSubstitute;
using NUnit.Framework;
using TrashLib.Radarr.CustomFormat.Api;
using TrashLib.Radarr.CustomFormat.Models;
using TrashLib.Radarr.CustomFormat.Models.Cache;
using TrashLib.Radarr.CustomFormat.Processors.PersistenceSteps;
using TrashLib.TestLibrary;
@ -12,18 +11,13 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps;
[Parallelizable(ParallelScope.All)]
public class CustomFormatApiPersistenceStepTest
{
private static ProcessedCustomFormatData QuickMakeCf(string cfName, string trashId, int cfId)
{
return NewCf.Processed(cfName, trashId, new TrashIdMapping(trashId, cfName) {CustomFormatId = cfId});
}
[Test]
public async Task All_api_operations_behave_normally()
{
var transactions = new CustomFormatTransactionData();
transactions.NewCustomFormats.Add(QuickMakeCf("cfname1", "trashid1", 1));
transactions.UpdatedCustomFormats.Add(QuickMakeCf("cfname2", "trashid2", 2));
transactions.UnchangedCustomFormats.Add(QuickMakeCf("cfname3", "trashid3", 3));
transactions.NewCustomFormats.Add(NewCf.Processed("cfname1", "", "trashid1", 1));
transactions.UpdatedCustomFormats.Add(NewCf.Processed("cfname2", "", "trashid2", 2));
transactions.UnchangedCustomFormats.Add(NewCf.Processed("cfname3", "", "trashid3", 3));
transactions.DeletedCustomFormatIds.Add(new TrashIdMapping("trashid4", "cfname4") {CustomFormatId = 4});
var api = Substitute.For<ICustomFormatService>();

@ -72,7 +72,7 @@ public class JsonTransactionStepTest
var guideCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed(guideCfName, "", guideCfData, cacheEntry)
NewCf.Processed(guideCfName, "", "", guideCfData, cacheEntry)
};
var processor = new JsonTransactionStep();
@ -174,9 +174,9 @@ public class JsonTransactionStepTest
var radarrCfs = JsonConvert.DeserializeObject<List<JObject>>(radarrCfData);
var guideCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("created", "", guideCfData![0]),
NewCf.Processed("updated_different_name", "", guideCfData[1], new TrashIdMapping("", "", 2)),
NewCf.Processed("no_change", "", guideCfData[2])
NewCf.Processed("created", "", "", guideCfData![0]),
NewCf.Processed("updated_different_name", "", "", guideCfData[1], new TrashIdMapping("", "", 2)),
NewCf.Processed("no_change", "", "", guideCfData[2])
};
var processor = new JsonTransactionStep();
@ -287,7 +287,7 @@ public class JsonTransactionStepTest
var guideCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("updated", "", guideCfData, new TrashIdMapping("", "") {CustomFormatId = 1})
NewCf.Processed("updated", "", "", guideCfData, new TrashIdMapping("", "") {CustomFormatId = 1})
};
var radarrCfs = JsonConvert.DeserializeObject<List<JObject>>(radarrCfData);
@ -406,8 +406,8 @@ public class JsonTransactionStepTest
var radarrCfs = JsonConvert.DeserializeObject<List<JObject>>(radarrCfData);
var guideCfs = new List<ProcessedCustomFormatData>
{
NewCf.Processed("updated", "", guideCfData![0]),
NewCf.Processed("no_change", "", guideCfData[1])
NewCf.Processed("updated", "", "", guideCfData![0]),
NewCf.Processed("no_change", "", "", guideCfData[1])
};
var processor = new JsonTransactionStep();

@ -48,7 +48,7 @@ public class QualityProfileApiPersistenceStepTest
{
{
"profile1", CfTestUtils.NewMapping(new FormatMappingEntry(
NewCf.Processed("", "", new TrashIdMapping("", "") {CustomFormatId = 4}), 100))
NewCf.Processed("", "", "", new TrashIdMapping("", "") {CustomFormatId = 4}), 100))
}
};
@ -109,7 +109,7 @@ public class QualityProfileApiPersistenceStepTest
{
{
"profile1", CfTestUtils.NewMappingWithReset(
new FormatMappingEntry(NewCf.Processed("", "", new TrashIdMapping("", "", 2)), 100))
new FormatMappingEntry(NewCf.Processed("", "", "", new TrashIdMapping("", "", 2)), 100))
}
};
@ -183,11 +183,11 @@ public class QualityProfileApiPersistenceStepTest
{
"profile1", CfTestUtils.NewMapping(
// First match by ID
new FormatMappingEntry(NewCf.Processed("", "", new TrashIdMapping("", "", 4)), 100),
new FormatMappingEntry(NewCf.Processed("", "", "", new TrashIdMapping("", "", 4)), 100),
// Should NOT match because we do not use names to assign scores
new FormatMappingEntry(NewCf.Processed("", "", new TrashIdMapping("", "BR-DISK")), 101),
new FormatMappingEntry(NewCf.Processed("", "", "", new TrashIdMapping("", "BR-DISK")), 101),
// Second match by ID
new FormatMappingEntry(NewCf.Processed("", "", new TrashIdMapping("", "", 1)), 102))
new FormatMappingEntry(NewCf.Processed("", "", "", new TrashIdMapping("", "", 1)), 102))
}
};

@ -14,11 +14,13 @@ public class LocalRepoCustomFormatJsonParser : IRadarrGuideService
{
private readonly IAppPaths _paths;
private readonly ILogger _log;
private readonly IFileSystem _fs;
public LocalRepoCustomFormatJsonParser(IAppPaths paths, ILogger log)
public LocalRepoCustomFormatJsonParser(IAppPaths paths, ILogger log, IFileSystem fs)
{
_paths = paths;
_log = log;
_fs = fs;
}
public ICollection<CustomFormatData> GetCustomFormatData()
@ -40,7 +42,7 @@ public class LocalRepoCustomFormatJsonParser : IRadarrGuideService
{
return Observable.Using(file.OpenText, x => x.ReadToEndAsync().ToObservable())
.Do(_ => _log.Debug("Parsing CF Json: {Name}", file.Name))
.Select(ParseCustomFormatData)
.Select(x => ParseCustomFormatData(x, _fs.Path.GetFileNameWithoutExtension(file.Name)))
.Catch((JsonException e) =>
{
_log.Warning("Failed to parse JSON file: {File} ({Reason})", file.Name, e.Message);
@ -48,7 +50,7 @@ public class LocalRepoCustomFormatJsonParser : IRadarrGuideService
});
}
public static CustomFormatData ParseCustomFormatData(string guideData)
public static CustomFormatData ParseCustomFormatData(string guideData, string fileName)
{
var obj = JObject.Parse(guideData);
@ -66,6 +68,6 @@ public class LocalRepoCustomFormatJsonParser : IRadarrGuideService
// Radarr supposedly drops this anyway, but I prefer it to be removed.
obj.Property("trash_id")?.Remove();
return new CustomFormatData(name, trashId, finalScore, obj);
return new CustomFormatData(name, fileName, trashId, finalScore, obj);
}
}

@ -5,6 +5,7 @@ namespace TrashLib.Radarr.CustomFormat.Models;
public record CustomFormatData(
string Name,
string FileName, // Excludes file extension!
string TrashId,
int? Score,
[property: JsonExtensionData] JObject ExtraJson

Loading…
Cancel
Save