fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Add initial Blazor Server project

recyclarr
Robert Dailey 4 years ago
parent 24c41ff4f4
commit 0ce95b58c0

@ -1039,7 +1039,7 @@ resharper_place_event_attribute_on_same_line = false
resharper_place_expr_accessor_on_single_line = true
resharper_place_expr_method_on_single_line = true
resharper_place_expr_property_on_single_line = true
resharper_place_field_attribute_on_same_line = true
resharper_place_field_attribute_on_same_line = false
resharper_place_linq_into_on_new_line = true
resharper_place_method_attribute_on_same_line = false
resharper_place_namespace_definitions_on_same_line = false

@ -1,11 +1,12 @@
@page "/radarr/quality-profiles"
@using TrashLib.Radarr.CustomFormat.Api
@using TrashLib.Radarr.CustomFormat.Api.Models
@using TrashLib.Radarr.Config
@using TrashLib.Config
@using Recyclarr.Components
@using Newtonsoft.Json.Linq
@using Flurl.Http
@using Newtonsoft.Json
@using TrashLib.Cache
@using TrashLib.Radarr.CustomFormat
<div class="d-flex mb-4 flex-column flex-sm-row">
@ -49,11 +50,11 @@
return;
}
<MudSelect @bind-Value="@_selectedProfileId" Class="mb-2" Style="width: auto">
<MudSelect @bind-Value="@SelectedProfile" Class="mb-2" Style="width: auto">
@foreach (var profile in _profiles)
{
<MudSelectItem @key="profile" Value="@((int) profile["id"])">
@((string) profile["name"])
<MudSelectItem @key="profile" Value="@profile">
@profile.Name
</MudSelectItem>
}
</MudSelect>
@ -76,13 +77,19 @@
<tbody>
@if (SelectedProfile != null)
{
@foreach (var formatItem in SelectedProfile["formatItems"].Children<JObject>())
@foreach (var formatItem in SelectedProfile.FormatItems)
{
<tr>
<td>@formatItem.Name</td>
<td>
<MudTextField @bind-Value="@formatItem.Score"
FullWidth="false"
Margin="Margin.Dense"
Variant="Variant.Outlined"
Style="width: 100px" />
</td>
<td></td>
<td></td>
<td></td>
<td>@((int)formatItem["score"])</td>
<td>@formatItem.Score</td>
</tr>
}
}
@ -108,6 +115,26 @@
await base.OnInitializedAsync();
}
class ProfileSelectionPageManager
{
private readonly Func<ICacheGuidBuilder, ICachePersister> _cachePersisterFactory;
private readonly Func<string, ICustomFormatService> _customFormatServiceFactory;
public ProfileSelectionPageManager(
Func<ICacheGuidBuilder, ICachePersister> cachePersisterFactory,
Func<string, ICustomFormatService> customFormatServiceFactory)
{
_cachePersisterFactory = cachePersisterFactory;
_customFormatServiceFactory = customFormatServiceFactory;
}
async Task RequestCustomFormatsAndUpdateCache(RadarrConfiguration config)
{
var cfService = _customFormatServiceFactory(config.BuildUrl());
var customFormats = await cfService.GetCustomFormats();
}
}
private async Task OnSelectedInstanceChanged(RadarrConfiguration? activeConfig)
{
try
@ -119,7 +146,14 @@
if (activeConfig != null)
{
_profiles.AddRange(await ProfileServiceFactory(activeConfig.BuildUrl()).GetQualityProfiles());
// todo:
// - Build the cache (for TrashID -> CfId mapping)
// - Need to pair guide score with current profile score
// - Exclude FormatItems that represent Custom Formats not selected by the user
//
var qualityProfiles = await ProfileServiceFactory(activeConfig.BuildUrl()).GetQualityProfiles();
qualityProfiles.Where(_activeConfig.CustomFormats)
_profiles.AddRange();
}
SelectedProfile = _profiles.FirstOrDefault();
@ -134,7 +168,7 @@
private RadarrConfiguration? _activeConfig;
private ServerSelector<RadarrConfiguration>? _serverSelector;
private readonly List<JObject> _profiles = new();
private readonly List<QualityProfileData> _profiles = new();
private Exception? _exception;
private bool _loading;
@ -143,11 +177,7 @@
await OnSelectedInstanceChanged(_activeConfig);
}
private JObject? SelectedProfile
{
get => _profiles.FirstOrDefault(p => (int) p["id"] == _selectedProfileId);
set => _selectedProfileId = (int?) value?["id"];
}
private QualityProfileData? SelectedProfile { get; set; }
private int? _selectedProfileId;
// private int? _selectedProfileId;
}

@ -1,6 +1,5 @@
using System.Collections.Generic;
using FluentAssertions;
using FluentAssertions.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NSubstitute;
@ -8,6 +7,7 @@ using NUnit.Framework;
using TestLibrary.NSubstitute;
using Trash.TestLibrary;
using TrashLib.Radarr.CustomFormat.Api;
using TrashLib.Radarr.CustomFormat.Api.Models;
using TrashLib.Radarr.CustomFormat.Models;
using TrashLib.Radarr.CustomFormat.Models.Cache;
using TrashLib.Radarr.CustomFormat.Processors.PersistenceSteps;
@ -43,7 +43,8 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps
}]";
var api = Substitute.For<IQualityProfileService>();
api.GetQualityProfiles().Returns(JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
api.GetQualityProfiles()
.Returns(JsonConvert.DeserializeObject<List<QualityProfileData>>(radarrQualityProfileData));
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
{
@ -59,7 +60,7 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps
var processor = new QualityProfileApiPersistenceStep();
processor.Process(api, cfScores);
api.DidNotReceive().UpdateQualityProfile(Arg.Any<JObject>(), Arg.Any<int>());
api.DidNotReceive().UpdateQualityProfile(Arg.Any<QualityProfileData>(), Arg.Any<int>());
}
[Test]
@ -68,7 +69,8 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps
const string radarrQualityProfileData = @"[{'name': 'profile1'}]";
var api = Substitute.For<IQualityProfileService>();
api.GetQualityProfiles().Returns(JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
api.GetQualityProfiles()
.Returns(JsonConvert.DeserializeObject<List<QualityProfileData>>(radarrQualityProfileData));
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
{
@ -107,7 +109,8 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps
}]";
var api = Substitute.For<IQualityProfileService>();
api.GetQualityProfiles().Returns(JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
api.GetQualityProfiles()
.Returns(JsonConvert.DeserializeObject<List<QualityProfileData>>(radarrQualityProfileData));
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
{
@ -134,7 +137,7 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps
});
api.Received().UpdateQualityProfile(
Verify.That<JObject>(j => j["formatItems"].Children().Should().HaveCount(3)),
Verify.That<QualityProfileData>(j => j.FormatItems.Should().HaveCount(3)),
Arg.Any<int>());
}
@ -183,7 +186,8 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps
}]";
var api = Substitute.For<IQualityProfileService>();
api.GetQualityProfiles().Returns(JsonConvert.DeserializeObject<List<JObject>>(radarrQualityProfileData));
api.GetQualityProfiles()
.Returns(JsonConvert.DeserializeObject<List<QualityProfileData>>(radarrQualityProfileData));
var cfScores = new Dictionary<string, QualityProfileCustomFormatScoreMapping>
{
@ -251,8 +255,8 @@ namespace TrashLib.Tests.Radarr.CustomFormat.Processors.PersistenceSteps
'id': 1
}");
api.Received()
.UpdateQualityProfile(Verify.That<JObject>(a => a.Should().BeEquivalentTo(expectedProfileJson)), 1);
api.Received().UpdateQualityProfile(
Verify.That<QualityProfileData>(a => a.Should().BeEquivalentTo(expectedProfileJson)), 1);
processor.InvalidProfileNames.Should().BeEmpty();
processor.UpdatedScores.Should()
.ContainKey("profile1").WhichValue.Should()

@ -1,12 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using TrashLib.Radarr.CustomFormat.Api.Models;
namespace TrashLib.Radarr.CustomFormat.Api
{
public interface IQualityProfileService
{
Task<List<JObject>> GetQualityProfiles();
Task<JObject> UpdateQualityProfile(JObject profileJson, int id);
Task<List<QualityProfileData>> GetQualityProfiles();
Task<QualityProfileData> UpdateQualityProfile(QualityProfileData profile, int profileId);
}
}

@ -0,0 +1,31 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
// ReSharper disable CollectionNeverUpdated.Global
namespace TrashLib.Radarr.CustomFormat.Api.Models
{
public class QualityProfileData
{
[UsedImplicitly]
[JsonExtensionData]
private JObject? _extraJson;
public int Id { get; [UsedImplicitly] set; }
public string Name { get; [UsedImplicitly] set; } = "";
public List<FormatItemData> FormatItems { get; [UsedImplicitly] set; } = new();
public class FormatItemData
{
[UsedImplicitly]
[JsonExtensionData]
private JObject? _extraJson;
public int Format { get; [UsedImplicitly] set; }
public string Name { get; [UsedImplicitly] set; } = "";
public int Score { get; [UsedImplicitly] set; }
}
}
}

@ -1,22 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace TrashLib.Radarr.CustomFormat.Api.Models
{
public class QualityProfileData
{
[JsonExtensionData] private IDictionary<string, JToken> _extraJson;
public int Id { get; set; }
public string Name { get; set; }
public List<FormatItemData> FormatItems { get; set; }
public class FormatItemData
{
// public int Format { get; set; }
public string Name { get; set; }
public int Score { get; set; }
}
}
}

@ -2,7 +2,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Flurl;
using Flurl.Http;
using Newtonsoft.Json.Linq;
using TrashLib.Radarr.CustomFormat.Api.Models;
namespace TrashLib.Radarr.CustomFormat.Api
{
@ -15,19 +15,19 @@ namespace TrashLib.Radarr.CustomFormat.Api
_baseUrl = baseUrl;
}
public async Task<List<JObject>> GetQualityProfiles()
public async Task<List<QualityProfileData>> GetQualityProfiles()
{
return await _baseUrl
.AppendPathSegment("qualityprofile")
.GetJsonAsync<List<JObject>>();
.GetJsonAsync<List<QualityProfileData>>();
}
public async Task<JObject> UpdateQualityProfile(JObject profileJson, int id)
public async Task<QualityProfileData> UpdateQualityProfile(QualityProfileData profile, int profileId)
{
return await _baseUrl
.AppendPathSegment($"qualityprofile/{id}")
.PutJsonAsync(profileJson)
.ReceiveJson<JObject>();
.AppendPathSegment($"qualityprofile/{profileId}")
.PutJsonAsync(profile)
.ReceiveJson<QualityProfileData>();
}
}
}

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Common.Extensions;
using Newtonsoft.Json.Linq;
using TrashLib.Radarr.CustomFormat.Api;
using TrashLib.Radarr.CustomFormat.Api.Models;
using TrashLib.Radarr.CustomFormat.Models;
namespace TrashLib.Radarr.CustomFormat.Processors.PersistenceSteps
@ -28,21 +28,23 @@ namespace TrashLib.Radarr.CustomFormat.Processors.PersistenceSteps
// do not match profiles in Radarr.
var profileScores = cfScores.GroupJoin(radarrProfiles,
s => s.Key,
p => (string) p["name"],
(s, p) => (s.Key, s.Value, p.SelectMany(pi => pi["formatItems"].Children<JObject>()).ToList()),
p => p.Name,
(s, p) => (s.Key, s.Value, p.First()),
StringComparer.InvariantCultureIgnoreCase);
foreach (var (profileName, scoreMap, formatItems) in profileScores)
foreach (var (profileName, scoreMap, profileData) in profileScores)
{
// `SelectMany` is only needed here because we used GroupJoin() above the loop.
var formatItems = profileData.FormatItems;
if (formatItems.Count == 0)
{
_invalidProfileNames.Add(profileName);
continue;
}
foreach (var json in formatItems)
foreach (var formatItem in formatItems)
{
var map = FindScoreEntry(json, scoreMap);
var map = FindScoreEntry(formatItem, scoreMap);
int? scoreToUse = null;
FormatScoreUpdateReason? reason = null;
@ -58,33 +60,31 @@ namespace TrashLib.Radarr.CustomFormat.Processors.PersistenceSteps
reason = FormatScoreUpdateReason.Reset;
}
if (scoreToUse == null || reason == null || (int) json["score"] == scoreToUse)
if (scoreToUse == null || reason == null || formatItem.Score == scoreToUse)
{
continue;
}
json["score"] = scoreToUse.Value;
formatItem.Score = scoreToUse.Value;
_updatedScores.GetOrCreate(profileName)
.Add(new UpdatedFormatScore((string) json["name"], scoreToUse.Value, reason.Value));
.Add(new UpdatedFormatScore(formatItem.Name, scoreToUse.Value, reason.Value));
}
if (!_updatedScores.TryGetValue(profileName, out var updatedScores) || updatedScores.Count == 0)
{
// No scores to update, so don't bother with the API call
continue;
}
var jsonRoot = (JObject) formatItems.First().Root;
await api.UpdateQualityProfile(jsonRoot, (int) jsonRoot["id"]);
await api.UpdateQualityProfile(profileData, profileData.Id);
}
}
private static FormatMappingEntry? FindScoreEntry(JObject formatItem,
private static FormatMappingEntry? FindScoreEntry(QualityProfileData.FormatItemData formatItem,
QualityProfileCustomFormatScoreMapping scoreMap)
{
return scoreMap.Mapping.FirstOrDefault(
m => m.CustomFormat.CacheEntry != null &&
(int) formatItem["format"] == m.CustomFormat.CacheEntry.CustomFormatId);
formatItem.Format == m.CustomFormat.CacheEntry.CustomFormatId);
}
}
}

@ -17,24 +17,24 @@ namespace TrashLib.Radarr
{
public class RadarrAutofacModule : Module
{
class CachePersisterFactory
{
private readonly Func<IServiceConfiguration, ICacheGuidBuilder> _guidBuilderFactory;
private readonly Func<ICacheGuidBuilder, ICachePersister> _persisterFactory;
public CachePersisterFactory(
Func<IServiceConfiguration, ICacheGuidBuilder> guidBuilderFactory,
Func<ICacheGuidBuilder, ICachePersister> persisterFactory)
{
_guidBuilderFactory = guidBuilderFactory;
_persisterFactory = persisterFactory;
}
public ICachePersister Create(IServiceConfiguration config)
{
return _persisterFactory(_guidBuilderFactory(config));
}
}
// class CachePersisterFactory
// {
// private readonly Func<IServiceConfiguration, ICacheGuidBuilder> _guidBuilderFactory;
// private readonly Func<ICacheGuidBuilder, ICachePersister> _persisterFactory;
//
// public CachePersisterFactory(
// Func<IServiceConfiguration, ICacheGuidBuilder> guidBuilderFactory,
// Func<ICacheGuidBuilder, ICachePersister> persisterFactory)
// {
// _guidBuilderFactory = guidBuilderFactory;
// _persisterFactory = persisterFactory;
// }
//
// public ICachePersister Create(IServiceConfiguration config)
// {
// return _persisterFactory(_guidBuilderFactory(config));
// }
// }
protected override void Load(ContainerBuilder builder)
{

Loading…
Cancel
Save