/// <summary> /// Starts this instance. /// </summary> /// <returns>Task.</returns> public async Task Start() { var item = _mediaPlayer.CurrentMedia; if (item == null) { return; } if (item.Id == null) { // Item is local media to the client (i.e playing local dvd) // todo - fix up local media progress reporting return; } try { var queueTypes = _mediaPlayer.CanQueue ? new List<string> { item.MediaType } : new List<string> { }; var info = new PlaybackStartInfo { ItemId = item.Id, CanSeek = _mediaPlayer.CanSeek, QueueableMediaTypes = queueTypes.ToList(), // TODO: Remove this hardcoding PlayMethod = PlayMethod.DirectPlay }; var apiClient = _connectionManager.GetApiClient(item); await apiClient.ReportPlaybackStartAsync(info); if (_mediaPlayer.CanTrackProgress) { _timer = new Timer(TimerCallback, null, 100, 900); } _mediaPlayer.MediaChanged += _mediaPlayer_MediaChanged; _mediaPlayer.PlaybackCompleted += _mediaPlayer_PlaybackCompleted; } catch (Exception ex) { _logger.ErrorException("Error sending playback start checking for {0}", ex, item.Name); throw; } }
/// <summary> /// Starts this instance. /// </summary> /// <returns>Task.</returns> public async Task Start() { var item = _mediaPlayer.CurrentMedia; if (item == null) { throw new InvalidOperationException("Nothing is currently playing"); } if (item.Id == null) { // Item is local media to the client (i.e playing local dvd) // todo - fix up local media progress reporting return; } try { var queueTypes = _mediaPlayer.CanQueue ? new List<string> { item.MediaType } : new List<string> { }; var info = new PlaybackStartInfo { ItemId = item.Id, UserId = _apiClient.CurrentUserId, IsSeekable = _mediaPlayer.CanSeek, QueueableMediaTypes = queueTypes.ToArray() }; await _apiClient.ReportPlaybackStartAsync(info); if (_mediaPlayer.CanTrackProgress) { _timer = new Timer(TimerCallback, null, 1000, 1000); } _mediaPlayer.MediaChanged += _mediaPlayer_MediaChanged; _mediaPlayer.PlaybackCompleted += _mediaPlayer_PlaybackCompleted; } catch (Exception ex) { _logger.ErrorException("Error sending playback start checking for {0}", ex, item.Name); throw; } }
/// <summary> /// Used to report that playback has started for an item /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> /// <exception cref="System.ArgumentNullException">info</exception> public async Task OnPlaybackStart(PlaybackStartInfo info) { if (info == null) { throw new ArgumentNullException("info"); } var session = GetSession(info.SessionId); var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) ? null : _libraryManager.GetItemById(new Guid(info.ItemId)); UpdateNowPlayingItem(session, info, libraryItem); if (!string.IsNullOrEmpty(session.DeviceId) && info.PlayMethod != PlayMethod.Transcode) { ClearTranscodingInfo(session.DeviceId); } session.QueueableMediaTypes = info.QueueableMediaTypes; var users = GetUsers(session); if (libraryItem != null) { var key = libraryItem.GetUserDataKey(); foreach (var user in users) { await OnPlaybackStart(user.Id, key, libraryItem).ConfigureAwait(false); } } // Nothing to save here // Fire events to inform plugins EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs { Item = libraryItem, Users = users, MediaSourceId = info.MediaSourceId, MediaInfo = info.Item, DeviceName = session.DeviceName, ClientName = session.Client, DeviceId = session.DeviceId }, _logger); await SendPlaybackStartNotification(session, CancellationToken.None).ConfigureAwait(false); }
private async Task InitiatePlayback(bool isResume, int? subtitleIndex = null) { Messenger.Default.Send(new NotificationMessage(Constants.Messages.ClearNowPlayingMsg)); EndTime = TimeSpan.Zero; var streamInfo = new StreamInfo(); switch (PlayerSourceType) { case PlayerSourceType.Playlist: case PlayerSourceType.Video: if (SelectedItem.VideoType != VideoType.VideoFile) { var result = MessageBox.Show(AppResources.MessageExperimentalVideo, AppResources.MessageExperimentalTitle, MessageBoxButton.OKCancel); if (result == MessageBoxResult.Cancel) { NavigationService.GoBack(); return; } } if (SelectedItem.UserData != null && isResume) { _startPositionTicks = SelectedItem.UserData.PlaybackPositionTicks; } if (_startPositionTicks == 0) { // Although the API will return 0 items if the user doesn't have cinema mode enabled, // that's still precious time on a slow connection needlessly wasted. So let's make sure // the user actually has it enabled first. if (AuthenticationService.Current.LoggedInUser.Configuration.EnableCinemaMode) { try { var items = await ApiClient.GetIntrosAsync(SelectedItem.Id, AuthenticationService.Current.LoggedInUserId); if (items != null && !items.Items.IsNullOrEmpty()) { if (PlaylistItems == null) { PlaylistItems = new List<BaseItemDto>(items.Items) {SelectedItem}; } else { var list = items.Items.ToList(); list.AddRange(PlaylistItems); PlaylistItems = list; } var firstItem = PlaylistItems.FirstOrDefault(); if (firstItem != null) SelectedItem = firstItem; } } catch (HttpException ex) { Log.ErrorException("GetIntros (Cinema Mode)", ex); } } } streamInfo = await CreateVideoStream(SelectedItem.Id, _startPositionTicks, SelectedItem.MediaSources, SelectedItem.Type.ToLower().Equals("channelvideoitem")); if (SelectedItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(SelectedItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", SelectedItem.Type, SelectedItem.Name, SelectedItem.Id); break; case PlayerSourceType.Recording: streamInfo = await CreateVideoStream(RecordingItem.Id, _startPositionTicks); if (RecordingItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(RecordingItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", RecordingItem.Type, RecordingItem.Name, RecordingItem.Id); break; case PlayerSourceType.Programme: try { var channel = await ApiClient.GetItemAsync(ProgrammeItem.ChannelId, AuthenticationService.Current.LoggedInUserId); streamInfo = await CreateVideoStream(ProgrammeItem.ChannelId, _startPositionTicks, channel.MediaSources, useHls: true); } catch (HttpException ex) { Utils.HandleHttpException(ex, "GetVideoChannel", NavigationService, Log); NavigationService.GoBack(); return; } if (ProgrammeItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(ProgrammeItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", ProgrammeItem.Type, ProgrammeItem.Name, ProgrammeItem.Id); break; } if (streamInfo == null) { NavigationService.GoBack(); return; } if (subtitleIndex.HasValue) { streamInfo.SubtitleStreamIndex = subtitleIndex.Value; } var url = streamInfo.ToUrl(ApiClient.GetApiUrl("/"), ApiClient.AccessToken); _streamInfo = streamInfo; //Captions = GetSubtitles(SelectedItem); var isSyncedVideo = url.StartsWith("AnyTime", StringComparison.InvariantCultureIgnoreCase); if (EndTime.Ticks > 0 && !IsDirectStream) { EndTime = TimeSpan.FromTicks(EndTime.Ticks - _startPositionTicks); } StopAudioPlayback(); _streamInfo = streamInfo; if (_isResume && IsDirectStream) { StartFrom = TimeSpan.FromTicks(_startPositionTicks); } RaisePropertyChanged(() => IsDirectStream); if (isSyncedVideo) { SetVideoUrl(string.Empty); if (VideoStream == null || _storageUrl != url) { _storageUrl = url; using (var storage = IsolatedStorageFile.GetUserStoreForApplication()) { var stream = storage.OpenFile(url, FileMode.Open); VideoStream = stream; } } } else { VideoStream = null; SetVideoUrl(url); _storageUrl = string.Empty; } Debug.WriteLine(VideoUrl); Log.Debug(VideoUrl); try { Log.Info("Sending playback started message to the server."); _itemId = streamInfo.ItemId; var info = new PlaybackStartInfo { ItemId = _itemId, CanSeek = false, QueueableMediaTypes = new List<string>() }; await ApiClient.ReportPlaybackStartAsync(info); } catch (HttpException ex) { Utils.HandleHttpException("VideoPageLoaded", ex, NavigationService, Log); } }
/// <summary> /// Reports the playback start. /// </summary> /// <param name="message">The message.</param> private void OnPlaybackStart(WebSocketMessageInfo message) { _logger.Debug("Received PlaybackStart message"); var session = GetSessionFromMessage(message); if (session != null && session.UserId.HasValue) { var vals = message.Data.Split('|'); var itemId = vals[0]; var queueableMediaTypes = string.Empty; var canSeek = true; if (vals.Length > 1) { canSeek = string.Equals(vals[1], "true", StringComparison.OrdinalIgnoreCase); } if (vals.Length > 2) { queueableMediaTypes = vals[2]; } var info = new PlaybackStartInfo { CanSeek = canSeek, ItemId = itemId, SessionId = session.Id, QueueableMediaTypes = queueableMediaTypes.Split(',').ToList() }; if (vals.Length > 3) { info.MediaSourceId = vals[3]; } if (vals.Length > 4 && !string.IsNullOrWhiteSpace(vals[4])) { info.AudioStreamIndex = int.Parse(vals[4], _usCulture); } if (vals.Length > 5 && !string.IsNullOrWhiteSpace(vals[5])) { info.SubtitleStreamIndex = int.Parse(vals[5], _usCulture); } _sessionManager.OnPlaybackStart(info); } }
private async Task InitiatePlayback(bool isResume, int? subtitleIndex = null) { Messenger.Default.Send(new NotificationMessage(Constants.Messages.ClearNowPlayingMsg)); EndTime = TimeSpan.Zero; var streamInfo = new StreamInfo(); //var query = new VideoStreamOptions(); switch (PlayerSourceType) { case PlayerSourceType.Playlist: case PlayerSourceType.Video: if (SelectedItem.VideoType != VideoType.VideoFile) { var result = MessageBox.Show(AppResources.MessageExperimentalVideo, AppResources.MessageExperimentalTitle, MessageBoxButton.OKCancel); if (result == MessageBoxResult.Cancel) { NavigationService.GoBack(); return; } } if (SelectedItem.UserData != null && isResume) { _startPositionTicks = SelectedItem.UserData.PlaybackPositionTicks; } if (_startPositionTicks == 0) { try { var items = await ApiClient.GetIntrosAsync(SelectedItem.Id, AuthenticationService.Current.LoggedInUserId); if (items != null && !items.Items.IsNullOrEmpty()) { if (PlaylistItems == null) { PlaylistItems = new List<BaseItemDto>(items.Items) {SelectedItem}; } else { var list = items.Items.ToList(); list.AddRange(PlaylistItems); PlaylistItems = list; } var firstItem = PlaylistItems.FirstOrDefault(); if (firstItem != null) SelectedItem = firstItem; } } catch (HttpException ex) { Log.ErrorException("GetIntros (Cinema Mode)", ex); } } streamInfo = CreateVideoStream(SelectedItem.Id, _startPositionTicks, SelectedItem.MediaSources, SelectedItem.Type.ToLower().Equals("channelvideoitem")); //query = CreateVideoStreamOptions(SelectedItem.Id, _startPositionTicks, SelectedItem.Type.ToLower().Equals("channelvideoitem")); if (SelectedItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(SelectedItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", SelectedItem.Type, SelectedItem.Name, SelectedItem.Id); break; case PlayerSourceType.Recording: //query = CreateVideoStreamOptions(RecordingItem.Id, _startPositionTicks); streamInfo = CreateVideoStream(RecordingItem.Id, _startPositionTicks); if (RecordingItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(RecordingItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", RecordingItem.Type, RecordingItem.Name, RecordingItem.Id); break; case PlayerSourceType.Programme: //query = CreateVideoStreamOptions(ProgrammeItem.ChannelId, _startPositionTicks, true); try { var channel = await ApiClient.GetItemAsync(ProgrammeItem.ChannelId, AuthenticationService.Current.LoggedInUserId); streamInfo = CreateVideoStream(ProgrammeItem.ChannelId, _startPositionTicks, channel.MediaSources, useHls: true); } catch (HttpException ex) { Utils.HandleHttpException(ex, "GetVideoChannel", NavigationService, Log); NavigationService.GoBack(); return; } if (ProgrammeItem.RunTimeTicks.HasValue) EndTime = TimeSpan.FromTicks(ProgrammeItem.RunTimeTicks.Value); Log.Info("Playing {0} [{1}] ({2})", ProgrammeItem.Type, ProgrammeItem.Name, ProgrammeItem.Id); break; } if (subtitleIndex.HasValue) { streamInfo.SubtitleStreamIndex = subtitleIndex.Value; } var url = streamInfo.ToUrl(ApiClient.GetApiUrl("/"), ApiClient.AccessToken); _streamInfo = streamInfo; //var url = PlayerSourceType == PlayerSourceType.Programme ? ApiClient.GetHlsVideoStreamUrl(query) : ApiClient.GetVideoStreamUrl(query); //Captions = GetSubtitles(SelectedItem); if (EndTime.Ticks > 0 && !IsDirectStream) { EndTime = TimeSpan.FromTicks(EndTime.Ticks - _startPositionTicks); } StopAudioPlayback(); if (_isResume && IsDirectStream) { _startFrom = TimeSpan.FromTicks(_startPositionTicks); } RaisePropertyChanged(() => IsDirectStream); VideoUrl = url; Debug.WriteLine(VideoUrl); Log.Debug(VideoUrl); try { Log.Info("Sending playback started message to the server."); _itemId = streamInfo.ItemId; var info = new PlaybackStartInfo { ItemId = _itemId, CanSeek = false, QueueableMediaTypes = new List<string>() }; await ApiClient.ReportPlaybackStartAsync(info); } catch (HttpException ex) { Utils.HandleHttpException("VideoPageLoaded", ex, NavigationService, Log); } }
/// <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, UserId = _apiClient.CurrentUserId, PositionTicks = e.EndingPositionTicks }; 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, UserId = _apiClient.CurrentUserId, IsSeekable = _mediaPlayer.CanSeek, QueueableMediaTypes = queueTypes.ToArray() }; await _apiClient.ReportPlaybackStartAsync(info); } catch (Exception ex) { _logger.ErrorException("Error sending playback start checking for {0}", ex, e.NewMedia.Name); } } }
/// <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> /// Handles the MediaChanged event of the _mediaPlayer control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="eventArgs">The <see cref="MediaChangeEventArgs"/> instance containing the event data.</param> async void _mediaPlayer_MediaChanged(object sender, MediaChangeEventArgs eventArgs) { if (eventArgs.PreviousMedia != null) { var apiClient = _connectionManager.GetApiClient(eventArgs.PreviousMedia); var stopInfo = new PlaybackStopInfo { ItemId = eventArgs.PreviousMedia.Id, PositionTicks = eventArgs.EndingPositionTicks }; // Have to test this for null because external players are currently not supplying this // Also some players will play in contexts not currently supported by common playback managers, e.g. direct play of folder rips, and iso-mounted media // Remove when implemented if (eventArgs.PreviousStreamInfo != null) { await _apiPlaybackManager.ReportPlaybackStopped(stopInfo, eventArgs.PreviousStreamInfo, eventArgs.PreviousMedia.ServerId, apiClient.CurrentUserId, false, apiClient); } else { await apiClient.ReportPlaybackStoppedAsync(stopInfo); } } if (eventArgs.NewMedia != null) { try { var queueTypes = _mediaPlayer.CanQueue ? new List<string> { eventArgs.NewMedia.MediaType } : new List<string> { }; var info = new PlaybackStartInfo { ItemId = eventArgs.NewMedia.Id, CanSeek = _mediaPlayer.CanSeek, QueueableMediaTypes = queueTypes.ToList(), // TODO: Remove this hardcoding PlayMethod = PlayMethod.DirectPlay }; var apiClient = _connectionManager.GetApiClient(eventArgs.NewMedia); await apiClient.ReportPlaybackStartAsync(info); } catch (Exception ex) { _logger.ErrorException("Error sending internalPlaybackManager start checking for {0}", ex, eventArgs.NewMedia.Name); } } }
/// <summary> /// Reports playback start /// </summary> /// <param name="info">The information.</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 ReportPlaybackStart(PlaybackStartInfo info, bool isOffline, IApiClient apiClient) { if (!isOffline) { await apiClient.ReportPlaybackStartAsync(info).ConfigureAwait(false); } }
private async Task InformOfPlayingTrack() { if (_apiClient == null) { return; } try { var track = BackgroundAudioPlayer.Instance.Track; if (track != null) { var info = new PlaybackStartInfo { CanSeek = false, ItemId = track.Tag, QueueableMediaTypes = new List<string>(), }; await _apiClient.ReportPlaybackStartAsync(info); } } catch (HttpException ex) { _logger.FatalException("InformOfPlayingTrack()", ex); } NotifyComplete(); }