upd
@@ -1,22 +1,78 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using NAudio.CoreAudioApi;
|
||||||
using Application = System.Windows.Application;
|
using Application = System.Windows.Application;
|
||||||
|
|
||||||
namespace HttpKeys;
|
namespace HttpKeys;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum MicMode
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Any = 1 << 0,
|
||||||
|
Ts = 1 << 1,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for App.xaml
|
/// Interaction logic for App.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
|
private Dictionary<MicMode, Icon> _trayIcons = new();
|
||||||
|
|
||||||
private NotifyIcon _trayIcon;
|
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)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnStartup(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
|
_trayIcon = new NotifyIcon
|
||||||
{
|
{
|
||||||
Icon = new System.Drawing.Icon("app.ico"),
|
Icon = _trayIcons[MicMode.None],
|
||||||
Visible = true,
|
Visible = true,
|
||||||
Text = "My WPF Tray App"
|
Text = "My WPF Tray App"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,22 @@
|
|||||||
<None Update="app.ico">
|
<None Update="app.ico">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</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>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace HttpKeys;
|
|||||||
class Params
|
class Params
|
||||||
{
|
{
|
||||||
public string ActionId { get; set; } = null!;
|
public string ActionId { get; set; } = null!;
|
||||||
public string State { get; set; } = null!;
|
public bool IsOn { get; set; }
|
||||||
public bool ToggleChanged { get; set; }
|
public bool ToggleChanged { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,22 +52,34 @@ public class Listener
|
|||||||
var p = JsonSerializer.Deserialize<Params>(body, options);
|
var p = JsonSerializer.Deserialize<Params>(body, options);
|
||||||
if (p == null)
|
if (p == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
Log(body);
|
||||||
|
|
||||||
|
|
||||||
Log("---- REQUEST ----");
|
if(p.ActionId == "globalmute")
|
||||||
Log("URL: " + p.ActionId);
|
MicController.ToggleAllMicsSimple(p.IsOn);
|
||||||
Log("URL: " + p.State);
|
|
||||||
Log("URL: " + p.ToggleChanged);
|
|
||||||
|
|
||||||
var responseBytes = "OK"u8.ToArray();
|
if (p.ActionId == "tsmute")
|
||||||
ctx.Response.StatusCode = 200;
|
MicController.ToggleTsMicsSimple(p.IsOn);
|
||||||
ctx.Response.ContentType = "text/plain; charset=utf-8";
|
|
||||||
await ctx.Response.OutputStream.WriteAsync(responseBytes);
|
await WriteText(ctx, 200, "OK");
|
||||||
ctx.Response.Close();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log("Error: " + ex.Message);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,6 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Text;
|
|
||||||
using System.Windows;
|
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;
|
namespace HttpKeys;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 65 KiB |
@@ -3,21 +3,12 @@ let uuid = null;
|
|||||||
let ctxSettings = new Map();
|
let ctxSettings = new Map();
|
||||||
|
|
||||||
const ACTION_MAP = {
|
const ACTION_MAP = {
|
||||||
"ru.wildtail.httpkeys.discord_micro_mute": {
|
"ru.wildtail.httpkeys.globalmute": {
|
||||||
actionId: "discord_micro_mute"
|
actionId: "globalmute"
|
||||||
},
|
},
|
||||||
"ru.wildtail.httpkeys.discord_mute": {
|
"ru.wildtail.httpkeys.tsmute": {
|
||||||
actionId: "discord_mute"
|
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
|
// подключение от D6
|
||||||
@@ -42,6 +33,17 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo)
|
|||||||
const s = (payload && payload.settings) || { isOn: false };
|
const s = (payload && payload.settings) || { isOn: false };
|
||||||
ctxSettings.set(context, s);
|
ctxSettings.set(context, s);
|
||||||
setState(context, s.isOn ? 1 : 0);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,13 +86,8 @@ function handleKeyDown(action, context) {
|
|||||||
post("http://127.0.0.1:16888/press", {
|
post("http://127.0.0.1:16888/press", {
|
||||||
actionUUID: action,
|
actionUUID: action,
|
||||||
actionId: cfg.actionId,
|
actionId: cfg.actionId,
|
||||||
|
|
||||||
toggleChanged: prevState !== newState,
|
toggleChanged: prevState !== newState,
|
||||||
state: newState ? "on" : "off",
|
isOn: !newState,
|
||||||
|
|
||||||
isOn: newState,
|
|
||||||
context: context,
|
|
||||||
timestamp: Date.now()
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,99 +15,38 @@
|
|||||||
|
|
||||||
"Actions": [
|
"Actions": [
|
||||||
{
|
{
|
||||||
"Name": "Discord Mute Micro",
|
"Name": "Global Mute Micro",
|
||||||
"UUID": "ru.wildtail.httpkeys.discord_micro_mute",
|
"UUID": "ru.wildtail.httpkeys.globalmute",
|
||||||
"Icon": "icon",
|
"Icon": "icon",
|
||||||
"Tooltip": "Toggle Discord Mute Micro",
|
"Tooltip": "Global Mute Micro",
|
||||||
"PropertyInspectorPath": "index.html",
|
"PropertyInspectorPath": "index.html",
|
||||||
"States": [
|
"States": [
|
||||||
{
|
{
|
||||||
"Image": "icons/DiscordUnmuteMicro",
|
"Image": "icons/micro_on",
|
||||||
"TitleAlignment": "middle",
|
"TitleAlignment": "middle",
|
||||||
"FontSize": "12"
|
"FontSize": "12"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Image": "icons/DiscordMuteMicro",
|
"Image": "icons/micro_off",
|
||||||
"TitleAlignment": "middle",
|
"TitleAlignment": "middle",
|
||||||
"FontSize": "12"
|
"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",
|
"Name": "Teamspeacks Mute Micro",
|
||||||
"UUID": "ru.wildtail.httpkeys.teamspeak_micro_mute",
|
"UUID": "ru.wildtail.httpkeys.tsmute",
|
||||||
"Icon": "icon",
|
"Icon": "icon",
|
||||||
"Tooltip": "Toggle Teamspeak Mute Micro",
|
"Tooltip": "Teamspeacks Mute Micro",
|
||||||
"PropertyInspectorPath": "index.html",
|
"PropertyInspectorPath": "index.html",
|
||||||
"States": [
|
"States": [
|
||||||
{
|
{
|
||||||
"Image": "icons/TeamspeakUnmuteMicro",
|
"Image": "icons/groupmicro_on",
|
||||||
"TitleAlignment": "middle",
|
"TitleAlignment": "middle",
|
||||||
"FontSize": "12"
|
"FontSize": "12"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Image": "icons/TeamspeakMuteMicro",
|
"Image": "icons/groupmicro_off",
|
||||||
"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",
|
|
||||||
"TitleAlignment": "middle",
|
"TitleAlignment": "middle",
|
||||||
"FontSize": "12"
|
"FontSize": "12"
|
||||||
}
|
}
|
||||||
|
|||||||