Beispiel #1
0
        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;
        }
Beispiel #2
0
        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);
            }
        }
Beispiel #3
0
        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);
            }
        }
Beispiel #4
0
        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;
            }
        }
Beispiel #5
0
        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();
        }
Beispiel #6
0
        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);
            }
        }
Beispiel #7
0
        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;
            }
        }
Beispiel #8
0
        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);
            }
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        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();
        }
Beispiel #11
0
        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);
            }
        }
Beispiel #12
0
        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();
        }
Beispiel #13
0
        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);
            }
        }