浏览代码

update closing of streams

Luke Pulverenti 8 年之前
父节点
当前提交
76c7bfcb67
共有 36 个文件被更改,包括 475 次插入326 次删除
  1. 4 4
      MediaBrowser.Api/ApiEntryPoint.cs
  2. 42 4
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  3. 8 8
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  4. 1 1
      MediaBrowser.Api/Playback/MediaInfoService.cs
  5. 0 2
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  6. 1 1
      MediaBrowser.Api/Playback/StreamState.cs
  7. 2 0
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  8. 1 2
      MediaBrowser.Controller/Library/IMediaSourceManager.cs
  9. 1 2
      MediaBrowser.Controller/Library/IMediaSourceProvider.cs
  10. 1 2
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  11. 1 4
      MediaBrowser.Controller/LiveTv/ITunerHost.cs
  12. 17 6
      MediaBrowser.Controller/LiveTv/LiveStream.cs
  13. 52 1
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  14. 1 0
      MediaBrowser.Controller/LiveTv/TimerInfo.cs
  15. 1 1
      MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
  16. 5 2
      MediaBrowser.Model/Dto/MediaSourceInfo.cs
  17. 24 0
      MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
  18. 7 0
      MediaBrowser.Model/LiveTv/ProgramQuery.cs
  19. 6 0
      MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs
  20. 1 0
      MediaBrowser.Model/LiveTv/RecordingQuery.cs
  21. 1 1
      MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
  22. 32 27
      MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
  23. 3 13
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
  24. 90 53
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  25. 4 35
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  26. 1 0
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
  27. 58 32
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  28. 2 2
      MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  29. 2 2
      MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
  30. 0 8
      MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  31. 4 3
      MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
  32. 0 5
      MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  33. 93 20
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  34. 1 1
      MediaBrowser.Server.Implementations/Session/SessionManager.cs
  35. 1 1
      MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
  36. 7 83
      MediaBrowser.WebDashboard/Api/DashboardService.cs

+ 4 - 4
MediaBrowser.Api/ApiEntryPoint.cs

@@ -567,7 +567,7 @@ namespace MediaBrowser.Api
             {
                 try
                 {
-                    await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+                    await _mediaSourceManager.CloseLiveStream(job.LiveStreamId).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {
@@ -789,12 +789,12 @@ namespace MediaBrowser.Api
             {
                 if (KillTimer == null)
                 {
-                    Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+                    //Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
                     KillTimer = new Timer(callback, this, intervalMs, Timeout.Infinite);
                 }
                 else
                 {
-                    Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+                    //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
                     KillTimer.Change(intervalMs, Timeout.Infinite);
                 }
             }
@@ -813,7 +813,7 @@ namespace MediaBrowser.Api
                 {
                     var intervalMs = PingTimeout;
 
-                    Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
+                    //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
                     KillTimer.Change(intervalMs, Timeout.Infinite);
                 }
             }

+ 42 - 4
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -48,6 +48,21 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? StartIndex { get; set; }
 
+        [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsMovie { get; set; }
+
+        [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSeries { get; set; }
+
+        [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsNews { get; set; }
+
+        [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsKids { get; set; }
+
+        [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSports { get; set; }
+
         /// <summary>
         /// The maximum number of items to return
         /// </summary>
@@ -163,6 +178,7 @@ namespace MediaBrowser.Api.LiveTv
         public bool? IsSeries { get; set; }
         public bool? IsKids { get; set; }
         public bool? IsSports { get; set; }
+        public bool? IsNews { get; set; }
 
         public GetRecordings()
         {
@@ -309,6 +325,12 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsMovie { get; set; }
 
+        [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSeries { get; set; }
+
+        [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsNews { get; set; }
+
         [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsKids { get; set; }
 
@@ -380,15 +402,21 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool? HasAired { get; set; }
 
-        [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
-        public bool? IsSports { get; set; }
+        [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSeries { get; set; }
 
-        [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+        [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsMovie { get; set; }
 
-        [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+        [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsNews { get; set; }
+
+        [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
         public bool? IsKids { get; set; }
 
+        [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
+        public bool? IsSports { get; set; }
+
         [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? EnableImages { get; set; }
 
@@ -815,6 +843,11 @@ namespace MediaBrowser.Api.LiveTv
                 IsLiked = request.IsLiked,
                 IsDisliked = request.IsDisliked,
                 EnableFavoriteSorting = request.EnableFavoriteSorting,
+                IsMovie = request.IsMovie,
+                IsSeries = request.IsSeries,
+                IsNews = request.IsNews,
+                IsKids = request.IsKids,
+                IsSports = request.IsSports,
                 AddCurrentProgram = request.AddCurrentProgram
 
             }, CancellationToken.None).ConfigureAwait(false);
@@ -897,7 +930,9 @@ namespace MediaBrowser.Api.LiveTv
             query.Limit = request.Limit;
             query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
             query.SortOrder = request.SortOrder;
+            query.IsNews = request.IsNews;
             query.IsMovie = request.IsMovie;
+            query.IsSeries = request.IsSeries;
             query.IsKids = request.IsKids;
             query.IsSports = request.IsSports;
             query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
@@ -915,8 +950,10 @@ namespace MediaBrowser.Api.LiveTv
                 IsAiring = request.IsAiring,
                 Limit = request.Limit,
                 HasAired = request.HasAired,
+                IsSeries = request.IsSeries,
                 IsMovie = request.IsMovie,
                 IsKids = request.IsKids,
+                IsNews = request.IsNews,
                 IsSports = request.IsSports,
                 EnableTotalRecordCount = request.EnableTotalRecordCount
             };
@@ -948,6 +985,7 @@ namespace MediaBrowser.Api.LiveTv
                 IsInProgress = request.IsInProgress,
                 EnableTotalRecordCount = request.EnableTotalRecordCount,
                 IsMovie = request.IsMovie,
+                IsNews = request.IsNews,
                 IsSeries = request.IsSeries,
                 IsKids = request.IsKids,
                 IsSports = request.IsSports

+ 8 - 8
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -1861,14 +1861,14 @@ namespace MediaBrowser.Api.Playback
             MediaSourceInfo mediaSource = null;
             if (string.IsNullOrWhiteSpace(request.LiveStreamId))
             {
-                //TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ? 
-                //    ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
-                //    : null;
-
-                //if (currentJob != null)
-                //{
-                //    mediaSource = currentJob.MediaSource;
-                //}
+                TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ?
+                    ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId)
+                    : null;
+
+                if (currentJob != null)
+                {
+                    mediaSource = currentJob.MediaSource;
+                }
 
                 if (mediaSource == null)
                 {

+ 1 - 1
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback
 
         public void Post(CloseMediaSource request)
         {
-            var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None);
+            var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId);
             Task.WaitAll(task);
         }
 

+ 0 - 2
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -27,12 +27,10 @@ namespace MediaBrowser.Api.Playback.Progressive
     public abstract class BaseProgressiveStreamingService : BaseStreamingService
     {
         protected readonly IImageProcessor ImageProcessor;
-        protected readonly IHttpClient HttpClient;
 
         protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
         {
             ImageProcessor = imageProcessor;
-            HttpClient = httpClient;
         }
 
         /// <summary>

+ 1 - 1
MediaBrowser.Api/Playback/StreamState.cs

@@ -225,7 +225,7 @@ namespace MediaBrowser.Api.Playback
             {
                 try
                 {
-                    await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+                    await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {

+ 2 - 0
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -101,6 +101,8 @@ namespace MediaBrowser.Controller.Entities
         public bool? IsMovie { get; set; }
         public bool? IsSports { get; set; }
         public bool? IsKids { get; set; }
+        public bool? IsNews { get; set; }
+        public bool? IsSeries { get; set; }
 
         public int? MinPlayers { get; set; }
         public int? MaxPlayers { get; set; }

+ 1 - 2
MediaBrowser.Controller/Library/IMediaSourceManager.cs

@@ -92,8 +92,7 @@ namespace MediaBrowser.Controller.Library
         /// Closes the media source.
         /// </summary>
         /// <param name="id">The live stream identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task CloseLiveStream(string id, CancellationToken cancellationToken);
+        Task CloseLiveStream(string id);
     }
 }

+ 1 - 2
MediaBrowser.Controller/Library/IMediaSourceProvider.cs

@@ -28,8 +28,7 @@ namespace MediaBrowser.Controller.Library
         /// Closes the media source.
         /// </summary>
         /// <param name="liveStreamId">The live stream identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken);
+        Task CloseMediaSource(string liveStreamId);
     }
 }

+ 1 - 2
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -220,9 +220,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// Closes the live stream.
         /// </summary>
         /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task CloseLiveStream(string id, CancellationToken cancellationToken);
+        Task CloseLiveStream(string id);
 
         /// <summary>
         /// Gets the guide information.

+ 1 - 4
MediaBrowser.Controller/LiveTv/ITunerHost.cs

@@ -22,9 +22,8 @@ namespace MediaBrowser.Controller.LiveTv
         /// <summary>
         /// Gets the channels.
         /// </summary>
-        /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
-        Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken);
+        Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
         /// <summary>
         /// Gets the tuner infos.
         /// </summary>
@@ -46,8 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
         Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
-
-        string ApplyDuration(string streamPath, TimeSpan duration);
     }
     public interface IConfigurableTunerHost
     {

+ 17 - 6
MediaBrowser.Controller/LiveTv/LiveStream.cs

@@ -1,4 +1,5 @@
-using System.Threading;
+using System;
+using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Dto;
 
@@ -7,17 +8,27 @@ namespace MediaBrowser.Controller.LiveTv
     public class LiveStream
     {
         public MediaSourceInfo OriginalMediaSource { get; set; }
-        public MediaSourceInfo PublicMediaSource { get; set; }
-        public string Id { get; set; }
+        public MediaSourceInfo OpenedMediaSource { get; set; }
+        public DateTime DateOpened { get; set; }
+        public int ConsumerCount { get; set; }
+        public ITunerHost TunerHost { get; set; }
+        public string OriginalStreamId { get; set; }
 
         public LiveStream(MediaSourceInfo mediaSource)
         {
             OriginalMediaSource = mediaSource;
-            PublicMediaSource = mediaSource;
-            Id = mediaSource.Id;
+            OpenedMediaSource = mediaSource;
         }
 
-        public virtual Task Open(CancellationToken cancellationToken)
+        public async Task Open(CancellationToken cancellationToken)
+        {
+            await OpenInternal(cancellationToken).ConfigureAwait(false);
+            DateOpened = DateTime.UtcNow;
+
+            OpenedMediaSource.DateLiveStreamOpened = DateOpened;
+        }
+
+        protected virtual Task OpenInternal(CancellationToken cancellationToken)
         {
             return Task.FromResult(true);
         }

+ 52 - 1
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -9,7 +9,7 @@ using System.Runtime.Serialization;
 
 namespace MediaBrowser.Controller.LiveTv
 {
-    public class LiveTvChannel : BaseItem, IHasMediaSources
+    public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes
     {
         public override List<string> GetUserDataKeys()
         {
@@ -137,5 +137,56 @@ namespace MediaBrowser.Controller.LiveTv
         {
             return false;
         }
+
+        [IgnoreDataMember]
+        public bool IsMovie { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is sports.
+        /// </summary>
+        /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsSports { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is series.
+        /// </summary>
+        /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsSeries { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is live.
+        /// </summary>
+        /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsLive { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is news.
+        /// </summary>
+        /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsNews { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is kids.
+        /// </summary>
+        /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
+        public bool IsKids { get; set; }
+
+        [IgnoreDataMember]
+        public bool IsPremiere { get; set; }
+
+        [IgnoreDataMember]
+        public bool IsRepeat { get; set; }
+
+        /// <summary>
+        /// Gets or sets the episode title.
+        /// </summary>
+        /// <value>The episode title.</value>
+        [IgnoreDataMember]
+        public string EpisodeTitle { get; set; }
     }
 }

+ 1 - 0
MediaBrowser.Controller/LiveTv/TimerInfo.cs

@@ -101,6 +101,7 @@ namespace MediaBrowser.Controller.LiveTv
         public bool IsMovie { get; set; }
         public bool IsKids { get; set; }
         public bool IsSports { get; set; }
+        public bool IsNews { get; set; }
         public int? ProductionYear { get; set; }
         public string EpisodeTitle { get; set; }
         public DateTime? OriginalAirDate { get; set; }

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs

@@ -140,7 +140,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
             {
                 try
                 {
-                    await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+                    await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {

+ 5 - 2
MediaBrowser.Model/Dto/MediaSourceInfo.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Entities;
+using System;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.MediaInfo;
 using System.Collections.Generic;
@@ -52,7 +53,9 @@ namespace MediaBrowser.Model.Dto
         public string TranscodingUrl { get; set; }
         public string TranscodingSubProtocol { get; set; }
         public string TranscodingContainer { get; set; }
-        
+
+        public DateTime? DateLiveStreamOpened { get; set; }
+
         public MediaSourceInfo()
         {
             Formats = new List<string>();

+ 24 - 0
MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs

@@ -61,6 +61,30 @@ namespace MediaBrowser.Model.LiveTv
         public bool AddCurrentProgram { get; set; }
         public bool EnableUserData { get; set; }
 
+        /// <summary>
+        /// Used to specific whether to return news or not
+        /// </summary>
+        /// <remarks>If set to null, all programs will be returned</remarks>
+        public bool? IsNews { get; set; }
+
+        /// <summary>
+        /// Used to specific whether to return movies or not
+        /// </summary>
+        /// <remarks>If set to null, all programs will be returned</remarks>
+        public bool? IsMovie { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is kids.
+        /// </summary>
+        /// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value>
+        public bool? IsKids { get; set; }
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is sports.
+        /// </summary>
+        /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value>
+        public bool? IsSports { get; set; }
+        public bool? IsSeries { get; set; }
+
         public LiveTvChannelQuery()
         {
             EnableUserData = true;

+ 7 - 0
MediaBrowser.Model/LiveTv/ProgramQuery.cs

@@ -62,6 +62,12 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         public DateTime? MaxEndDate { get; set; }
 
+        /// <summary>
+        /// Used to specific whether to return news or not
+        /// </summary>
+        /// <remarks>If set to null, all programs will be returned</remarks>
+        public bool? IsNews { get; set; }
+
         /// <summary>
         /// Used to specific whether to return movies or not
         /// </summary>
@@ -83,6 +89,7 @@ namespace MediaBrowser.Model.LiveTv
         /// Skips over a given number of items within the results. Use for paging.
         /// </summary>
         public int? StartIndex { get; set; }
+        public bool? IsSeries { get; set; }
 
         /// <summary>
         /// Gets or sets a value indicating whether this instance has aired.

+ 6 - 0
MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs

@@ -45,11 +45,17 @@ namespace MediaBrowser.Model.LiveTv
         /// <value>The limit.</value>
         public int? Limit { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is movie.
+        /// </summary>
+        /// <value><c>null</c> if [is movie] contains no value, <c>true</c> if [is movie]; otherwise, <c>false</c>.</value>
+        public bool? IsNews { get; set; }
         /// <summary>
         /// Gets or sets a value indicating whether this instance is movie.
         /// </summary>
         /// <value><c>null</c> if [is movie] contains no value, <c>true</c> if [is movie]; otherwise, <c>false</c>.</value>
         public bool? IsMovie { get; set; }
+        public bool? IsSeries { get; set; }
         /// <summary>
         /// Gets or sets a value indicating whether this instance is kids.
         /// </summary>

+ 1 - 0
MediaBrowser.Model/LiveTv/RecordingQuery.cs

@@ -68,6 +68,7 @@ namespace MediaBrowser.Model.LiveTv
         /// <value>The fields.</value>
         public ItemFields[] Fields { get; set; }
         public bool? EnableImages { get; set; }
+        public bool? IsNews { get; set; }
         public bool? IsMovie { get; set; }
         public bool? IsSeries { get; set; }
         public bool? IsKids { get; set; }

+ 1 - 1
MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs

@@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Channels
             throw new NotImplementedException();
         }
 
-        public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken)
+        public Task CloseMediaSource(string liveStreamId)
         {
             throw new NotImplementedException();
         }

+ 32 - 27
MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs

@@ -355,7 +355,7 @@ namespace MediaBrowser.Server.Implementations.Library
             .ToList();
         }
 
-        private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams = new ConcurrentDictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
+        private readonly Dictionary<string, LiveStreamInfo> _openStreams = new Dictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
         private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
 
         public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken)
@@ -383,7 +383,7 @@ namespace MediaBrowser.Server.Implementations.Library
                     Id = mediaSource.LiveStreamId,
                     MediaSource = mediaSource
                 };
-                _openStreams.AddOrUpdate(mediaSource.LiveStreamId, info, (key, i) => info);
+                _openStreams[mediaSource.LiveStreamId] = info;
 
                 if (enableAutoClose)
                 {
@@ -421,7 +421,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException("id");
             }
 
-            _logger.Debug("Getting live stream {0}", id);
+            _logger.Debug("Getting already opened live stream {0}", id);
 
             await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 
@@ -465,17 +465,16 @@ namespace MediaBrowser.Server.Implementations.Library
             }
         }
 
-        private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId, CancellationToken cancellationToken)
+        private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId)
         {
             _logger.Info("Closing live stream {0} with provider {1}", streamId, provider.GetType().Name);
 
             try
             {
-                await provider.CloseMediaSource(streamId, cancellationToken).ConfigureAwait(false);
+                await provider.CloseMediaSource(streamId).ConfigureAwait(false);
             }
             catch (NotImplementedException)
             {
-
             }
             catch (Exception ex)
             {
@@ -483,37 +482,35 @@ namespace MediaBrowser.Server.Implementations.Library
             }
         }
 
-        public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
+        public async Task CloseLiveStream(string id)
         {
             if (string.IsNullOrWhiteSpace(id))
             {
                 throw new ArgumentNullException("id");
             }
 
-            await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+            await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false);
 
             try
             {
                 LiveStreamInfo current;
+
                 if (_openStreams.TryGetValue(id, out current))
                 {
+                    _openStreams.Remove(id);
+                    current.Closed = true;
+
                     if (current.MediaSource.RequiresClosing)
                     {
                         var tuple = GetProvider(id);
 
-                        await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
+                        await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false);
                     }
-                }
 
-                LiveStreamInfo removed;
-                if (_openStreams.TryRemove(id, out removed))
-                {
-                    removed.Closed = true;
-                }
-
-                if (_openStreams.Count == 0)
-                {
-                    StopCloseTimer();
+                    if (_openStreams.Count == 0)
+                    {
+                        StopCloseTimer();
+                    }
                 }
             }
             finally
@@ -565,10 +562,20 @@ namespace MediaBrowser.Server.Implementations.Library
 
         private async void CloseTimerCallback(object state)
         {
-            var infos = _openStreams
-                .Values
-                .Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge)
-                .ToList();
+            List<LiveStreamInfo> infos;
+            await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false);
+
+            try
+            {
+               infos = _openStreams
+                    .Values
+                    .Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge)
+                    .ToList();
+            }
+            finally
+            {
+                _liveStreamSemaphore.Release();
+            }
 
             foreach (var info in infos)
             {
@@ -576,7 +583,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 {
                     try
                     {
-                        await CloseLiveStream(info.Id, CancellationToken.None).ConfigureAwait(false);
+                        await CloseLiveStream(info.Id).ConfigureAwait(false);
                     }
                     catch (Exception ex)
                     {
@@ -608,12 +615,10 @@ namespace MediaBrowser.Server.Implementations.Library
                 {
                     foreach (var key in _openStreams.Keys.ToList())
                     {
-                        var task = CloseLiveStream(key, CancellationToken.None);
+                        var task = CloseLiveStream(key);
 
                         Task.WaitAll(task);
                     }
-
-                    _openStreams.Clear();
                 }
             }
         }

+ 3 - 13
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs

@@ -47,19 +47,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
                     _logger.Info("Copying recording stream to file {0}", targetFile);
 
-                    if (mediaSource.RunTimeTicks.HasValue)
-                    {
-                        // The media source already has a fixed duration
-                        // But add another stop 1 minute later just in case the recording gets stuck for any reason
-                        var durationToken = new CancellationTokenSource(duration.Add(TimeSpan.FromMinutes(1)));
-                        cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
-                    }
-                    else
-                    {
-                        // The media source if infinite so we need to handle stopping ourselves
-                        var durationToken = new CancellationTokenSource(duration);
-                        cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
-                    }
+                    // The media source if infinite so we need to handle stopping ourselves
+                    var durationToken = new CancellationTokenSource(duration);
+                    cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
 
                     await CopyUntilCancelled(response.Content, output, cancellationToken).ConfigureAwait(false);
                 }

+ 90 - 53
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -340,22 +340,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             _timerProvider.Delete(timer);
         }
 
-        private List<ChannelInfo> _channelCache = null;
         private async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken)
         {
-            if (enableCache && _channelCache != null)
-            {
-
-                return _channelCache.ToList();
-            }
-
             var list = new List<ChannelInfo>();
 
             foreach (var hostInstance in _liveTvManager.TunerHosts)
             {
                 try
                 {
-                    var channels = await hostInstance.GetChannels(cancellationToken).ConfigureAwait(false);
+                    var channels = await hostInstance.GetChannels(enableCache, cancellationToken).ConfigureAwait(false);
 
                     list.AddRange(channels);
                 }
@@ -388,7 +381,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 }
             }
 
-            _channelCache = list.ToList();
             return list;
         }
 
@@ -400,7 +392,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             {
                 try
                 {
-                    var channels = await hostInstance.GetChannels(cancellationToken).ConfigureAwait(false);
+                    var channels = await hostInstance.GetChannels(false, cancellationToken).ConfigureAwait(false);
 
                     list.AddRange(channels);
                 }
@@ -632,6 +624,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             existingTimer.Genres = updatedTimer.Genres;
             existingTimer.HomePageUrl = updatedTimer.HomePageUrl;
             existingTimer.IsKids = updatedTimer.IsKids;
+            existingTimer.IsNews = updatedTimer.IsNews;
             existingTimer.IsMovie = updatedTimer.IsMovie;
             existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries;
             existingTimer.IsSports = updatedTimer.IsSports;
@@ -836,33 +829,68 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         {
             var result = await GetChannelStreamInternal(channelId, streamId, cancellationToken).ConfigureAwait(false);
 
-            return result.Item1.PublicMediaSource;
+            return result.Item2;
+        }
+
+        private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId)
+        {
+            var json = _jsonSerializer.SerializeToString(mediaSource);
+            mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
+
+            mediaSource.Id = consumerId.ToString(CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
+
+            return mediaSource;
         }
 
-        private async Task<Tuple<LiveStream, ITunerHost>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
+        private async Task<Tuple<LiveStream, MediaSourceInfo, ITunerHost>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
         {
             _logger.Info("Streaming Channel " + channelId);
 
-            foreach (var hostInstance in _liveTvManager.TunerHosts)
+            await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+            var result = _liveStreams.Values.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
+
+            if (result != null)
             {
-                try
-                {
-                    var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
+                //result.ConsumerCount++;
 
-                    await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false);
-                    _liveStreams[result.Id] = result;
-                    _liveStreamsSemaphore.Release();
+                //_logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
 
-                    return new Tuple<LiveStream, ITunerHost>(result, hostInstance);
-                }
-                catch (FileNotFoundException)
-                {
-                }
-                catch (Exception e)
+                //_liveStreamsSemaphore.Release();
+                //return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1), result.TunerHost);
+            }
+
+            try
+            {
+                foreach (var hostInstance in _liveTvManager.TunerHosts)
                 {
-                    _logger.ErrorException("Error getting channel stream", e);
+                    try
+                    {
+                        result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
+
+                        _liveStreams[result.OpenedMediaSource.Id] = result;
+
+                        result.ConsumerCount++;
+                        result.TunerHost = hostInstance;
+                        result.OriginalStreamId = streamId;
+
+                        _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
+                            streamId, result.OpenedMediaSource.Id, result.OpenedMediaSource.LiveStreamId);
+
+                        return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, 0), hostInstance);
+                    }
+                    catch (FileNotFoundException)
+                    {
+                    }
+                    catch (OperationCanceledException)
+                    {
+                    }
                 }
             }
+            finally
+            {
+                _liveStreamsSemaphore.Release();
+            }
 
             throw new ApplicationException("Tuner not found.");
         }
@@ -896,25 +924,41 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
         public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
         {
-            await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false);
+            // Ignore the consumer id
+            id = id.Substring(id.IndexOf('_') + 1);
+
+            await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 
             try
             {
                 LiveStream stream;
                 if (_liveStreams.TryGetValue(id, out stream))
                 {
-                    _liveStreams.Remove(id);
+                    stream.ConsumerCount--;
 
-                    try
+                    _logger.Info("Live stream {0} consumer count is now {1}", id, stream.ConsumerCount);
+
+                    if (stream.ConsumerCount <= 0)
                     {
+                        _liveStreams.Remove(id);
+
+                        _logger.Info("Closing live stream {0}", id);
+
                         await stream.Close().ConfigureAwait(false);
                         _logger.Info("Live stream {0} closed successfully", id);
                     }
-                    catch (Exception ex)
-                    {
-                        _logger.ErrorException("Error closing live stream", ex);
-                    }
                 }
+                else
+                {
+                    _logger.Warn("Live stream not found: {0}, unable to close", id);
+                }
+            }
+            catch (OperationCanceledException)
+            {
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error closing live stream", ex);
             }
             finally
             {
@@ -1095,20 +1139,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             var recordPath = GetRecordingPath(timer, out seriesPath);
             var recordingStatus = RecordingStatus.New;
 
-            LiveStream liveStream = null;
+            string liveStreamId = null;
 
             try
             {
                 var allMediaSources =
                     await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
 
-                var liveStreamInfo =
-                    await
-                        GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
+                var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
                             .ConfigureAwait(false);
-                liveStream = liveStreamInfo.Item1;
-                var mediaStreamInfo = liveStreamInfo.Item1.PublicMediaSource;
-                var tunerHost = liveStreamInfo.Item2;
+
+                var mediaStreamInfo = liveStreamInfo.Item2;
+                liveStreamId = mediaStreamInfo.Id;
 
                 // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
                 //await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
@@ -1140,15 +1182,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                     EnforceKeepUpTo(timer);
                 };
 
-                var pathWithDuration = tunerHost.ApplyDuration(mediaStreamInfo.Path, duration);
-
-                // If it supports supplying duration via url
-                if (!string.Equals(pathWithDuration, mediaStreamInfo.Path, StringComparison.OrdinalIgnoreCase))
-                {
-                    mediaStreamInfo.Path = pathWithDuration;
-                    mediaStreamInfo.RunTimeTicks = duration.Ticks;
-                }
-
                 await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken)
                         .ConfigureAwait(false);
 
@@ -1166,11 +1199,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 recordingStatus = RecordingStatus.Error;
             }
 
-            if (liveStream != null)
+            if (!string.IsNullOrWhiteSpace(liveStreamId))
             {
                 try
                 {
-                    await CloseLiveStream(liveStream.Id, CancellationToken.None).ConfigureAwait(false);
+                    await CloseLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {
@@ -1251,7 +1284,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             }
         }
 
-        private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1,1);
+        private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1);
         private async Task DeleteLibraryItemsForTimers(List<TimerInfo> timers)
         {
             foreach (var timer in timers)
@@ -1295,7 +1328,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 }
                 catch (FileNotFoundException)
                 {
-                    
+
                 }
             }
 
@@ -1492,6 +1525,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                     {
                         AddGenre(timer.Genres, "Kids");
                     }
+                    if (timer.IsNews)
+                    {
+                        AddGenre(timer.Genres, "News");
+                    }
 
                     foreach (var genre in timer.Genres)
                     {

+ 4 - 35
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -71,38 +71,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             var durationToken = new CancellationTokenSource(duration);
             cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
 
-            await RecordFromFile(mediaSource, mediaSource.Path, targetFile, false, duration, onStarted, cancellationToken).ConfigureAwait(false);
+            await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false);
 
             _logger.Info("Recording completed to file {0}", targetFile);
         }
 
-        private async void DeleteTempFile(string path)
-        {
-            for (var i = 0; i < 10; i++)
-            {
-                try
-                {
-                    File.Delete(path);
-                    return;
-                }
-                catch (FileNotFoundException)
-                {
-                    return;
-                }
-                catch (DirectoryNotFoundException)
-                {
-                    return;
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error deleting recording temp file", ex);
-                }
-
-                await Task.Delay(1000).ConfigureAwait(false);
-            }
-        }
-
-        private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, bool deleteInputFileAfterCompletion, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+        private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
         {
             _targetPath = targetFile;
             _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
@@ -143,7 +117,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
             _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
 
-            process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile, deleteInputFileAfterCompletion);
+            process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
 
             process.Start();
 
@@ -252,7 +226,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         /// <summary>
         /// Processes the exited.
         /// </summary>
-        private void OnFfMpegProcessExited(Process process, string inputFile, bool deleteInputFileAfterCompletion)
+        private void OnFfMpegProcessExited(Process process, string inputFile)
         {
             _hasExited = true;
 
@@ -278,11 +252,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 _logger.Error("FFMpeg recording exited with an error for {0}.", _targetPath);
                 _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
             }
-
-            if (deleteInputFileAfterCompletion)
-            {
-                DeleteTempFile(inputFile);
-            }
         }
 
         private void DisposeLogStream()

+ 1 - 0
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs

@@ -42,6 +42,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             timerInfo.EpisodeNumber = programInfo.EpisodeNumber;
             timerInfo.IsMovie = programInfo.IsMovie;
             timerInfo.IsKids = programInfo.IsKids;
+            timerInfo.IsNews = programInfo.IsNews;
             timerInfo.IsSports = programInfo.IsSports;
             timerInfo.ProductionYear = programInfo.ProductionYear;
             timerInfo.EpisodeTitle = programInfo.EpisodeTitle;

+ 58 - 32
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -62,9 +62,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
         private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
 
-        private readonly ConcurrentDictionary<string, LiveStreamData> _openStreams =
-            new ConcurrentDictionary<string, LiveStreamData>();
-
         private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1);
 
         private readonly List<ITunerHost> _tunerHosts = new List<ITunerHost>();
@@ -153,6 +150,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var channels = _libraryManager.GetItemList(new InternalItemsQuery
             {
+                IsMovie = query.IsMovie,
+                IsNews = query.IsNews,
+                IsKids = query.IsKids,
+                IsSports = query.IsSports,
+                IsSeries = query.IsSeries,
                 IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
                 SortBy = new[] { ItemSortBy.SortName },
                 TopParentIds = new[] { topFolder.Id.ToString("N") }
@@ -407,15 +409,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info));
                 Normalize(info, service, isVideo);
 
-                var data = new LiveStreamData
-                {
-                    Info = info,
-                    IsChannel = isChannel,
-                    ItemId = id
-                };
-
-                _openStreams.AddOrUpdate(info.Id, data, (key, i) => data);
-
                 return info;
             }
             catch (Exception ex)
@@ -937,8 +930,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 MaxStartDate = query.MaxStartDate,
                 ChannelIds = query.ChannelIds,
                 IsMovie = query.IsMovie,
+                IsSeries = query.IsSeries,
                 IsSports = query.IsSports,
                 IsKids = query.IsKids,
+                IsNews = query.IsNews,
                 Genres = query.Genres,
                 StartIndex = query.StartIndex,
                 Limit = query.Limit,
@@ -985,7 +980,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             {
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 IsAiring = query.IsAiring,
+                IsNews = query.IsNews,
                 IsMovie = query.IsMovie,
+                IsSeries = query.IsSeries,
                 IsSports = query.IsSports,
                 IsKids = query.IsKids,
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
@@ -1014,7 +1011,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var programList = programs.ToList();
 
-            var factorChannelWatchCount = (query.IsAiring ?? false) || (query.IsKids ?? false) || (query.IsSports ?? false) || (query.IsMovie ?? false);
+            var factorChannelWatchCount = (query.IsAiring ?? false) || (query.IsKids ?? false) || (query.IsSports ?? false) || (query.IsMovie ?? false) || (query.IsNews ?? false) || (query.IsSeries ?? false);
 
             programs = programList.OrderBy(i => i.StartDate.Date)
                 .ThenByDescending(i => GetRecommendationScore(i, user.Id, factorChannelWatchCount))
@@ -1305,6 +1302,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                     var start = DateTime.UtcNow.AddHours(-1);
                     var end = start.AddDays(guideDays);
 
+                    var isMovie = false;
+                    var isSports = false;
+                    var isNews = false;
+                    var isKids = false;
+                    var iSSeries = false;
+
                     var channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false);
 
                     foreach (var program in channelPrograms)
@@ -1312,7 +1315,40 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                         var programItem = await GetProgram(program, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false);
 
                         programs.Add(programItem.Id);
+
+                        if (program.IsMovie)
+                        {
+                            isMovie = true;
+                        }
+
+                        if (program.IsSeries)
+                        {
+                            iSSeries = true;
+                        }
+
+                        if (program.IsSports)
+                        {
+                            isSports = true;
+                        }
+
+                        if (program.IsNews)
+                        {
+                            isNews = true;
+                        }
+
+                        if (program.IsKids)
+                        {
+                            isKids = true;
+                        }
                     }
+
+                    currentChannel.IsMovie = isMovie;
+                    currentChannel.IsNews = isNews;
+                    currentChannel.IsSports = isSports;
+                    currentChannel.IsKids = isKids;
+                    currentChannel.IsSeries = iSSeries;
+
+                    await currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
                 }
                 catch (OperationCanceledException)
                 {
@@ -1647,6 +1683,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 recordings = recordings.Where(i => i.IsMovie == val);
             }
 
+            if (query.IsNews.HasValue)
+            {
+                var val = query.IsNews.Value;
+                recordings = recordings.Where(i => i.IsNews == val);
+            }
+
             if (query.IsSeries.HasValue)
             {
                 var val = query.IsSeries.Value;
@@ -2444,9 +2486,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             internal bool IsChannel;
         }
 
-        public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
+        public async Task CloseLiveStream(string id)
         {
-            await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+            await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false);
 
             try
             {
@@ -2461,12 +2503,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
                 id = parts[1];
 
-                LiveStreamData data;
-                _openStreams.TryRemove(id, out data);
-
                 _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id);
 
-                await service.CloseLiveStream(id, cancellationToken).ConfigureAwait(false);
+                await service.CloseLiveStream(id, CancellationToken.None).ConfigureAwait(false);
             }
             catch (Exception ex)
             {
@@ -2500,7 +2539,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             Dispose(true);
         }
 
-        private readonly object _disposeLock = new object();
         private bool _isDisposed = false;
         /// <summary>
         /// Releases unmanaged and - optionally - managed resources.
@@ -2511,18 +2549,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             if (dispose)
             {
                 _isDisposed = true;
-
-                lock (_disposeLock)
-                {
-                    foreach (var stream in _openStreams.Values.ToList())
-                    {
-                        var task = CloseLiveStream(stream.Info.Id, CancellationToken.None);
-
-                        Task.WaitAll(task);
-                    }
-
-                    _openStreams.Clear();
-                }
             }
         }
 

+ 2 - 2
MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -204,9 +204,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             }
         }
 
-        public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken)
+        public Task CloseMediaSource(string liveStreamId)
         {
-            return _liveTvManager.CloseLiveStream(liveStreamId, cancellationToken);
+            return _liveTvManager.CloseLiveStream(liveStreamId);
         }
     }
 }

+ 2 - 2
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                 .ToList();
         }
 
-        public async Task<IEnumerable<ChannelInfo>> GetChannels(CancellationToken cancellationToken)
+        public async Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken)
         {
             var list = new List<ChannelInfo>();
 
@@ -83,7 +83,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
             {
                 try
                 {
-                    var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
+                    var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false);
                     var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
 
                     list.AddRange(newChannels);

+ 0 - 8
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -67,14 +67,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return id;
         }
 
-        public string ApplyDuration(string streamPath, TimeSpan duration)
-        {
-            streamPath += streamPath.IndexOf('?') == -1 ? "?" : "&";
-            streamPath += "duration=" + Convert.ToInt32(duration.TotalSeconds).ToString(CultureInfo.InvariantCulture);
-
-            return streamPath;
-        }
-
         private async Task<IEnumerable<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
         {
             var options = new HttpRequestOptions

+ 4 - 3
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs

@@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             _appHost = appHost;
         }
 
-        public override async Task Open(CancellationToken openCancellationToken)
+        protected override async Task OpenInternal(CancellationToken openCancellationToken)
         {
             _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
 
@@ -54,13 +54,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
             await taskCompletionSource.Task.ConfigureAwait(false);
 
-            PublicMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts";
+            OpenedMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts";
 
-            PublicMediaSource.Protocol = MediaProtocol.Http;
+            OpenedMediaSource.Protocol = MediaProtocol.Http;
         }
 
         public override Task Close()
         {
+            _logger.Info("Closing HDHR live stream");
             _liveStreamCancellationTokenSource.Cancel();
 
             return _liveStreamTaskCompletionSource.Task;

+ 0 - 5
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -153,10 +153,5 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
         {
             return Task.FromResult(true);
         }
-
-        public string ApplyDuration(string streamPath, TimeSpan duration)
-        {
-            return streamPath;
-        }
     }
 }

+ 93 - 20
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -2530,38 +2530,111 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 whereClauses.Add("IsOffline=@IsOffline");
                 cmd.Parameters.Add(cmd, "@IsOffline", DbType.Boolean).Value = query.IsOffline;
             }
-            if (query.IsMovie.HasValue)
+
+            var exclusiveProgramAttribtues = !(query.IsMovie ?? true) ||
+                                             !(query.IsSports ?? true) ||
+                                             !(query.IsKids ?? true) ||
+                                             !(query.IsNews ?? true) ||
+                                             !(query.IsSeries ?? true);
+
+            if (exclusiveProgramAttribtues)
             {
-                var alternateTypes = new List<string>();
-                if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
+                if (query.IsMovie.HasValue)
+                {
+                    var alternateTypes = new List<string>();
+                    if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
+                    {
+                        alternateTypes.Add(typeof(Movie).FullName);
+                    }
+                    if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
+                    {
+                        alternateTypes.Add(typeof(Trailer).FullName);
+                    }
+
+                    if (alternateTypes.Count == 0)
+                    {
+                        whereClauses.Add("IsMovie=@IsMovie");
+                        cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie;
+                    }
+                    else
+                    {
+                        whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)");
+                        cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie;
+                    }
+                }
+                if (query.IsSeries.HasValue)
                 {
-                    alternateTypes.Add(typeof(Movie).FullName);
+                    whereClauses.Add("IsSeries=@IsSeries");
+                    cmd.Parameters.Add(cmd, "@IsSeries", DbType.Boolean).Value = query.IsSeries;
                 }
-                if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
+                if (query.IsNews.HasValue)
                 {
-                    alternateTypes.Add(typeof(Trailer).FullName);
+                    whereClauses.Add("IsNews=@IsNews");
+                    cmd.Parameters.Add(cmd, "@IsNews", DbType.Boolean).Value = query.IsNews;
                 }
-
-                if (alternateTypes.Count == 0)
+                if (query.IsKids.HasValue)
                 {
-                    whereClauses.Add("IsMovie=@IsMovie");
+                    whereClauses.Add("IsKids=@IsKids");
+                    cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = query.IsKids;
                 }
-                else
+                if (query.IsSports.HasValue)
                 {
-                    whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)");
+                    whereClauses.Add("IsSports=@IsSports");
+                    cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = query.IsSports;
                 }
-                cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie;
             }
-            if (query.IsKids.HasValue)
-            {
-                whereClauses.Add("IsKids=@IsKids");
-                cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = query.IsKids;
-            }
-            if (query.IsSports.HasValue)
+            else
             {
-                whereClauses.Add("IsSports=@IsSports");
-                cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = query.IsSports;
+                var programAttribtues = new List<string>();
+                if (query.IsMovie ?? false)
+                {
+                    var alternateTypes = new List<string>();
+                    if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name))
+                    {
+                        alternateTypes.Add(typeof(Movie).FullName);
+                    }
+                    if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
+                    {
+                        alternateTypes.Add(typeof(Trailer).FullName);
+                    }
+
+                    if (alternateTypes.Count == 0)
+                    {
+                        programAttribtues.Add("IsMovie=@IsMovie");
+                    }
+                    else
+                    {
+                        programAttribtues.Add("(IsMovie is null OR IsMovie=@IsMovie)");
+                    }
+
+                    cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = true;
+                }
+                if (query.IsSports ?? false)
+                {
+                    programAttribtues.Add("IsSports=@IsSports");
+                    cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = true;
+                }
+                if (query.IsNews ?? false)
+                {
+                    programAttribtues.Add("IsNews=@IsNews");
+                    cmd.Parameters.Add(cmd, "@IsNews", DbType.Boolean).Value = true;
+                }
+                if (query.IsSeries ?? false)
+                {
+                    programAttribtues.Add("IsSeries=@IsSeries");
+                    cmd.Parameters.Add(cmd, "@IsSeries", DbType.Boolean).Value = true;
+                }
+                if (query.IsKids ?? false)
+                {
+                    programAttribtues.Add("IsKids=@IsKids");
+                    cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = true;
+                }
+                if (programAttribtues.Count > 0)
+                {
+                    whereClauses.Add("("+string.Join(" OR ", programAttribtues.ToArray())+")");
+                }
             }
+
             if (query.IsFolder.HasValue)
             {
                 whereClauses.Add("IsFolder=@IsFolder");

+ 1 - 1
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -818,7 +818,7 @@ namespace MediaBrowser.Server.Implementations.Session
             {
                 try
                 {
-                    await _mediaSourceManager.CloseLiveStream(info.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
+                    await _mediaSourceManager.CloseLiveStream(info.LiveStreamId).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {

+ 1 - 1
MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs

@@ -150,7 +150,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             }
         }
 
-        public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken)
+        public Task CloseMediaSource(string liveStreamId)
         {
             throw new NotImplementedException();
         }

+ 7 - 83
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -289,7 +289,7 @@ namespace MediaBrowser.WebDashboard.Api
             return list;
         }
 
-        private List<Tuple<string,bool>> GetDeployIgnoreFilenames()
+        private List<Tuple<string, bool>> GetDeployIgnoreFilenames()
         {
             var list = new List<Tuple<string, bool>>();
 
@@ -313,8 +313,11 @@ namespace MediaBrowser.WebDashboard.Api
 
         public async Task<object> Get(GetDashboardPackage request)
         {
-            var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath,
-                "webclient-dump");
+            var mode = request.Mode;
+
+            var path = string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase) ?
+                Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath, "webclient-dump")
+                : "C:\\dev\\emby-web-mobile\\src";
 
             try
             {
@@ -333,8 +336,6 @@ namespace MediaBrowser.WebDashboard.Api
 
             var appVersion = _appHost.ApplicationVersion.ToString();
 
-            var mode = request.Mode;
-
             // Try to trim the output size a bit
             var bowerPath = Path.Combine(path, "bower_components");
 
@@ -372,11 +373,6 @@ namespace MediaBrowser.WebDashboard.Api
                 // Delete things that are unneeded in an attempt to keep the output as trim as possible
                 _fileSystem.DeleteDirectory(Path.Combine(path, "css", "images", "tour"), true);
             }
-            else
-            {
-                MinifyCssDirectory(path);
-                MinifyJsDirectory(path);
-            }
 
             await DumpHtml(creator.DashboardUIPath, path, mode, culture, appVersion);
 
@@ -444,78 +440,6 @@ namespace MediaBrowser.WebDashboard.Api
             }
         }
 
-        private void MinifyCssDirectory(string path)
-        {
-            foreach (var file in Directory.GetFiles(path, "*.css", SearchOption.AllDirectories))
-            {
-                if (file.IndexOf(".min.", StringComparison.OrdinalIgnoreCase) != -1)
-                {
-                    continue;
-                }
-                if (file.IndexOf("bower_", StringComparison.OrdinalIgnoreCase) != -1)
-                {
-                    continue;
-                }
-
-                try
-                {
-                    var text = _fileSystem.ReadAllText(file, Encoding.UTF8);
-
-                    var result = new KristensenCssMinifier().Minify(text, false, Encoding.UTF8);
-
-                    if (result.Errors.Count > 0)
-                    {
-                        Logger.Error("Error minifying css: " + result.Errors[0].Message);
-                    }
-                    else
-                    {
-                        text = result.MinifiedContent;
-                        _fileSystem.WriteAllText(file, text, Encoding.UTF8);
-                    }
-                }
-                catch (Exception ex)
-                {
-                    Logger.ErrorException("Error minifying css", ex);
-                }
-            }
-        }
-
-        private void MinifyJsDirectory(string path)
-        {
-            foreach (var file in Directory.GetFiles(path, "*.js", SearchOption.AllDirectories))
-            {
-                if (file.IndexOf(".min.", StringComparison.OrdinalIgnoreCase) != -1)
-                {
-                    continue;
-                }
-                if (file.IndexOf("bower_", StringComparison.OrdinalIgnoreCase) != -1)
-                {
-                    continue;
-                }
-
-                try
-                {
-                    var text = _fileSystem.ReadAllText(file, Encoding.UTF8);
-
-                    var result = new CrockfordJsMinifier().Minify(text, false, Encoding.UTF8);
-
-                    if (result.Errors.Count > 0)
-                    {
-                        Logger.Error("Error minifying javascript: " + result.Errors[0].Message);
-                    }
-                    else
-                    {
-                        text = result.MinifiedContent;
-                        _fileSystem.WriteAllText(file, text, Encoding.UTF8);
-                    }
-                }
-                catch (Exception ex)
-                {
-                    Logger.ErrorException("Error minifying css", ex);
-                }
-            }
-        }
-
         private async Task DumpHtml(string source, string destination, string mode, string culture, string appVersion)
         {
             foreach (var file in Directory.GetFiles(source, "*", SearchOption.TopDirectoryOnly))
@@ -528,7 +452,7 @@ namespace MediaBrowser.WebDashboard.Api
 
         private async Task DumpFile(string resourceVirtualPath, string destinationFilePath, string mode, string culture, string appVersion)
         {
-            using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, mode, culture, appVersion, true).ConfigureAwait(false))
+            using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, mode, culture, appVersion, false).ConfigureAwait(false))
             {
                 using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
                 {