@@ -277,6 +277,97 @@ await DownloadFileAsync(
277
277
}
278
278
}
279
279
280
+ /// <summary>
281
+ /// Updates the plugin to the latest version available from its source.
282
+ /// </summary>
283
+ /// <param name="silentUpdate">If true, do not show any messages when there is no udpate available.</param>
284
+ /// <param name="usePrimaryUrlOnly">If true, only use the primary URL for updates.</param>
285
+ /// <param name="token">Cancellation token to cancel the update operation.</param>
286
+ /// <returns></returns>
287
+ public static async Task UpdatePluginAsync ( bool silentUpdate = true , bool usePrimaryUrlOnly = false , CancellationToken token = default )
288
+ {
289
+ // Update the plugin manifest
290
+ await API . UpdatePluginManifestAsync ( usePrimaryUrlOnly , token ) ;
291
+
292
+ // Get all plugins that can be updated
293
+ var resultsForUpdate = (
294
+ from existingPlugin in API . GetAllPlugins ( )
295
+ join pluginUpdateSource in API . GetPluginManifest ( )
296
+ on existingPlugin . Metadata . ID equals pluginUpdateSource . ID
297
+ where string . Compare ( existingPlugin . Metadata . Version , pluginUpdateSource . Version ,
298
+ StringComparison . InvariantCulture ) <
299
+ 0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest)
300
+ && ! API . PluginModified ( existingPlugin . Metadata . ID )
301
+ select
302
+ new
303
+ {
304
+ existingPlugin . Metadata . ID ,
305
+ pluginUpdateSource . Name ,
306
+ pluginUpdateSource . Author ,
307
+ CurrentVersion = existingPlugin . Metadata . Version ,
308
+ NewVersion = pluginUpdateSource . Version ,
309
+ existingPlugin . Metadata . IcoPath ,
310
+ PluginExistingMetadata = existingPlugin . Metadata ,
311
+ PluginNewUserPlugin = pluginUpdateSource
312
+ } ) . ToList ( ) ;
313
+
314
+ // No updates
315
+ if ( ! resultsForUpdate . Any ( ) )
316
+ {
317
+ if ( ! silentUpdate )
318
+ {
319
+ API . ShowMsg ( API . GetTranslation ( "updateNoResultTitle" ) , API . GetTranslation ( "updateNoResultSubtitle" ) ) ;
320
+ }
321
+ return ;
322
+ }
323
+
324
+ // If all plugins are modified, just return
325
+ if ( resultsForUpdate . All ( x => API . PluginModified ( x . ID ) ) )
326
+ {
327
+ return ;
328
+ }
329
+
330
+ if ( API . ShowMsgBox (
331
+ string . Format ( API . GetTranslation ( "updateAllPluginsSubtitle" ) ,
332
+ Environment . NewLine , string . Join ( ", " , resultsForUpdate . Select ( x => x . PluginExistingMetadata . Name ) ) ) ,
333
+ API . GetTranslation ( "updateAllPluginsTitle" ) ,
334
+ MessageBoxButton . YesNo ) == MessageBoxResult . No )
335
+ {
336
+ return ;
337
+ }
338
+
339
+ // Update all plugins
340
+ await Task . WhenAll ( resultsForUpdate . Select ( async plugin =>
341
+ {
342
+ var downloadToFilePath = Path . Combine ( Path . GetTempPath ( ) , $ "{ plugin . Name } -{ plugin . NewVersion } .zip") ;
343
+
344
+ try
345
+ {
346
+ using var cts = new CancellationTokenSource ( ) ;
347
+
348
+ await DownloadFileAsync (
349
+ $ "{ API . GetTranslation ( "DownloadingPlugin" ) } { plugin . PluginNewUserPlugin . Name } ",
350
+ plugin . PluginNewUserPlugin . UrlDownload , downloadToFilePath , cts ) ;
351
+
352
+ // check if user cancelled download before installing plugin
353
+ if ( cts . IsCancellationRequested )
354
+ {
355
+ return ;
356
+ }
357
+
358
+ if ( ! await API . UpdatePluginAsync ( plugin . PluginExistingMetadata , plugin . PluginNewUserPlugin , downloadToFilePath ) )
359
+ {
360
+ return ;
361
+ }
362
+ }
363
+ catch ( Exception e )
364
+ {
365
+ API . LogException ( ClassName , "Failed to update plugin" , e ) ;
366
+ API . ShowMsgError ( API . GetTranslation ( "ErrorUpdatingPlugin" ) ) ;
367
+ }
368
+ } ) ) ;
369
+ }
370
+
280
371
/// <summary>
281
372
/// Downloads a file from a URL to a local path, optionally showing a progress box and handling cancellation.
282
373
/// </summary>
0 commit comments