Skip to content

Commit 2c8e902

Browse files
committed
More powerful image scanning
1 parent d7a5c00 commit 2c8e902

File tree

9 files changed

+157
-81
lines changed

9 files changed

+157
-81
lines changed

SmartImage.Lib/Engines/Impl/IqdbEngine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ protected override SearchResult Process(object obj, SearchResult sr)
184184

185185
var best = images[0];
186186
sr.PrimaryResult.UpdateFrom(best);
187-
sr.OtherResults.AddRange(images);
187+
sr.OtherResults.AddRange(images.Skip(1));
188188

189189
sr.PrimaryResult.Quality = sr.PrimaryResult.Similarity switch
190190
{

SmartImage.Lib/SearchClient.cs

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Threading;
77
using System.Threading.Tasks;
88
using JetBrains.Annotations;
9+
using Kantan.Net;
910
using Kantan.Threading;
1011
using Novus.Utilities;
1112
using SmartImage.Lib.Engines;
@@ -176,7 +177,10 @@ public async Task RunSearchAsync()
176177
DetailedResults.Add(value.PrimaryResult);
177178
}
178179

179-
ThreadPool.QueueUserWorkItem(c => FindDirectResults(c, value));
180+
/* 1st pass */
181+
if (value.IsSuccessful && value.IsNonPrimitive) {
182+
ThreadPool.QueueUserWorkItem(c => FindDirectResults(c, value));
183+
}
180184

181185
// Call event
182186
ResultCompleted?.Invoke(null, new ResultCompletedEventArgs(value)
@@ -190,6 +194,10 @@ public async Task RunSearchAsync()
190194

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

197+
198+
/* 2nd pass */
199+
200+
193201
var args = new SearchCompletedEventArgs
194202
{
195203
Results = Results,
@@ -198,9 +206,65 @@ public async Task RunSearchAsync()
198206
Filtered = FilteredResults
199207
};
200208

209+
201210
SearchCompleted?.Invoke(null, args);
202211
}
203212

213+
public async void FindDirectResults(SearchResult result)
214+
{
215+
Debug.WriteLine($"searching within {result.Engine.Name}");
216+
217+
for (int i = 0; i < result.AllResults.Count; i++) {
218+
var ir = result.AllResults[i];
219+
220+
var b = await ir.FindDirectImages();
221+
222+
if (b && !DirectResults.Contains(ir)) {
223+
ir.Direct = new Uri(UriUtilities.NormalizeUrl(ir.Direct));
224+
225+
Debug.WriteLine($"{ir.Direct}");
226+
227+
DirectResults.Add(ir);
228+
229+
result.PrimaryResult.Direct ??= ir.Direct;
230+
231+
DirectFound?.Invoke(null, new DirectResultsFoundEventArgs
232+
{
233+
DirectResultsSubset = new() { ir },
234+
});
235+
ResultUpdated?.Invoke(null, EventArgs.Empty);
236+
}
237+
}
238+
}
239+
240+
241+
private void FindDirectResults(object state, SearchResult value, int take1 = 10, int take2 = 5)
242+
{
243+
244+
var imageResults = value.AllResults;
245+
246+
var images = imageResults.AsParallel()
247+
.Where(x => x.CheckDirect(DirectImageCriterion.Regex))
248+
.Take(take1)
249+
.Where(x => x.CheckDirect(DirectImageCriterion.Binary))
250+
.Take(take2)
251+
.ToList();
252+
253+
if (images.Any()) {
254+
Debug.WriteLine($"*{nameof(SearchClient)}: Found {images.Count} direct results", C_DEBUG);
255+
DirectResults.AddRange(images);
256+
257+
DirectFound?.Invoke(null, new DirectResultsFoundEventArgs
258+
{
259+
DirectResultsSubset = images,
260+
});
261+
}
262+
else {
263+
var t = Task.Factory.StartNew(() => FindDirectResults(value));
264+
265+
}
266+
}
267+
204268
/// <summary>
205269
/// Waits until <see cref="DirectResults"/> contains any elements
206270
/// </summary>
@@ -211,8 +275,10 @@ public Task<List<ImageResult>> WaitForDirectResults()
211275
{
212276
while (Results.Any() && !DirectResults.Any()) {
213277
if (IsComplete) {
214-
List<ImageResult> rescan = RescanImageResults(Results);
215-
DirectResults.AddRange(rescan);
278+
var b = Results.SelectMany(x => x.AllResults.Where(x2 => x2.Direct != null))
279+
.OrderByDescending(x => x.PixelResolution)
280+
.ToList();
281+
DirectResults.AddRange(b);
216282

217283
break;
218284
}
@@ -222,13 +288,7 @@ public Task<List<ImageResult>> WaitForDirectResults()
222288
});
223289
}
224290

225-
private static List<ImageResult> RescanImageResults(List<SearchResult> results)
226-
{
227-
var b = results.SelectMany(x => x.AllResults.Where(x2 => x2.Direct != null))
228-
.OrderByDescending(x => x.PixelResolution)
229-
.ToList();
230-
return b;
231-
}
291+
#endregion
232292

233293
/// <summary>
234294
/// Refines search results by searching with the most-detailed result (<see cref="FindDirectResults" />).
@@ -258,29 +318,6 @@ public async Task RefineSearchAsync()
258318
await RunSearchAsync();
259319
}
260320

261-
private void FindDirectResults(object state, SearchResult value, int take1 = 10, int take2 = 5)
262-
{
263-
var imageResults = value.AllResults;
264-
265-
var images = imageResults.AsParallel()
266-
.Where(x => x.CheckDirect(DirectImageCriterion.Regex))
267-
.Take(take1)
268-
.Where(x => x.CheckDirect(DirectImageCriterion.Binary))
269-
.Take(take2)
270-
.ToList();
271-
272-
if (images.Any()) {
273-
Debug.WriteLine($"*{nameof(SearchClient)}: Found {images.Count} direct results", C_DEBUG);
274-
DirectResults.AddRange(images);
275-
276-
DirectFound?.Invoke(null, new DirectResultsFoundEventArgs
277-
{
278-
DirectResultsSubset = images,
279-
});
280-
}
281-
}
282-
283-
284321
public static List<ImageResult> ApplyPredicateFilter(List<SearchResult> results, Predicate<SearchResult> predicate)
285322
{
286323
var query = results.Where(r => predicate(r))
@@ -309,8 +346,6 @@ public List<SearchResult> MaximizeResults<T>(Func<SearchResult, T> property)
309346
return res;
310347
}
311348

312-
#endregion
313-
314349
public static BaseUploadEngine[] GetAllUploadEngines()
315350
{
316351
return typeof(BaseUploadEngine).GetAllSubclasses()
@@ -327,6 +362,11 @@ public static BaseSearchEngine[] GetAllSearchEngines()
327362
.ToArray();
328363
}
329364

365+
/// <summary>
366+
/// Fires when a result has been updated with new information
367+
/// </summary>
368+
public event EventHandler ResultUpdated;
369+
330370
/// <summary>
331371
/// Fires when a result is returned (<see cref="RunSearchAsync" />).
332372
/// </summary>

SmartImage.Lib/Searching/ImageResult.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Drawing;
66
using System.Linq;
77
using System.Reflection;
8+
using System.Threading.Tasks;
89
using JetBrains.Annotations;
910
using Kantan.Model;
1011
using Kantan.Numeric;
@@ -210,10 +211,10 @@ public void UpdateFrom(ImageResult result)
210211

211212
}
212213

213-
public async void FindDirectImages()
214+
public async Task<bool> FindDirectImages()
214215
{
215216
if (Url == null || Direct != null) {
216-
return;
217+
return true;
217218
}
218219

219220
try {
@@ -224,13 +225,15 @@ public async void FindDirectImages()
224225

225226
if (direct != null) {
226227
Direct = new Uri((direct));
228+
UpdateImageData();
229+
return true;
227230
}
228231
}
229232
catch {
230233
//
231234
}
232235

233-
UpdateImageData();
236+
return false;
234237
}
235238

236239
public bool CheckDirect(DirectImageCriterion d)

SmartImage.Lib/Utilities/ImageHelper.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Novus.Win32;
1818
using RestSharp;
1919
using static Kantan.Diagnostics.LogCategories;
20+
2021
#pragma warning disable CS0618
2122
#pragma warning disable SYSLIB0014
2223
#pragma warning disable CA1416
@@ -106,7 +107,6 @@ public static string Download(Uri src, string path)
106107
}
107108
}
108109

109-
110110

111111
/// <summary>
112112
/// Scans for direct images within a webpage.
@@ -130,7 +130,7 @@ public static async Task<List<string>> ScanForImages(string url, int count = 10,
130130
}
131131

132132
using var cts = new CancellationTokenSource();
133-
var flat = new List<string>();
133+
var flat = new List<string>();
134134

135135
flat.AddRange(document.QuerySelectorAttributes("a", "href"));
136136
flat.AddRange(document.QuerySelectorAttributes("img", "src"));
@@ -164,14 +164,15 @@ public static async Task<List<string>> ScanForImages(string url, int count = 10,
164164
count--;
165165
}
166166
}
167-
167+
168168

169169
return images;
170170
}
171171

172172
public static bool IsImage(string url, DirectImageCriterion directCriterion = DirectImageCriterion.Binary)
173173
=> IsImage(url, TimeoutMS, directCriterion);
174174

175+
175176
public static bool IsImage(string url, long timeout, DirectImageCriterion directCriterion)
176177
{
177178
switch (directCriterion) {
@@ -195,20 +196,26 @@ public static bool IsImage(string url, long timeout, DirectImageCriterion direct
195196

196197
// The content-type returned from the response may not be the actual content-type, so
197198
// we'll resolve it using binary data instead to be sure
199+
bool a, b;
200+
201+
try {
202+
var stream = WebUtilities.GetStream(url);
203+
var buffer = new byte[256];
204+
stream.Read(buffer, 0, buffer.Length);
205+
// var rg = response.RawBytes;
206+
var m = MediaTypes.ResolveFromData(buffer);
207+
a = m.StartsWith("image") && m != "image/svg+xml";
208+
b = response.ContentLength is -1 or >= 50_000;
209+
}
210+
catch {
211+
a = response.ContentType.StartsWith("image") && response.ContentType != "image/svg+xml";
212+
b = response.ContentLength >= 50_000;
213+
}
198214

199-
var stream = WebUtilities.GetStream(url);
200-
var buffer = new byte[256];
201-
stream.Read(buffer, 0, buffer.Length);
202-
// var rg = response.RawBytes;
203-
var m = MediaTypes.ResolveFromData(buffer);
204-
205-
// var a = response.ContentType.StartsWith("image") && response.ContentType != "image/svg+xml";
206-
// var b = response.ContentLength >= 50_000;
207215

208-
var a = m.StartsWith("image") && m != "image/svg+xml";
209216
// var b = stream.Length >= 50_000;
210217

211-
return a;
218+
return a && b;
212219
default:
213220
throw new ArgumentOutOfRangeException(nameof(directCriterion), directCriterion, null);
214221
}

SmartImage/Program.cs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,17 @@ public static class Program
128128
await Client.RefineSearchAsync();
129129
}
130130
catch (Exception e) {
131-
Console.WriteLine("\nError: {0}", e.Message);
132-
ConsoleManager.WaitForTimeSpan(TimeSpan.FromSeconds(3));
131+
Console.WriteLine(
132+
$"\n{Strings.Constants.CHEVRON} Error: {e.Message.AddColor(Elements.ColorError)}");
133+
134+
ConsoleManager.WaitForTimeSpan(TimeSpan.FromSeconds(2));
133135
ResultDialog.Options.Clear();
134136
// ResultDialog.Options.Add(_originalResult);
135137

136-
(ResultDialog.Options as List<ConsoleOption>).AddRange(buf);
138+
foreach (ConsoleOption t in buf) {
139+
ResultDialog.Options.Add(t);
140+
}
141+
137142
}
138143

139144
ResultDialog.Refresh();
@@ -157,7 +162,8 @@ private static async Task Main(string[] args)
157162

158163

159164
ToastNotificationManagerCompat.OnActivated += AppToast.OnToastActivated;
160-
Console.OutputEncoding = Encoding.Unicode;
165+
166+
Console.OutputEncoding = Encoding.Unicode;
161167

162168
Console.Title = $"{AppInfo.NAME}";
163169

@@ -212,14 +218,27 @@ private static async Task Main(string[] args)
212218
// ...
213219
};
214220

221+
Client.ResultUpdated += (sender, result) =>
222+
{
223+
/*var option = ResultDialog.Options.First(x => x.Name == result.Engine.Name);
224+
var i = ResultDialog.Options.IndexOf(option);
225+
ResultDialog.Options[i] = ConsoleUIFactory.CreateResultOption(result);
226+
ResultDialog.Refresh();
227+
228+
Debug.WriteLine($"{i} update");*/
229+
ResultDialog.Refresh();
230+
231+
};
232+
233+
215234
ConsoleProgressIndicator.Start(_cancellationToken);
216235

217236

218237
// Show results
219238
var searchTask = Client.RunSearchAsync();
220239

221240
_originalResult = ConsoleUIFactory.CreateResultOption(Config.Query.GetImageResult(), "(Original image)",
222-
Elements.ColorMain, -0.1f);
241+
Elements.ColorMain, -0.1f);
223242

224243
// Add original image
225244
ResultDialog.Options.Add(_originalResult);
@@ -361,7 +380,7 @@ private static void OnSearchCompleted(object sender, SearchCompletedEventArgs ev
361380
cts.Cancel();
362381
cts.Dispose();
363382

364-
SystemSounds.Exclamation.Play();
383+
// SystemSounds.Exclamation.Play();
365384

366385
ResultDialog.Refresh();
367386

@@ -371,6 +390,7 @@ private static void OnSearchCompleted(object sender, SearchCompletedEventArgs ev
371390
WebUtilities.OpenUrl(m.First().PrimaryResult.Url.ToString());
372391
}
373392

393+
374394
}
375395

376396
private static void OnResultCompleted(object sender, ResultCompletedEventArgs eventArgs)

SmartImage/SmartImage.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696

9797
<PropertyGroup>
9898
<PackageId>SmartImage</PackageId>
99-
<Version>2.1.9</Version>
99+
<Version>2.2.0</Version>
100100
<Authors>Read Stanton (Decimation)</Authors>
101101
<PackageTags>Image reverse search identification source sauce</PackageTags>
102102
<RepositoryUrl>https://github.com/Decimation/SmartImage</RepositoryUrl>

0 commit comments

Comments
 (0)