using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Platform.Storage; using SlamCross.ViewModels; namespace SlamCross.Views; public partial class MainWindow : Window { private MainWindowViewModel Vm => (MainWindowViewModel)DataContext!; public MainWindow() { InitializeComponent(); DataContext = new MainWindowViewModel(); Vm.ErrorOccurred += msg => Avalonia.Threading.Dispatcher.UIThread.Post(() => ShowMessage(msg)); Closing += (_, e) => { Vm.StopPoll(); Vm.StopStream(); Vm.SaveSettings(); }; if (Vm.Settings.StartMinimized) WindowState = WindowState.Minimized; } private async void ShowMessage(string msg) { var ok = new Button { Content = "OK", HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right }; var dlg = new Window { Title = "SLAM", Width = 400, Height = 150, Content = new StackPanel { Margin = new Avalonia.Thickness(16), Spacing = 12, Children = { new TextBlock { Text = msg, TextWrapping = Avalonia.Media.TextWrapping.Wrap }, ok } } }; ok.Click += (_, _) => dlg.Close(); await dlg.ShowDialog(this); } private async void ImportButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = "Import Audio Files", AllowMultiple = true, FileTypeFilter = new[] { new FilePickerFileType("Audio/Video") { Patterns = new[] { "*.mp3","*.wav","*.aac","*.wma","*.m4a","*.mp4","*.wmv","*.avi","*.m4v","*.mov","*.flac","*.ogg" } } } }); if (files.Count > 0) await Vm.ImportFilesAsync(files.Select(f => f.Path.LocalPath)); } private async void YTButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var dlg = new YTImportWindow(); await dlg.ShowDialog(this); if (dlg.ResultUrl is string url) await DownloadAndImportYt(url); } private async Task DownloadAndImportYt(string url) { var tmpDir = Path.Combine(Path.GetTempPath(), "slam_yt"); Directory.CreateDirectory(tmpDir); var outTemplate = Path.Combine(tmpDir, "%(title)s.%(ext)s"); var psi = new ProcessStartInfo("yt-dlp") { UseShellExecute = false, CreateNoWindow = true }; psi.ArgumentList.Add("-x"); psi.ArgumentList.Add("--audio-format"); psi.ArgumentList.Add("mp3"); psi.ArgumentList.Add("-o"); psi.ArgumentList.Add(outTemplate); psi.ArgumentList.Add(url); try { using var proc = Process.Start(psi); if (proc != null) await proc.WaitForExitAsync(); } catch { Vm.ShowError("yt-dlp not found. Install it to import from YouTube."); return; } var downloaded = Directory.GetFiles(tmpDir); if (downloaded.Length > 0) await Vm.ImportFilesAsync(downloaded, deleteSource: true); try { Directory.Delete(tmpDir, true); } catch { } } private async void SettingsButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var dlg = new SettingsWindow(Vm.Settings); await dlg.ShowDialog(this); Vm.SaveSettings(); } private async void PlayKeyButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var dlg = new SelectKeyWindow(); await dlg.ShowDialog(this); if (dlg.ChosenKey is string key) Vm.SetPlayKey(key); } private void StartStopButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { if (Vm.IsRunning) Vm.StopPoll(); else _ = Vm.StartPollAsync(); } private void StreamButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { if (Vm.IsStreaming) { Vm.StopStream(); return; } var idx = GetSelectedIndices().FirstOrDefault(-1); if (idx < 0) { Vm.ShowError("Select a track first."); return; } Vm.StreamToMic(idx); } private void StopStreamButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) => Vm.StopStream(); private void TrackGrid_DoubleTapped(object? sender, TappedEventArgs e) { var selected = GetSelectedIndices().FirstOrDefault(-1); if (selected < 0) return; if (Vm.IsRunning) Vm.PlayTrack(selected); else Vm.StreamToMic(selected); } private void MenuPlay_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var idx = GetSelectedIndices().FirstOrDefault(-1); if (idx < 0) return; if (Vm.IsRunning) Vm.PlayTrack(idx); else Vm.StreamToMic(idx); } private void MenuRefresh_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) => Vm.RefreshTracks(); private async void MenuHotkey_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var idx = GetSelectedIndices().FirstOrDefault(-1); if (idx < 0) return; var dlg = new SelectKeyWindow(); await dlg.ShowDialog(this); if (dlg.ChosenKey is string key) Vm.SetTrackHotkey(idx, key); } private void MenuRemoveHotkey_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { foreach (var idx in GetSelectedIndices()) Vm.RemoveHotkey(idx); } private async void MenuVolume_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var indices = GetSelectedIndices().ToList(); if (indices.Count == 0) return; var dlg = new SetVolumeWindow(); await dlg.ShowDialog(this); if (dlg.Volume >= 0) Vm.SetTrackVolume(indices, dlg.Volume); } private async void MenuTrim_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var idx = GetSelectedIndices().FirstOrDefault(-1); if (idx < 0) return; var track = Vm.GetTrack(idx); if (track == null) return; var filePath = Vm.GetTrackFilePath(idx); var dlg = new TrimWindow(filePath, track.StartPos, track.EndPos); await dlg.ShowDialog(this); if (dlg.Confirmed) Vm.SetTrackTrim(idx, dlg.StartPos, dlg.EndPos); } private async void MenuRename_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var idx = GetSelectedIndices().FirstOrDefault(-1); if (idx < 0) return; var track = Vm.GetTrack(idx); if (track == null) return; var dlg = new RenameWindow(track.Name); await dlg.ShowDialog(this); if (dlg.NewName is string name) Vm.RenameTrack(idx, name); } private void MenuGoTo_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var idx = GetSelectedIndices().FirstOrDefault(-1); if (idx < 0) return; var path = Vm.GetTrackFilePath(idx); if (!File.Exists(path)) return; var dir = Path.GetDirectoryName(path)!; if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( System.Runtime.InteropServices.OSPlatform.Linux)) Process.Start("xdg-open", dir); else Process.Start("explorer.exe", $"/select,\"{path}\""); } private async void MenuDelete_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e) { var indices = GetSelectedIndices().ToList(); if (indices.Count == 0) return; var names = indices.Select(i => Vm.Tracks[i].Name); var confirm = new ConfirmWindow($"Delete {string.Join(", ", names)}?"); await confirm.ShowDialog(this); if (confirm.Confirmed) Vm.DeleteTracks(indices); } private IEnumerable GetSelectedIndices() { var grid = this.FindControl("TrackGrid"); if (grid == null) yield break; foreach (var item in grid.SelectedItems.OfType()) yield return item.Index; } }