This commit is contained in:
wildtail
2026-03-04 01:29:53 +03:00
parent a65681bca4
commit be8ca26bde
21 changed files with 216 additions and 110 deletions
+57 -1
View File
@@ -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,
}
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private Dictionary<MicMode, Icon> _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"
};
+16
View File
@@ -13,6 +13,22 @@
<None Update="app.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="micro_any.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="micro_off.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="micro_on.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="micro_ts.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="NAudio" Version="2.2.1" />
</ItemGroup>
</Project>
+22 -10
View File
@@ -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<Params>(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();
}
}
-9
View File
@@ -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;
+95
View File
@@ -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();
}
}
/// <summary>
/// Управляет всеми активными микрофонами.
/// isOn = true → микрофоны включены (Mute = false)
/// isOn = false → микрофоны выключены (Mute = true)
/// </summary>
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());
}
/// <summary>
/// Управляет конкретным микрофоном по ID.
/// </summary>
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;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

+16 -19
View File
@@ -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,
});
}
@@ -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"
}