Example #1
0
        public async Task PlayAsync(string deviceUniqueId, string friendlyName)
        {
            DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Playing);
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

            if (m_ActivePlayers.ContainsKey(deviceUniqueId) == false)
            {
                m_ActivePlayers.Add(deviceUniqueId, cancellationTokenSource);
            }
            else
            {
                m_ActivePlayers[deviceUniqueId] = cancellationTokenSource;
                Logger.Error("device {0} already existed in m_ActivePlayers", friendlyName);
            }
            try
            {
                await _PlayAsync(deviceUniqueId, cancellationTokenSource).ConfigureAwait(false);
            }
            catch (NullReferenceException e)
            {
                Logger.Error(e, "Error while trying to play, device {0}", friendlyName);
                Snapcast.Instance.ShowNotification("Device not found", string.Format("Couldn't find sound device '{0}'. Has it been unplugged?", friendlyName));
                DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Stopped);
                m_ActivePlayers.Remove(deviceUniqueId);
                if (System.Windows.MessageBox.Show(string.Format("The audio device '{0}' had been marked for auto-play, but is missing from the system. Would you like to remove it from auto-play?", friendlyName),
                                                   "Snap.Net - Auto-play device missing", System.Windows.MessageBoxButton.YesNo) == System.Windows.MessageBoxResult.Yes)
                {
                    SnapSettings.SetAudioDeviceAutoPlay(deviceUniqueId, false, friendlyName);
                }
            }
        }
Example #2
0
 public void Stop(string deviceUniqueId)
 {
     if (m_ActivePlayers.ContainsKey(deviceUniqueId))
     {
         m_ActivePlayers[deviceUniqueId].Cancel();
         DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Stopped);
         m_ActivePlayers.Remove(deviceUniqueId);
     }
 }
Example #3
0
        private async Task _PlayAsync(string deviceUniqueId, CancellationTokenSource cancellationTokenSource, int attempts = 0)
        {
            Device device = await FindDeviceAsync(deviceUniqueId);

            if (device == null)
            {
                await Task.FromException(new NullReferenceException("Couldn't get device with uniqueId " + deviceUniqueId)).ConfigureAwait(false);
            }
            else
            {
                int            deviceInstanceId = SnapSettings.DetermineInstanceId(deviceUniqueId, device.Index);
                DeviceSettings deviceSettings   = SnapSettings.GetDeviceSettings(deviceUniqueId);
                if (deviceInstanceId != -1)
                {
                    // update device's last seen:
                    deviceSettings.LastSeen = DateTime.Now;
                    SnapSettings.SaveDeviceSettings(deviceUniqueId, deviceSettings);

                    StringBuilder   stdError = new StringBuilder();
                    string          lastLine = "";
                    Action <string> stdOut   = (line) =>
                    {
                        lastLine = line; // we only care about the last line from the output - in case there's an error (snapclient should probably be sending these to stderr though)
                    };
                    string resampleArg = "";
                    string hostIdArg   = "";

                    CommandTask <CommandResult> task = null;

                    if (deviceSettings.UseSnapClientNet == false)
                    {
                        // Launch native client:
                        if (string.IsNullOrEmpty(deviceSettings.ResampleFormat) == false)
                        {
                            resampleArg = $"--sampleformat {deviceSettings.ResampleFormat}:0";
                        }

                        if (string.IsNullOrWhiteSpace(deviceSettings.HostId) == false)
                        {
                            hostIdArg = $"--hostID \"{deviceSettings.HostId}\"";
                        }

                        string command =
                            $"-h {SnapSettings.Server} -p {SnapSettings.PlayerPort} -s {device.Index} -i {deviceInstanceId} --sharingmode={deviceSettings.ShareMode.ToString().ToLower()} {resampleArg} {hostIdArg}";
                        Logger.Debug("Snapclient command: {0}", command);
                        task = Cli.Wrap(_SnapClient())
                               .WithArguments(command)
                               .WithStandardOutputPipe(PipeTarget.ToDelegate(stdOut))
                               .ExecuteAsync(cancellationTokenSource.Token);
                    }
                    else
                    {
                        // launch experimental .NET port of snapclient:
                        string command =
                            $"-h {SnapSettings.Server} -p {SnapSettings.PlayerPort} -s {device.Index} -i {deviceInstanceId}";

                        Logger.Debug("SnapClient.Net command: {0}", command);
                        task = Cli.Wrap(_SnapClientDotNet())
                               .WithArguments(command)
                               .WithStandardOutputPipe(PipeTarget.ToDelegate(stdOut))
                               .ExecuteAsync(cancellationTokenSource.Token);
                    }

                    Logger.Debug("Snapclient PID: {0}", task.ProcessId);
                    ChildProcessTracker.AddProcess(Process.GetProcessById(task.ProcessId)); // this utility helps us make sure the player process doesn't keep going if our process is killed / crashes
                    try
                    {
                        await task;
                    }
                    catch (CliWrap.Exceptions.CommandExecutionException e)
                    {
                        OnSnapClientErrored?.Invoke();
                        // add type to ShowNotification (level?), show notification with type and print log at type level
                        Snapcast.Instance.ShowNotification("Snapclient error", _BuildErrorMessage(device, lastLine));
                        // todo: parse WASAPI error code here and provide human friendly output
                        Logger.Error("Snapclient exited with non-zero exit code. Exception:");
                        Logger.Error(e.Message);
                        DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Stopped);
                        m_ActivePlayers.Remove(deviceUniqueId);

                        // settings might have changed while we were playing - refetch them
                        DeviceSettings nDeviceSettings = SnapSettings.GetDeviceSettings(deviceUniqueId);
                        if (nDeviceSettings.AutoRestartOnFailure == true && (attempts <= nDeviceSettings.RestartAttempts || nDeviceSettings.RestartAttempts == 0))
                        {
                            m_ActivePlayers.Add(deviceUniqueId, cancellationTokenSource);
                            DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Playing);
                            await _PlayAsync(deviceUniqueId, cancellationTokenSource, attempts + 1).ConfigureAwait(false);
                        }
                    }
                }
            }
            DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Stopped);
            m_ActivePlayers.Remove(deviceUniqueId);
        }
Example #4
0
        public async Task PlayAsync(string deviceUniqueId, string friendlyName)
        {
            DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Playing);
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

            if (m_ActivePlayers.ContainsKey(deviceUniqueId) == false)
            {
                m_ActivePlayers.Add(deviceUniqueId, cancellationTokenSource);
            }
            else
            {
                m_ActivePlayers[deviceUniqueId] = cancellationTokenSource;
                Logger.Error("device {0} already existed in m_ActivePlayers", friendlyName);
            }
            try
            {
                await _PlayAsync(deviceUniqueId, cancellationTokenSource).ConfigureAwait(false);
            }
            catch (NullReferenceException e)
            {
                DevicePlayStateChanged?.Invoke(deviceUniqueId, EState.Stopped);
                m_ActivePlayers.Remove(deviceUniqueId);
                Logger.Error(e, "Error while trying to play, device {0}", friendlyName);

                if (SnapSettings.DeviceMissingBehaviour == SnapSettings.EDeviceMissingBehaviour.Default)
                {
                    Snapcast.Instance.ShowNotification("Device not found", string.Format("Couldn't find sound device '{0}'. Has it been unplugged?", friendlyName));
                }
                else if (SnapSettings.DeviceMissingRetryIntervalSeconds > 0)
                {
                    Timer timer = new Timer(SnapSettings.DeviceMissingRetryIntervalSeconds * 1000);
                    timer.Start();
                    timer.Elapsed += async(sender, args) =>
                    {
                        timer.Stop();
                        await PlayAsync(deviceUniqueId, friendlyName);
                    };
                }

                DeviceSettings settings = SnapSettings.GetDeviceSettings(deviceUniqueId);
                if (settings.LastSeen == null) // in case we're migrating from a version that didn't have the LastSeen field yet
                {
                    settings.LastSeen = DateTime.Now;
                    SnapSettings.SaveDeviceSettings(deviceUniqueId, settings);
                }

                // if a device hasn't been seen for x days, stop trying to auto-play it
                if (SnapSettings.DeviceMissingExpiryDays != 0)
                {
                    if (DateTime.Now - settings.LastSeen > new TimeSpan(SnapSettings.DeviceMissingExpiryDays, 0, 0, 0))
                    {
                        SnapSettings.SetAudioDeviceAutoPlay(deviceUniqueId, false, friendlyName);
                    }
                }

                // this prompt needs rethinking - it's only useful when a device has permanently disappeared
                // maybe add an extra player list at the bottom with "not found" devices? (disable play button etc)

                //if (System.Windows.MessageBox.Show(string.Format("The audio device '{0}' had been marked for auto-play, but is missing from the system. Would you like to remove it from auto-play?", friendlyName),
                //    "Snap.Net - Auto-play device missing", System.Windows.MessageBoxButton.YesNo) == System.Windows.MessageBoxResult.Yes)
                //{
                //    SnapSettings.SetAudioDeviceAutoPlay(deviceUniqueId, false, friendlyName);
                //}
            }
        }