private async Task InformOfStoppedTrack() { if (_apiClient == null) { return; } try { var track = BackgroundAudioPlayer.Instance.Track; if (track != null) { var info = new PlaybackStopInfo { ItemId = track.Tag, //UserId = _apiClient.CurrentUserId }; await _apiClient.ReportPlaybackStoppedAsync(info); } } catch (HttpException ex) { _logger.FatalException("InformOfStoppedTrack()", ex); } NotifyComplete(); }
/// <summary> /// Handles the PlaybackCompleted event of the _mediaPlayer control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="PlaybackStopEventArgs"/> instance containing the event data.</param> async void _mediaPlayer_PlaybackCompleted(object sender, PlaybackStopEventArgs e) { _mediaPlayer.MediaChanged -= _mediaPlayer_MediaChanged; _mediaPlayer.PlaybackCompleted -= _mediaPlayer_PlaybackCompleted; if (_timer != null) { _timer.Dispose(); _timer = null; } if (e.EndingMedia != null) { var info = new PlaybackStopInfo { ItemId = e.EndingMedia.Id, PositionTicks = e.EndingPositionTicks }; var apiClient = _connectionManager.GetApiClient(e.EndingMedia); try { await apiClient.ReportPlaybackStoppedAsync(info); } catch (Exception ex) { _logger.ErrorException("Error sending playback stopped checking for {0}", ex, e.EndingMedia.Name); } } }
public async Task <ActionResult> OnPlaybackStopped( [FromRoute] Guid userId, [FromRoute] Guid itemId, [FromQuery] string?mediaSourceId, [FromQuery] string?nextMediaType, [FromQuery] long?positionTicks, [FromQuery] string?liveStreamId, [FromQuery] string?playSessionId) { var playbackStopInfo = new PlaybackStopInfo { ItemId = itemId, PositionTicks = positionTicks, MediaSourceId = mediaSourceId, PlaySessionId = playSessionId, LiveStreamId = liveStreamId, NextMediaType = nextMediaType }; _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { await _transcodingJobHelper.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } playbackStopInfo.SessionId = RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id; await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return(NoContent()); }
/// <summary> /// Reports the playback stopped. /// </summary> /// <param name="message">The message.</param> private void ReportPlaybackStopped(WebSocketMessageInfo message) { _logger.Debug("Received PlaybackStopped message"); var session = GetSessionFromMessage(message); if (session != null && session.User != null) { var vals = message.Data.Split('|'); var item = _dtoService.GetItemByDtoId(vals[0]); long?positionTicks = null; if (vals.Length > 1) { long pos; if (long.TryParse(vals[1], out pos)) { positionTicks = pos; } } var info = new PlaybackStopInfo { Item = item, PositionTicks = positionTicks, SessionId = session.Id }; _sessionManager.OnPlaybackStopped(info); } }
/// <summary> /// Reports playback progress /// </summary> /// <param name="info">The information.</param> /// <param name="serverId">The server identifier.</param> /// <param name="userId">The user identifier.</param> /// <param name="isOffline">if set to <c>true</c> [is offline].</param> /// <param name="isVideo">if set to <c>true</c> [is video].</param> /// <param name="apiClient">The current apiClient. It can be null if offline</param> /// <returns>Task.</returns> public async Task ReportPlaybackStopped(PlaybackStopInfo info, string serverId, string userId, bool isOffline, bool isVideo, IApiClient apiClient) { if (isOffline) { var action = new UserAction { Date = DateTime.UtcNow, ItemId = info.ItemId, PositionTicks = info.PositionTicks, ServerId = serverId, Type = UserActionType.PlayedItem, UserId = userId }; await _localAssetManager.RecordUserAction(action).ConfigureAwait(false); return; } // Put a try/catch here because we need to stop transcoding regardless try { await apiClient.ReportPlaybackStoppedAsync(info).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error in ReportPlaybackStoppedAsync", ex); } if (isVideo) { await apiClient.StopTranscodingProcesses(_device.DeviceId).ConfigureAwait(false); } }
/// <summary> /// Handles the MediaChanged event of the _mediaPlayer control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="MediaChangeEventArgs"/> instance containing the event data.</param> async void _mediaPlayer_MediaChanged(object sender, MediaChangeEventArgs e) { if (e.PreviousMedia != null) { var info = new PlaybackStopInfo { ItemId = e.PreviousMedia.Id, PositionTicks = e.EndingPositionTicks }; var apiClient = _connectionManager.GetApiClient(e.PreviousMedia); try { await apiClient.ReportPlaybackStoppedAsync(info); } catch (Exception ex) { _logger.ErrorException("Error sending playback stopped checking for {0}", ex, e.PreviousMedia.Name); } } if (e.NewMedia != null) { try { var queueTypes = _mediaPlayer.CanQueue ? new List <string> { e.NewMedia.MediaType } : new List <string> { }; var info = new PlaybackStartInfo { ItemId = e.NewMedia.Id, CanSeek = _mediaPlayer.CanSeek, QueueableMediaTypes = queueTypes.ToList(), // TODO: Remove this hardcoding PlayMethod = PlayMethod.DirectPlay }; var apiClient = _connectionManager.GetApiClient(e.NewMedia); await apiClient.ReportPlaybackStartAsync(info); } catch (Exception ex) { _logger.ErrorException("Error sending playback start checking for {0}", ex, e.NewMedia.Name); } } }
/// <summary> /// Used to report that playback has ended for an item /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">info</exception> /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception> public async Task OnPlaybackStopped(PlaybackStopInfo info) { if (info == null) { throw new ArgumentNullException("info"); } if (info.Item == null) { throw new ArgumentException("PlaybackStopInfo.Item cannot be null"); } if (info.SessionId == Guid.Empty) { throw new ArgumentException("PlaybackStopInfo.SessionId cannot be Guid.Empty"); } if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0) { throw new ArgumentOutOfRangeException("positionTicks"); } var session = Sessions.First(i => i.Id.Equals(info.SessionId)); RemoveNowPlayingItem(session, info.Item); var key = info.Item.GetUserDataKey(); var user = session.User; var data = _userDataRepository.GetUserData(user.Id, key); if (info.PositionTicks.HasValue) { UpdatePlayState(info.Item, data, info.PositionTicks.Value); } else { // If the client isn't able to report this, then we'll just have to make an assumption data.PlayCount++; data.Played = true; data.PlaybackPositionTicks = 0; } await _userDataRepository.SaveUserData(user.Id, info.Item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None).ConfigureAwait(false); EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackProgressEventArgs { Item = info.Item, User = user, PlaybackPositionTicks = info.PositionTicks }, _logger); }
public async Task <ActionResult> ReportPlaybackStopped([FromBody] PlaybackStopInfo playbackStopInfo) { _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { await _transcodingJobHelper.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } playbackStopInfo.SessionId = RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id; await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return(NoContent()); }
/// <summary> /// Used to report that playback has ended for an item /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">info</exception> /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception> public async Task OnPlaybackStopped(PlaybackStopInfo info) { if (info == null) { throw new ArgumentNullException("info"); } if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0) { throw new ArgumentOutOfRangeException("positionTicks"); } var session = GetSession(info.SessionId); var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) ? null : _libraryManager.GetItemById(new Guid(info.ItemId)); // Normalize if (string.IsNullOrWhiteSpace(info.MediaSourceId)) { info.MediaSourceId = info.ItemId; } RemoveNowPlayingItem(session); var users = GetUsers(session); var playedToCompletion = false; if (libraryItem != null) { var key = libraryItem.GetUserDataKey(); foreach (var user in users) { playedToCompletion = await OnPlaybackStopped(user.Id, key, libraryItem, info.PositionTicks).ConfigureAwait(false); } } EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs { Item = libraryItem, Users = users, PlaybackPositionTicks = info.PositionTicks, PlayedToCompletion = playedToCompletion, MediaSourceId = info.MediaSourceId }, _logger); await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false); }
/// <summary> /// Used to report that playback has ended for an item /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">info</exception> /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception> public async Task OnPlaybackStopped(PlaybackStopInfo info) { if (info == null) { throw new ArgumentNullException("info"); } if (info.Item == null) { throw new ArgumentException("PlaybackStopInfo.Item cannot be null"); } if (info.SessionId == Guid.Empty) { throw new ArgumentException("PlaybackStopInfo.SessionId cannot be Guid.Empty"); } if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0) { throw new ArgumentOutOfRangeException("positionTicks"); } var session = Sessions.First(i => i.Id.Equals(info.SessionId)); RemoveNowPlayingItem(session, info.Item); var key = info.Item.GetUserDataKey(); var users = GetUsers(session); var playedToCompletion = false; foreach (var user in users) { playedToCompletion = await OnPlaybackStopped(user.Id, key, info.Item, info.PositionTicks).ConfigureAwait(false); } EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs { Item = info.Item, Users = users, PlaybackPositionTicks = info.PositionTicks, PlayedToCompletion = playedToCompletion }, _logger); }
/// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> public void Delete(OnPlaybackStopped request) { var user = _userManager.GetUserById(request.UserId); var item = _dtoService.GetItemByDtoId(request.Id, user.Id); var session = GetSession(); var info = new PlaybackStopInfo { Item = item, PositionTicks = request.PositionTicks, SessionId = session.Id }; var task = _sessionManager.OnPlaybackStopped(info); Task.WaitAll(task); }
/// <summary> /// Reports the playback stopped. /// </summary> /// <param name="message">The message.</param> private void OnPlaybackStopped(WebSocketMessageInfo message) { _logger.Debug("Received PlaybackStopped message"); var session = GetSessionFromMessage(message); if (session != null && session.UserId.HasValue) { var vals = message.Data.Split('|'); var itemId = vals[0]; long?positionTicks = null; if (vals.Length > 1) { long pos; if (long.TryParse(vals[1], out pos)) { positionTicks = pos; } } var info = new PlaybackStopInfo { ItemId = itemId, PositionTicks = positionTicks, SessionId = session.Id }; if (vals.Length > 2) { info.MediaSourceId = vals[2]; } _sessionManager.OnPlaybackStopped(info); } }
/// <summary> /// Reports playback progress /// </summary> /// <param name="info">The information.</param> /// <param name="streamInfo">The stream information.</param> /// <param name="serverId">The server identifier.</param> /// <param name="userId">The user identifier.</param> /// <param name="isOffline">if set to <c>true</c> [is offline].</param> /// <param name="apiClient">The current apiClient. It can be null if offline</param> /// <returns>Task.</returns> public async Task ReportPlaybackStopped(PlaybackStopInfo info, StreamInfo streamInfo, string serverId, string userId, bool isOffline, IApiClient apiClient) { if (isOffline) { var action = new UserAction { Date = DateTime.UtcNow, ItemId = info.ItemId, PositionTicks = info.PositionTicks, ServerId = serverId, Type = UserActionType.PlayedItem, UserId = userId }; await _localAssetManager.RecordUserAction(action).ConfigureAwait(false); return; } if (streamInfo != null) { info.PlaySessionId = streamInfo.PlaySessionId; if (streamInfo.MediaSource != null) { info.LiveStreamId = streamInfo.MediaSource.LiveStreamId; } } // Put a try/catch here because we need to stop transcoding regardless try { await apiClient.ReportPlaybackStoppedAsync(info).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error in ReportPlaybackStoppedAsync", ex); } }
/// <summary> /// Reports the playback stopped. /// </summary> /// <param name="message">The message.</param> private void OnPlaybackStopped(WebSocketMessageInfo message) { _logger.Debug("Received PlaybackStopped message"); var session = GetSessionFromMessage(message); if (session != null && session.UserId.HasValue) { var vals = message.Data.Split('|'); var itemId = vals[0]; long? positionTicks = null; if (vals.Length > 1) { long pos; if (long.TryParse(vals[1], out pos)) { positionTicks = pos; } } var info = new PlaybackStopInfo { ItemId = itemId, PositionTicks = positionTicks, SessionId = session.Id }; if (vals.Length > 2) { info.MediaSourceId = vals[2]; } _sessionManager.OnPlaybackStopped(info); } }
/// <summary> /// Used to report that playback has ended for an item /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">info</exception> /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception> public async Task OnPlaybackStopped(PlaybackStopInfo info) { if (info == null) { throw new ArgumentNullException("info"); } if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0) { throw new ArgumentOutOfRangeException("positionTicks"); } var session = GetSession(info.SessionId); var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) ? null : _libraryManager.GetItemById(new Guid(info.ItemId)); // Normalize if (string.IsNullOrWhiteSpace(info.MediaSourceId)) { info.MediaSourceId = info.ItemId; } if (!string.IsNullOrWhiteSpace(info.ItemId) && libraryItem != null) { var current = session.NowPlayingItem; if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase)) { info.Item = GetItemInfo(libraryItem, libraryItem, info.MediaSourceId); } else { info.Item = current; } } RemoveNowPlayingItem(session); var users = GetUsers(session); var playedToCompletion = false; if (libraryItem != null) { var key = libraryItem.GetUserDataKey(); foreach (var user in users) { playedToCompletion = await OnPlaybackStopped(user.Id, key, libraryItem, info.PositionTicks).ConfigureAwait(false); } } EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs { Item = libraryItem, Users = users, PlaybackPositionTicks = info.PositionTicks, PlayedToCompletion = playedToCompletion, MediaSourceId = info.MediaSourceId, MediaInfo = info.Item, DeviceName = session.DeviceName, ClientName = session.Client, DeviceId = session.DeviceId }, _logger); await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false); }
private void ReportEmbyStatus() { //used to report position on stop - by the time the reporter polls, the player might have closed already //long lastPosition = 0; //when MPC-HC is loading a file, its state is None; //before playback starts properly, newer versions of MPC-HC may also report Paused or even Stopped //This flag prevents the reporter from terminating before the player loads the file bool startedPlaying = false; var stopwatch = new Stopwatch(); log.Debug("ProgressReporter thread starting"); for (;;) { stopwatch.Start(); var status = player.GetPlayerStatus(); if (!startedPlaying) { startedPlaying = status.State == PlayerState.Playing; } else { if (status.State == PlayerState.Stopped) { log.Info("MPC-HC is no longer playing"); var stopInfo = new PlaybackStopInfo { ItemId = itemId, PositionTicks = status.Position * Utils.EmbyTicksPerMs }; Utils.IgnoreExceptions(() => embyClient.ReportPlaybackStoppedAsync(stopInfo).Wait()); OnPlayerStopped(EventArgs.Empty); log.Info("Progress reporter terminating"); return; } var progressInfo = new PlaybackProgressInfo { CanSeek = player.CanSeek(), IsPaused = status.State == PlayerState.Paused, IsMuted = status.Muted, ItemId = itemId, PlayMethod = PlayMethod.DirectPlay, PositionTicks = status.Position * Utils.EmbyTicksPerMs, VolumeLevel = status.VolumeLevel, RepeatMode = RepeatMode.RepeatNone }; Utils.IgnoreExceptions(() => embyClient.ReportPlaybackProgressAsync(progressInfo).Wait()); //lastPosition = status.Position; } stopwatch.Stop(); int sleepTime = reportPeriod - (int)stopwatch.ElapsedMilliseconds; log.DebugFormat("Status report took {0}ms.", stopwatch.ElapsedMilliseconds); lock (monitor) { if (stop) { break; } if (sleepTime > 0) { Monitor.Wait(monitor, sleepTime); } if (stop) { break; } } stopwatch.Reset(); } }
public override void WireMessages() { Messenger.Default.Register <VideoMessage>(this, m => { PlaylistItems = null; switch (m.PlayerSourceType) { case PlayerSourceType.Video: if (m.VideoItem != null) { SelectedItem = m.VideoItem; PlaylistItems = null; } break; case PlayerSourceType.Recording: if (m.VideoItem != null) { RecordingItem = m.VideoItem; PlaylistItems = null; } break; case PlayerSourceType.Programme: if (m.VideoItem != null) { ProgrammeItem = m.VideoItem; PlaylistItems = null; } break; case PlayerSourceType.Playlist: PlaylistItems = m.VideoPlaylists; SelectedItem = m.VideoItem; break; } PlayerSourceType = m.PlayerSourceType; _isResume = m.IsResume; _startPositionTicks = m.ResumeTicks ?? 0; }); Messenger.Default.Register <NotificationMessage>(this, async m => { if (m.Notification.Equals(Constants.Messages.SetResumeMsg)) { _isResume = true; } if (m.Notification.Equals(Constants.Messages.SendVideoTimeToServerMsg)) { try { var totalTicks = _startPositionTicks + PlayedVideoDuration.Ticks; Log.Info("Sending current runtime [{0}] to the server", totalTicks); var info = new PlaybackStopInfo { ItemId = _itemId, PositionTicks = totalTicks }; await _playbackManager.ReportPlaybackStopped(info, _streamInfo, App.ServerInfo.Id, AuthenticationService.Current.LoggedInUserId, false, ApiClient); SetPlaybackTicks(totalTicks); if (_timer != null && _timer.IsEnabled) { _timer.Stop(); } Messenger.Default.Send(new NotificationMessage(_itemId, totalTicks, Constants.Messages.RefreshResumeMsg)); } catch (HttpException ex) { Utils.HandleHttpException("SendVideoTimeToServer", ex, NavigationService, Log); } if (!PlaylistItems.IsNullOrEmpty()) { var index = PlaylistItems.IndexOf(SelectedItem); if (index >= 0 && index < PlaylistItems.Count - 1) { SelectedItem = PlaylistItems[index + 1]; await InitiatePlayback(false); } } } if (m.Notification.Equals(Constants.Messages.VideoStateChangedMsg)) { try { var totalTicks = _startPositionTicks + PlayedVideoDuration.Ticks; var isPaused = m.Sender != null && (bool)m.Sender; if (_timer != null) { if (isPaused) { if (_timer.IsEnabled) { _timer.Stop(); } } else { if (!_timer.IsEnabled) { _timer.Start(); } } } Log.Info("Sending current runtime [{0}] to the server", totalTicks); var info = new PlaybackProgressInfo { IsMuted = false, ItemId = _itemId, PositionTicks = totalTicks, IsPaused = isPaused }; await ApiClient.ReportPlaybackProgressAsync(info); SetPlaybackTicks(totalTicks); } catch (HttpException ex) { Utils.HandleHttpException("VideoStateChanged", ex, NavigationService, Log); } } }); }