Skip to content

Commit 8367d04

Browse files
committed
Refactored searching to use tasks and async operations
1 parent 37a2825 commit 8367d04

File tree

8 files changed

+98
-115
lines changed

8 files changed

+98
-115
lines changed

SmartImage/Core/Interface.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ private static string GetContextMenuString(bool added) =>
356356
string? img = Path.Combine(cd2, testImg);
357357

358358
SearchConfig.Config.Image = img;
359-
SearchConfig.Config.PriorityEngines = SearchEngineOptions.None;
359+
//SearchConfig.Config.PriorityEngines = SearchEngineOptions.None;
360360

361361
return true;
362362
}

SmartImage/Engines/Other/YandexEngine.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ public YandexEngine() : base("https://yandex.com/images/search?rpt=imageview&url
2525

2626
private const int TOTAL_RES_MIN = 500_000;
2727

28-
private static BasicSearchResult[] FilterAndSelectBestImages(List<BasicSearchResult> rg)
28+
private static ISearchResult[] FilterAndSelectBestImages(List<BasicSearchResult> rg)
2929
{
3030
const int TAKE_N = 5;
3131

3232
var best = rg.OrderByDescending(i => i.FullResolution)
3333
.Take(TAKE_N)
34+
.Cast<ISearchResult>()
3435
.ToArray();
3536

3637
return best;
@@ -98,7 +99,7 @@ public override FullSearchResult GetResult(string url)
9899

99100
// Get more info from Yandex
100101

101-
string? html = Network.GetString(sr.RawUrl);
102+
string? html = Network.GetString(sr.RawUrl!);
102103
var doc = new HtmlDocument();
103104
doc.LoadHtml(html);
104105

@@ -115,7 +116,7 @@ public override FullSearchResult GetResult(string url)
115116

116117
var images = GetYandexImages(doc);
117118

118-
BasicSearchResult[] bestImages = FilterAndSelectBestImages(images);
119+
ISearchResult[] bestImages = FilterAndSelectBestImages(images);
119120

120121
//
121122
var best = images[0];

SmartImage/Engines/SauceNao/SauceNaoEngine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ public SauceNaoEngine() : this(SearchConfig.Config.SauceNaoAuth) { }
5555

5656
public override float? FilterThreshold => 70.00F;
5757

58-
private BasicSearchResult[] ConvertResults(SauceNaoDataResult[] results)
58+
private ISearchResult[] ConvertResults(SauceNaoDataResult[] results)
5959
{
60-
var rg = new List<BasicSearchResult>();
60+
var rg = new List<ISearchResult>();
6161

6262
foreach (var sn in results) {
6363
if (sn.Urls != null) {

SmartImage/Engines/SauceNao/SauceNaoSiteIndex.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public enum SauceNaoSiteIndex
3737
MangaUpdates = 36,
3838

3939
//
40-
40+
ArtStation = 39,
4141
FurAffinity = 40,
4242
Twitter = 41,
4343
}

SmartImage/Searching/BasicSearchResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#nullable enable
88
namespace SmartImage.Searching
99
{
10-
public class BasicSearchResult : ISearchResult
10+
public struct BasicSearchResult : ISearchResult
1111
{
1212
public string Url { get; set; }
1313

SmartImage/Searching/FullSearchResult.cs

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,50 @@
44
using System.Collections.Generic;
55
using System.Diagnostics;
66
using System.Drawing;
7+
using System.IO;
78
using System.Linq;
89
using System.Text;
10+
using Novus.Utilities;
911
using Novus.Win32;
1012
using SimpleCore.Console.CommandLine;
1113
using SimpleCore.Net;
1214
using SimpleCore.Utilities;
15+
using SmartImage.Core;
1316
using SmartImage.Engines;
17+
using SmartImage.Utilities;
1418

1519
namespace SmartImage.Searching
1620
{
1721
/// <summary>
18-
/// Contains search result and information
22+
/// Represents a complete search result
1923
/// </summary>
2024
public sealed class FullSearchResult : NConsoleOption, ISearchResult
2125
{
2226
public FullSearchResult(ISearchEngine engine, string url, float? similarity = null)
23-
: this(engine.Color, engine.Name, url, similarity) { }
27+
: this(engine.Engine, engine.Color, engine.Name, url, similarity) { }
2428

25-
public FullSearchResult(Color color, string name, string url, float? similarity = null)
29+
public FullSearchResult(SearchEngineOptions src, Color color, string name, string url, float? similarity = null)
2630
{
27-
Url = url;
28-
Name = name;
29-
Color = color;
31+
Engine = src;
32+
Url = url;
33+
Name = name;
34+
Color = color;
3035

3136
Similarity = similarity;
3237
ExtendedInfo = new List<string>();
3338
ExtendedResults = new List<FullSearchResult>();
3439
}
3540

41+
/// <summary>
42+
/// Engine
43+
/// </summary>
44+
public SearchEngineOptions Engine { get; }
45+
46+
/// <summary>
47+
/// Whether this result is a result from a priority engine
48+
/// </summary>
49+
public bool IsPriority => SearchConfig.Config.PriorityEngines.HasFlag(Engine);
50+
3651
/// <summary>
3752
/// Displays <see cref="ExtendedResults" />, if any, in a new menu
3853
/// </summary>
@@ -200,6 +215,10 @@ public override string ToString()
200215
sb.Append("-").Append(Formatting.SPACE);
201216
}
202217

218+
if (IsPriority) {
219+
sb.Append("*").Append(Formatting.SPACE);
220+
}
221+
203222
sb.AppendLine();
204223

205224
/*
@@ -259,7 +278,7 @@ public void UpdateFrom(ISearchResult result)
259278

260279
private FullSearchResult CreateExtendedResult(ISearchResult result)
261280
{
262-
var sr = new FullSearchResult(Color, Name, result.Url, result.Similarity)
281+
var sr = new FullSearchResult(Engine, Color, Name, result.Url, result.Similarity)
263282
{
264283
Width = result.Width,
265284
Height = result.Height,
@@ -316,5 +335,36 @@ public static int CompareResults(FullSearchResult x, FullSearchResult y)
316335

317336
return 0;
318337
}
338+
339+
private const string ORIGINAL_IMAGE_NAME = "(Original image)";
340+
341+
/// <summary>
342+
/// Creates a <see cref="FullSearchResult"/> for the original image
343+
/// </summary>
344+
public static FullSearchResult GetOriginalImageResult(string imageUrl, FileInfo imageFile)
345+
{
346+
var result = new FullSearchResult(SearchEngineOptions.None, Color.White, ORIGINAL_IMAGE_NAME, imageUrl)
347+
{
348+
Similarity = 100.0f,
349+
};
350+
351+
var fileFormat = FileSystem.ResolveFileType(imageFile.FullName);
352+
353+
double fileSizeMegabytes =
354+
MathHelper.ConvertToUnit(FileSystem.GetFileSize(imageFile.FullName), MetricUnit.Mega);
355+
356+
(int width, int height) = Images.GetDimensions(imageFile.FullName);
357+
358+
result.Width = width;
359+
result.Height = height;
360+
361+
double mpx = MathHelper.ConvertToUnit(width * height, MetricUnit.Mega);
362+
363+
string infoStr = $"Info: {imageFile.Name} ({fileSizeMegabytes:F} MB) ({mpx:F} MP) ({fileFormat.Name})";
364+
365+
result.ExtendedInfo.Add(infoStr);
366+
367+
return result;
368+
}
319369
}
320370
}

SmartImage/Searching/SearchClient.cs

Lines changed: 29 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ namespace SmartImage.Searching
3030
/// </summary>
3131
public sealed class SearchClient
3232
{
33-
/*
34-
* todo: use async/tasks
35-
* todo: timeout handling
36-
*/
37-
38-
private const string ORIGINAL_IMAGE_NAME = "(Original image)";
39-
4033
private static readonly string InterfacePrompt =
4134
$"Enter the option number to open or {NConsole.NC_GLOBAL_EXIT_KEY} to exit.\n" +
4235
$"Hold down {NC_ALT_FUNC_MODIFIER} to show more info.\n" +
@@ -58,17 +51,13 @@ public sealed class SearchClient
5851
/// </summary>
5952
private string ImageUrl { get; }
6053

61-
/// <summary>
62-
/// Thread that monitors the search (<see cref="RunSearchMonitor"/>)
63-
/// </summary>
64-
private Thread SearchMonitor { get; }
6554

6655
/// <summary>
6756
/// Search tasks (<seealso cref="CreateSearchTasks"/>)
6857
/// </summary>
69-
private Task[] SearchTasks { get; }
58+
private List<Task<FullSearchResult>> SearchTasks { get; }
59+
7060

71-
7261
/// <summary>
7362
/// Whether the search is complete
7463
/// </summary>
@@ -115,15 +104,6 @@ private SearchClient(string img)
115104

116105
SearchTasks = CreateSearchTasks();
117106

118-
// Joining each thread isn't necessary as this object is disposed upon program exit
119-
// Background threads won't prevent program termination
120-
121-
SearchMonitor = new Thread(RunSearchMonitor)
122-
{
123-
Priority = ThreadPriority.Highest,
124-
IsBackground = true
125-
};
126-
127107
Complete = false;
128108

129109
Interface = new NConsoleInterface(Results)
@@ -133,16 +113,34 @@ private SearchClient(string img)
133113
};
134114
}
135115

116+
136117
/// <summary>
137-
/// Monitors the search process
118+
/// Starts search and handles results
138119
/// </summary>
139-
private void RunSearchMonitor()
120+
public async void Start()
140121
{
141-
while (SearchTasks.Any(t => !t.IsCompleted)) {
142-
int inProgress = SearchTasks.Count(t => t.IsCompleted);
143-
int len = SearchTasks.Length;
122+
int len = SearchTasks.Count;
123+
124+
while (SearchTasks.Any()) {
125+
Task<FullSearchResult> finished = await Task.WhenAny(SearchTasks);
126+
SearchTasks.Remove(finished);
127+
128+
var result = finished.Result;
129+
130+
Results.Add(result);
131+
Results.Sort(FullSearchResult.CompareResults);
132+
133+
// If the engine is priority, open its result in the browser
134+
if (result.IsPriority) {
135+
HandleResultOpen(result);
136+
}
137+
138+
int inProgress = len - SearchTasks.Count;
144139

145140
Interface.Status = $"Searching: {inProgress}/{len}";
141+
142+
// Reload console UI
143+
NConsole.Refresh();
146144
}
147145

148146
/*
@@ -177,76 +175,17 @@ private void RunSearchMonitor()
177175
}
178176
}
179177

180-
/// <summary>
181-
/// Starts search
182-
/// </summary>
183-
public void Start()
184-
{
185-
SearchMonitor.Start();
186-
187-
foreach (var task in SearchTasks) {
188-
task.Start();
189-
}
190-
}
191-
192-
/// <summary>
193-
/// Creates a <see cref="FullSearchResult"/> for the original image
194-
/// </summary>
195-
private FullSearchResult GetOriginalImageResult()
196-
{
197-
var result = new FullSearchResult(Color.White, ORIGINAL_IMAGE_NAME, ImageUrl)
198-
{
199-
Similarity = 100.0f,
200-
};
201-
202-
var fileFormat = FileSystem.ResolveFileType(ImageFile.FullName);
203-
204-
double fileSizeMegabytes =
205-
MathHelper.ConvertToUnit(FileSystem.GetFileSize(ImageFile.FullName), MetricUnit.Mega);
206-
207-
(int width, int height) = Images.GetDimensions(ImageFile.FullName);
208178

209-
result.Width = width;
210-
result.Height = height;
211-
212-
double mpx = MathHelper.ConvertToUnit(width * height, MetricUnit.Mega);
213-
214-
string infoStr = $"Info: {ImageFile.Name} ({fileSizeMegabytes:F} MB) ({mpx:F} MP) ({fileFormat.Name})";
215-
216-
result.ExtendedInfo.Add(infoStr);
217-
218-
return result;
219-
}
220-
221-
private Task[] CreateSearchTasks()
179+
private List<Task<FullSearchResult>> CreateSearchTasks()
222180
{
223-
// todo: improve, hacky :(
224-
225181
var availableEngines = GetAllEngines()
226182
.Where(e => Engines.HasFlag(e.Engine))
227183
.ToArray();
228184

229-
230-
Results.Add(GetOriginalImageResult());
185+
// Add original image to results
186+
Results.Add(FullSearchResult.GetOriginalImageResult(ImageUrl, ImageFile));
231187

232-
233-
return availableEngines.Select(currentEngine => new Task(() =>
234-
{
235-
var result = currentEngine.GetResult(ImageUrl);
236-
237-
Results.Add(result);
238-
239-
// If the engine is priority, open its result in the browser
240-
if (SearchConfig.Config.PriorityEngines.HasFlag(currentEngine.Engine)) {
241-
HandleResultOpen(result);
242-
}
243-
244-
// Sort results
245-
Results.Sort(FullSearchResult.CompareResults);
246-
247-
// Reload console UI
248-
NConsole.Refresh();
249-
})).ToArray();
188+
return availableEngines.Select(currentEngine => Task.Run(() => currentEngine.GetResult(ImageUrl))).ToList();
250189
}
251190

252191
/// <summary>
@@ -276,8 +215,6 @@ private static void HandleResultOpen(FullSearchResult result)
276215
else {
277216
Debug.WriteLine($"Filtering result {result.Name}");
278217
}
279-
280-
281218
}
282219

283220

SmartImage/Utilities/NativeImports.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@ internal static class NativeImports
1717
private static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
1818

1919

20-
[DllImport(USER32_DLL, EntryPoint = "FindWindow", SetLastError = true, CharSet = CharSet.Unicode)]
21-
private static extern IntPtr FindWindowByCaption(IntPtr zeroOnly, string lpWindowName);
20+
2221

2322

24-
[DllImport(KERNEL32_DLL, ExactSpelling = true)]
25-
private static extern IntPtr GetConsoleWindow();
23+
2624

2725
[DllImport(USER32_DLL)]
2826
[return: MarshalAs(UnmanagedType.Bool)]
@@ -86,10 +84,7 @@ internal static void FlashWindow(IntPtr hWnd)
8684
}
8785

8886

89-
/// <summary>
90-
/// Gets console application's window handle. <see cref="Process.MainWindowHandle"/> does not work in some cases.
91-
/// </summary>
92-
internal static IntPtr GetConsoleWindowHandle() => FindWindowByCaption(IntPtr.Zero, Console.Title);
87+
9388

9489
internal static void FlashConsoleWindow() => FlashWindow(GetConsoleWindowHandle());
9590

0 commit comments

Comments
 (0)