Initial commit to new repository
This commit is contained in:
107
src/AxCopilot/Handlers/MediaHandler.cs
Normal file
107
src/AxCopilot/Handlers/MediaHandler.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using AxCopilot.SDK;
|
||||
using AxCopilot.Services;
|
||||
using AxCopilot.Themes;
|
||||
|
||||
namespace AxCopilot.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 미디어 컨트롤 핸들러. "media" 프리픽스로 사용합니다.
|
||||
/// 예: media play → 재생/일시정지
|
||||
/// media next → 다음 트랙
|
||||
/// media prev → 이전 트랙
|
||||
/// media vol+ → 볼륨 올리기
|
||||
/// media vol- → 볼륨 낮추기
|
||||
/// media mute → 음소거 토글
|
||||
/// </summary>
|
||||
public class MediaHandler : IActionHandler
|
||||
{
|
||||
public string? Prefix => "media";
|
||||
|
||||
public PluginMetadata Metadata => new(
|
||||
"MediaControl",
|
||||
"미디어 컨트롤 — media 뒤에 명령어 입력",
|
||||
"1.0",
|
||||
"AX");
|
||||
|
||||
// Windows 미디어/볼륨 가상 키 코드
|
||||
private const byte VK_MEDIA_PLAY_PAUSE = 0xB3;
|
||||
private const byte VK_MEDIA_NEXT_TRACK = 0xB0;
|
||||
private const byte VK_MEDIA_PREV_TRACK = 0xB1;
|
||||
private const byte VK_VOLUME_UP = 0xAF;
|
||||
private const byte VK_VOLUME_DOWN = 0xAE;
|
||||
private const byte VK_VOLUME_MUTE = 0xAD;
|
||||
|
||||
// KEYEVENTF flags
|
||||
private const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
|
||||
private const uint KEYEVENTF_KEYUP = 0x0002;
|
||||
|
||||
[DllImport("user32.dll", SetLastError = false)]
|
||||
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, nuint dwExtraInfo);
|
||||
|
||||
// 명령어 → (제목, 설명, VK코드, 심볼)
|
||||
private static readonly List<(string[] Keys, string Title, string Subtitle, byte Vk, string Symbol)> _commands =
|
||||
[
|
||||
(["play", "pause", "pp"], "재생 / 일시정지", "현재 미디어 재생 또는 일시정지", VK_MEDIA_PLAY_PAUSE, Symbols.MediaPlay),
|
||||
(["next", ">>"], "다음 트랙", "다음 곡으로 이동", VK_MEDIA_NEXT_TRACK, Symbols.MediaNext),
|
||||
(["prev", "previous", "<<"], "이전 트랙", "이전 곡으로 이동", VK_MEDIA_PREV_TRACK, Symbols.MediaPrev),
|
||||
(["vol+", "volup", "up"], "볼륨 올리기", "시스템 볼륨 증가", VK_VOLUME_UP, Symbols.VolumeUp),
|
||||
(["vol-", "voldown", "down"], "볼륨 낮추기", "시스템 볼륨 감소", VK_VOLUME_DOWN, Symbols.VolumeDown),
|
||||
(["mute", "음소거"], "음소거 토글", "볼륨 음소거 / 해제", VK_VOLUME_MUTE, Symbols.VolumeMute),
|
||||
];
|
||||
|
||||
public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||||
{
|
||||
var q = query.Trim().ToLowerInvariant();
|
||||
|
||||
IEnumerable<(string[] Keys, string Title, string Subtitle, byte Vk, string Symbol)> matches;
|
||||
|
||||
if (string.IsNullOrEmpty(q))
|
||||
{
|
||||
// 쿼리 없으면 전체 목록 표시
|
||||
matches = _commands;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 입력어와 일치하는 명령 필터
|
||||
matches = _commands.Where(c =>
|
||||
c.Keys.Any(k => k.StartsWith(q, StringComparison.OrdinalIgnoreCase)) ||
|
||||
c.Title.Contains(q, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
var items = matches
|
||||
.Select(c => new LauncherItem(c.Title, c.Subtitle, null, new MediaKeyData(c.Vk), Symbol: c.Symbol))
|
||||
.ToList();
|
||||
|
||||
if (items.Count == 0)
|
||||
{
|
||||
items.Add(new LauncherItem(
|
||||
"알 수 없는 명령어",
|
||||
"play · next · prev · vol+ · vol- · mute 중 하나를 입력하세요",
|
||||
null, null, Symbol: Symbols.Warning));
|
||||
}
|
||||
|
||||
return Task.FromResult<IEnumerable<LauncherItem>>(items);
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
|
||||
{
|
||||
if (item.Data is not MediaKeyData data) return Task.CompletedTask;
|
||||
|
||||
try
|
||||
{
|
||||
// 키 누름 → 키 뗌
|
||||
keybd_event(data.Vk, 0, KEYEVENTF_EXTENDEDKEY, 0);
|
||||
keybd_event(data.Vk, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
|
||||
LogService.Info($"미디어 키 전송: VK=0x{data.Vk:X2}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogService.Warn($"미디어 키 전송 실패: {ex.Message}");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private record MediaKeyData(byte Vk);
|
||||
}
|
||||
Reference in New Issue
Block a user