@@ -30,53 +30,97 @@ public IndexerPageableRequestChain GetSearchRequests(AlbumSearchCriteria searchC
3030 {
3131 _logger . Trace ( $ "Generating search requests for album: { searchCriteria . AlbumQuery } by artist: { searchCriteria . ArtistQuery } ") ;
3232 IndexerPageableRequestChain chain = new ( ) ;
33- chain . AddTier ( GetRequests ( searchCriteria . ArtistQuery , searchCriteria . AlbumQuery , searchCriteria . InteractiveSearch ) ) ;
33+
34+ chain . AddTier ( DeferredGetRequests ( searchCriteria . ArtistQuery , searchCriteria . AlbumQuery , searchCriteria . InteractiveSearch ) ) ;
35+
36+ if ( ! Settings . UseFallbackSearch )
37+ return chain ;
38+
39+ List < string > aliases = searchCriteria . Artist . Metadata . Value . Aliases ;
40+ for ( int i = 0 ; i < 2 && i < aliases . Count ; i ++ )
41+ if ( aliases [ i ] . Length > 3 )
42+ chain . AddTier ( DeferredGetRequests ( aliases [ i ] , searchCriteria . AlbumQuery , searchCriteria . InteractiveSearch ) ) ;
43+ if ( searchCriteria . AlbumQuery . Length > 20 )
44+ {
45+ string [ ] albumWords = searchCriteria . AlbumQuery . Split ( new [ ] { ' ' } , StringSplitOptions . RemoveEmptyEntries ) ;
46+ int halfLength = ( int ) Math . Ceiling ( albumWords . Length / 2.0 ) ;
47+ string halfAlbumTitle = string . Join ( " " , albumWords . Take ( halfLength ) ) ;
48+ chain . AddTier ( DeferredGetRequests ( searchCriteria . ArtistQuery , halfAlbumTitle , searchCriteria . InteractiveSearch , searchCriteria . AlbumQuery ) ) ;
49+ }
50+ chain . AddTier ( DeferredGetRequests ( searchCriteria . ArtistQuery , null , searchCriteria . InteractiveSearch , searchCriteria . AlbumQuery ) ) ;
3451 return chain ;
3552 }
3653
54+
3755 public IndexerPageableRequestChain GetSearchRequests ( ArtistSearchCriteria searchCriteria )
3856 {
3957 _logger . Trace ( $ "Generating search requests for artist: { searchCriteria . ArtistQuery } ") ;
4058 IndexerPageableRequestChain chain = new ( ) ;
41- chain . AddTier ( GetRequests ( searchCriteria . ArtistQuery , null , searchCriteria . InteractiveSearch ) ) ;
59+ List < string > aliases = searchCriteria . Artist . Metadata . Value . Aliases ;
60+ for ( int i = 0 ; i < 3 && i < aliases . Count && Settings . UseFallbackSearch ; i ++ )
61+ if ( aliases [ i ] . Length > 3 )
62+ chain . AddTier ( DeferredGetRequests ( aliases [ i ] , null , searchCriteria . InteractiveSearch ) ) ;
4263 return chain ;
4364 }
4465
45- private IEnumerable < IndexerRequest > GetRequests ( string artist , string ? album = null , bool interactive = false )
66+
67+ private IEnumerable < IndexerRequest > DeferredGetRequests ( string artist , string ? album , bool interactive , string ? fullAlbum = null )
68+ {
69+ _searchResultsRequest = null ;
70+ IndexerRequest ? request = GetRequestsAsync ( artist , album , interactive , fullAlbum ) . Result ;
71+ if ( request != null )
72+ yield return request ;
73+ }
74+
75+ private async Task < IndexerRequest ? > GetRequestsAsync ( string artist , string ? album , bool interactive , string ? fullAlbum = null )
4676 {
47- var searchData = new
77+ try
4878 {
49- Id = Guid . NewGuid ( ) . ToString ( ) ,
50- Settings . FileLimit ,
51- FilterResponses = true ,
52- Settings . MaximumPeerQueueLength ,
53- Settings . MinimumPeerUploadSpeed ,
54- Settings . MinimumResponseFileCount ,
55- Settings . ResponseLimit ,
56- SearchText = $ "{ album } { artist } ",
57- SearchTimeout = ( int ) ( Settings . TimeoutInSeconds * 1000 ) ,
58- } ;
59-
60- HttpRequest searchRequest = new HttpRequestBuilder ( $ "{ Settings . BaseUrl } /api/v0/searches")
61- . SetHeader ( "X-API-KEY" , Settings . ApiKey )
62- . SetHeader ( "Content-Type" , "application/json" )
63- . Post ( )
64- . Build ( ) ;
65-
66- searchRequest . SetContent ( JsonConvert . SerializeObject ( searchData ) ) ;
67- _client . Execute ( searchRequest ) ;
68- WaitOnSearchCompletionAsync ( searchData . Id , TimeSpan . FromSeconds ( Settings . TimeoutInSeconds ) ) . Wait ( ) ;
69-
70- _logger . Trace ( $ "Generated search initiation request: { searchRequest . Url } ") ;
71-
72- HttpRequest request = new HttpRequestBuilder ( $ "{ Settings . BaseUrl } /api/v0/searches/{ searchData . Id } ")
73- . AddQueryParam ( "includeResponses" , true )
74- . SetHeader ( "X-API-KEY" , Settings . ApiKey )
75- . SetHeader ( "X-ALBUM" , Convert . ToBase64String ( Encoding . UTF8 . GetBytes ( album ?? "" ) ) )
76- . SetHeader ( "X-ARTIST" , Convert . ToBase64String ( Encoding . UTF8 . GetBytes ( artist ) ) )
77- . SetHeader ( "X-INTERACTIVE" , interactive . ToString ( ) )
78- . Build ( ) ;
79- yield return new IndexerRequest ( request ) ;
79+ var searchData = new
80+ {
81+ Id = Guid . NewGuid ( ) . ToString ( ) ,
82+ Settings . FileLimit ,
83+ FilterResponses = true ,
84+ Settings . MaximumPeerQueueLength ,
85+ Settings . MinimumPeerUploadSpeed ,
86+ Settings . MinimumResponseFileCount ,
87+ Settings . ResponseLimit ,
88+ SearchText = $ "{ album } { artist } ",
89+ SearchTimeout = ( int ) ( Settings . TimeoutInSeconds * 1000 ) ,
90+ } ;
91+
92+ HttpRequest searchRequest = new HttpRequestBuilder ( $ "{ Settings . BaseUrl } /api/v0/searches")
93+ . SetHeader ( "X-API-KEY" , Settings . ApiKey )
94+ . SetHeader ( "Content-Type" , "application/json" )
95+ . Post ( )
96+ . Build ( ) ;
97+
98+ searchRequest . SetContent ( JsonConvert . SerializeObject ( searchData ) ) ;
99+ await _client . ExecuteAsync ( searchRequest ) ;
100+ await WaitOnSearchCompletionAsync ( searchData . Id , TimeSpan . FromSeconds ( Settings . TimeoutInSeconds ) ) ;
101+
102+ _logger . Trace ( $ "Generated search initiation request: { searchRequest . Url } ") ;
103+
104+ HttpRequest request = new HttpRequestBuilder ( $ "{ Settings . BaseUrl } /api/v0/searches/{ searchData . Id } ")
105+ . AddQueryParam ( "includeResponses" , true )
106+ . SetHeader ( "X-API-KEY" , Settings . ApiKey )
107+ . SetHeader ( "X-ALBUM" , Convert . ToBase64String ( Encoding . UTF8 . GetBytes ( fullAlbum ?? album ?? "" ) ) )
108+ . SetHeader ( "X-ARTIST" , Convert . ToBase64String ( Encoding . UTF8 . GetBytes ( artist ) ) )
109+ . SetHeader ( "X-INTERACTIVE" , interactive . ToString ( ) )
110+ . Build ( ) ;
111+
112+ return new IndexerRequest ( request ) ;
113+ }
114+ catch ( HttpException ex ) when ( ex . Response . StatusCode == HttpStatusCode . NotFound )
115+ {
116+ _logger . Warn ( $ "Search request failed for artist: { artist } , album: { album } . Error: { ex . Message } ") ;
117+ return null ;
118+ }
119+ catch ( Exception ex )
120+ {
121+ _logger . Error ( ex , $ "An error occurred while generating search request for artist: { artist } , album: { album } ") ;
122+ return null ;
123+ }
80124 }
81125
82126 private async Task WaitOnSearchCompletionAsync ( string searchId , TimeSpan timeout )
@@ -137,9 +181,7 @@ private static double CalculateQuadraticDelay(double progress)
137181 private async Task < dynamic ? > GetSearchResultsAsync ( string searchId )
138182 {
139183 _searchResultsRequest ??= new HttpRequestBuilder ( $ "{ Settings . BaseUrl } /api/v0/searches/{ searchId } ")
140- . SetHeader ( "X-API-KEY" , Settings . ApiKey )
141- . Build ( ) ;
142-
184+ . SetHeader ( "X-API-KEY" , Settings . ApiKey ) . Build ( ) ;
143185 HttpResponse response = await _client . ExecuteAsync ( _searchResultsRequest ) ;
144186
145187 if ( response . StatusCode != HttpStatusCode . OK )
0 commit comments