From d5622367ccb82485e35e7956bc048dd7ae933f4f Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Sat, 13 Jan 2024 23:49:08 -0600 Subject: [PATCH 1/9] ignore null datetimes for pluginstoreitem --- .../ExternalPlugins/CommunityPluginSource.cs | 19 +++++++++++++++---- .../ExternalPlugins/UserPlugin.cs | 4 ++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs b/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs index d3ee4695cc2..68be746f28e 100644 --- a/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs +++ b/Flow.Launcher.Core/ExternalPlugins/CommunityPluginSource.cs @@ -5,6 +5,8 @@ using System.Net; using System.Net.Http; using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; @@ -16,6 +18,11 @@ public record CommunityPluginSource(string ManifestFileUrl) private List plugins = new(); + private static JsonSerializerOptions PluginStoreItemSerializationOption = new JsonSerializerOptions() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault + }; + /// /// Fetch and deserialize the contents of a plugins.json file found at . /// We use conditional http requests to keep repeat requests fast. @@ -32,12 +39,15 @@ public async Task> FetchAsync(CancellationToken token) request.Headers.Add("If-None-Match", latestEtag); - using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); + using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token) + .ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.OK) { - this.plugins = await response.Content.ReadFromJsonAsync>(cancellationToken: token).ConfigureAwait(false); - this.latestEtag = response.Headers.ETag.Tag; + this.plugins = await response.Content + .ReadFromJsonAsync>(PluginStoreItemSerializationOption, cancellationToken: token) + .ConfigureAwait(false); + this.latestEtag = response.Headers.ETag?.Tag; Log.Info(nameof(CommunityPluginSource), $"Loaded {this.plugins.Count} plugins from {ManifestFileUrl}"); return this.plugins; @@ -49,7 +59,8 @@ public async Task> FetchAsync(CancellationToken token) } else { - Log.Warn(nameof(CommunityPluginSource), $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}"); + Log.Warn(nameof(CommunityPluginSource), + $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}"); throw new Exception($"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}"); } } diff --git a/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs b/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs index bb1279b2c61..64c4cd627d2 100644 --- a/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs +++ b/Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs @@ -14,8 +14,8 @@ public record UserPlugin public string UrlDownload { get; set; } public string UrlSourceCode { get; set; } public string IcoPath { get; set; } - public DateTime LatestReleaseDate { get; set; } - public DateTime DateAdded { get; set; } + public DateTime? LatestReleaseDate { get; set; } + public DateTime? DateAdded { get; set; } } } From a524096fc2fcbeae20f43bcde506b0fd6812e98e Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Tue, 6 Feb 2024 13:07:21 -0600 Subject: [PATCH 2/9] Use job object to control child process --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 1 + .../Plugin/ProcessStreamPluginV2.cs | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 5cd09d407f1..68e7c115cc6 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -55,6 +55,7 @@ + diff --git a/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs b/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs index a476f06e9bf..16f9dfbf98f 100644 --- a/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs +++ b/Flow.Launcher.Core/Plugin/ProcessStreamPluginV2.cs @@ -5,12 +5,24 @@ using System.Threading.Tasks; using Flow.Launcher.Infrastructure; using Flow.Launcher.Plugin; +using Meziantou.Framework.Win32; using Nerdbank.Streams; namespace Flow.Launcher.Core.Plugin { internal abstract class ProcessStreamPluginV2 : JsonRPCPluginV2 { + private static JobObject _jobObject = new JobObject(); + + static ProcessStreamPluginV2() + { + _jobObject.SetLimits(new JobObjectLimits() + { + Flags = JobObjectLimitFlags.KillOnJobClose | JobObjectLimitFlags.DieOnUnhandledException + }); + + _jobObject.AssignProcess(Process.GetCurrentProcess()); + } public override string SupportedLanguage { get; set; } protected sealed override IDuplexPipe ClientPipe { get; set; } @@ -30,22 +42,22 @@ public override async Task InitAsync(PluginInitContext context) ClientProcess = Process.Start(StartInfo); ArgumentNullException.ThrowIfNull(ClientProcess); - + SetupPipe(ClientProcess); ErrorStream = ClientProcess.StandardError; await base.InitAsync(context); } - + private void SetupPipe(Process process) { var (reader, writer) = (PipeReader.Create(process.StandardOutput.BaseStream), PipeWriter.Create(process.StandardInput.BaseStream)); ClientPipe = new DuplexPipe(reader, writer); } - - + + public override async Task ReloadDataAsync() { var oldProcess = ClientProcess; @@ -57,8 +69,8 @@ public override async Task ReloadDataAsync() await oldProcess.WaitForExitAsync(); oldProcess.Dispose(); } - - + + public override async ValueTask DisposeAsync() { ClientProcess.Kill(true); From 74970a6673b3cf522ec60aa70ff52ca0f95fe839 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Sat, 10 Feb 2024 19:59:52 +0600 Subject: [PATCH 3/9] Fix Nodejs plugins not working --- Flow.Launcher.Core/Plugin/NodePlugin.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/NodePlugin.cs b/Flow.Launcher.Core/Plugin/NodePlugin.cs index 40eb057cbc7..aeac18ca955 100644 --- a/Flow.Launcher.Core/Plugin/NodePlugin.cs +++ b/Flow.Launcher.Core/Plugin/NodePlugin.cs @@ -28,20 +28,21 @@ public NodePlugin(string filename) protected override Task RequestAsync(JsonRPCRequestModel request, CancellationToken token = default) { - _startInfo.ArgumentList[1] = JsonSerializer.Serialize(request); + _startInfo.ArgumentList[1] = JsonSerializer.Serialize(request, RequestSerializeOption); return ExecuteAsync(_startInfo, token); } protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default) { // since this is not static, request strings will build up in ArgumentList if index is not specified - _startInfo.ArgumentList[1] = JsonSerializer.Serialize(rpcRequest); + _startInfo.ArgumentList[1] = JsonSerializer.Serialize(rpcRequest, RequestSerializeOption); return Execute(_startInfo); } public override async Task InitAsync(PluginInitContext context) { _startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath); + _startInfo.ArgumentList.Add(string.Empty); _startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory; await base.InitAsync(context); } From b0be1e3821c0ca423dad58509f81a167a64d8479 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Sat, 10 Feb 2024 20:23:31 +0600 Subject: [PATCH 4/9] Implement DuckDuckGo web search suggestions --- .../Settings.cs | 3 +- .../SuggestionSources/DuckDuckGo.cs | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/DuckDuckGo.cs diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs index 6cf1446afbd..f0e5ac32744 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Settings.cs @@ -197,7 +197,8 @@ public Settings() public SuggestionSource[] Suggestions { get; set; } = { new Google(), new Baidu(), - new Bing() + new Bing(), + new DuckDuckGo() }; [JsonIgnore] diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/DuckDuckGo.cs b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/DuckDuckGo.cs new file mode 100644 index 00000000000..8fafb44cce1 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/SuggestionSources/DuckDuckGo.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Flow.Launcher.Infrastructure.Http; +using Flow.Launcher.Infrastructure.Logger; +using System.Net.Http; +using System.Threading; +using System.Text.Json; + +namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources +{ + public class DuckDuckGo : SuggestionSource + { + public override async Task> SuggestionsAsync(string query, CancellationToken token) + { + // When the search query is empty, DuckDuckGo returns `[]`. When it's not empty, it returns data + // in the following format: `["query", ["suggestion1", "suggestion2", ...]]`. + if (string.IsNullOrEmpty(query)) + { + return new List(); + } + + try + { + const string api = "https://duckduckgo.com/ac/?type=list&q="; + + await using var resultStream = await Http.GetStreamAsync(api + Uri.EscapeDataString(query), token: token).ConfigureAwait(false); + + using var json = await JsonDocument.ParseAsync(resultStream, cancellationToken: token); + + var results = json.RootElement.EnumerateArray().ElementAt(1); + + return results.EnumerateArray().Select(o => o.GetString()).ToList(); + + } + catch (Exception e) when (e is HttpRequestException or {InnerException: TimeoutException}) + { + Log.Exception("|DuckDuckGo.Suggestions|Can't get suggestion from DuckDuckGo", e); + return null; + } + catch (JsonException e) + { + Log.Exception("|DuckDuckGo.Suggestions|can't parse suggestions", e); + return new List(); + } + } + + public override string ToString() + { + return "DuckDuckGo"; + } + } +} From cf48e532fa7f2847d9db69e605beed422db23cc2 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Sun, 11 Feb 2024 02:33:48 -0500 Subject: [PATCH 5/9] Remove short circuit on empty Settings --- Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs index 3848af6a4a6..408d9785d60 100644 --- a/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs +++ b/Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs @@ -37,7 +37,7 @@ public async Task InitializeAsync() _storage = new JsonStorage>(SettingPath); Settings = await _storage.LoadAsync(); - if (Settings != null || Configuration == null) + if (Configuration == null) { return; } From ae3b0c2a73c111ae346ea2aa2299527974c36192 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Mon, 12 Feb 2024 21:24:06 +1100 Subject: [PATCH 6/9] New translations en.xaml (German) (#2544) --- .../Languages/de.xaml | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml index c929a1b16db..04238424211 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/de.xaml @@ -5,57 +5,57 @@ Befehl Beschreibung - Shutdown - Restart - Restart With Advanced Boot Options - Log Off/Sign Out - Lock - Sleep - Hibernate - Index Option - Empty Recycle Bin - Open Recycle Bin + Herunterfahren + Neu starten + Neustart mit erweiterten Startoptionen + Abmelden + Sperren + Energie sparen + Ruhezustand + Indizierungsoptionen + Papierkorb leeren + Papierkorb öffnen Schließen - Save Settings - Restart Flow Launcher" + Einstellungen speichern + Flow Launcher neu starten Einstellungen Plugin-Daten neu laden - Check For Update - Open Log Location - Flow Launcher Tips - Flow Launcher UserData Folder + Auf Updates prüfen + Log-Speicherort öffnen + Flow Launcher Tipps + Flow Launcher Benutzerdaten-Ordner Gott Modus Computer herunterfahren Computer neu starten - Restart the computer with Advanced Boot Options for Safe and Debugging modes, as well as other options + Startet den Computer mit erweiterten Startoptionen für den abgesicherten Modus neu Abmelden Computer sperren Flow Launcher schließen Flow Launcher neu starten - Tweak Flow Launcher's settings + Einstellungen des Flow Launchers anpassen Computer in Schlafmodus versetzen Papierkorb leeren - Open recycle bin - Indexierungsoptionen - Hibernate computer - Save all Flow Launcher settings - Refreshes plugin data with new content - Open Flow Launcher's log location - Check for new Flow Launcher update - Visit Flow Launcher's documentation for more help and how to use tips - Open the location where Flow Launcher's settings are stored + Papierkorb öffnen + Indizierungsoptionen + Computer in den Ruhezustand versetzen + Alle Flow Launcher Einstellungen speichern + Aktualisiert Plugin-Daten mit neuen Inhalten + Öffnet den Speicherort der Flow Launcher Logs + Prüft auf neue Flow Launcher Updates + Öffnet die Dokumentation von Flow Launcher für Hilfe und Tipps + Öffnet den Ort an dem Flow Laucher Einstellungen speichert Gott Modus Erfolgreich - All Flow Launcher settings saved - Reloaded all applicable plugin data - Are you sure you want to shut the computer down? - Are you sure you want to restart the computer? - Are you sure you want to restart the computer with Advanced Boot Options? - Are you sure you want to log off? + Alle Flow Launcher Einstellungen wurden gespeichert + Alle betroffenen Plugin-Daten wurden neu geladen + Soll der Computer wirklich heruntergefahren werden? + Soll der Computer wirklich neu gestartet werden? + Soll der Computer wirklich mit erweiterten Startoptionen neu gestartet werden? + Soll der aktuelle Benutzer wirklich abgemeldet werden? Systembefehle Stellt Systemrelevante Befehle bereit. z.B. herunterfahren, sperren, Einstellungen, usw. From a8dc43cc8982d4dcab3ab5a3945dde9ce7c6682e Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 12 Feb 2024 21:24:27 +1100 Subject: [PATCH 7/9] version bump WebSearch plugin --- Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json index 0efa9c10f0b..12e0566f9ed 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json @@ -26,7 +26,7 @@ "Name": "Web Searches", "Description": "Provide the web search ability", "Author": "qianlifeng", - "Version": "3.0.6", + "Version": "3.0.7", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.WebSearch.dll", From 36c96710d36ecc861164bbf21499b535413da1f5 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 12 Feb 2024 21:24:39 +1100 Subject: [PATCH 8/9] version bump flow --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 602efba0efe..b84230c434c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: '1.17.1.{build}' +version: '1.17.2.{build}' init: - ps: | From 0a7e0ab6aba4eff6e2d67b68aeeeb075aaf65fa0 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 12 Feb 2024 21:25:33 +1100 Subject: [PATCH 9/9] version bump Sys plugin --- Plugins/Flow.Launcher.Plugin.Sys/plugin.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json index 0da3aaca744..5276690e30d 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/plugin.json +++ b/Plugins/Flow.Launcher.Plugin.Sys/plugin.json @@ -4,7 +4,7 @@ "Name": "System Commands", "Description": "Provide System related commands. e.g. shutdown,lock, setting etc.", "Author": "qianlifeng", - "Version": "3.1.0", + "Version": "3.1.1", "Language": "csharp", "Website": "https://github.com/Flow-Launcher/Flow.Launcher", "ExecuteFileName": "Flow.Launcher.Plugin.Sys.dll",