77using System . Diagnostics ;
88using System . Globalization ;
99using System . IO ;
10+ using System . IO . Packaging ;
1011using System . Linq ;
12+ using System . Net ;
13+ using System . Net . Http ;
14+ using System . Net . Http . Json ;
1115using System . Reflection ;
16+ using System . Text . Json ;
1217using System . Text . RegularExpressions ;
1318using System . Threading ;
1419using System . Windows . Controls ;
@@ -28,6 +33,8 @@ public partial class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider
2833 // Should only be set in Init()
2934 private Action onPluginError ;
3035
36+ private static List < WingetPackage > packagesList ;
37+
3138 private const string NotGlobalIfUri = nameof ( NotGlobalIfUri ) ;
3239
3340 /// <summary>If true, dont show global result on queries that are URIs</summary>
@@ -58,6 +65,7 @@ public partial class Main : IPlugin, IPluginI18n, IContextMenu, ISettingProvider
5865 // constructor
5966 public Main ( )
6067 {
68+ GetPackages ( ) ;
6169 LoadInstalledList ( ) ;
6270 }
6371
@@ -85,6 +93,35 @@ private static void LoadInstalledList()
8593 installed = output ;
8694 }
8795
96+ public class WingetPackage
97+ {
98+ public string Name { get ; set ; }
99+
100+ public string Company { get ; set ; }
101+
102+ public string Version { get ; set ; }
103+ }
104+
105+ private static async void GetPackages ( )
106+ {
107+ // Download packages list
108+ var packages = new List < string > ( ) ;
109+ HttpClient client = new HttpClient ( ) ;
110+ HttpResponseMessage response = await client . GetAsync ( "https://bostrot.github.io/PowerToysRunPluginWinget/pkgs.json" ) ;
111+ response . EnsureSuccessStatusCode ( ) ;
112+ string responseBody = await response . Content . ReadAsStringAsync ( ) ;
113+
114+ // Allow trailing comma
115+ var options = new JsonSerializerOptions
116+ {
117+ AllowTrailingCommas = true ,
118+ PropertyNameCaseInsensitive = true ,
119+ } ;
120+
121+ // Parse response to JSON and ignore use lowercase first letter instead of uppercase
122+ packagesList = JsonSerializer . Deserialize < List < WingetPackage > > ( responseBody , options ) ;
123+ }
124+
88125 public List < Result > Query ( Query query )
89126 {
90127 if ( query is null )
@@ -115,126 +152,24 @@ public List<Result> Query(Query query)
115152 else
116153 {
117154 string searchTerm = query . Search ;
118-
119- Process process = new Process ( ) ;
120-
121- process . StartInfo . FileName = "winget" ;
122- process . StartInfo . Arguments = $ "search \" { searchTerm } \" ";
123- process . StartInfo . UseShellExecute = false ;
124- process . StartInfo . CreateNoWindow = true ;
125- process . StartInfo . RedirectStandardOutput = true ;
126- process . Start ( ) ;
127-
128- string output = process . StandardOutput . ReadToEnd ( ) ;
129- process . WaitForExit ( ) ;
130-
131- // UTF16 to UTF8
132- output = System . Text . Encoding . UTF8 . GetString (
133- System . Text . Encoding . Convert (
134- System . Text . Encoding . Unicode ,
135- System . Text . Encoding . UTF8 ,
136- System . Text . Encoding . Unicode . GetBytes ( output ) ) ) ;
137-
138- // If there is no error, iterate through the output and add each line as a result
139- string [ ] lines = output . Split ( new [ ] { Environment . NewLine } , StringSplitOptions . None ) ;
140-
141- var id = 0 ;
142-
143- int nameChars = 0 ;
144- int idChars = 0 ;
145- int versionChars = 0 ;
146- int matchChars = 0 ;
147-
148- // Regex for words in header
149- string v = lines [ 0 ] ;
150- var matches = Regex . Matches ( v , @"\S+" ) ;
151-
152- if ( matches != null )
155+ foreach ( WingetPackage package in packagesList )
153156 {
154- // Get chars between Name, ID, Version, Matches, Source length including spaces
155- if ( matches . Count == 5 )
157+ var idStr = $ " { package . Company } . { package . Name } " ;
158+ if ( package . Name . ToLower ( ) . Contains ( searchTerm . ToLower ( ) ) || package . Company . ToLower ( ) . Contains ( searchTerm . ToLower ( ) ) )
156159 {
157- nameChars = matches [ 2 ] . Index - matches [ 1 ] . Index ;
158- idChars = matches [ 3 ] . Index - 1 - matches [ 2 ] . Index ;
159- versionChars = matches [ 4 ] . Index - matches [ 3 ] . Index ;
160- }
161- else if ( matches . Count == 6 )
162- {
163- nameChars = matches [ 2 ] . Index - matches [ 1 ] . Index ;
164- idChars = matches [ 3 ] . Index - 1 - matches [ 2 ] . Index ;
165- versionChars = matches [ 4 ] . Index - matches [ 3 ] . Index ;
166- matchChars = matches [ 5 ] . Index - 1 - matches [ 4 ] . Index ;
167- }
168- }
169-
170- foreach ( string line0 in lines )
171- {
172- // Skip header
173- if ( id < 2 )
174- {
175- id ++ ;
176- continue ;
177- }
178-
179- // Filter non-text, non-number, non-space and non (-_.,) characters
180- var line = AllowedCharacters ( ) . Replace ( line0 , string . Empty ) ;
181-
182- if ( line != string . Empty )
183- {
184- string name = string . Empty ;
185- string idStr = string . Empty ;
186- string version = string . Empty ;
187- string match = string . Empty ;
188- string source = string . Empty ;
189- try
190- {
191- // Divide line into 5 parts by split
192- name = line . Substring ( 0 , nameChars ) . Trim ( ) ;
193- idStr = line . Substring ( nameChars , idChars ) . Trim ( ) ;
194- version = line . Substring ( nameChars + idChars , versionChars ) . Trim ( ) ;
195- if ( matches . Count == 6 )
196- {
197- match = line . Substring ( versionChars + nameChars + idChars , matchChars ) . Trim ( ) ;
198- source = line . Substring ( matchChars + versionChars + nameChars + idChars ) . Trim ( ) ;
199- }
200- else
201- {
202- match = string . Empty ;
203- }
204- }
205- catch ( Exception e )
206- {
207- name = e . ToString ( ) ;
208- }
209-
210- // Check if result is empty
211- if ( name == string . Empty )
212- {
213- continue ;
214- }
215-
216- string title = $ "{ name } ({ idStr } )";
217- string subTitle = $ "{ Properties . Resources . plugin_result_name } { name } [{ version } ] ({ source } ) { match } ";
218-
219- if ( source == string . Empty )
220- {
221- subTitle = $ "{ Properties . Resources . plugin_result_name } { name } [{ version } ]";
222- }
223-
224160 results . Add ( new Result
225161 {
226- Title = title ,
227- SubTitle = subTitle ,
228- QueryTextDisplay = name ,
162+ Title = package . Name ,
163+ SubTitle = $ "by { package . Company } version: { package . Version } " ,
164+ QueryTextDisplay = idStr ,
229165 IcoPath = _iconPath ,
230166 ProgramArguments = idStr ,
231167 Action = action =>
232168 {
233- Winget ( "install " + idStr + " --wait") ;
169+ Winget ( $ "install { idStr } --wait") ;
234170 return true ;
235171 } ,
236172 } ) ;
237- id ++ ;
238173 }
239174 }
240175 }
0 commit comments