public void MemberJoin(RoomMember member) { if (_members.Any(x => x.UserName == member.UserName)) { return; } _members.Add(member); OnRoomMembersChanged?.Invoke(this, member.UserName); _roomEvents.Add(new UserEvent(member.UserName, member.FriendlyName, UserEventType.JoinedRoom)); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Information, Message = $"{member.FriendlyName} joined room" }); ToggleDj(member, false); if (_currentTrack != null) { StartSongForMemberUgly(member); } _timeSinceEmpty = CustomFutureDateTimeOffset; }
private async Task <FullTrack> GetSongFromQueue(RoomMember member, string playlist) { try { var api = await _spotifyAccessService.TryGetMemberApi(member.UserName); var queueList = await api.GetPlaylistTracksAsync(playlist); queueList.ThrowOnError(nameof(api.GetPlaylistTracks)); if (!queueList.Items.Any()) { return(null); } var track = queueList.Items.First().Track; var remove = await api.RemovePlaylistTrackAsync(playlist, new DeleteTrackUri(track.Uri, 0)); remove.ThrowOnError(nameof(api.RemovePlaylistTrackAsync)); return(track); } catch (Exception e) { _logger.Log(LogLevel.Warning, "Failed to get song from {Username}'s queue because {@Exception}", member.UserName, e); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Warning, Message = $"Failed to get song from {member.FriendlyName}'s queue" }); Debug.WriteLine(e); return(null); } }
public async Task AddToLiked(RoomMember member) { try { var api = await _spotifyAccessService.TryGetMemberApi(member.UserName); var track = _currentTrack; var result = await api.SaveTrackAsync(track.Id); result.ThrowOnError(nameof(api.SaveTrackAsync)); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Success, Message = $"Successfully added {string.Join(", ", track.Artists.Select(x => x.Name).ToArray())} - {track.Name} to your Liked Songs", TargetId = member.ConnectionId }); } catch (Exception e) { _logger.Log(LogLevel.Warning, "Failed to add song to {Username}'s liked songs because {@Exception}", member.UserName, e); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Error, Message = $"Failed to add song to your Liked Songs", TargetId = member.ConnectionId }); Debug.WriteLine(e); } }
public void VoteSkipSong(RoomMember member) { var oldVal = member.VotedSkipSong; member.VotedSkipSong = true; if (oldVal == false) { OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Information, Message = $"{member.FriendlyName} voted to skip song" }); } if (_members.Count / 2 > _members.Count(x => x.VotedSkipSong)) { return; } _roomEvents.Add(new SongSkippedEvent()); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Success, Message = $"Skipping song with {_members.Count(x => x.VotedSkipSong)} votes" }); _handledUntil = DateTimeOffset.Now; foreach (var roomMember in _members) { roomMember.VotedSkipSong = false; } }
public void VoteSkipSong(RoomMember member) { var oldVal = member.VotedSkipSong; member.VotedSkipSong = true; var requiredVotes = _members.Count / 2; var totalVotes = _members.Count(x => x.VotedSkipSong); var changedCount = oldVal == false; if (requiredVotes > totalVotes) { if (changedCount) { OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Information, Message = $"{member.FriendlyName} voted to skip song, {requiredVotes-totalVotes} more vote(s) until skipping..." }); } return; } _logger.Log(LogLevel.Information, "Skipping current song in {Room} with group vote", RoomId); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Success, Message = $"Skipping song with {_members.Count(x => x.VotedSkipSong)} vote(s)" }); SkipSongActual(); }
public void MemberJoin(RoomMember member) { if (_members.Any(x => x.UserName == member.UserName)) { return; } _statisticsService.IncrementUserCount(); _members.Add(member); OnRoomMembersChanged?.Invoke(this, member.UserName); _roomEvents.Add(new UserEvent(member.UserName, member.FriendlyName, UserEventType.JoinedRoom)); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Information, Message = $"{member.FriendlyName} joined room" }); ToggleDj(member, false); if (_currentTrack != null) { StartSongForMemberUgly(member); } }
public void MemberLeave(RoomMember member) { var didRemove = _members.Remove(member); if (!didRemove) { return; } _roomEvents.Add(new UserEvent(member.UserName, member.FriendlyName, UserEventType.LeftRoom)); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Information, Message = $"{member.FriendlyName} left the room" }); OnRoomMembersChanged?.Invoke(this, member.UserName); UpdateReactionTotals(); // this was the last member to leave if (!_members.Any()) { _timeSinceEmpty = DateTimeOffset.Now; } }
private async Task PlaySong(RoomMember member, FullTrack song, int positionMs = 0, bool canRetry = true) { try { var api = await _spotifyAccessService.TryGetMemberApi(member.UserName); var devices = await api.GetDevicesAsync(); devices.ThrowOnError(nameof(api.GetDevices)); if (!devices.Devices.Any()) { throw new Exception("No devices available to play on!"); } var device = devices.Devices.FirstOrDefault(x => x.IsActive) ?? devices.Devices.First(); var resume = await api.ResumePlaybackAsync(deviceId : device.Id, uris : new List <string> { song.Uri }, offset : 0, positionMs : positionMs); resume.ThrowOnError(nameof(api.ResumePlaybackAsync)); } catch (Exception e) { _logger.Log(LogLevel.Warning, "Failed to play song for {Username} because {@Exception}", member.UserName, e); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Error, Message = $"Failed to play song", TargetId = member.ConnectionId }); if (e.Message.Contains("HTTP 5") && canRetry) { // if it's a server side error let's add it to the retry queue async Task RetryTask() { // make sure user hasn't left in the last room-cycle if (!_members.Contains(member)) { return; } // try starting song again var left = _handledUntil.ToUnixTimeMilliseconds() - DateTimeOffset.Now.ToUnixTimeMilliseconds(); await PlaySong(member, _currentTrack, (int)(_currentTrack.DurationMs - left), false); } _logger.Log(LogLevel.Information, "Added retry task for {UserName}", member.UserName); _roomRetries.Add(RetryTask); } // else oh well Debug.WriteLine(e); } }
public void ToggleDj(RoomMember member, bool isDj) { member.IsDj = isDj; member.DjOrderNumber = isDj ? _members.Where(x => x.IsDj).Max(y => y.DjOrderNumber) + 1 : -1; _roomEvents.Add(new UserEvent(member.UserName, member.FriendlyName, isDj ? UserEventType.BecameDj : UserEventType.BecameListener)); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Information, Message = $"{member.FriendlyName} became a {(isDj ? "DJ" : "listener")}" }); OnRoomMembersChanged?.Invoke(this, null); }
public void TryForceSkipAsDj(RoomMember member) { if (CurrentRoomState.CurrentDjUsername != member.UserName) { return; } _logger.Log(LogLevel.Information, "Skipping current song in {Room} per request of the DJ", RoomId); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Success, Message = $"Skipping song per DJ's request" }); SkipSongActual(); }
private async Task PlaySong(RoomMember member, FullTrack song, int positionMs = 0) { try { var api = await _spotifyAccessService.TryGetMemberApi(member.UserName); var devices = await api.GetDevicesAsync(); devices.ThrowOnError(nameof(api.GetDevices)); if (!devices.Devices.Any()) { throw new Exception("No devices available to play on!"); } var device = devices.Devices.FirstOrDefault(x => x.IsActive) ?? devices.Devices.First(); var resume = await api.ResumePlaybackAsync(deviceId : device.Id, uris : new List <string> { song.Uri }, offset : 0, positionMs : positionMs); resume.ThrowOnError(nameof(api.ResumePlaybackAsync)); // we don't care if this one fails await api.SetRepeatModeAsync(RepeatState.Off, device.Id); } catch (Exception e) { _logger.Log(LogLevel.Warning, "Failed to play song for {Username} because {@Exception}", member.UserName, e); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Error, Message = $"Failed to play song", TargetId = member.ConnectionId }); // oh well Debug.WriteLine(e); } }
public void MemberLeave(RoomMember member) { var didRemove = _members.Remove(member); if (!didRemove) { return; } _statisticsService.DecrementUserCount(); _roomEvents.Add(new UserEvent(member.UserName, member.FriendlyName, UserEventType.LeftRoom)); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Information, Message = $"{member.FriendlyName} left the room" }); OnRoomMembersChanged?.Invoke(this, member.UserName); UpdateReactionTotals(); }
private async Task PlaySong(RoomMember member, FullTrack song, int positionMs = 0, bool canRetry = true) { try { var api = await _spotifyAccessService.TryGetMemberApi(member.UserName); var devices = await api.GetDevicesAsync(); devices.ThrowOnError(nameof(api.GetDevices)); if (!devices.Devices.Any()) { throw new Exception("No devices available to play on!"); } var activeDevice = devices.Devices.FirstOrDefault(x => x.IsActive); var hasShouldBeActive = _devicePersistenceService.TryGetActiveDeviceId(member.UserName, out var shouldBeActiveDeviceId); if (activeDevice != null && activeDevice.Id != shouldBeActiveDeviceId) { // set if changed _devicePersistenceService.SetDeviceState(member.UserName, activeDevice.Id); } // active device was somehow lost, if it's still in the list then reactivate it if (activeDevice == null && hasShouldBeActive) { activeDevice = devices.Devices.FirstOrDefault(x => x.Id == shouldBeActiveDeviceId); } if (activeDevice == null) { _devicePersistenceService.CleanDeviceState(member.UserName); throw new Exception("No active device found to play music on."); } var resume = await api.ResumePlaybackAsync(deviceId : activeDevice.Id, uris : new List <string> { song.Uri }, offset : 0, positionMs : positionMs); resume.ThrowOnError(nameof(api.ResumePlaybackAsync)); // reset failure count if playback started successfully if (member.PlayFailureCount > 0) { member.PlayFailureCount = 0; _logger.Log(LogLevel.Information, "Reset play failure count for {Username}", member.UserName); } } catch (Exception e) { _logger.Log(LogLevel.Warning, "Failed to play song for {Username} because {@Exception}", member.UserName, e); OnRoomNotification?.Invoke(this, new RoomNotification { Category = RoomNotificationCategory.Error, Message = $"Failed to play song", TargetId = member.ConnectionId }); if (e.Message.Contains("HTTP 5") && canRetry) { // if it's a server side error let's add it to the retry queue async Task RetryTask() { // make sure user hasn't left in the last room-cycle if (!_members.Contains(member)) { return; } // try starting song again var left = _handledUntil.ToUnixTimeMilliseconds() - DateTimeOffset.Now.ToUnixTimeMilliseconds(); await PlaySong(member, _currentTrack, (int)(_currentTrack.DurationMs - left), false); } _logger.Log(LogLevel.Information, "Added retry task for {UserName}", member.UserName); _roomRetries.Add(RetryTask); } else { // not a server side error, increment member failure count member.PlayFailureCount += 1; _logger.Log(LogLevel.Information, "Incremented play failure count to {PlayFailureCount} for {Username}", member.PlayFailureCount, member.UserName); } Debug.WriteLine(e); } }