@@ -42,6 +42,8 @@ namespace SmartImage.Lib;
4242public sealed class SearchClient : IDisposable
4343{
4444
45+ public SearchQuery Query { get ; set ; }
46+
4547 public SearchConfig Config { get ; init ; }
4648
4749 public bool IsComplete { get ; private set ; }
@@ -54,16 +56,31 @@ public sealed class SearchClient : IDisposable
5456
5557 private static readonly ILogger s_logger = AppSupport . Factory . CreateLogger ( nameof ( SearchClient ) ) ;
5658
57- public SearchClient ( SearchConfig cfg )
59+ private static readonly Lock m_lock = new Lock ( ) ;
60+
61+ public SearchClient ( SearchConfig cfg , SearchQuery query )
5862 {
63+ Query = query ;
5964 Config = cfg ;
6065 ConfigApplied = false ;
6166 IsRunning = false ;
6267
68+ Config . PropertyChanged += ( sender , args ) =>
69+ {
70+ if ( args . PropertyName == nameof ( SearchConfig . SearchEngines ) ) {
71+ lock ( m_lock ) {
72+ Engines = BaseSearchEngine . GetSelectedEngines ( Config . SearchEngines ) . ToArray ( ) ;
73+
74+ }
75+ }
76+ } ;
77+
6378 // GetSelectedEngines();
6479
6580 }
6681
82+ public SearchClient ( SearchConfig cfg ) : this ( cfg , SearchQuery . Null ) { }
83+
6784 static SearchClient ( ) { }
6885
6986 [ ModuleInitializer ]
@@ -106,141 +123,43 @@ public void OpenChannel()
106123 // ...
107124 }
108125
109- ResultChannel = Channel . CreateUnbounded < SearchResult > ( new UnboundedChannelOptions ( )
126+ ResultChannel = Channel . CreateBounded < SearchResult > ( new BoundedChannelOptions ( Engines . Length )
110127 {
111128 SingleWriter = true ,
112-
113129 } ) ;
114130 }
115131
116- #if OLD
117- /// <summary>
118- /// Runs a search of <paramref name="query"/>.
119- /// </summary>
120- /// <param name="query">Search query</param>
121- /// <param name="scheduler"></param>
122- /// <param name="token">Cancellation token passed to <see cref="WebSearchEngine{T}.GetResultAsync(SmartImage.Lib.SearchQuery,System.Threading.CancellationToken)"/></param>
123- public async Task < SearchResult [ ] > RunSearchAsync1 ( SearchQuery query ,
124- TaskScheduler scheduler = default ,
125- CancellationToken token = default )
126- {
127- scheduler ??= TaskScheduler . Default ;
128-
129- // Requires.NotNull(ResultChannel);
130- if ( ResultChannel == null || ( IsComplete && ! IsRunning ) ) {
131- OpenChannel ( ) ;
132- }
133-
134- if ( ! query . IsUploaded ) {
135- throw new ArgumentException ( $ "Query was not uploaded", nameof ( query ) ) ;
136- }
137-
138- IsRunning = true ;
139-
140- if ( ! ConfigApplied ) {
141- await LoadEnginesAsync ( token ) ; // todo
142-
143- }
144- else {
145- Debug . WriteLine ( "Not reloading engines" ) ;
146- }
147-
148- Debug . WriteLine ( $ "Config: { Config } | { Engines . QuickJoin ( ) } ") ;
149-
150- var tasks = GetSearchTasks ( query , scheduler , token ) ;
151-
152- // var results = new SearchResult[tasks.Count];
153- int i = 0 ;
154-
155- /*while (tasks.Count > 0) {
156- if (token.IsCancellationRequested) {
157-
158- Debugger.Break();
159- s_logger.LogWarning("Cancellation requested");
160- CompleteSearchAsync();
161- return results;
162- }
163-
164- Task<SearchResult> task = await Task.WhenAny(tasks);
165- tasks.Remove(task);
166-
167- if (task.IsFaulted) {
168- Trace.WriteLine($"{task} faulted!",LogCategories.C_ERROR);
169- }
170- SearchResult result = await task;
171-
172- results[i] = result;
173- i++;
174- }*/
175-
176-
177- await foreach ( var task in Task . WhenEach ( tasks ) . WithCancellation ( token ) ) {
178- if ( token . IsCancellationRequested ) {
179-
180- Debugger . Break ( ) ;
181- s_logger . LogWarning ( "Cancellation requested" ) ;
182- CompleteSearchAsync ( ) ;
183- }
184-
185- var result = await task ;
186- ProcessResult ( result ) ;
187- }
188-
189- CompleteSearchAsync ( ) ;
190- OnSearchComplete ? . Invoke ( this , results ) ;
191-
192- if ( Config . PriorityEngines == SearchEngineOptions . Auto ) {
193-
194- try {
195-
196- SearchResultItem item = GetBest ( results ) ;
197-
198- if ( item != null ) {
199- OpenResult ( item . Url ) ;
200- }
201- }
202- catch ( Exception e ) {
203- Debug . WriteLine ( $ "{ e . Message } ") ;
204-
205- Debugger . Break ( ) ;
206- }
207-
208- }
209-
210- IsRunning = false ;
211-
212- return results ;
213- }
214- #endif
215-
216- public async IAsyncEnumerable < SearchResult > RunSearchAsync ( SearchQuery query ,
217- TaskScheduler scheduler = default ,
132+ public async IAsyncEnumerable < SearchResult > RunSearchAsync ( TaskScheduler scheduler = default ,
218133 [ EC ] CancellationToken token = default )
219134 {
220- await RunSearchAsync2 ( query , ResultChannel . Writer , token ) ;
135+ await RunSearchAsync2 ( token ) ;
221136
222- await foreach ( var result in ResultChannel . Reader . ReadAllAsync ( token ) ) {
223- yield return result ;
137+ while ( await ResultChannel . Reader . WaitToReadAsync ( token ) ) {
138+ while ( ResultChannel . Reader . TryRead ( out var result ) ) {
139+ yield return result ;
140+ }
224141 }
225142 }
226143
144+ public ValueTask < bool > RunSearchAsync2 ( CancellationToken token = default )
145+ => RunSearchAsync2 ( ResultChannel . Writer , token ) ;
227146
228- /// <summary>
229- /// Runs a search of <paramref name="query"/>.
230- /// </summary>
231- public async ValueTask < bool > RunSearchAsync2 ( SearchQuery query ,
232- ChannelWriter < SearchResult > cw ,
147+ public async ValueTask < bool > RunSearchAsync2 ( ChannelWriter < SearchResult > cw ,
233148 CancellationToken token = default )
234149 {
235- if ( ! query . IsUploaded ) {
236- throw new SmartImageException ( $ "{ query } was not uploaded") ;
150+ if ( ! Query . IsUploaded ) {
151+ throw new SmartImageException ( $ "{ Query } was not uploaded") ;
237152 }
238153
239- var tasks = Engines . Select ( e =>
240- {
241- var task = e . GetResultAsync ( query , token ) ;
242- return task ;
243- } ) ;
154+ IEnumerable < Task < SearchResult > > tasks ;
155+
156+ lock ( m_lock ) {
157+ tasks = Engines . Select ( e =>
158+ {
159+ var task = e . GetResultAsync ( Query , token ) ;
160+ return task ;
161+ } ) ;
162+ }
244163
245164 await foreach ( var task in Task . WhenEach ( tasks ) . WithCancellation ( token ) ) {
246165
@@ -369,40 +288,16 @@ public async ValueTask LoadEnginesAsync(CancellationToken token = default)
369288
370289 Trace . WriteLine ( "Loading engines" ) ;
371290
372- Engines = BaseSearchEngine . GetSelectedEngines ( Config . SearchEngines ) . ToArray ( ) ;
373291
374292 if ( Config . ReadCookies ) {
375293
376- try {
377- await ( ( DefaultCookiesProvider ) DefaultCookiesProvider . Instance ) . OpenAsync ( ) ;
378- }
379- catch ( Exception e ) {
380- Trace . WriteLine ( $ "{ e } ") ;
381- Config . ReadCookies = false ;
382- DefaultCookiesProvider . Instance . Dispose ( ) ;
383- }
294+ await InitCookiesProvider ( ) ;
384295 }
385296
386297 if ( Config . FlareSolverr && ! FlareSolverrClient . Value . IsInitialized ) {
387298
388299
389- var ok = FlareSolverrClient . Value . Configure ( Config . FlareSolverrApiUrl ) ;
390-
391- if ( ! ok ) {
392- Debugger . Break ( ) ;
393- }
394- else {
395- // Ensure FlareSolverr
396-
397- try {
398- var idx = await FlareSolverrClient . Value . Clearance . Solverr . GetIndexAsync ( ) ;
399- }
400- catch ( Exception e ) {
401- Trace . WriteLine ( $ "{ nameof ( FlareSolverrClient ) } : { e . Message } ") ;
402- Config . FlareSolverr = false ;
403- FlareSolverrClient . Value . Dispose ( ) ;
404- }
405- }
300+ await InitFlareSolverr ( ) ;
406301 }
407302
408303 foreach ( BaseSearchEngine bse in Engines ) {
@@ -424,6 +319,39 @@ public async ValueTask LoadEnginesAsync(CancellationToken token = default)
424319 ConfigApplied = true ;
425320 }
426321
322+ private async Task InitFlareSolverr ( )
323+ {
324+ var ok = FlareSolverrClient . Value . Configure ( Config . FlareSolverrApiUrl ) ;
325+
326+ if ( ! ok ) {
327+ Debugger . Break ( ) ;
328+ }
329+ else {
330+ // Ensure FlareSolverr
331+
332+ try {
333+ var idx = await FlareSolverrClient . Value . Clearance . Solverr . GetIndexAsync ( ) ;
334+ }
335+ catch ( Exception e ) {
336+ Trace . WriteLine ( $ "{ nameof ( FlareSolverrClient ) } : { e . Message } ") ;
337+ Config . FlareSolverr = false ;
338+ FlareSolverrClient . Value . Dispose ( ) ;
339+ }
340+ }
341+ }
342+
343+ private async Task InitCookiesProvider ( )
344+ {
345+ try {
346+ await ( ( DefaultCookiesProvider ) DefaultCookiesProvider . Instance ) . OpenAsync ( ) ;
347+ }
348+ catch ( Exception e ) {
349+ Trace . WriteLine ( $ "{ e } ") ;
350+ Config . ReadCookies = false ;
351+ DefaultCookiesProvider . Instance . Dispose ( ) ;
352+ }
353+ }
354+
427355 public void Dispose ( )
428356 {
429357 Debug . WriteLine ( $ "Disposing { nameof ( SearchClient ) } ") ;
0 commit comments