From d5b5c8e1a5548f2f1321f0f9a63d86919de0b01e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 11 Oct 2016 02:46:59 -0400 Subject: [PATCH] update display of active recordings --- .../Playback/BaseStreamingService.cs | 4 +- .../Entities/AggregateFolder.cs | 9 ++ .../Entities/Audio/MusicAlbum.cs | 16 ++- .../Entities/Audio/MusicArtist.cs | 9 ++ MediaBrowser.Controller/Entities/BaseItem.cs | 9 ++ .../Entities/CollectionFolder.cs | 9 ++ MediaBrowser.Controller/Entities/Folder.cs | 9 ++ .../Entities/GameSystem.cs | 9 ++ .../Entities/IHasUserData.cs | 2 + MediaBrowser.Controller/Entities/Photo.cs | 4 +- .../Entities/PhotoAlbum.cs | 9 ++ .../Entities/UserRootFolder.cs | 9 ++ MediaBrowser.Controller/Entities/UserView.cs | 9 ++ MediaBrowser.Controller/Entities/Video.cs | 9 ++ .../LiveTv/LiveTvVideoRecording.cs | 9 ++ .../MediaEncoding/IMediaEncoder.cs | 2 +- .../MediaEncoding/MediaInfoRequest.cs | 1 + MediaBrowser.Controller/Playlists/Playlist.cs | 9 ++ .../Encoder/BaseEncoder.cs | 4 +- .../Encoder/EncodingUtils.cs | 15 ++- .../Encoder/MediaEncoder.cs | 40 ++++++- MediaBrowser.Model/MediaInfo/MediaProtocol.cs | 3 +- .../Dto/DtoService.cs | 2 +- .../Library/UserDataManager.cs | 3 +- .../LiveTv/EmbyTV/EmbyTV.cs | 54 ++++++--- .../LiveTv/LiveStreamHelper.cs | 110 ++++++++++++++++++ .../LiveTv/LiveTvManager.cs | 19 +++ .../LiveTv/LiveTvMediaSourceProvider.cs | 88 +------------- ...MediaBrowser.Server.Implementations.csproj | 1 + .../Session/SessionManager.cs | 10 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 33 files changed, 361 insertions(+), 135 deletions(-) create mode 100644 MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index b5bef1ce9f..0e91c0b9e3 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -819,10 +819,10 @@ namespace MediaBrowser.Api.Playback { if (state.PlayableStreamFileNames.Count > 0) { - return MediaEncoder.GetProbeSizeArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol); + return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol); } - return MediaEncoder.GetProbeSizeArgument(new[] { state.MediaPath }, state.InputProtocol); + return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(new[] { state.MediaPath }, state.InputProtocol); } /// diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index 9709813dcd..7b769deb3e 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -45,6 +45,15 @@ namespace MediaBrowser.Controller.Entities return false; } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + /// /// The _virtual children /// diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 1f3b0c92a5..3f457d89a9 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -17,6 +17,9 @@ namespace MediaBrowser.Controller.Entities.Audio /// public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo, IMetadataContainer { + public List AlbumArtists { get; set; } + public List Artists { get; set; } + public MusicAlbum() { Artists = new List(); @@ -48,6 +51,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + [IgnoreDataMember] public override bool SupportsCumulativeRunTimeTicks { @@ -83,8 +95,6 @@ namespace MediaBrowser.Controller.Entities.Audio get { return false; } } - public List AlbumArtists { get; set; } - /// /// Gets the tracks. /// @@ -103,8 +113,6 @@ namespace MediaBrowser.Controller.Entities.Audio return Tracks; } - public List Artists { get; set; } - public override List GetUserDataKeys() { var list = base.GetUserDataKeys(); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 076a7031aa..a31f04fc4f 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -48,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.Audio get { return true; } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + public override bool CanDelete() { return !IsAccessedByName; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index cc4a8fdb9c..7eb84fc804 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -129,6 +129,15 @@ namespace MediaBrowser.Controller.Entities get { return false; } } + [IgnoreDataMember] + public virtual bool SupportsPlayedStatus + { + get + { + return false; + } + } + public bool DetectIsInMixedFolder() { if (SupportsIsInMixedFolderDetection) diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 04ba532631..41e277b7ca 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -38,6 +38,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + public override bool CanDelete() { return false; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index e5994fde59..993e36fd82 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -61,6 +61,15 @@ namespace MediaBrowser.Controller.Entities get { return false; } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return true; + } + } + /// /// Gets a value indicating whether this instance is folder. /// diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs index 1c09ee5075..9e00ab260d 100644 --- a/MediaBrowser.Controller/Entities/GameSystem.cs +++ b/MediaBrowser.Controller/Entities/GameSystem.cs @@ -26,6 +26,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + /// /// Gets or sets the game system. /// diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs index 2495b0ccd9..c21e170ae7 100644 --- a/MediaBrowser.Controller/Entities/IHasUserData.cs +++ b/MediaBrowser.Controller/Entities/IHasUserData.cs @@ -20,5 +20,7 @@ namespace MediaBrowser.Controller.Entities Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user); bool EnableRememberingTrackSelections { get; } + + bool SupportsPlayedStatus { get; } } } diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 41e25e4062..a42fce8bbd 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -29,13 +29,13 @@ namespace MediaBrowser.Controller.Entities { get { - return Album; + return AlbumEntity; } } [IgnoreDataMember] - public PhotoAlbum Album + public PhotoAlbum AlbumEntity { get { diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs index b0ddcfb8c6..cb5c5c4539 100644 --- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -16,6 +16,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + protected override bool GetBlockUnratedValue(UserPolicy config) { return config.BlockUnratedItems.Contains(UnratedItem.Other); diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index aff1f5e286..b67817846f 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -33,6 +33,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + private void ClearCache() { lock (_childIdsLock) diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 92f8e8a9d8..a689e0d091 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -44,6 +44,15 @@ namespace MediaBrowser.Controller.Entities return list; } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return false; + } + } + public override int GetChildCount(User user) { return GetChildren(user, true).Count(); diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index baf9293bf1..e83d1298e1 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -44,6 +44,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return true; + } + } + public override string CreatePresentationUniqueKey() { if (!string.IsNullOrWhiteSpace(PrimaryVersionId)) diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index e26dd6a773..635df5dc7b 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -54,6 +54,15 @@ namespace MediaBrowser.Controller.LiveTv } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return Status == RecordingStatus.Completed && base.SupportsPlayedStatus; + } + } + [IgnoreDataMember] public override LocationType LocationType { diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index f97fe95604..866a08aa1f 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// The input files. /// The protocol. /// System.String. - string GetProbeSizeArgument(string[] inputFiles, MediaProtocol protocol); + string GetProbeSizeAndAnalyzeDurationArgument(string[] inputFiles, MediaProtocol protocol); /// /// Gets the input argument. diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs index ca0c2fdbb4..9ff7567d48 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.MediaEncoding public IIsoMount MountedIso { get; set; } public VideoType VideoType { get; set; } public List PlayableStreamFileNames { get; set; } + public int AnalyzeDurationSections { get; set; } public MediaInfoRequest() { diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 654b97d7d4..8e0ac7ea88 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -31,6 +31,15 @@ namespace MediaBrowser.Controller.Playlists } } + [IgnoreDataMember] + public override bool SupportsPlayedStatus + { + get + { + return string.Equals(MediaType, "Video", StringComparison.OrdinalIgnoreCase); + } + } + [IgnoreDataMember] public override bool AlwaysScanInternalMetadataPath { diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index f1e2f7241d..c7b78aae3a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -431,10 +431,10 @@ namespace MediaBrowser.MediaEncoding.Encoder { if (state.PlayableStreamFileNames.Count > 0) { - return MediaEncoder.GetProbeSizeArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol); + return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol); } - return MediaEncoder.GetProbeSizeArgument(new[] { state.MediaPath }, state.InputProtocol); + return MediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(new[] { state.MediaPath }, state.InputProtocol); } /// diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index 5d0f1f0754..cec272b398 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -26,6 +26,12 @@ namespace MediaBrowser.MediaEncoding.Encoder return string.Format("\"{0}\"", url); } + if (protocol == MediaProtocol.Udp) + { + var url = inputFiles.First(); + + return string.Format("\"{0}\"", url); + } return GetConcatInputArgument(inputFiles); } @@ -74,9 +80,14 @@ namespace MediaBrowser.MediaEncoding.Encoder return path.Replace("\"", "\\\""); } - public static string GetProbeSizeArgument(bool isDvd) + public static string GetProbeSizeArgument(int numInputFiles) + { + return numInputFiles > 1 ? "-probesize 1G" : ""; + } + + public static string GetAnalyzeDurationArgument(int numInputFiles) { - return isDvd ? "-probesize 1G -analyzeduration 200M" : ""; + return numInputFiles > 1 ? "-analyzeduration 200M" : ""; } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 5c33450084..fc1444e1bb 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -426,10 +426,24 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); - var probeSizeArgument = GetProbeSizeArgument(inputFiles, request.Protocol); + var probeSize = EncodingUtils.GetProbeSizeArgument(inputFiles.Length); + string analyzeDuration; + + if (request.AnalyzeDurationSections > 0) + { + analyzeDuration = "-analyzeduration " + + (request.AnalyzeDurationSections*1000000).ToString(CultureInfo.InvariantCulture); + } + else + { + analyzeDuration = EncodingUtils.GetAnalyzeDurationArgument(inputFiles.Length); + } + + probeSize = probeSize + " " + analyzeDuration; + probeSize = probeSize.Trim(); return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, - probeSizeArgument, request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); + probeSize, request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); } /// @@ -450,9 +464,23 @@ namespace MediaBrowser.MediaEncoding.Encoder /// The input files. /// The protocol. /// System.String. - public string GetProbeSizeArgument(string[] inputFiles, MediaProtocol protocol) + public string GetProbeSizeAndAnalyzeDurationArgument(string[] inputFiles, MediaProtocol protocol) { - return EncodingUtils.GetProbeSizeArgument(inputFiles.Length > 1); + var results = new List(); + + var probeSize = EncodingUtils.GetProbeSizeArgument(inputFiles.Length); + var analyzeDuration = EncodingUtils.GetAnalyzeDurationArgument(inputFiles.Length); + + if (!string.IsNullOrWhiteSpace(probeSize)) + { + results.Add(probeSize); + } + + if (!string.IsNullOrWhiteSpace(analyzeDuration)) + { + results.Add(analyzeDuration); + } + return string.Join(" ", results.ToArray()); } /// @@ -871,7 +899,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) : string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg); - var probeSize = GetProbeSizeArgument(new[] { inputPath }, protocol); + var probeSize = GetProbeSizeAndAnalyzeDurationArgument(new[] { inputPath }, protocol); if (!string.IsNullOrEmpty(probeSize)) { @@ -982,7 +1010,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf); - var probeSize = GetProbeSizeArgument(new[] { inputArgument }, protocol); + var probeSize = GetProbeSizeAndAnalyzeDurationArgument(new[] { inputArgument }, protocol); if (!string.IsNullOrEmpty(probeSize)) { diff --git a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs index 880310814c..1474e6d964 100644 --- a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs +++ b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs @@ -5,6 +5,7 @@ namespace MediaBrowser.Model.MediaInfo File = 0, Http = 1, Rtmp = 2, - Rtsp = 3 + Rtsp = 3, + Udp = 4 } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index b878226deb..a0f7aa999e 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -598,7 +598,7 @@ namespace MediaBrowser.Server.Implementations.Dto dto.Altitude = item.Altitude; dto.IsoSpeedRating = item.IsoSpeedRating; - var album = item.Album; + var album = item.AlbumEntity; if (album != null) { diff --git a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs index ec8ac1a42f..139ea0ab61 100644 --- a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs @@ -269,9 +269,10 @@ namespace MediaBrowser.Server.Implementations.Library positionTicks = 0; } - if (item is Audio) + if (!item.SupportsPlayedStatus) { positionTicks = 0; + data.Played = false; } data.PlaybackPositionTicks = positionTicks; diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 9781775d59..136a9e195c 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -27,6 +27,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml; using CommonIO; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; @@ -59,8 +60,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public static EmbyTV Current; - public event EventHandler DataSourceChanged { add { } remove { } } - public event EventHandler RecordingStatusChanged { add { } remove { } } + public event EventHandler DataSourceChanged; + public event EventHandler RecordingStatusChanged; private readonly ConcurrentDictionary _activeRecordings = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); @@ -1009,7 +1010,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new NotImplementedException(); } - public Task> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken) + public async Task> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken) { ActiveRecordingInfo info; @@ -1017,22 +1018,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (_activeRecordings.TryGetValue(recordingId, out info)) { - return Task.FromResult(new List + var stream = new MediaSourceInfo + { + Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream", + Id = recordingId, + SupportsDirectPlay = false, + SupportsDirectStream = true, + SupportsTranscoding = true, + IsInfiniteStream = true, + RequiresOpening = false, + RequiresClosing = false, + Protocol = Model.MediaInfo.MediaProtocol.Http, + BufferMs = 0 + }; + + var isAudio = false; + await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false); + + return new List { - new MediaSourceInfo - { - Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream", - Id = recordingId, - SupportsDirectPlay = false, - SupportsDirectStream = true, - SupportsTranscoding = true, - IsInfiniteStream = true, - RequiresOpening = false, - RequiresClosing = false, - Protocol = Model.MediaInfo.MediaProtocol.Http, - BufferMs = 0 - } - }); + stream + }; } throw new FileNotFoundException(); @@ -1258,6 +1264,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV string liveStreamId = null; + OnRecordingStatusChanged(); + try { var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); @@ -1353,6 +1361,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { _timerProvider.Delete(timer); } + + OnRecordingStatusChanged(); + } + + private void OnRecordingStatusChanged() + { + EventHelper.FireEventIfNotNull(RecordingStatusChanged, this, new RecordingStatusChangedEventArgs + { + + }, _logger); } private async void EnforceKeepUpTo(TimerInfo timer) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs new file mode 100644 index 0000000000..336c32baef --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs @@ -0,0 +1,110 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Server.Implementations.LiveTv +{ + public class LiveStreamHelper + { + private readonly IMediaEncoder _mediaEncoder; + private readonly ILogger _logger; + + public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger) + { + _mediaEncoder = mediaEncoder; + _logger = logger; + } + + public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) + { + var originalRuntime = mediaSource.RunTimeTicks; + + var now = DateTime.UtcNow; + + var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest + { + InputPath = mediaSource.Path, + Protocol = mediaSource.Protocol, + MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, + ExtractChapters = false, + AnalyzeDurationSections = 2 + + }, cancellationToken).ConfigureAwait(false); + + _logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture)); + + mediaSource.Bitrate = info.Bitrate; + mediaSource.Container = info.Container; + mediaSource.Formats = info.Formats; + mediaSource.MediaStreams = info.MediaStreams; + mediaSource.RunTimeTicks = info.RunTimeTicks; + mediaSource.Size = info.Size; + mediaSource.Timestamp = info.Timestamp; + mediaSource.Video3DFormat = info.Video3DFormat; + mediaSource.VideoType = info.VideoType; + + mediaSource.DefaultSubtitleStreamIndex = null; + + // Null this out so that it will be treated like a live stream + if (!originalRuntime.HasValue) + { + mediaSource.RunTimeTicks = null; + } + + var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio); + + if (audioStream == null || audioStream.Index == -1) + { + mediaSource.DefaultAudioStreamIndex = null; + } + else + { + mediaSource.DefaultAudioStreamIndex = audioStream.Index; + } + + var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video); + if (videoStream != null) + { + if (!videoStream.BitRate.HasValue) + { + var width = videoStream.Width ?? 1920; + + if (width >= 1900) + { + videoStream.BitRate = 8000000; + } + + else if (width >= 1260) + { + videoStream.BitRate = 3000000; + } + + else if (width >= 700) + { + videoStream.BitRate = 1000000; + } + } + + // This is coming up false and preventing stream copy + videoStream.IsAVC = null; + } + + // Try to estimate this + if (!mediaSource.Bitrate.HasValue) + { + var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum(); + + if (total > 0) + { + mediaSource.Bitrate = total; + } + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index a295320ec7..bac9789b5e 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -121,9 +121,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv foreach (var service in _services) { service.DataSourceChanged += service_DataSourceChanged; + service.RecordingStatusChanged += Service_RecordingStatusChanged; } } + private void Service_RecordingStatusChanged(object sender, RecordingStatusChangedEventArgs e) + { + _lastRecordingRefreshTime = DateTime.MinValue; + } + public List TunerHosts { get { return _tunerHosts; } @@ -2299,6 +2305,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false); + info.RecordAnyChannel = true; + info.RecordAnyTime = true; + info.Days = new List + { + DayOfWeek.Sunday, + DayOfWeek.Monday, + DayOfWeek.Tuesday, + DayOfWeek.Wednesday, + DayOfWeek.Thursday, + DayOfWeek.Friday, + DayOfWeek.Saturday + }; + info.Id = null; return new Tuple(info, service); diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 521f33e1c9..a62796036d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -146,7 +146,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } else { - await AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false); + await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) @@ -216,92 +216,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } - private async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) - { - var originalRuntime = mediaSource.RunTimeTicks; - - var now = DateTime.UtcNow; - - var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest - { - InputPath = mediaSource.Path, - Protocol = mediaSource.Protocol, - MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video, - ExtractChapters = false - - }, cancellationToken).ConfigureAwait(false); - - _logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture)); - - mediaSource.Bitrate = info.Bitrate; - mediaSource.Container = info.Container; - mediaSource.Formats = info.Formats; - mediaSource.MediaStreams = info.MediaStreams; - mediaSource.RunTimeTicks = info.RunTimeTicks; - mediaSource.Size = info.Size; - mediaSource.Timestamp = info.Timestamp; - mediaSource.Video3DFormat = info.Video3DFormat; - mediaSource.VideoType = info.VideoType; - - mediaSource.DefaultSubtitleStreamIndex = null; - - // Null this out so that it will be treated like a live stream - if (!originalRuntime.HasValue) - { - mediaSource.RunTimeTicks = null; - } - - var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio); - - if (audioStream == null || audioStream.Index == -1) - { - mediaSource.DefaultAudioStreamIndex = null; - } - else - { - mediaSource.DefaultAudioStreamIndex = audioStream.Index; - } - - var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video); - if (videoStream != null) - { - if (!videoStream.BitRate.HasValue) - { - var width = videoStream.Width ?? 1920; - - if (width >= 1900) - { - videoStream.BitRate = 8000000; - } - - else if (width >= 1260) - { - videoStream.BitRate = 3000000; - } - - else if (width >= 700) - { - videoStream.BitRate = 1000000; - } - } - - // This is coming up false and preventing stream copy - videoStream.IsAVC = null; - } - - // Try to estimate this - if (!mediaSource.Bitrate.HasValue) - { - var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum(); - - if (total > 0) - { - mediaSource.Bitrate = total; - } - } - } - - public Task CloseMediaSource(string liveStreamId) { return _liveTvManager.CloseLiveStream(liveStreamId); diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 73e6ce1a56..f01a107df5 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -234,6 +234,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 2fcc765889..e898a6abdd 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -633,10 +633,14 @@ namespace MediaBrowser.Server.Implementations.Session data.PlayCount++; data.LastPlayedDate = DateTime.UtcNow; - if (!(item is Video)) + if (!(item is Video) && item.SupportsPlayedStatus) { data.Played = true; } + else + { + data.Played = false; + } await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None).ConfigureAwait(false); } @@ -847,11 +851,11 @@ namespace MediaBrowser.Server.Implementations.Session { playedToCompletion = _userDataManager.UpdatePlayState(item, data, positionTicks.Value); } - else + 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.Played = item.SupportsPlayedStatus; data.PlaybackPositionTicks = 0; playedToCompletion = true; } diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 2e4584fd4f..1259a759b7 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.661 + 3.0.662 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption. Copyright © Emby 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 56c616bd1b..931db69fab 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.661 + 3.0.662 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 98a070c785..b3d929f597 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.661 + 3.0.662 Media Browser.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - +