Skip to content

Commit 88ac453

Browse files
committed
Rewrite direct image scanning algorithm
1 parent 4d982af commit 88ac453

File tree

2 files changed

+103
-84
lines changed

2 files changed

+103
-84
lines changed

SmartImage.Lib/SearchClient.cs

Lines changed: 62 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public SearchClient(SearchConfig config)
3333
Results = new List<SearchResult>();
3434
FilteredResults = new List<SearchResult>();
3535
DirectResults = new List<ImageResult>();
36+
DetailedResults = new List<ImageResult>();
37+
3638
Reload();
3739
}
3840

@@ -58,11 +60,16 @@ public SearchClient(SearchConfig config)
5860

5961
public List<ImageResult> DirectResults { get; }
6062

63+
public List<ImageResult> DetailedResults { get; }
64+
65+
6166
/// <summary>
6267
/// Contains filtered search results
6368
/// </summary>
6469
public List<SearchResult> FilteredResults { get; }
6570

71+
72+
6673
public int Pending { get; private set; }
6774

6875
/// <summary>
@@ -89,6 +96,7 @@ public void Reset()
8996
Results.Clear();
9097
DirectResults.Clear();
9198
FilteredResults.Clear();
99+
DetailedResults.Clear();
92100
IsComplete = false;
93101
Reload();
94102
}
@@ -159,8 +167,13 @@ public async Task RunSearchAsync()
159167
isFiltered = null;
160168
}
161169

162-
ThreadPool.QueueUserWorkItem((c) => FindDirectResults(c, value));
163170

171+
if (DetailPredicate(value)) {
172+
DetailedResults.Add(value.PrimaryResult);
173+
}
174+
175+
ThreadPool.QueueUserWorkItem((c) => FindDirectResults(c, value));
176+
164177
// Call event
165178
ResultCompleted?.Invoke(null, new ResultCompletedEventArgs(value)
166179
{
@@ -173,34 +186,38 @@ public async Task RunSearchAsync()
173186

174187
Trace.WriteLine($"{nameof(SearchClient)}: Search complete", C_SUCCESS);
175188

176-
177189
var args = new SearchCompletedEventArgs
178190
{
179-
Results = Results,
180-
FirstDetailed = RefineFilter(DetailPredicate).FirstOrDefault(),
181-
/*Direct = new Lazy<ImageResult[]>(() =>
182-
{
183-
Debug.WriteLine($"{nameof(SearchClient)}: Finding direct results", C_DEBUG);
184-
ImageResult[] direct = GetDirectImageResults();
185-
186-
return direct;
187-
}),
188-
FirstDirect = new Lazy<ImageResult>(GetDirectImageResult)*/
189-
Direct = DirectResults,
190-
FirstDirect = DirectResults.FirstOrDefault()
191+
Results = Results,
192+
Detailed = PredicateFilter(Results, DetailPredicate),
193+
Direct = DirectResults,
194+
Filtered = FilteredResults
191195
};
192196

193197
SearchCompleted?.Invoke(null, args);
194-
198+
}
199+
200+
public Task<List<ImageResult>> WaitForDirect()
201+
{
202+
return Task.Run(() =>
203+
{
204+
while (Results.Any() && !DirectResults.Any()) {
205+
206+
}
195207

208+
return DirectResults;
209+
});
196210
}
197211

198212
private void FindDirectResults(object state, SearchResult value, int count = 5, int i = 10)
199213
{
200-
var u = value.OtherResults.Union(new[] { value.PrimaryResult }).ToList();
201-
var u2 = RefineFilter(u, DirectFilterPredicate).ToList();
214+
// var u = value.OtherResults.Union(new[] { value.PrimaryResult }).ToList();
215+
// var u2 = RefineFilter(u, DetailPredicate).ToList();
216+
217+
var u2 = value.OtherResults.Union(new[] { value.PrimaryResult }).ToList();
202218

203-
Debug.WriteLine($"*{nameof(SearchClient)}: Found {u2.Count} best results", C_DEBUG);
219+
220+
Debug.WriteLine($"*{nameof(SearchClient)}: Found {u2.Count} detailed results", C_DEBUG);
204221

205222
var query = u2.Where(x => x.CheckDirect(DirectImageCriterion.Regex))
206223
.Take(i)
@@ -214,36 +231,26 @@ private void FindDirectResults(object state, SearchResult value, int count = 5,
214231
Debug.WriteLine($"*{nameof(SearchClient)}: Found {images.Count} direct results", C_DEBUG);
215232
DirectResults.AddRange(images);
216233

217-
DirectFound?.Invoke(null, new DirectFoundEventArgs()
234+
DirectFound?.Invoke(null, new DirectResultsFoundEventArgs()
218235
{
219-
Direct = images,
236+
DirectResultsSubset = images,
220237
});
221238
}
222239
}
223240

224241

225-
public static IEnumerable<ImageResult> RefineFilter(List<ImageResult> results,
226-
Predicate<SearchResult> predicate)
242+
public List<ImageResult> PredicateFilter(List<SearchResult> results, Predicate<SearchResult> predicate)
227243
{
228-
var query = results.AsParallel()
229-
.OrderByDescending(r => r.Similarity)
230-
.ThenByDescending(r => r.PixelResolution)
231-
.ThenByDescending(r => r.DetailScore);
244+
var vb = results.Where((r) => predicate(r)).SelectMany(r =>
245+
{
246+
return r.OtherResults.Union(new[] { r.PrimaryResult });
247+
});
232248

233-
return query;
234-
}
249+
var query = vb.OrderByDescending(r => r.Similarity)
250+
.ThenByDescending(r => r.PixelResolution)
251+
.ThenByDescending(r => r.DetailScore).ToList();
235252

236-
public IEnumerable<ImageResult> RefineFilter(Predicate<SearchResult> predicate)
237-
{
238-
var query = Results.Where(r => predicate(r))
239-
.SelectMany(r =>
240-
{
241-
List<ImageResult> otherResults = r.OtherResults;
242-
otherResults.Insert(0, r.PrimaryResult);
243-
return otherResults;
244-
}).ToList();
245-
246-
return RefineFilter(query, predicate);
253+
return query;
247254
}
248255

249256
/// <summary>
@@ -255,12 +262,12 @@ public async Task RefineSearchAsync()
255262
throw SearchException;
256263
}
257264

258-
Debug.WriteLine($"{nameof(SearchClient)}: Finding best result", C_DEBUG);
265+
Debug.WriteLine($"{nameof(SearchClient)}: Finding direct result", C_DEBUG);
259266

260267
var directResult = DirectResults.FirstOrDefault();
261268

262269
if (directResult == null) {
263-
throw new SmartImageException("Could not find best result");
270+
throw new SmartImageException("Could not find direct result");
264271
}
265272

266273
var uri = directResult.Direct;
@@ -319,35 +326,36 @@ public static BaseSearchEngine[] GetAllSearchEngines()
319326
/// </summary>
320327
public event EventHandler<SearchCompletedEventArgs> SearchCompleted;
321328

322-
public event EventHandler<DirectFoundEventArgs> DirectFound;
329+
public event EventHandler<DirectResultsFoundEventArgs> DirectFound;
323330

324331
private static readonly Predicate<SearchResult> DetailPredicate = r => r.IsNonPrimitive;
325332

326333
private static readonly SmartImageException SearchException = new("Search must be completed");
327-
328-
private static readonly Predicate<SearchResult> DirectFilterPredicate = r => DetailPredicate(r)
329-
&& r.Engine.SearchType.HasFlag(EngineSearchType.Image);
330334
}
331335

332-
public sealed class DirectFoundEventArgs : EventArgs
336+
337+
public sealed class DirectResultsFoundEventArgs : EventArgs
333338
{
334-
public List<ImageResult> Direct { get; init; }
339+
/// <remarks>
340+
/// This field will always be a subset of <see cref="SearchClient.DirectResults"/>
341+
/// <para/>
342+
/// <see cref="DirectResultsSubset"/> &#x2282; <see cref="SearchClient.DirectResults"/>
343+
/// </remarks>
344+
public List<ImageResult> DirectResultsSubset { get; init; }
335345
}
336346

337347
public sealed class SearchCompletedEventArgs : EventArgs
338348
{
339349
public List<SearchResult> Results { get; init; }
340-
341-
[CanBeNull]
350+
342351
public List<ImageResult> Direct { get; internal set; }
352+
353+
public List<ImageResult> Detailed { get; internal set; }
354+
355+
public List<SearchResult> Filtered { get; internal set; }
343356

344-
[CanBeNull]
345-
public ImageResult FirstDirect { get; internal set; }
346357

347358

348-
[CanBeNull]
349-
public ImageResult FirstDetailed { get; internal set; }
350-
351359
}
352360

353361
public sealed class ResultCompletedEventArgs : EventArgs

SmartImage/UI/AppToast.cs

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
using System.Diagnostics;
22
using System.Drawing;
3+
using System.Text;
4+
using Windows.UI.Notifications;
35
using Kantan.Net;
46
using Kantan.Numeric;
57
using Kantan.Text;
68
using Microsoft.Toolkit.Uwp.Notifications;
79
using SmartImage.Lib;
10+
using SmartImage.Lib.Searching;
811
using SmartImage.Lib.Utilities;
912
using static Kantan.Diagnostics.LogCategories;
1013
using static SmartImage.UI.AppInterface;
@@ -13,11 +16,10 @@ namespace SmartImage.UI;
1316

1417
internal static class AppToast
1518
{
16-
internal static void ShowToast(object sender, SearchCompletedEventArgs args)
19+
internal static async void ShowToast(object sender, SearchCompletedEventArgs args)
1720
{
1821
Debug.WriteLine($"Building toast", C_DEBUG);
19-
20-
var bestResult = args.FirstDetailed;
22+
2123

2224
var builder = new ToastContentBuilder();
2325
var button = new ToastButton();
@@ -26,59 +28,68 @@ internal static void ShowToast(object sender, SearchCompletedEventArgs args)
2628
button2.SetContent("Dismiss")
2729
.AddArgument(Elements.ARG_KEY_ACTION, Elements.ARG_VALUE_DISMISS);
2830

31+
32+
var sb = new StringBuilder();
33+
string url = null;
34+
35+
36+
if (args.Results.Any()) {
37+
var b = args.Results.First();
38+
url = b.PrimaryResult.Url.ToString();
39+
}
40+
2941
button.SetContent("Open")
30-
.AddArgument(Elements.ARG_KEY_ACTION, $"{bestResult.Url}");
42+
.AddArgument(Elements.ARG_KEY_ACTION, $"{url}");
3143

3244
builder.AddButton(button)
3345
.AddButton(button2)
3446
.AddText("Search complete")
35-
.AddText($"{bestResult}")
47+
.AddText($"{sb}")
3648
.AddText($"Results: {Program.Client.Results.Count}");
3749

3850
if (Program.Config.Notification && Program.Config.NotificationImage) {
3951

40-
Debug.Assert(args.FirstDirect != null);
52+
var directResults = await Program.Client.WaitForDirect();
4153

42-
var imageResult = args.FirstDirect;
43-
44-
if (imageResult != null) {
45-
var path = Path.GetTempPath();
46-
47-
string file = ImageHelper.Download(imageResult.Direct, path);
54+
if (!directResults.Any()) {
55+
goto ShowToast;
56+
}
4857

49-
if (file == null) {
50-
int i = 0;
58+
var directImage = directResults.First();
59+
var path = Path.GetTempPath();
5160

52-
Debug.Assert(args.Direct != null);
61+
string file = ImageHelper.Download(directImage.Direct, path);
5362

54-
var imageResults = args.Direct;
63+
if (file == null) {
64+
int i = 0;
5565

56-
do {
57-
file = ImageHelper.Download(imageResults[i++].Direct, path);
66+
do {
67+
file = ImageHelper.Download(directResults[i++].Direct, path);
5868

59-
} while (String.IsNullOrWhiteSpace(file) && i < imageResults.Count);
69+
} while (String.IsNullOrWhiteSpace(file) && i < directResults.Count);
70+
}
6071

61-
}
6272

63-
if (file != null) {
64-
// NOTE: The file size limit doesn't seem to actually matter ...
65-
//file = GetHeroImage(path, file);
73+
/**/
6674

67-
Debug.WriteLine($"{nameof(AppInterface)}: Downloaded {file}", C_INFO);
75+
if (file != null) {
76+
// NOTE: The file size limit doesn't seem to actually matter ...
77+
//file = GetHeroImage(path, file);
6878

69-
builder.AddHeroImage(new Uri(file));
79+
Debug.WriteLine($"{nameof(AppInterface)}: Downloaded {file}", C_INFO);
7080

71-
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
72-
{
73-
File.Delete(file);
74-
};
75-
}
81+
builder.AddHeroImage(new Uri(file));
7682

83+
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
84+
{
85+
File.Delete(file);
86+
};
7787
}
7888

7989

8090
}
8191

92+
ShowToast:
8293
builder.SetBackgroundActivation();
8394
builder.Show();
8495
}

0 commit comments

Comments
 (0)