| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- 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<int> GetSelectedIndices()
- {
- var grid = this.FindControl<DataGrid>("TrackGrid");
- if (grid == null) yield break;
- foreach (var item in grid.SelectedItems.OfType<TrackItem>())
- yield return item.Index;
- }
- }
|