diff --git a/HttpKeys/App.xaml.cs b/HttpKeys/App.xaml.cs index f0e5026..6a1f0a6 100644 --- a/HttpKeys/App.xaml.cs +++ b/HttpKeys/App.xaml.cs @@ -1,22 +1,78 @@ using System.Windows; +using NAudio.CoreAudioApi; using Application = System.Windows.Application; namespace HttpKeys; +[Flags] +public enum MicMode +{ + None = 0, + Any = 1 << 0, + Ts = 1 << 1, +} + /// /// Interaction logic for App.xaml /// public partial class App : Application { + private Dictionary _trayIcons = new(); + private NotifyIcon _trayIcon; + + private const string TsDeviceId = + "{0.0.1.00000000}.{cf287ff4-c39e-4b09-bc8d-b927c7d59779}"; + public void UpdateIcon() + { + using var enumerator = new MMDeviceEnumerator(); + + var devices = enumerator + .EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active) + .Select(d => new + { + Id = d.ID, + Muted = d.AudioEndpointVolume.Mute + }) + .ToList(); + + if (devices.Count == 0) + { + _trayIcon.Icon = _trayIcons[MicMode.None]; + return; + } + + bool tsMuted = devices.Any(x => + string.Equals(x.Id, TsDeviceId, StringComparison.OrdinalIgnoreCase) && x.Muted); + + bool anyMuted = devices.Any(x => + !string.Equals(x.Id, TsDeviceId, StringComparison.OrdinalIgnoreCase) && x.Muted); + + MicMode mode = MicMode.None; + + if (tsMuted) + mode |= MicMode.Ts; + + if (anyMuted) + mode |= MicMode.Any; + + + _trayIcon.Icon = _trayIcons[mode]; + } + protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); + + _trayIcons.Add(MicMode.Ts | MicMode.Any, new Icon("micro_off.ico")); + _trayIcons.Add(MicMode.None, new Icon("micro_on.ico")); + _trayIcons.Add(MicMode.Ts, new Icon("micro_ts.ico")); + _trayIcons.Add(MicMode.Any, new Icon("micro_any.ico")); _trayIcon = new NotifyIcon { - Icon = new System.Drawing.Icon("app.ico"), + Icon = _trayIcons[MicMode.None], Visible = true, Text = "My WPF Tray App" }; diff --git a/HttpKeys/HttpKeys.csproj b/HttpKeys/HttpKeys.csproj index 9b070f9..b05d2cb 100644 --- a/HttpKeys/HttpKeys.csproj +++ b/HttpKeys/HttpKeys.csproj @@ -13,6 +13,22 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + diff --git a/HttpKeys/Listener.cs b/HttpKeys/Listener.cs index 8e5933f..0d86923 100644 --- a/HttpKeys/Listener.cs +++ b/HttpKeys/Listener.cs @@ -9,7 +9,7 @@ namespace HttpKeys; class Params { public string ActionId { get; set; } = null!; - public string State { get; set; } = null!; + public bool IsOn { get; set; } public bool ToggleChanged { get; set; } } @@ -52,22 +52,34 @@ public class Listener var p = JsonSerializer.Deserialize(body, options); if (p == null) continue; + + Log(body); + - Log("---- REQUEST ----"); - Log("URL: " + p.ActionId); - Log("URL: " + p.State); - Log("URL: " + p.ToggleChanged); + if(p.ActionId == "globalmute") + MicController.ToggleAllMicsSimple(p.IsOn); - var responseBytes = "OK"u8.ToArray(); - ctx.Response.StatusCode = 200; - ctx.Response.ContentType = "text/plain; charset=utf-8"; - await ctx.Response.OutputStream.WriteAsync(responseBytes); - ctx.Response.Close(); + if (p.ActionId == "tsmute") + MicController.ToggleTsMicsSimple(p.IsOn); + + await WriteText(ctx, 200, "OK"); } catch (Exception ex) { Log("Error: " + ex.Message); + await WriteText(ctx, 500, "Error"); } } } + + private static async Task WriteText(HttpListenerContext ctx, int statusCode, string text) + { + var bytes = Encoding.UTF8.GetBytes(text); + ctx.Response.StatusCode = statusCode; + ctx.Response.ContentType = "text/plain; charset=utf-8"; + ctx.Response.ContentLength64 = bytes.Length; + await ctx.Response.OutputStream.WriteAsync(bytes, 0, bytes.Length); + ctx.Response.OutputStream.Close(); + ctx.Response.Close(); + } } \ No newline at end of file diff --git a/HttpKeys/MainWindow.xaml.cs b/HttpKeys/MainWindow.xaml.cs index 2ff298b..7d1418b 100644 --- a/HttpKeys/MainWindow.xaml.cs +++ b/HttpKeys/MainWindow.xaml.cs @@ -1,15 +1,6 @@ using System.Collections.ObjectModel; using System.ComponentModel; -using System.Text; using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; namespace HttpKeys; diff --git a/HttpKeys/MicController.cs b/HttpKeys/MicController.cs new file mode 100644 index 0000000..a993812 --- /dev/null +++ b/HttpKeys/MicController.cs @@ -0,0 +1,95 @@ +using NAudio.CoreAudioApi; +using Application = System.Windows.Application; + +namespace HttpKeys; + +public static class MicController +{ + public static void PrintCaptureDevices() + { + using var enumerator = new MMDeviceEnumerator(); + var devices = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All); + + foreach (var device in devices) + { + Console.WriteLine("==================================="); + Console.WriteLine($"Name: {device.FriendlyName}"); + Console.WriteLine($"ID: {device.ID}"); + Console.WriteLine($"State:{device.State}"); + Console.WriteLine(); + } + } + + /// + /// Управляет всеми активными микрофонами. + /// isOn = true → микрофоны включены (Mute = false) + /// isOn = false → микрофоны выключены (Mute = true) + /// + public static void ToggleAllMicsSimple(bool isOn) + { + using var enumerator = new MMDeviceEnumerator(); + var devices = enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active); + + foreach (var device in devices) + { + var endpoint = device.AudioEndpointVolume; + + bool shouldBeMuted = !isOn; + + // Если уже в нужном состоянии — ничего не делаем + if (endpoint.Mute == shouldBeMuted) + continue; + + endpoint.Mute = shouldBeMuted; + } + + var app = Application.Current; + app.Dispatcher.Invoke(() =>(app as App)?.UpdateIcon()); + } + + /// + /// Управляет конкретным микрофоном по ID. + /// + public static void ToggleTsMicsSimple(bool isOn) + { + SetMicStateById( + "{0.0.1.00000000}.{cf287ff4-c39e-4b09-bc8d-b927c7d59779}", + isOn + ); + + var app = Application.Current; + app.Dispatcher.Invoke(() =>(app as App)?.UpdateIcon()); + } + + private static void SetMicStateById(string deviceId, bool isOn) + { + using var enumerator = new MMDeviceEnumerator(); + + MMDevice device; + + try + { + device = enumerator.GetDevice(deviceId); + } + catch + { + return; + } + + if (device.DataFlow != DataFlow.Capture) + return; + + if (device.State != DeviceState.Active) + return; + + var endpoint = device.AudioEndpointVolume; + + bool shouldBeMuted = !isOn; + + // Уже в нужном состоянии — выходим + if (endpoint.Mute == shouldBeMuted) + return; + + endpoint.Mute = shouldBeMuted; + } +} \ No newline at end of file diff --git a/HttpKeys/micro_any.ico b/HttpKeys/micro_any.ico new file mode 100644 index 0000000..cd4bfc8 Binary files /dev/null and b/HttpKeys/micro_any.ico differ diff --git a/HttpKeys/micro_off.ico b/HttpKeys/micro_off.ico new file mode 100644 index 0000000..a763c39 Binary files /dev/null and b/HttpKeys/micro_off.ico differ diff --git a/HttpKeys/micro_on.ico b/HttpKeys/micro_on.ico new file mode 100644 index 0000000..85cc1a3 Binary files /dev/null and b/HttpKeys/micro_on.ico differ diff --git a/HttpKeys/micro_ts.ico b/HttpKeys/micro_ts.ico new file mode 100644 index 0000000..0a351e0 Binary files /dev/null and b/HttpKeys/micro_ts.ico differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordMute.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordMute.png deleted file mode 100644 index 3410ca1..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordMute.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordMuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordMuteMicro.png deleted file mode 100644 index 617a2a4..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordMuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordUnmute.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordUnmute.png deleted file mode 100644 index 1b0dccd..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordUnmute.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordUnmuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordUnmuteMicro.png deleted file mode 100644 index 83f5043..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/DiscordUnmuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/SystemMuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/SystemMuteMicro.png deleted file mode 100644 index f2dd086..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/SystemMuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/SystemUnmuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/SystemUnmuteMicro.png deleted file mode 100644 index b1144cb..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/SystemUnmuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TalkMuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TalkMuteMicro.png deleted file mode 100644 index a2bbba6..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TalkMuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TalkUnmuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TalkUnmuteMicro.png deleted file mode 100644 index f696717..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TalkUnmuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TeamspeakMuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TeamspeakMuteMicro.png deleted file mode 100644 index ef0eb4e..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TeamspeakMuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TeamspeakUnmuteMicro.png b/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TeamspeakUnmuteMicro.png deleted file mode 100644 index 9a5c4b2..0000000 Binary files a/Plugin/ru.wildtail.httpkeys.sdPlugin/icons/TeamspeakUnmuteMicro.png and /dev/null differ diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/main.js b/Plugin/ru.wildtail.httpkeys.sdPlugin/main.js index 3e46681..c585081 100644 --- a/Plugin/ru.wildtail.httpkeys.sdPlugin/main.js +++ b/Plugin/ru.wildtail.httpkeys.sdPlugin/main.js @@ -3,21 +3,12 @@ let uuid = null; let ctxSettings = new Map(); const ACTION_MAP = { - "ru.wildtail.httpkeys.discord_micro_mute": { - actionId: "discord_micro_mute" + "ru.wildtail.httpkeys.globalmute": { + actionId: "globalmute" }, - "ru.wildtail.httpkeys.discord_mute": { - actionId: "discord_mute" + "ru.wildtail.httpkeys.tsmute": { + actionId: "tsmute" }, - "ru.wildtail.httpkeys.teamspeak_micro_mute": { - actionId: "teamspeak_micro_mute" - }, - "ru.wildtail.httpkeys.talk_micro_mute": { - actionId: "talk_micro_mute" - }, - "ru.wildtail.httpkeys.system_micro_mute": { - actionId: "system_micro_mute" - } }; // подключение от D6 @@ -42,6 +33,17 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo) const s = (payload && payload.settings) || { isOn: false }; ctxSettings.set(context, s); setState(context, s.isOn ? 1 : 0); + + const cfg = ACTION_MAP[action]; + if (!cfg) return; + + post("http://127.0.0.1:16888/press", { + actionUUID: action, + actionId: cfg.actionId, + toggleChanged: false, + isOn: !s.isOn, + }); + return; } @@ -84,13 +86,8 @@ function handleKeyDown(action, context) { post("http://127.0.0.1:16888/press", { actionUUID: action, actionId: cfg.actionId, - toggleChanged: prevState !== newState, - state: newState ? "on" : "off", - - isOn: newState, - context: context, - timestamp: Date.now() + isOn: !newState, }); } diff --git a/Plugin/ru.wildtail.httpkeys.sdPlugin/manifest.json b/Plugin/ru.wildtail.httpkeys.sdPlugin/manifest.json index 7ec115e..3c1837f 100644 --- a/Plugin/ru.wildtail.httpkeys.sdPlugin/manifest.json +++ b/Plugin/ru.wildtail.httpkeys.sdPlugin/manifest.json @@ -15,99 +15,38 @@ "Actions": [ { - "Name": "Discord Mute Micro", - "UUID": "ru.wildtail.httpkeys.discord_micro_mute", + "Name": "Global Mute Micro", + "UUID": "ru.wildtail.httpkeys.globalmute", "Icon": "icon", - "Tooltip": "Toggle Discord Mute Micro", + "Tooltip": "Global Mute Micro", "PropertyInspectorPath": "index.html", "States": [ { - "Image": "icons/DiscordUnmuteMicro", + "Image": "icons/micro_on", "TitleAlignment": "middle", "FontSize": "12" }, { - "Image": "icons/DiscordMuteMicro", + "Image": "icons/micro_off", "TitleAlignment": "middle", "FontSize": "12" } ] }, - - { - "Name": "Discord Mute", - "UUID": "ru.wildtail.httpkeys.discord_mute", - "Icon": "icon", - "Tooltip": "Toggle Discord Full Mute", - "PropertyInspectorPath": "index.html", - "States": [ - { - "Image": "icons/DiscordUnmute", - "TitleAlignment": "middle", - "FontSize": "12" - }, - { - "Image": "icons/DiscordMute", - "TitleAlignment": "middle", - "FontSize": "12" - } - ] - }, - { - "Name": "Teamspeak Mute Micro", - "UUID": "ru.wildtail.httpkeys.teamspeak_micro_mute", + "Name": "Teamspeacks Mute Micro", + "UUID": "ru.wildtail.httpkeys.tsmute", "Icon": "icon", - "Tooltip": "Toggle Teamspeak Mute Micro", + "Tooltip": "Teamspeacks Mute Micro", "PropertyInspectorPath": "index.html", "States": [ { - "Image": "icons/TeamspeakUnmuteMicro", + "Image": "icons/groupmicro_on", "TitleAlignment": "middle", "FontSize": "12" }, { - "Image": "icons/TeamspeakMuteMicro", - "TitleAlignment": "middle", - "FontSize": "12" - } - ] - }, - - { - "Name": "Talk Mute Micro", - "UUID": "ru.wildtail.httpkeys.talk_micro_mute", - "Icon": "icon", - "Tooltip": "Toggle Talk Mute", - "PropertyInspectorPath": "index.html", - "States": [ - { - "Image": "icons/TalkUnmuteMicro", - "TitleAlignment": "middle", - "FontSize": "12" - }, - { - "Image": "icons/TalkMuteMicro", - "TitleAlignment": "middle", - "FontSize": "12" - } - ] - }, - - { - "Name": "System Mute Micro", - "UUID": "ru.wildtail.httpkeys.system_micro_mute", - "Icon": "icon", - "Tooltip": "Toggle System Mute Micro", - "PropertyInspectorPath": "index.html", - "States": [ - { - "Image": "icons/SystemUnmuteMicro", - "TitleAlignment": "middle", - "FontSize": "12" - }, - { - "Image": "icons/SystemMuteMicro", + "Image": "icons/groupmicro_off", "TitleAlignment": "middle", "FontSize": "12" }