public async Task <bool> PauseAsync(PlaybackSession session) { var jobId = session.CurrentSongPlayback.JobId; var jobKey = new JobKey(jobId); var job = await _jobScheduler.GetJobDetail(jobKey); if (job.JobType == typeof(SongFinishedJob)) { var songTracker = session.CurrentSongPlayback; TimeSpan pausedAtDuration = (songTracker.State == PlaybackState.Resumed && songTracker.ResumedUtc.HasValue && songTracker.PausedAtMs.HasValue) ? DateTime.UtcNow - songTracker.ResumedUtc.Value + TimeSpan.FromMilliseconds(songTracker.PausedAtMs.Value) : DateTime.UtcNow - songTracker.StartedUtc; songTracker.PausedAtMs = Convert.ToInt32(pausedAtDuration.TotalMilliseconds); songTracker.PausedUtc = DateTime.UtcNow; songTracker.State = PlaybackState.Paused; await _songRepo.Upsert(songTracker); await _jobScheduler.DeleteJob(jobKey); await _playbackService.PauseSpotifyUserPlaybackAsync(session.JoinedUsers.Select(u => u.Id)); return(true); } else { throw new InvalidOperationException( "PlaybackSession cant be paused if its job type is not SongFinishedJob"); } }
public async Task <bool> SkipAsync(PlaybackSession session) { var jobId = session.CurrentSongPlayback.JobId; var jobKey = new JobKey(jobId); var job = await _jobScheduler.GetJobDetail(jobKey); if (job != null) { session.CurrentSongPlayback.State = PlaybackState.Skipped; await _songRepo.Upsert(session.CurrentSongPlayback); var triggers = await _jobScheduler.GetTriggersOfJob(jobKey); await _jobScheduler.TriggerJob(jobKey, job.JobDataMap); await _jobScheduler.UnscheduleJobs(triggers.Select(t => t.Key).ToImmutableList()); return(true); } else { // Start new playback if there is no scheduled job await StartNewPlayback(session); return(true); } }
private async Task <IEnumerable <PlaylistTrack> > GetNextTracksAsync(PlaybackSession session, string finishedSongUri = null, int previewCount = 2) { // Call spotifyApi to get playlist from playlistId // TODO this will have performance issues for larger playlists. Instead use the limit: and offset: params List <PlaylistTrack> playlistTracks; using (var client = await _spotifyApiFactory.CreateAppClient()) { playlistTracks = (await client.GetPlaylistTracksAsync(null, session.SpotifyPlaylistId)) .Items; } // Get next track by finding the one after the finished song uri var finishedIndex = playlistTracks.FindIndex(t => t.Track.Uri == finishedSongUri); // Next song, or start back at 0 var nextIndex = finishedIndex >= playlistTracks.Count - 1 ? 0 : finishedIndex + 1; // Take next track, plus preview tracks var requiredCount = 1 + previewCount; // Get the next song, and the previews var nextTracks = playlistTracks.Skip(nextIndex).Take(requiredCount).ToList(); // If we are at the end of the playlist, get the previews from the start if (nextTracks.Count() < requiredCount) { var take = requiredCount - nextTracks.Count(); nextTracks.AddRange(playlistTracks.Take(take)); } return(nextTracks); }
public void PlaySoundInDevice(int deviceNumber) { try { var w = _waveOuts[deviceNumber]; w.Stop(); if (outputDevices.ContainsKey(deviceNumber)) { outputDevices[deviceNumber].WaveOut.Dispose(); outputDevices[deviceNumber].WaveStream.Dispose(); } WaveStream waveStream = IsMp3(_fileName) ? (WaveStream) new Mp3FileReader(_fileName) : new WaveFileReader(_fileName); LoopStream loop = new LoopStream(waveStream); // hold onto the WaveOut and WaveStream so we can dispose them later outputDevices[deviceNumber] = new PlaybackSession { WaveOut = w, WaveStream = loop }; w.Init(loop); w.Play(); } catch (Exception) { // } }
// Constructor internal EventHandlerDelegate(PlaybackSession session, Tuning tuning, OverlaySurface overlaySurface, UserInterface userInterface) { _tuning = tuning; _session = session; _overlaySurface = overlaySurface; _userInterface = userInterface; _eKeyboardHandler = null; _eChannelChangeHandler = null; }
public TimeSpan GetDuration() { PlaybackSession session = _playbackProcessor.PlaybackSession; if (session == null) return TimeSpan.Zero; IInputSource source = session.CurrentInputSource; if (source == null) return TimeSpan.Zero; return source.Length; }
private async Task PopulatePropertiesAsync(PlaybackSession session, PlaybackSessionQueryOptions queryOptions = null) { // Looks like this related property is being eager loaded. The inner if was never hit in testing if (session != null && !string.IsNullOrWhiteSpace(session.CurrentSongPlaybackId) && queryOptions != null) { var currentPlaybackId = session.CurrentSongPlaybackId; var playback = await _db.SongPlaybackTrackers.FirstOrDefaultAsync(m => m.Id == currentPlaybackId); session.CurrentSongPlayback = playback ?? throw new Exception("Current playback cannot be null"); } }
public async Task <bool> ResumeAsync(PlaybackSession session) { if (session.CurrentSongPlayback != null && session.CurrentSongPlayback.State == PlaybackState.Paused && session.CurrentSongPlayback.PausedAtMs.HasValue) { var userIds = session.JoinedUsers.Select(u => u.Id); // Nothing to do if there is no more joined users, or playlist if (!session.JoinedUsers.Any() || string.IsNullOrWhiteSpace(session.SpotifyPlaylistId)) { _logger.Log(LogLevel.Information, "No more users on {sessionId}. Halting playback", session.Id); return(false); } var songTracker = session.CurrentSongPlayback; songTracker.State = PlaybackState.Resumed; songTracker.ResumedUtc = DateTime.UtcNow; await _songRepo.Upsert(songTracker); var startPlaybackTask = _playbackService.StartSpotifyUserPlaybackAsync(userIds, session.CurrentSongPlayback.SpotifySongUri, session.CurrentSongPlayback.PausedAtMs ?? 0); // Spawn next job var jobId = $"SongFinishedJob-{Guid.NewGuid().ToString()}"; session.CurrentSongPlayback.JobId = jobId; await _songRepo.Upsert(session.CurrentSongPlayback); var job = JobBuilder.Create <SongFinishedJob>() .WithIdentity(jobId) .WithDescription($"SongFinishedJob SessionId: {session.Id}, PlaylistId: {session.SpotifyPlaylistId}, SongUri: {session.CurrentSongPlayback.SpotifySongUri}") .UsingJobData("PlaybackSessionId", session.Id) .UsingJobData("SpotifySongUri", session.CurrentSongPlayback.SpotifySongUri) .Build(); var trigger = TriggerBuilder.Create() .ForJob(job) .StartAt(session.CurrentSongPlayback.ExpectedFinishUtc) .Build(); var scheduleJobTask = _jobScheduler.ScheduleJob(job, trigger); await Task.WhenAll(startPlaybackTask, scheduleJobTask); return(true); } else { throw new InvalidOperationException( "PlaybackSession cant be resumed if it is not paused"); } }
private async Task StartNewPlayback(PlaybackSession session) { var jobId = $"SongFinishedJob-{Guid.NewGuid().ToString()}"; var job = JobBuilder.Create <SongFinishedJob>() .WithIdentity(jobId) .WithDescription($"SongFinishedJob SessionId: {session.Id}") .UsingJobData("PlaybackSessionId", session.Id.ToString()) .Build(); var trigger = TriggerBuilder.Create() .ForJob(job) .StartNow() .Build(); await _jobScheduler.ScheduleJob(job, trigger); }
// Constructor internal UserInterface(ItvApp.AddIn addin) { _addin = addin; _overlaySurface = _addin.OverlaySurface; _videoSurface = _addin.VideoSurface; _tuning = _addin.Tuning; _session = _addin.Session; // create the object to receive keyboard hits. _eventHandler = new EventHandlerDelegate(_session, _tuning, _overlaySurface, this); // create a new audiostreamswticher _audioStreamSwitcher = new AudioStreamSwitcher(addin); }
public async Task SetPlaybackSession(SynthbotUser user, PlaybackSession session) { await _dbSignal.WaitAsync(); try { _db.Users.Attach(user); user.ActivePlaybackSession = session; await _db.SaveChangesAsync(); } finally { _dbSignal.Release(); } }
public TAG_INFO GetTags() { PlaybackSession session = _playbackProcessor.PlaybackSession; if (session == null) { return(null); } ITagSource source = session.CurrentInputSource as ITagSource; if (source == null) { return(null); } return(source.Tags); }
public void Dispose() { if (disposed) { return; } if (MediaList != null) { MediaList.Dispose(); MediaList = null; // Setter triggers vector unsubscribe logic } PlaybackSession.Dispose(); disposed = true; }
private void PlaybackStartedHandler(PlaybackSession session, IEnumerable <FullTrack> tracks) { var fullTracks = tracks.Take(2).ToArray(); _logger.Log(LogLevel.Information, "New playback started for session: {session}. New track: {trackInfo}", JsonConvert.SerializeObject(session), JsonConvert.SerializeObject(fullTracks.First())); // To set the channel, run "@Bot set-update-channel" in the channel you want updates posted to var success = ulong.TryParse(session.UpdateChannelDiscordId, out ulong id); if (success) { var updateChannel = _discordClient.GetChannel(id) as IMessageChannel; var currentPlaylist = _spotifyApi.GetPlaylist(null, session.SpotifyPlaylistId); updateChannel?.SendMessageAsync("", false, EmbedFactory.NowPlaying(session.DiscordVoiceChannelId, fullTracks[0], fullTracks[1], currentPlaylist)).Wait(); } }
async Task PlayAsync(PlaybackSession playbackSession, Uri source, CancellationToken cancellationToken) { try { using (playbackSession) { _playbackSession = playbackSession; await playbackSession.PlayAsync(source, cancellationToken).ConfigureAwait(false); _playbackSession = null; } } catch (Exception ex) { Debug.WriteLine("StreamingMediaPlugin.PlayAsync() failed: " + ex.ExtendedMessage()); } }
public async Task <bool> ChangePlaylist(PlaybackSession session, string newSpotifyPlaylistId) { session.SpotifyPlaylistId = newSpotifyPlaylistId; await _sessionRepo.UpsertSession(session); // If there is current playback, remove it's current job, and create a new one if (session.CurrentSongPlayback != null) { var songTracker = session.CurrentSongPlayback; songTracker.State = PlaybackState.Skipped; await _songRepo.Upsert(songTracker); await _jobScheduler.DeleteJob(new JobKey(session.CurrentSongPlayback.JobId)); } await StartNewPlayback(session); return(true); }
public async Task UpsertSession(PlaybackSession session) { if (!session.IsValidForInsert()) { throw new Exception("PlaybackSession is not valid for Upsert"); } await _dbSignal.WaitAsync(); try { var entity = await _db.PlaybackSessions .FirstOrDefaultAsync(e => e.DiscordVoiceChannelId == session.DiscordVoiceChannelId); if (entity == null) { await _db.PlaybackSessions.AddAsync(session); await _db.SaveChangesAsync(); } else { _db.PlaybackSessions.Attach(entity); entity = session; await _db.SaveChangesAsync(); } if (session.CurrentSongPlayback != null) { // Upsert the session's Id to the CurrentSongPlayback. The relationship is not created automatically var retrievedSession = await GetByDiscordIdAsync(session.DiscordVoiceChannelId); retrievedSession.CurrentSongPlayback.PlaybackSession = retrievedSession; await _playbackRepo.Upsert(retrievedSession.CurrentSongPlayback); } await _db.SaveChangesAsync(); } finally { _dbSignal.Release(); } }
// Initilize method to uninitialize the members public void Uninitialize() { _description = null; // delete the overlaySurface. if (null != _overlaySurface) { _overlaySurface.Visible = false; _overlaySurface.Free(); _overlaySurface = null; } if (null != _videoSurface) { _videoSurface = null; } // remove the Tuning object if (null != _tuning) { _tuning = null; } // remove the event handler object if (null != _eventHandler) { _eventHandler.UnInitialize(); _eventHandler = null; } // delete the session object if (null != _session) { _session = null; } // delete the AudioStreamSwitcher if (null != _audioStreamSwitcher) { _audioStreamSwitcher.Uninitialize(); _audioStreamSwitcher = null; } }
public async Task <IActionResult> ChangePlaylist(string voiceChannelId, string newSpotifyPlaylistId) { var session = await _playbackRepo.GetByDiscordIdAsync(voiceChannelId, new PlaybackSessionQueryOptions() { IncludeCurrentPlayback = true }); var payload = new ChangePlaylistInfo(); if (session == null) { var playbackSession = new PlaybackSession() { DiscordVoiceChannelId = voiceChannelId, SpotifyPlaylistId = newSpotifyPlaylistId }; await _playbackRepo.UpsertSession(playbackSession); return(Ok(playbackSession)); } else { payload.PreviousPlaylist = session.SpotifyPlaylistId; } var result = await _playbackSessionService.ChangePlaylist(session, newSpotifyPlaylistId); session = await _playbackRepo.GetByDiscordIdAsync(voiceChannelId, new PlaybackSessionQueryOptions() { IncludeCurrentPlayback = true }); payload.NewPlaylist = session.SpotifyPlaylistId; if (result) { return(Ok(payload)); } else { return(StatusCode(500)); } }
public async Task Upsert_New() { using (var context = await EntityFrameworkHelpers.SqlContextAsync()) { var original = new PlaybackSession() { Id = "foo", SpotifyPlaylistId = "spotify-playlist", DiscordVoiceChannelId = "discord-channel-id" }; var songRepo = new SongPlaybackRepository(new NullLogger <SongPlaybackRepository>(), context); var sessionRepo = new PlaybackSessionRepository(new NullLogger <PlaybackSessionRepository>(), context, songRepo); await sessionRepo.UpsertSession(original); var result = await sessionRepo.GetById("foo"); Assert.NotNull(result); Assert.Equal(result, original); } }
public async Task Upsert_New_WithCurrentPlayback() { using (var context = await EntityFrameworkHelpers.SqlContextAsync()) { var originalSongTracker = new SongPlaybackTracker() { Id = "foo-song", SpotifySongUri = "test-spotify-song-uri", State = PlaybackState.Playing, Duration = TimeSpan.FromSeconds(30), StartedUtc = DateTime.UtcNow }; var original = new PlaybackSession() { Id = "foo", SpotifyPlaylistId = "spotify-playlist", DiscordVoiceChannelId = "discord-channel-id", CurrentSongPlayback = originalSongTracker }; var songRepo = new SongPlaybackRepository(new NullLogger <SongPlaybackRepository>(), context); var sessionRepo = new PlaybackSessionRepository(new NullLogger <PlaybackSessionRepository>(), context, songRepo); await sessionRepo.UpsertSession(original); var result = await sessionRepo.GetById("foo"); var songResult = await songRepo.GetById("foo-song"); Assert.NotNull(result); Assert.Equal(result, original); Assert.NotNull(result.CurrentSongPlayback); Assert.Equal(result.CurrentSongPlayback, originalSongTracker); Assert.Equal(result, songResult.PlaybackSession); } }
async Task PlaybackLoadingAsync(MediaLoadingEventArgs mediaLoadingEventArgs) { // ReSharper disable once PossiblyMistakenUseOfParamsMethod var mediaLoadingCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_unloadCancellationTokenSource.Token); var oldTokenSource = Interlocked.Exchange(ref _mediaLoadingCancellationTokenSource, mediaLoadingCancellationTokenSource); if (null != oldTokenSource) { oldTokenSource.CancelDisposeSafe(); } var source = mediaLoadingEventArgs.Source; if (null == source) { return; } MediaPlayerDeferral deferral = null; try { deferral = mediaLoadingEventArgs.DeferrableOperation.GetDeferral(); if (deferral.CancellationToken.IsCancellationRequested) { return; } using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(deferral.CancellationToken, mediaLoadingCancellationTokenSource.Token)) { var cancellationToken = linkedTokenSource.Token; PlaybackSession playbackSession; using (await _asyncLock.LockAsync(cancellationToken).ConfigureAwait(false)) { playbackSession = _playbackSession; if (null != playbackSession) { await playbackSession.StopAsync(cancellationToken); playbackSession = _playbackSession; if (null != playbackSession) { Debug.WriteLine("StreamingMediaPlugin MediaLoading non-null _playbackSession"); return; } } if (string.Equals(source.Scheme, "stop", StringComparison.OrdinalIgnoreCase)) { Debug.WriteLine("StreamingMediaPlugin MediaLoading stop"); return; } var passThrough = StreamingMediaSettings.Parameters.IsPassThrough; if (null != passThrough) { if (passThrough(source)) { Debug.WriteLine("StreamingMediaPlugin.PlayerOnMediaLoading() passing through " + source); deferral.Complete(); deferral = null; return; } } playbackSession = new PlaybackSession(InitializeMediaStream()); var playTask = PlayAsync(playbackSession, source, cancellationToken); TaskCollector.Default.Add(playTask, "StreamingMediaPlugin MediaLoading playTask"); } mediaLoadingEventArgs.MediaStreamSource = await playbackSession.GetMediaSourceAsync(cancellationToken).ConfigureAwait(false); mediaLoadingEventArgs.Source = null; //Debug.WriteLine("StreamingMediaPlugin MediaLoading deferral.Complete()"); deferral.Complete(); deferral = null; } } catch (OperationCanceledException) { } catch (Exception ex) { Debug.WriteLine("StreamingMediaPlugin.PlayerOnMediaLoading() failed: " + ex.Message); } finally { if (null != deferral) { Debug.WriteLine("StreamingMediaPlugin MediaLoading deferral.Cancel()"); deferral.Cancel(); } } }
async Task PlaybackLoadingAsync(MediaLoadingEventArgs mediaLoadingEventArgs) { // ReSharper disable once PossiblyMistakenUseOfParamsMethod var mediaLoadingCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_unloadCancellationTokenSource.Token); var oldTokenSource = Interlocked.Exchange(ref _mediaLoadingCancellationTokenSource, mediaLoadingCancellationTokenSource); if (null != oldTokenSource) oldTokenSource.CancelDisposeSafe(); var source = mediaLoadingEventArgs.Source; if (null == source) return; MediaPlayerDeferral deferral = null; try { deferral = mediaLoadingEventArgs.DeferrableOperation.GetDeferral(); if (deferral.CancellationToken.IsCancellationRequested) return; using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(deferral.CancellationToken, mediaLoadingCancellationTokenSource.Token)) { var cancellationToken = linkedTokenSource.Token; PlaybackSession playbackSession; using (await _asyncLock.LockAsync(cancellationToken).ConfigureAwait(false)) { playbackSession = _playbackSession; if (null != playbackSession) { await playbackSession.StopAsync(cancellationToken); playbackSession = _playbackSession; if (null != playbackSession) { Debug.WriteLine("StreamingMediaPlugin MediaLoading non-null _playbackSession"); return; } } if (string.Equals(source.Scheme, "stop", StringComparison.OrdinalIgnoreCase)) { Debug.WriteLine("StreamingMediaPlugin MediaLoading stop"); return; } var passThrough = StreamingMediaSettings.Parameters.IsPassThrough; if (null != passThrough) { if (passThrough(source)) { Debug.WriteLine("StreamingMediaPlugin.PlayerOnMediaLoading() passing through " + source); deferral.Complete(); deferral = null; return; } } playbackSession = new PlaybackSession(InitializeMediaStream()); var playTask = PlayAsync(playbackSession, source, cancellationToken); TaskCollector.Default.Add(playTask, "StreamingMediaPlugin MediaLoading playTask"); } mediaLoadingEventArgs.MediaStreamSource = await playbackSession.GetMediaSourceAsync(cancellationToken).ConfigureAwait(false); mediaLoadingEventArgs.Source = null; //Debug.WriteLine("StreamingMediaPlugin MediaLoading deferral.Complete()"); deferral.Complete(); deferral = null; } } catch (OperationCanceledException) { } catch (Exception ex) { Debug.WriteLine("StreamingMediaPlugin.PlayerOnMediaLoading() failed: " + ex.Message); } finally { if (null != deferral) { Debug.WriteLine("StreamingMediaPlugin MediaLoading deferral.Cancel()"); deferral.Cancel(); } } }
public Task SetTextUpdateChannel(PlaybackSession session, string updateChannelDiscordId) { session.UpdateChannelDiscordId = updateChannelDiscordId; return(_sessionRepo.UpsertSession(session)); }