From 907474698ec13d3184a6324975aa91a4ce16e650 Mon Sep 17 00:00:00 2001 From: Read Stanton Date: Wed, 11 Dec 2024 23:21:18 -0600 Subject: [PATCH] Search server; other fixes --- SmartImage.Lib/Clients/HydrusClient.cs | 4 +- SmartImage.Lib/Engines/BaseSearchEngine.cs | 5 +- SmartImage.Lib/Images/Uni/UniImage.cs | 4 +- SmartImage.Lib/Results/SearchResult.cs | 10 ++-- SmartImage.Lib/SearchClient.cs | 21 ++++--- SmartImage.Lib/SearchConfig.cs | 26 ++++----- SmartImage.Lib/SearchQuery.cs | 1 + SmartImage.Lib/SearchServer.cs | 55 +++++++++++++++++-- .../BaseSearchEngineTypeConverter.cs | 27 +++++++++ .../Utilities/SearchResultTypeConverter.cs | 12 +++- SmartImage.Rdx/Commands/SearchCommand.cs | 3 - SmartImage.Rdx/Commands/ServerCommand.cs | 3 + .../Commands/ServerCommandSettings.cs | 1 + SmartImage.Rdx/Program.cs | 4 ++ SmartImage.Rdx/Shell/ConsoleFormat.cs | 2 +- 15 files changed, 140 insertions(+), 38 deletions(-) create mode 100644 SmartImage.Lib/Utilities/BaseSearchEngineTypeConverter.cs diff --git a/SmartImage.Lib/Clients/HydrusClient.cs b/SmartImage.Lib/Clients/HydrusClient.cs index 59af0930..ea0ebda7 100644 --- a/SmartImage.Lib/Clients/HydrusClient.cs +++ b/SmartImage.Lib/Clients/HydrusClient.cs @@ -178,12 +178,12 @@ public void Dispose() public event PropertyChangedEventHandler PropertyChanged; - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + protected virtual void OnPropertyChanged([CMN] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) + protected bool SetField(ref T field, T value, [CMN] string propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false; diff --git a/SmartImage.Lib/Engines/BaseSearchEngine.cs b/SmartImage.Lib/Engines/BaseSearchEngine.cs index d5389080..4aea66cb 100644 --- a/SmartImage.Lib/Engines/BaseSearchEngine.cs +++ b/SmartImage.Lib/Engines/BaseSearchEngine.cs @@ -16,7 +16,6 @@ namespace SmartImage.Lib.Engines; #nullable enable - public abstract class BaseSearchEngine : IDisposable, IEquatable { @@ -53,6 +52,7 @@ protected BaseSearchEngine(Url baseUrl, Url? endpoint = null) /// /// The corresponding of this engine /// + [JI] public abstract SearchEngineOptions EngineOption { get; } /// @@ -62,12 +62,15 @@ protected BaseSearchEngine(Url baseUrl, Url? endpoint = null) public virtual Url BaseUrl { get; } + [JI] public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(15); public Url? EndpointUrl { get; } + [JI] protected long? MaxSize { get; set; } + [JI] protected virtual string[] ErrorBodyMessages { get; } = []; protected static FlurlClient Client { get; } diff --git a/SmartImage.Lib/Images/Uni/UniImage.cs b/SmartImage.Lib/Images/Uni/UniImage.cs index 46eeda43..a8bf91fa 100644 --- a/SmartImage.Lib/Images/Uni/UniImage.cs +++ b/SmartImage.Lib/Images/Uni/UniImage.cs @@ -347,7 +347,7 @@ protected string WriteStreamToFile(string fn = null) public virtual void Dispose() { - Debug.WriteLine($"Disposing {ValueString} w/ {Size}"); + Trace.WriteLine($"Disposing {ValueString} w/ {Size}"); Stream?.Dispose(); Image?.Dispose(); @@ -357,7 +357,7 @@ public virtual void Dispose() public virtual async ValueTask DisposeAsync() { - Debug.WriteLine($"Disposing {ValueString} w/ {Size}"); + Trace.WriteLine($"Disposing {ValueString} w/ {Size}"); if (Stream != null) await Stream.DisposeAsync(); diff --git a/SmartImage.Lib/Results/SearchResult.cs b/SmartImage.Lib/Results/SearchResult.cs index 2594417a..7ac5c7df 100644 --- a/SmartImage.Lib/Results/SearchResult.cs +++ b/SmartImage.Lib/Results/SearchResult.cs @@ -75,6 +75,7 @@ public class SearchResult : IDisposable, INotifyPropertyChanged /// /// Engine which returned this result /// + [JI] public BaseSearchEngine Engine { get; } // todo: make the engine reference weak @@ -84,6 +85,7 @@ public class SearchResult : IDisposable, INotifyPropertyChanged /// public Url RawUrl { get; internal set; } + [JI] public bool HasResults { get @@ -96,7 +98,7 @@ public bool HasResults public bool IsSuccessful => Status.IsSuccessful(); /// - /// Results; first element should be + /// Results; first element should be /// [NN] public List Results { get; } @@ -143,12 +145,12 @@ public void Update() public event PropertyChangedEventHandler PropertyChanged; - private void OnPropertyChanged([CallerMemberName] string propertyName = null) + private void OnPropertyChanged([CMN] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - private bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) + private bool SetField(ref T field, T value, [CMN] string propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false; @@ -171,7 +173,7 @@ public SearchResultItem GetBestResult() .FirstOrDefault(static r => Url.IsValid(r.Url)); } - + [JI] public SearchResultItem RawResultItem { get diff --git a/SmartImage.Lib/SearchClient.cs b/SmartImage.Lib/SearchClient.cs index 349f6059..031e1a7c 100644 --- a/SmartImage.Lib/SearchClient.cs +++ b/SmartImage.Lib/SearchClient.cs @@ -1,4 +1,5 @@ -global using JI = System.Text.Json.Serialization.JsonIgnoreAttribute; +global using CMN = System.Runtime.CompilerServices.CallerMemberNameAttribute; +global using JI = System.Text.Json.Serialization.JsonIgnoreAttribute; global using ICBN = JetBrains.Annotations.ItemCanBeNullAttribute; global using INN = JetBrains.Annotations.ItemNotNullAttribute; using System.Collections; @@ -111,7 +112,7 @@ public void OpenChannel() /// /// Search query /// - /// Cancellation token passed to + /// Cancellation token passed to public async Task RunSearchAsync(SearchQuery query, TaskScheduler scheduler = default, CancellationToken token = default) @@ -172,11 +173,7 @@ public async Task RunSearchAsync(SearchQuery query, try { - var ordered = results.Select(x => x.GetBestResult()) - .Where(x => x != null) - .OrderByDescending(x => x.Similarity); - - var item = ordered.FirstOrDefault(); + SearchResultItem item = GetBest(results); if (item != null) { OpenResult(item.Url); @@ -195,6 +192,16 @@ public async Task RunSearchAsync(SearchQuery query, return results; } + public static SearchResultItem GetBest(SearchResult[] results) + { + var ordered = results.Select(x => x.GetBestResult()) + .Where(x => x != null) + .OrderByDescending(x => x.Similarity); + + var item = ordered.FirstOrDefault(); + return item; + } + private void CompleteSearchAsync() { ResultChannel?.Writer.Complete(); diff --git a/SmartImage.Lib/SearchConfig.cs b/SmartImage.Lib/SearchConfig.cs index 076f36f8..59805042 100644 --- a/SmartImage.Lib/SearchConfig.cs +++ b/SmartImage.Lib/SearchConfig.cs @@ -73,7 +73,7 @@ public SearchEngineOptions SearchEngines public SearchEngineOptions PriorityEngines { get => Get(PE_DEFAULT); - set { Set(value); } + set => Set(value); } /// @@ -82,7 +82,7 @@ public SearchEngineOptions PriorityEngines public bool OnTop { get => Get(ON_TOP_DEFAULT); - set { Set(value); } + set => Set(value); } /* @@ -116,25 +116,25 @@ public string HydrusKey public bool OpenRaw { get => Get(false); - set { Set(value); } + set => Set(value); } public bool Silent { get => Get(false); - set { Set(value); } + set => Set(value); } public bool Clipboard { get => Get(true); - set { Set(value); } + set => Set(value); } public bool AutoSearch { get => Get(false); - set { Set(value); } + set => Set(value); } /// @@ -143,7 +143,7 @@ public bool AutoSearch public string SauceNaoKey { get => Get(String.Empty); - set { Set(value); } + set => Set(value); } /// @@ -156,7 +156,7 @@ public string SauceNaoKey public bool ReadCookies { get => Get(READCOOKIES_DEFAULT); - set { Set(value); } + set => Set(value); } @@ -168,7 +168,7 @@ public bool ReadCookies public bool FlareSolverr { get => Get(FLARESOLVERR_DEFAULT); - set { Set(value); } + set => Set(value); } @@ -178,7 +178,7 @@ public bool FlareSolverr public string FlareSolverrApiUrl { get => Get(FLARE_SOLVERR_API_URL_DEFAULT); - set { Set(value); } + set => Set(value); } /// @@ -203,14 +203,14 @@ public SearchConfig() public static readonly Configuration Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); - private bool Set(T s = default, [CallerMemberName] string name = default) + private bool Set(T s = default, [CMN] string name = default) { bool b = Configuration.AddUpdateSetting(name, s.ToString()); OnPropertyChanged(name); return b; } - private T Get(T t = default, [CallerMemberName] string name = default) + private T Get(T t = default, [CMN] string name = default) { T v = Configuration.ReadSetting(name, t); return v; @@ -252,7 +252,7 @@ public void Save() public event PropertyChangedEventHandler PropertyChanged; - private void OnPropertyChanged([CallerMemberName] string propertyName = null) + private void OnPropertyChanged([CMN] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } diff --git a/SmartImage.Lib/SearchQuery.cs b/SmartImage.Lib/SearchQuery.cs index 2c24750a..945608ce 100644 --- a/SmartImage.Lib/SearchQuery.cs +++ b/SmartImage.Lib/SearchQuery.cs @@ -131,6 +131,7 @@ public async Task UploadAsync(BaseUploadEngine engine = null, CancellationT public void Dispose() { + Trace.WriteLine($"Disposing {Source}"); Source?.Dispose(); } diff --git a/SmartImage.Lib/SearchServer.cs b/SmartImage.Lib/SearchServer.cs index e8d9edeb..137a89e0 100644 --- a/SmartImage.Lib/SearchServer.cs +++ b/SmartImage.Lib/SearchServer.cs @@ -10,6 +10,7 @@ using Kantan.Net; using Kantan.Net.Utilities; using SmartImage.Lib.Images; +using SmartImage.Lib.Results; using SmartImage.Lib.Utilities; namespace SmartImage.Lib; @@ -21,7 +22,14 @@ public class SearchServer : IDisposable public static readonly JsonSerializerOptions Options2 = new(HttpUtilities.Options) { + WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = + { + new UrlTypeConverter(), + new BaseSearchEngineTypeConverter(), + // new SearchResultTypeConverter(), + } }; @@ -38,7 +46,8 @@ public SearchServer(SearchClient client, int port) Handlers = new RouteCallbackMap() { - ["search"] = HandleRequestAsync + ["search"] = HandleRequestAsync, + }; var uriPrefix = $"http://*:{port}/"; @@ -51,7 +60,11 @@ private async Task HandleRequestAsync(HttpListenerRequest request, HttpL { object ok; + var redirHdr = request.Headers["redirect"]; + + try { + var sz = await request.ReadRequestStringAsync(); if (String.IsNullOrWhiteSpace(sz)) { @@ -70,14 +83,30 @@ private async Task HandleRequestAsync(HttpListenerRequest request, HttpL var results = await Client.RunSearchAsync(sq); - var allResults = results.SelectMany(x => x.Results).ToArray(); - var ok1 = await response.WriteResponseJsonAsync(allResults); + var best = SearchClient.GetBest(results); + + var allResults = new SearchResults(results, best) + { }; + + var bytes = JsonSerializer.Serialize(allResults, Options2); + var rg = Listener.Encoding.GetBytes(bytes); + + var ok1 = await response.WriteResponseDataAsync(rg); ok = ok1; + /* var json = JsonSerializer.Serialize(allResults, Options2); ok = await response.WriteResponseStringAsync(json); - */ + */ + + if (!String.IsNullOrWhiteSpace(redirHdr)) { + response.Redirect(allResults.Best.Url); + } + + response.OutputStream.Close(); + response.Close(); + } catch (IOException io) { Trace.WriteLine($"{io}"); @@ -88,6 +117,24 @@ private async Task HandleRequestAsync(HttpListenerRequest request, HttpL return ok; } + public class SearchResults + { + + [JsonPropertyOrder(0)] + [MN] + public SearchResultItem Best { get; internal set; } + + [JsonPropertyOrder(1)] + public SearchResult[] Results { get; } + + public SearchResults(SearchResult[] results, SearchResultItem best) + { + Best = best; + Results = results; + } + + } + public Task StartAsync(CancellationToken ct = default) { return Listener.StartAsync(ct); diff --git a/SmartImage.Lib/Utilities/BaseSearchEngineTypeConverter.cs b/SmartImage.Lib/Utilities/BaseSearchEngineTypeConverter.cs new file mode 100644 index 00000000..d7e2b90c --- /dev/null +++ b/SmartImage.Lib/Utilities/BaseSearchEngineTypeConverter.cs @@ -0,0 +1,27 @@ +// Author: Deci | Project: SmartImage.Lib | Name: BaseSearchEngineTypeConverter.cs +// Date: 2024/12/11 @ 23:12:21 + +using System.Text.Json; +using System.Text.Json.Serialization; +using SmartImage.Lib.Engines; + +namespace SmartImage.Lib.Utilities; + +public sealed class BaseSearchEngineTypeConverter : JsonConverter +{ + + #region Overrides of JsonConverter + + public override BaseSearchEngine Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, BaseSearchEngine value, JsonSerializerOptions options) + { + writer.WriteString(nameof(BaseSearchEngine.Name), value.Name); + } + + #endregion + +} \ No newline at end of file diff --git a/SmartImage.Lib/Utilities/SearchResultTypeConverter.cs b/SmartImage.Lib/Utilities/SearchResultTypeConverter.cs index 4cb05d20..e036ad74 100644 --- a/SmartImage.Lib/Utilities/SearchResultTypeConverter.cs +++ b/SmartImage.Lib/Utilities/SearchResultTypeConverter.cs @@ -19,9 +19,19 @@ public override SearchResult Read(ref Utf8JsonReader reader, Type typeToConvert, public override void Write(Utf8JsonWriter writer, SearchResult value, JsonSerializerOptions options) { + writer.WriteStartObject(); writer.WriteString(nameof(SearchResult.Engine.Name), value.Engine.EngineOption.ToString()); - writer.WriteString(nameof(SearchResult.Results), $"{value.Results.Count}"); writer.WriteString(nameof(SearchResult.Status), $"{value.Status}"); + writer.WriteStartArray(nameof(SearchResult.Results)); + + foreach (SearchResultItem result in value.Results) { + writer.WriteStartObject(); + writer.WriteRawValue(JsonSerializer.Serialize(result, options)); + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); } #endregion diff --git a/SmartImage.Rdx/Commands/SearchCommand.cs b/SmartImage.Rdx/Commands/SearchCommand.cs index fa4a1c20..2fc3eaff 100644 --- a/SmartImage.Rdx/Commands/SearchCommand.cs +++ b/SmartImage.Rdx/Commands/SearchCommand.cs @@ -71,9 +71,6 @@ public sealed class SearchCommand : AsyncCommand, IDispos private readonly STable m_table; - public static readonly Assembly Assembly = Assembly.GetExecutingAssembly(); - public static readonly Version Version = Assembly.GetName().Version; - public SearchCommand() { Config = new SearchConfig(); diff --git a/SmartImage.Rdx/Commands/ServerCommand.cs b/SmartImage.Rdx/Commands/ServerCommand.cs index 360fd0d2..903e685b 100644 --- a/SmartImage.Rdx/Commands/ServerCommand.cs +++ b/SmartImage.Rdx/Commands/ServerCommand.cs @@ -1,6 +1,7 @@ // Author: Deci | Project: SmartImage.Rdx | Name: ServerCommand.cs // Date: 2024/11/22 @ 03:11:26 +using System.Diagnostics; using SmartImage.Lib; using SmartImage.Lib.Utilities.Integration; using SmartImage.Rdx.Shell; @@ -21,6 +22,7 @@ public sealed class ServerCommand : AsyncCommand, IDispos public ServerCommand() { Client = new SearchClient(SearchConfig.Default); + Server = new SearchServer(Client, 25565); m_scs = null; } @@ -38,6 +40,7 @@ public override async Task ExecuteAsync(CommandContext context, ServerComma public void Dispose() { + Trace.WriteLine($"Disposing {Server}"); Server.Dispose(); } diff --git a/SmartImage.Rdx/Commands/ServerCommandSettings.cs b/SmartImage.Rdx/Commands/ServerCommandSettings.cs index cf06db92..b0bbb799 100644 --- a/SmartImage.Rdx/Commands/ServerCommandSettings.cs +++ b/SmartImage.Rdx/Commands/ServerCommandSettings.cs @@ -16,6 +16,7 @@ public override ValidationResult Validate() /*if (!BaseOSIntegration.Integration.IsRoot) { throw new SmartImageException("Must be admin"); }*/ + return base.Validate(); } diff --git a/SmartImage.Rdx/Program.cs b/SmartImage.Rdx/Program.cs index 6b17f30e..568465b9 100644 --- a/SmartImage.Rdx/Program.cs +++ b/SmartImage.Rdx/Program.cs @@ -197,4 +197,8 @@ private static IConfigurationRoot GetConfig() return cfg; } + public static readonly Assembly Assembly = Assembly.GetExecutingAssembly(); + + public static readonly Version Version = Assembly.GetName().Version; + } \ No newline at end of file diff --git a/SmartImage.Rdx/Shell/ConsoleFormat.cs b/SmartImage.Rdx/Shell/ConsoleFormat.cs index 644bf983..72edc08a 100644 --- a/SmartImage.Rdx/Shell/ConsoleFormat.cs +++ b/SmartImage.Rdx/Shell/ConsoleFormat.cs @@ -78,7 +78,7 @@ static ConsoleFormat() { } ["Terminal colors"] = ProfileCapabilities.ColorSystem, ["Terminal links"] = ProfileCapabilities.Links, ["Terminal Unicode"] = ProfileCapabilities.Unicode, - ["Version"] = $"{SearchCommand.Version}", + ["Version"] = $"{Program.Version}", ["Location"] = BaseOSIntegration.Executable };