1+ // Author: Deci | Project: SmartImage.Lib | Name: GoogleLens.cs
2+ // Date: 2025/04/15 @ 11:04:23
3+
4+ using AngleSharp ;
5+ using AngleSharp . Css . Values ;
6+ using AngleSharp . Dom ;
7+ using AngleSharp . Html . Parser ;
8+ using Flurl . Http ;
9+ using Kantan . Net . Utilities ;
10+ using SmartImage . Lib . Images . Uni ;
11+ using SmartImage . Lib . Results ;
12+
13+ namespace SmartImage . Lib . Engines . Impl . Search ;
14+
15+ public class GoogleLensEngine : WebSearchEngine , IEndpointEngine
16+ {
17+
18+ // TODO: WIP
19+
20+ public const string URL_BASE = "https://lens.google.com" ;
21+
22+ public override string Name => "Google Lens" ;
23+
24+ public override Url BaseUrl => URL_BASE ;
25+
26+ public override SearchEngineOptions EngineOption => SearchEngineOptions . GoogleLens ;
27+
28+ protected override string NodesSelector => throw new NotImplementedException ( ) ;
29+
30+ public Url EndpointUrl => URL_BASE ;
31+
32+ public GoogleLensEngine ( ) : base ( URL_BASE ) { }
33+
34+ public static readonly string [ ] SearchTypes = [ "all" , "products" , "visual_matches" , "exact_matches" ] ;
35+
36+ public FlurlCookie Nid { get ; set ; }
37+
38+ public string HlParam { get ; set ; } = "en-US" ;
39+
40+ public string SearchType { get ; set ; } = SearchTypes [ 0 ] ;
41+
42+ public object Headers = new
43+ {
44+ User_Agent = HttpUtilities . UserAgent ,
45+ Connection = "keep-alive" ,
46+ Accept_Encoding = "gzip, deflate, br" ,
47+ Accept = "*/*"
48+ } ;
49+
50+ protected override async ValueTask < SearchResultItem > ParseResultItem ( INode n , SearchResult r )
51+ {
52+ throw new NotImplementedException ( ) ;
53+ }
54+
55+ public override async Task < SearchResult > GetResultAsync ( SearchQuery query , CancellationToken token = default )
56+ {
57+ return await base . GetResultAsync ( query , token ) ;
58+ }
59+
60+ protected override async Task < IDocument > GetDocumentAsync ( SearchResult sr , SearchQuery query , CancellationToken token = default )
61+ {
62+ string endpoint , filename ;
63+ Task < IFlurlResponse > req ;
64+ IFlurlResponse res ;
65+
66+ /*if (query.Source.IsFile) {
67+ req = SearchFile(query, token);
68+ }
69+ else if (query.Source.IsUri) {
70+ req = SearchUrl(query, token);
71+ }
72+ else {
73+ return null;
74+ }*/
75+
76+ req = SearchUrlAsync ( query , token ) ;
77+
78+ res = await req ;
79+
80+ // var stream = await res.GetStringAsync();
81+ var url = res . ResponseMessage . RequestMessage . RequestUri ;
82+
83+ var res2 = await Client . Request ( url )
84+ . WithTimeout ( Timeout )
85+ // .WithHeaders(Headers)
86+ . WithCookie ( Nid . Name , Nid . Value )
87+ . GetAsync ( cancellationToken : token ) ;
88+
89+ var resData = await res2 . GetStringAsync ( ) ;
90+
91+ var parser = new HtmlParser ( new HtmlParserOptions ( )
92+ {
93+ IsScripting = true ,
94+ IsStrictMode = false ,
95+ IsAcceptingCustomElementsEverywhere = true ,
96+ IsEmbedded = true
97+ } ) ;
98+
99+ var doc = await parser . ParseDocumentAsync ( resData ) ;
100+
101+ // BrowsingContext.New(Configuration.Default.WithCookies().WithCss());
102+
103+ return doc ;
104+ }
105+
106+ private Task < IFlurlResponse > SearchFileAsync ( SearchQuery query , CancellationToken token )
107+ {
108+ string endpoint ;
109+ string filename ;
110+ UniImageFile uif = query . Source as UniImageFile ;
111+ Task < IFlurlResponse > req ;
112+ endpoint = "v3/upload" ;
113+
114+ // filename = "image.jpg";
115+ // filename = (query.Source is UniImageFile uif) ? uif.FileInfo.Name : "image.jpg";
116+ filename = uif . FileInfo . Name ;
117+
118+ req = Client . Request ( EndpointUrl , endpoint )
119+ . SetQueryParam ( "hl" , HlParam )
120+ . WithTimeout ( Timeout )
121+ . WithCookie ( Nid . Name , Nid . Value )
122+ . WithHeaders ( Headers )
123+ . PostMultipartAsync ( bc =>
124+ {
125+ //
126+ bc . AddFile ( filename , uif . FilePath ) ;
127+ } , cancellationToken : token ) ;
128+
129+ return req ;
130+ }
131+
132+ private Task < IFlurlResponse > SearchUrlAsync ( SearchQuery query , CancellationToken token )
133+ {
134+ return SearchUrlAsync ( query . Upload , token ) ;
135+ }
136+
137+ private Task < IFlurlResponse > SearchUrlAsync ( Url url , CancellationToken token )
138+ {
139+ string endpoint ;
140+ Task < IFlurlResponse > req ;
141+ endpoint = "uploadbyurl" ;
142+
143+ req = Client . Request ( EndpointUrl , endpoint )
144+ . SetQueryParam ( "hl" , HlParam )
145+ . SetQueryParam ( "url" , url )
146+ . WithCookie ( Nid . Name , Nid . Value )
147+ . WithHeaders ( Headers )
148+ . WithTimeout ( Timeout )
149+ . GetAsync ( cancellationToken : token ) ;
150+ return req ;
151+ }
152+
153+ protected override async ValueTask < List < INode > > GetNodes ( IDocument d )
154+ {
155+ return await base . GetNodes ( d ) ;
156+ }
157+
158+ protected override Url GetRawUrl ( SearchQuery query )
159+ {
160+ return base . GetRawUrl ( query ) ;
161+ }
162+
163+ public override void Dispose ( ) { }
164+
165+ }
0 commit comments