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); // oh well Debug.WriteLine(e); } }
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 async Task <IActionResult> Index(string join = null) { var username = HttpContext.User.Claims.GetSpotifyUsername(); var api = await _spotifyAccessService.TryGetMemberApi(username); // guarantee playlist var profile = await api.GetPrivateProfileAsync(); var queuePlaylist = ""; var queuePlaylistUrl = ""; var playlistMessage = ""; var offset = 0; const int paginationLimit = 50; var playlists = await api.GetUserPlaylistsAsync(profile.Id, paginationLimit, offset); // ReSharper disable once UselessBinaryOperation (this seems to be a resharper issue) offset += paginationLimit; do { var playlist = playlists.Items.FirstOrDefault(x => !x.Collaborative && !x.Public && x.Name == Constants.QueuePlaylistName); // found it if (playlist != null) { queuePlaylist = playlist.Id; queuePlaylistUrl = playlist.Uri; playlistMessage = "Found existing queue playlist: "; break; } playlists = await api.GetUserPlaylistsAsync(profile.Id, paginationLimit, offset); offset += paginationLimit; } while (playlists.HasNextPage()); // if we didn't find the playlist create it if (string.IsNullOrWhiteSpace(queuePlaylist)) { var newPlaylist = await api.CreatePlaylistAsync(profile.Id, Constants.QueuePlaylistName, false, false, "PugetSound Queue playlist - add songs here you want to play"); queuePlaylist = newPlaylist.Id; queuePlaylistUrl = newPlaylist.Uri; playlistMessage = "Created queue playlist: "; _logger.Log(LogLevel.Information, "Created queue playlist for {Username}", username); } else { _logger.Log(LogLevel.Information, "Found queue playlist for {Username}", username); } var friendlyName = HttpContext.User.Claims.GetSpotifyFriendlyName(); var alreadyInRoom = _roomService.TryGetRoomForUsername(username, out var prevRoom); // log _logger.Log(LogLevel.Information, "Welcoming {FriendlyName} as {Username}", friendlyName, username); if (alreadyInRoom) { _logger.Log(LogLevel.Information, "Pre-filled {Room} for {Username}", prevRoom.RoomId, username); } // return login page return(View(new IndexModel { UserName = username, FriendlyName = friendlyName, ProfileLink = profile.Uri, PlaylistLink = queuePlaylistUrl, PlaylistMessage = playlistMessage, PlaylistId = queuePlaylist, IsAlreadyInRoom = alreadyInRoom, RoomName = alreadyInRoom ? prevRoom.RoomId : join ?? "" })); }
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); } }