From 151d88f20db2d6fbb8ea901d84d25d26ccbb136c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 12 Sep 2016 14:10:09 -0400 Subject: encapsulate path substitution --- MediaBrowser.Server.Implementations/Library/LibraryManager.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 442e2ebe5..bd408c9d3 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2527,6 +2527,16 @@ namespace MediaBrowser.Server.Implementations.Library }).OrderBy(i => i.Path).ToList(); } + public string GetPathAfterNetworkSubstitution(string path) + { + foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) + { + path = SubstitutePath(path, map.From, map.To); + } + + return path; + } + public string SubstitutePath(string path, string from, string to) { if (string.IsNullOrWhiteSpace(path)) -- cgit v1.2.3 From d95c0e8324284b7391fb1d6a275c8af18508141b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 14 Sep 2016 17:34:19 -0400 Subject: fix web socket session creation --- MediaBrowser.Api/ConnectService.cs | 34 ++++++++++++++++++---- .../Session/AuthenticationRequest.cs | 1 + MediaBrowser.Providers/Manager/MetadataService.cs | 2 -- .../Library/LibraryManager.cs | 12 ++++---- .../Session/SessionManager.cs | 15 ++++++++-- 5 files changed, 49 insertions(+), 15 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs index 4bcd33d9e..494a6e756 100644 --- a/MediaBrowser.Api/ConnectService.cs +++ b/MediaBrowser.Api/ConnectService.cs @@ -7,6 +7,7 @@ using ServiceStack; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using MediaBrowser.Controller.Session; namespace MediaBrowser.Api { @@ -76,12 +77,12 @@ namespace MediaBrowser.Api public class ConnectService : BaseApiService { private readonly IConnectManager _connectManager; - private readonly IUserManager _userManager; + private readonly ISessionManager _sessionManager; - public ConnectService(IConnectManager connectManager, IUserManager userManager) + public ConnectService(IConnectManager connectManager, ISessionManager sessionManager) { _connectManager = connectManager; - _userManager = userManager; + _sessionManager = sessionManager; } public object Post(CreateConnectLink request) @@ -141,10 +142,33 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException(); } + var auth = AuthorizationContext.GetAuthorizationInfo(Request); + + if (string.IsNullOrWhiteSpace(auth.Client)) + { + return ToOptimizedResult(new ConnectAuthenticationExchangeResult + { + AccessToken = user.ConnectAccessKey, + LocalUserId = user.Id.ToString("N") + }); + } + + var session = await _sessionManager.CreateNewSession(new AuthenticationRequest + { + App = auth.Client, + AppVersion = auth.Version, + DeviceId = auth.DeviceId, + DeviceName = auth.Device, + RemoteEndPoint = Request.RemoteIp, + Username = user.Name, + UserId = user.Id.ToString("N") + + }).ConfigureAwait(false); + return ToOptimizedResult(new ConnectAuthenticationExchangeResult { - AccessToken = user.ConnectAccessKey, - LocalUserId = user.Id.ToString("N") + AccessToken = session.AccessToken, + LocalUserId = session.User.Id }); } } diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs index bfd7f928b..362f5b2b9 100644 --- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs +++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs @@ -4,6 +4,7 @@ namespace MediaBrowser.Controller.Session public class AuthenticationRequest { public string Username { get; set; } + public string UserId { get; set; } public string PasswordSha1 { get; set; } public string PasswordMd5 { get; set; } public string App { get; set; } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 85e25d2b6..e16e76bfc 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -547,8 +547,6 @@ namespace MediaBrowser.Providers.Manager } catch (Exception ex) { - refreshResult.Failures++; - Logger.ErrorException("Error in {0}", ex, provider.Name); // If a local provider fails, consider that a failure diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index bd408c9d3..1f8c77953 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -695,7 +695,7 @@ namespace MediaBrowser.Server.Implementations.Library public IEnumerable ResolvePaths(IEnumerable files, IDirectoryService directoryService, - Folder parent, + Folder parent, LibraryOptions libraryOptions, string collectionType, IItemResolver[] resolvers) @@ -1490,10 +1490,10 @@ namespace MediaBrowser.Server.Implementations.Library private void AddUserToQuery(InternalItemsQuery query, User user) { - if (query.AncestorIds.Length == 0 && - !query.ParentId.HasValue && - query.ChannelIds.Length == 0 && - query.TopParentIds.Length == 0 && + if (query.AncestorIds.Length == 0 && + !query.ParentId.HasValue && + query.ChannelIds.Length == 0 && + query.TopParentIds.Length == 0 && string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey) && query.ItemIds.Length == 0) { @@ -2552,7 +2552,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("to"); } - var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase); + var newPath = path.Replace(from.Trim(), to.Trim(), StringComparison.OrdinalIgnoreCase); if (!string.Equals(newPath, path)) { diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index b21fcddd4..afcdf9d90 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1341,8 +1341,19 @@ namespace MediaBrowser.Server.Implementations.Session private async Task AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword) { - var user = _userManager.Users - .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase)); + User user = null; + if (!string.IsNullOrWhiteSpace(request.UserId)) + { + var idGuid = new Guid(request.UserId); + user = _userManager.Users + .FirstOrDefault(i => i.Id == idGuid); + } + + if (user == null) + { + user = _userManager.Users + .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase)); + } if (user != null && !string.IsNullOrWhiteSpace(request.DeviceId)) { -- cgit v1.2.3 From cc6680f48f76278fecc0efc6b6732effe6fe1e2a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 15 Sep 2016 19:19:27 -0400 Subject: save nfo during recording process --- .../Library/Resolvers/TV/SeriesResolver.cs | 5 +- .../LiveTv/EmbyTV/EmbyTV.cs | 83 ++++++++++++++++++---- .../LiveTv/Listings/SchedulesDirect.cs | 49 ++++++++----- 3 files changed, 102 insertions(+), 35 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 4c6254330..207486f26 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -54,7 +54,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { if (args.IsDirectory) { - if (args.HasParent()) + if (args.HasParent() || args.HasParent()) { return null; } @@ -80,7 +80,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { return null; } - if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false)) + if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false) || + args.ContainsFileSystemEntryByName("tvshow.nfo")) { return new Series { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3eb032ba8..b508110cf 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -22,12 +22,15 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Xml; using CommonIO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.FileOrganization; using Microsoft.Win32; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV @@ -867,10 +870,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private string GetRecordingPath(TimerInfo timer) + private string GetRecordingPath(TimerInfo timer, out string seriesPath) { var recordPath = RecordingPath; var config = GetConfiguration(); + seriesPath = null; if (timer.IsProgramSeries) { @@ -892,6 +896,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV // Can't use the year here in the folder name because it is the year of the episode, not the series. recordPath = Path.Combine(recordPath, folderName); + seriesPath = recordPath; + if (timer.SeasonNumber.HasValue) { folderName = string.Format("Season {0}", timer.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); @@ -980,7 +986,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer); } - var recordPath = GetRecordingPath(timer); + string seriesPath = null; + var recordPath = GetRecordingPath(timer, out seriesPath); var recordingStatus = RecordingStatus.New; var isResourceOpen = false; SemaphoreSlim semaphore = null; @@ -1064,7 +1071,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV timer.Status = RecordingStatus.Completed; _timerProvider.Delete(timer); - OnSuccessfulRecording(timer.IsProgramSeries, recordPath); + OnSuccessfulRecording(timer, recordPath, seriesPath); } else if (DateTime.UtcNow < timer.EndDate) { @@ -1132,26 +1139,72 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return new DirectRecorder(_logger, _httpClient, _fileSystem); } - private async void OnSuccessfulRecording(bool isSeries, string path) + private async void OnSuccessfulRecording(TimerInfo timer, string path, string seriesPath) { - if (GetConfiguration().EnableAutoOrganize) + if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize) { - if (isSeries) + try { - try - { - // this is to account for the library monitor holding a lock for additional time after the change is complete. - // ideally this shouldn't be hard-coded - await Task.Delay(30000).ConfigureAwait(false); + // this is to account for the library monitor holding a lock for additional time after the change is complete. + // ideally this shouldn't be hard-coded + await Task.Delay(30000).ConfigureAwait(false); + + var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); - var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); + var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false); - var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false); + if (result.Status == FileSortingStatus.Success) + { + return; } - catch (Exception ex) + } + catch (Exception ex) + { + _logger.ErrorException("Error processing new recording", ex); + } + } + + SaveNfo(timer, path, seriesPath); + } + + private void SaveNfo(TimerInfo timer, string recordingPath, string seriesPath) + { + if (timer.IsProgramSeries) + { + SaveSeriesNfo(timer, recordingPath, seriesPath); + } + } + + private void SaveSeriesNfo(TimerInfo timer, string recordingPath, string seriesPath) + { + var nfoPath = Path.Combine(seriesPath, "tvshow.nfo"); + + if (File.Exists(nfoPath)) + { + return; + } + + using (var stream = _fileSystem.GetFileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + var settings = new XmlWriterSettings + { + Indent = true, + Encoding = Encoding.UTF8, + CloseOutput = false + }; + + using (XmlWriter writer = XmlWriter.Create(stream, settings)) + { + writer.WriteStartDocument(true); + writer.WriteStartElement("tvshow"); + + if (!string.IsNullOrWhiteSpace(timer.Name)) { - _logger.ErrorException("Error processing new recording", ex); + writer.WriteElementString("title", timer.Name); } + + writer.WriteEndElement(); + writer.WriteEndDocument(); } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index d863c4587..c30263388 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -171,22 +171,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings var data = images[imageIndex].data ?? new List(); data = data.OrderByDescending(GetSizeOrder).ToList(); - programEntry.primaryImage = GetProgramImage(ApiUrl, data, "Logo", true); + programEntry.primaryImage = GetProgramImage(ApiUrl, data, "Logo", true, 1280); //programEntry.thumbImage = GetProgramImage(ApiUrl, data, "Iconic", false); //programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ?? // GetProgramImage(ApiUrl, data, "Banner-L1", false) ?? // GetProgramImage(ApiUrl, data, "Banner-LO", false) ?? // GetProgramImage(ApiUrl, data, "Banner-LOT", false); - - if (!string.IsNullOrWhiteSpace(programEntry.thumbImage)) - { - var b = true; - } - - if (!string.IsNullOrWhiteSpace(programEntry.bannerImage)) - { - var b = true; - } } } @@ -201,10 +191,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings private int GetSizeOrder(ScheduleDirect.ImageData image) { - if (!string.IsNullOrWhiteSpace(image.size)) + if (!string.IsNullOrWhiteSpace(image.height)) { int value; - if (int.TryParse(image.size, out value)) + if (int.TryParse(image.height, out value)) { return value; } @@ -517,20 +507,43 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings return date; } - private string GetProgramImage(string apiUrl, List images, string category, bool returnDefaultImage) + private string GetProgramImage(string apiUrl, List images, string category, bool returnDefaultImage, int desiredWidth) { string url = null; - var logoIndex = images.FindIndex(i => string.Equals(i.category, category, StringComparison.OrdinalIgnoreCase)); - if (logoIndex == -1) + var matches = images + .Where(i => string.Equals(i.category, category, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (matches.Count == 0) { if (!returnDefaultImage) { return null; } - logoIndex = 0; + matches = images; } - var uri = images[logoIndex].uri; + + var match = matches.FirstOrDefault(i => + { + if (!string.IsNullOrWhiteSpace(i.width)) + { + int value; + if (int.TryParse(i.width, out value)) + { + return value <= desiredWidth; + } + } + + return false; + }) ?? matches.FirstOrDefault(); + + if (match == null) + { + return null; + } + + var uri = match.uri; if (!string.IsNullOrWhiteSpace(uri)) { -- cgit v1.2.3 From 05edb9f4bdcdf7733bcd7e6e74a876bbc2b5c064 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 18 Sep 2016 16:38:38 -0400 Subject: update timeshifting --- MediaBrowser.Api/ApiEntryPoint.cs | 13 ++++++++++- MediaBrowser.Api/Playback/BaseStreamingService.cs | 26 ++++++++++++++++------ MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 15 +++---------- MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs | 4 +--- MediaBrowser.Api/Playback/StreamState.cs | 4 ++++ MediaBrowser.Api/Subtitles/SubtitleService.cs | 2 +- .../Library/IMediaSourceManager.cs | 5 +---- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 7 +++++- .../Library/MediaSourceManager.cs | 22 +++++++++++++++++- .../LiveTv/EmbyTV/EmbyTV.cs | 6 ++--- .../LiveTv/TunerHosts/BaseTunerHost.cs | 3 +-- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 17 ++++++++------ .../Session/SessionManager.cs | 8 +++---- 13 files changed, 86 insertions(+), 46 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 7c5f7cde4..214fb7488 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Api { @@ -187,7 +188,8 @@ namespace MediaBrowser.Api CancellationTokenSource = cancellationTokenSource, Id = transcodingJobId, PlaySessionId = playSessionId, - LiveStreamId = liveStreamId + LiveStreamId = liveStreamId, + MediaSource = state.MediaSource }; _activeTranscodingJobs.Add(job); @@ -281,6 +283,14 @@ namespace MediaBrowser.Api } } + public TranscodingJob GetTranscodingJob(string playSessionId) + { + lock (_activeTranscodingJobs) + { + return _activeTranscodingJobs.FirstOrDefault(j => string.Equals(j.PlaySessionId, playSessionId, StringComparison.OrdinalIgnoreCase)); + } + } + /// /// Called when [transcode begin request]. /// @@ -656,6 +666,7 @@ namespace MediaBrowser.Api /// Gets or sets the path. /// /// The path. + public MediaSourceInfo MediaSource { get; set; } public string Path { get; set; } /// /// Gets or sets the type. diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 4a62da6f6..eee6bfb65 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1847,18 +1847,30 @@ namespace MediaBrowser.Api.Playback var archivable = item as IArchivable; state.IsInputArchive = archivable != null && archivable.IsArchive; - MediaSourceInfo mediaSource; + MediaSourceInfo mediaSource = null; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { - var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList(); + //TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ? + // ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId) + // : null; - mediaSource = string.IsNullOrEmpty(request.MediaSourceId) - ? mediaSources.First() - : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)); + //if (currentJob != null) + //{ + // mediaSource = currentJob.MediaSource; + //} - if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase)) + if (mediaSource == null) { - mediaSource = mediaSources.First(); + var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(request.Id, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false)).ToList(); + + mediaSource = string.IsNullOrEmpty(request.MediaSourceId) + ? mediaSources.First() + : mediaSources.FirstOrDefault(i => string.Equals(i.Id, request.MediaSourceId)); + + if (mediaSource == null && string.Equals(request.Id, request.MediaSourceId, StringComparison.OrdinalIgnoreCase)) + { + mediaSource = mediaSources.First(); + } } } else diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index a0ac96b9d..1e056f670 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -104,7 +104,7 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - var waitForSegments = state.SegmentLength >= 10 ? 2 : 3; + var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 4); await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false); } } @@ -128,10 +128,9 @@ namespace MediaBrowser.Api.Playback.Hls var audioBitrate = state.OutputAudioBitrate ?? 0; var videoBitrate = state.OutputVideoBitrate ?? 0; - var appendBaselineStream = false; var baselineStreamBitrate = 64000; - var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate); + var playlistText = GetMasterPlaylistFileText(playlist, videoBitrate + audioBitrate, baselineStreamBitrate); job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType); @@ -161,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls } } - private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate) + private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, int baselineStreamBitrate) { var builder = new StringBuilder(); @@ -175,14 +174,6 @@ namespace MediaBrowser.Api.Playback.Hls var playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "/stream.m3u8"); builder.AppendLine(playlistUrl); - // Low bitrate stream - if (includeBaselineStream) - { - builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + baselineStreamBitrate.ToString(UsCulture)); - playlistUrl = "hls/" + Path.GetFileName(firstPlaylist).Replace(".m3u8", "-low/stream.m3u8"); - builder.AppendLine(playlistUrl); - } - return builder.ToString(); } diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 27deaf25e..976fed3f0 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -68,8 +68,6 @@ namespace MediaBrowser.Api.Playback.Hls [Api(Description = "Gets an Http live streaming segment file. Internal use only.")] public class GetHlsVideoSegmentLegacy : VideoStreamRequest { - // TODO: Deprecate with new iOS app - public string PlaylistId { get; set; } /// @@ -113,7 +111,7 @@ namespace MediaBrowser.Api.Playback.Hls var file = request.SegmentId + Path.GetExtension(Request.PathInfo); file = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, file); - var normalizedPlaylistId = request.PlaylistId.Replace("-low", string.Empty); + var normalizedPlaylistId = request.PlaylistId; var playlistPath = Directory.EnumerateFiles(_config.ApplicationPaths.TranscodingTempPath, "*") .FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1); diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 2e92c4a49..d6ccdd1fd 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -88,6 +88,10 @@ namespace MediaBrowser.Api.Playback return 10; } + if (!RunTimeTicks.HasValue) + { + return 10; + } return 6; } diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index fe13e8b21..b07a31a87 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -148,7 +148,7 @@ namespace MediaBrowser.Api.Subtitles { var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); - var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, false).ConfigureAwait(false); + var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, null, false, CancellationToken.None).ConfigureAwait(false); var builder = new StringBuilder(); diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index a77d88049..1df77cdc9 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -60,11 +60,8 @@ namespace MediaBrowser.Controller.Library /// /// Gets the static media source. /// - /// The item. - /// The media source identifier. - /// if set to true [enable path substitution]. /// MediaSourceInfo. - Task GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution); + Task GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken); /// /// Opens the media source. diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index d958d0e37..6345e2105 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -827,6 +827,7 @@ namespace MediaBrowser.Dlna.PlayTo public string DeviceId { get; set; } public string MediaSourceId { get; set; } + public string LiveStreamId { get; set; } public BaseItem Item { get; set; } public MediaSourceInfo MediaSource { get; set; } @@ -910,6 +911,10 @@ namespace MediaBrowser.Dlna.PlayTo { request.StartPositionTicks = long.Parse(val, CultureInfo.InvariantCulture); } + else if (i == 22) + { + request.LiveStreamId = val; + } } request.Item = string.IsNullOrWhiteSpace(request.ItemId) @@ -920,7 +925,7 @@ namespace MediaBrowser.Dlna.PlayTo request.MediaSource = hasMediaSources == null ? null - : (await mediaSourceManager.GetMediaSource(hasMediaSources, request.MediaSourceId, false).ConfigureAwait(false)); + : (await mediaSourceManager.GetMediaSource(hasMediaSources, request.MediaSourceId, request.LiveStreamId, false, CancellationToken.None).ConfigureAwait(false)); return request; } diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 4a533ff93..c20245a6e 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -221,8 +221,28 @@ namespace MediaBrowser.Server.Implementations.Library } } - public async Task GetMediaSource(IHasMediaSources item, string mediaSourceId, bool enablePathSubstitution) + public async Task GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken) { + if (!string.IsNullOrWhiteSpace(liveStreamId)) + { + return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false); + } + //await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + //try + //{ + // var stream = _openStreams.Values.FirstOrDefault(i => string.Equals(i.MediaSource.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + + // if (stream != null) + // { + // return stream.MediaSource; + // } + //} + //finally + //{ + // _liveStreamSemaphore.Release(); + //} + var sources = await GetPlayackMediaSources(item.Id.ToString("N"), null, enablePathSubstitution, new[] { MediaType.Audio, MediaType.Video }, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index b508110cf..3d7b4abef 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -763,7 +763,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new ApplicationException("Tuner not found."); } - private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) + private async Task> GetChannelStreamInternal(string channelId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); @@ -771,7 +771,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { try { - var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + var result = await hostInstance.GetChannelStream(channelId, null, cancellationToken).ConfigureAwait(false); return new Tuple(result.Item1, hostInstance, result.Item2); } @@ -994,7 +994,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { - var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false); + var result = await GetChannelStreamInternal(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); isResourceOpen = true; semaphore = result.Item3; var mediaStreamInfo = result.Item1; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 7aa9eb1cf..3f6bb140b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -223,8 +223,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } } - var stream = - await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); + var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); if (EnableMediaProbing) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index fd4775938..c5bd648cf 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -319,18 +319,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun videoBitrate = 1000000; } - if (string.IsNullOrWhiteSpace(videoCodec)) + var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false); + var channel = channels.FirstOrDefault(i => string.Equals(i.Number, channelId, StringComparison.OrdinalIgnoreCase)); + if (channel != null) { - var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false); - var channel = channels.FirstOrDefault(i => string.Equals(i.Number, channelId, StringComparison.OrdinalIgnoreCase)); - if (channel != null) + if (string.IsNullOrWhiteSpace(videoCodec)) { videoCodec = channel.VideoCodec; - audioCodec = channel.AudioCodec; + } + audioCodec = channel.AudioCodec; + if (!videoBitrate.HasValue) + { videoBitrate = (channel.IsHD ?? true) ? 15000000 : 2000000; - audioBitrate = (channel.IsHD ?? true) ? 448000 : 192000; } + audioBitrate = (channel.IsHD ?? true) ? 448000 : 192000; } // normalize @@ -380,7 +383,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun BitRate = audioBitrate } }, - RequiresOpening = false, + RequiresOpening = true, RequiresClosing = false, BufferMs = 0, Container = "ts", diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index afcdf9d90..f56af5b61 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -307,9 +307,9 @@ namespace MediaBrowser.Server.Implementations.Session } } - private Task GetMediaSource(IHasMediaSources item, string mediaSourceId) + private Task GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId) { - return _mediaSourceManager.GetMediaSource(item, mediaSourceId, false); + return _mediaSourceManager.GetMediaSource(item, mediaSourceId, liveStreamId, false, CancellationToken.None); } /// @@ -337,7 +337,7 @@ namespace MediaBrowser.Server.Implementations.Session var hasMediaSources = libraryItem as IHasMediaSources; if (hasMediaSources != null) { - mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId).ConfigureAwait(false); + mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); if (mediaSource != null) { @@ -792,7 +792,7 @@ namespace MediaBrowser.Server.Implementations.Session var hasMediaSources = libraryItem as IHasMediaSources; if (hasMediaSources != null) { - mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId).ConfigureAwait(false); + mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); } info.Item = GetItemInfo(libraryItem, libraryItem, mediaSource); -- cgit v1.2.3 From 55bbc051c2e5da93baef2efeb105684e4ff40c10 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 20 Sep 2016 11:22:00 -0400 Subject: exclude backdrops from photos --- MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs index 3f9475480..d4ebb8457 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -89,7 +89,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers return false; } - if (IgnoreFiles.Any(i => filename.IndexOf("-" + i, StringComparison.OrdinalIgnoreCase) != -1)) + if (IgnoreFiles.Any(i => filename.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1)) { return false; } -- cgit v1.2.3 From 8b096ccc0e87883655bcf9ea1f9e22e9961450d3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 23 Sep 2016 02:21:54 -0400 Subject: stub out storage for new path substitution --- .../Library/LibraryStructureService.cs | 20 ++- MediaBrowser.Controller/Entities/Audio/Audio.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 4 +- .../Entities/CollectionFolder.cs | 31 +++-- MediaBrowser.Controller/Entities/Video.cs | 2 +- MediaBrowser.Controller/Library/ILibraryManager.cs | 6 +- MediaBrowser.Model/Configuration/LibraryOptions.cs | 8 ++ .../Dto/DtoService.cs | 4 +- .../Library/CoreResolutionIgnoreRule.cs | 3 +- .../Library/LibraryManager.cs | 140 ++++++++++++++------- .../LiveTv/EmbyTV/EmbyTV.cs | 8 +- 11 files changed, 160 insertions(+), 68 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index 72966a7cd..0ed4ee2a2 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -112,6 +112,8 @@ namespace MediaBrowser.Api.Library /// The name. public string Path { get; set; } + public MediaPathInfo PathInfo { get; set; } + /// /// Gets or sets a value indicating whether [refresh library]. /// @@ -212,7 +214,12 @@ namespace MediaBrowser.Api.Library { var libraryOptions = request.LibraryOptions ?? new LibraryOptions(); - _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, request.Paths, libraryOptions, request.RefreshLibrary); + if (request.Paths != null && request.Paths.Length > 0) + { + libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray(); + } + + _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary); } /// @@ -308,7 +315,16 @@ namespace MediaBrowser.Api.Library try { - _libraryManager.AddMediaPath(request.Name, request.Path); + var mediaPath = request.PathInfo; + + if (mediaPath == null) + { + mediaPath = new MediaPathInfo + { + Path = request.Path + }; + } + _libraryManager.AddMediaPath(request.Name, mediaPath); } finally { diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 1af55a389..599e72314 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -262,7 +262,7 @@ namespace MediaBrowser.Controller.Entities.Audio Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(), Name = i.Name, - Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path, + Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path, RunTimeTicks = i.RunTimeTicks, Container = i.Container, Size = i.Size diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6e0a33620..30e0f3ee7 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2121,11 +2121,11 @@ namespace MediaBrowser.Controller.Entities return hasChanges; } - protected static string GetMappedPath(string path, LocationType locationType) + protected static string GetMappedPath(BaseItem item, string path, LocationType locationType) { if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { - return LibraryManager.GetPathAfterNetworkSubstitution(path); + return LibraryManager.GetPathAfterNetworkSubstitution(path, item); } return path; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 30ea26eb6..77d7ca7f2 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -48,24 +48,14 @@ namespace MediaBrowser.Controller.Entities private static readonly Dictionary LibraryOptions = new Dictionary(); public LibraryOptions GetLibraryOptions() { - lock (LibraryOptions) - { - LibraryOptions options; - if (!LibraryOptions.TryGetValue(Path, out options)) - { - options = LoadLibraryOptions(); - LibraryOptions[Path] = options; - } - - return options; - } + return GetLibraryOptions(Path); } - private LibraryOptions LoadLibraryOptions() + private static LibraryOptions LoadLibraryOptions(string path) { try { - var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(Path)) as LibraryOptions; + var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions; if (result == null) { @@ -100,6 +90,21 @@ namespace MediaBrowser.Controller.Entities SaveLibraryOptions(Path, options); } + public static LibraryOptions GetLibraryOptions(string path) + { + lock (LibraryOptions) + { + LibraryOptions options; + if (!LibraryOptions.TryGetValue(path, out options)) + { + options = LoadLibraryOptions(path); + LibraryOptions[path] = options; + } + + return options; + } + } + public static void SaveLibraryOptions(string path, LibraryOptions options) { lock (LibraryOptions) diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 8809f155c..1406a05ce 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -600,7 +600,7 @@ namespace MediaBrowser.Controller.Entities Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, MediaStreams = mediaStreams, Name = GetMediaSourceName(i, mediaStreams), - Path = enablePathSubstitution ? GetMappedPath(i.Path, locationType) : i.Path, + Path = enablePathSubstitution ? GetMappedPath(i, i.Path, locationType) : i.Path, RunTimeTicks = i.RunTimeTicks, Video3DFormat = i.Video3DFormat, VideoType = i.VideoType, diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index d5c2fcd20..184a245c1 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -506,7 +506,7 @@ namespace MediaBrowser.Controller.Library /// QueryResult<BaseItem>. QueryResult QueryItems(InternalItemsQuery query); - string GetPathAfterNetworkSubstitution(string path); + string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem = null); /// /// Substitutes the path. @@ -556,9 +556,9 @@ namespace MediaBrowser.Controller.Library /// true if XXXX, false otherwise. bool IgnoreFile(FileSystemMetadata file, BaseItem parent); - void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary); + void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary); void RemoveVirtualFolder(string name, bool refreshLibrary); - void AddMediaPath(string virtualFolderName, string path); + void AddMediaPath(string virtualFolderName, MediaPathInfo path); void RemoveMediaPath(string virtualFolderName, string path); QueryResult> GetGenres(InternalItemsQuery query); diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 770ad433d..460ee0918 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -9,11 +9,19 @@ public bool EnableChapterImageExtraction { get; set; } public bool ExtractChapterImagesDuringLibraryScan { get; set; } public bool DownloadImagesInAdvance { get; set; } + public MediaPathInfo[] PathInfos { get; set; } public LibraryOptions() { EnablePhotos = true; EnableRealtimeMonitor = true; + PathInfos = new MediaPathInfo[] { }; } } + + public class MediaPathInfo + { + public string Path { get; set; } + public string NetworkPath { get; set; } + } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index ae676626d..e4f1460c6 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1509,7 +1509,7 @@ namespace MediaBrowser.Server.Implementations.Dto } } - private string GetMappedPath(IHasMetadata item) + private string GetMappedPath(BaseItem item) { var path = item.Path; @@ -1517,7 +1517,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { - path = _libraryManager.GetPathAfterNetworkSubstitution(path); + path = _libraryManager.GetPathAfterNetworkSubstitution(path, item); } return path; diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index ba7e33890..b550d1dda 100644 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -33,7 +33,8 @@ namespace MediaBrowser.Server.Implementations.Library // Synology "@eaDir", - "eaDir" + "eaDir", + "#recycle" }; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 1f8c77953..cef0022cf 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2527,8 +2527,29 @@ namespace MediaBrowser.Server.Implementations.Library }).OrderBy(i => i.Path).ToList(); } - public string GetPathAfterNetworkSubstitution(string path) + public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem) { + if (ownerItem != null) + { + var libraryOptions = GetLibraryOptions(ownerItem); + if (libraryOptions != null) + { + foreach (var pathInfo in libraryOptions.PathInfos) + { + if (string.IsNullOrWhiteSpace(pathInfo.NetworkPath)) + { + continue; + } + + var substitutionResult = SubstitutePathInternal(path, pathInfo.Path, pathInfo.NetworkPath); + if (substitutionResult.Item2) + { + return substitutionResult.Item1; + } + } + } + } + foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) { path = SubstitutePath(path, map.From, map.To); @@ -2538,6 +2559,11 @@ namespace MediaBrowser.Server.Implementations.Library } public string SubstitutePath(string path, string from, string to) + { + return SubstitutePathInternal(path, from, to).Item1; + } + + private Tuple SubstitutePathInternal(string path, string from, string to) { if (string.IsNullOrWhiteSpace(path)) { @@ -2552,7 +2578,11 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("to"); } - var newPath = path.Replace(from.Trim(), to.Trim(), StringComparison.OrdinalIgnoreCase); + from = from.Trim(); + to = to.Trim(); + + var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase); + var changed = false; if (!string.Equals(newPath, path)) { @@ -2564,9 +2594,11 @@ namespace MediaBrowser.Server.Implementations.Library { newPath = newPath.Replace('/', '\\'); } + + changed = true; } - return newPath; + return new Tuple(newPath, changed); } private void SetExtraTypeFromFilename(Video item) @@ -2695,7 +2727,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new InvalidOperationException(); } - public void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary) + public void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary) { if (string.IsNullOrWhiteSpace(name)) { @@ -2713,12 +2745,13 @@ namespace MediaBrowser.Server.Implementations.Library virtualFolderPath = Path.Combine(rootFolderPath, name); } - if (mediaPaths != null) + var mediaPathInfos = options.PathInfos; + if (mediaPathInfos != null) { - var invalidpath = mediaPaths.FirstOrDefault(i => !_fileSystem.DirectoryExists(i)); + var invalidpath = mediaPathInfos.FirstOrDefault(i => !_fileSystem.DirectoryExists(i.Path)); if (invalidpath != null) { - throw new ArgumentException("The specified path does not exist: " + invalidpath + "."); + throw new ArgumentException("The specified path does not exist: " + invalidpath.Path + "."); } } @@ -2740,11 +2773,11 @@ namespace MediaBrowser.Server.Implementations.Library CollectionFolder.SaveLibraryOptions(virtualFolderPath, options); - if (mediaPaths != null) + if (mediaPathInfos != null) { - foreach (var path in mediaPaths) + foreach (var path in mediaPathInfos) { - AddMediaPath(name, path); + AddMediaPathInternal(name, path, false); } } } @@ -2770,6 +2803,61 @@ namespace MediaBrowser.Server.Implementations.Library } } + private const string ShortcutFileExtension = ".mblink"; + private const string ShortcutFileSearch = "*" + ShortcutFileExtension; + public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo) + { + AddMediaPathInternal(virtualFolderName, pathInfo, true); + } + + private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions) + { + if (pathInfo == null) + { + throw new ArgumentNullException("path"); + } + + var path = pathInfo.Path; + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException("path"); + } + + if (!_fileSystem.DirectoryExists(path)) + { + throw new DirectoryNotFoundException("The path does not exist."); + } + + var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); + + var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path); + + var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); + + while (_fileSystem.FileExists(lnk)) + { + shortcutFilename += "1"; + lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); + } + + _fileSystem.CreateShortcut(lnk, path); + + RemoveContentTypeOverrides(path); + + if (saveLibraryOptions) + { + var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); + + var list = libraryOptions.PathInfos.ToList(); + list.Add(pathInfo); + libraryOptions.PathInfos = list.ToArray(); + + CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); + } + } + public void RemoveVirtualFolder(string name, bool refreshLibrary) { if (string.IsNullOrWhiteSpace(name)) @@ -2814,38 +2902,6 @@ namespace MediaBrowser.Server.Implementations.Library } } - private const string ShortcutFileExtension = ".mblink"; - private const string ShortcutFileSearch = "*" + ShortcutFileExtension; - public void AddMediaPath(string virtualFolderName, string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - - if (!_fileSystem.DirectoryExists(path)) - { - throw new DirectoryNotFoundException("The path does not exist."); - } - - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - - var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path); - - var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - - while (_fileSystem.FileExists(lnk)) - { - shortcutFilename += "1"; - lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - } - - _fileSystem.CreateShortcut(lnk, path); - - RemoveContentTypeOverrides(path); - } - private void RemoveContentTypeOverrides(string path) { if (string.IsNullOrWhiteSpace(path)) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index a3ca54b4a..e358f9d25 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -143,9 +143,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV continue; } + var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray(); + + var libraryOptions = new LibraryOptions + { + PathInfos = mediaPathInfos + }; try { - _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, pathsToCreate.ToArray(), new LibraryOptions(), true); + _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, libraryOptions, true); } catch (Exception ex) { -- cgit v1.2.3 From fdc2826709495c5313c4a61060fd420d0f8214b8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Sep 2016 02:22:03 -0400 Subject: add network share config --- .../Library/LibraryStructureService.cs | 26 ++++++++++++++ MediaBrowser.Controller/Library/ILibraryManager.cs | 1 + .../IO/LibraryMonitor.cs | 5 ++- .../Library/LibraryManager.cs | 41 ++++++++++++++++++++-- .../LiveTv/LiveTvManager.cs | 6 +++- 5 files changed, 74 insertions(+), 5 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index 0ed4ee2a2..e872061a7 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -121,6 +121,18 @@ namespace MediaBrowser.Api.Library public bool RefreshLibrary { get; set; } } + [Route("/Library/VirtualFolders/Paths/Update", "POST")] + public class UpdateMediaPath : IReturnVoid + { + /// + /// Gets or sets the name. + /// + /// The name. + public string Name { get; set; } + + public MediaPathInfo PathInfo { get; set; } + } + [Route("/Library/VirtualFolders/Paths", "DELETE")] public class RemoveMediaPath : IReturnVoid { @@ -348,6 +360,20 @@ namespace MediaBrowser.Api.Library } } + /// + /// Posts the specified request. + /// + /// The request. + public void Post(UpdateMediaPath request) + { + if (string.IsNullOrWhiteSpace(request.Name)) + { + throw new ArgumentNullException("request"); + } + + _libraryManager.UpdateMediaPath(request.Name, request.PathInfo); + } + /// /// Deletes the specified request. /// diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 184a245c1..975c59659 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -559,6 +559,7 @@ namespace MediaBrowser.Controller.Library void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary); void RemoveVirtualFolder(string name, bool refreshLibrary); void AddMediaPath(string virtualFolderName, MediaPathInfo path); + void UpdateMediaPath(string virtualFolderName, MediaPathInfo path); void RemoveMediaPath(string virtualFolderName, string path); QueryResult> GetGenres(InternalItemsQuery query); diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index 6e0f9a3c4..bcc4e5dcf 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -43,7 +43,10 @@ namespace MediaBrowser.Server.Implementations.IO // WMC temp recording directories that will constantly be written to "TempRec", - "TempSBE" + "TempSBE", + "@eaDir", + "eaDir", + "#recycle" }; private readonly IReadOnlyList _alwaysIgnoreSubstrings = new List diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index cef0022cf..f62847bcc 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2858,6 +2858,32 @@ namespace MediaBrowser.Server.Implementations.Library } } + public void UpdateMediaPath(string virtualFolderName, MediaPathInfo pathInfo) + { + if (pathInfo == null) + { + throw new ArgumentNullException("path"); + } + + var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); + + var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); + + var list = libraryOptions.PathInfos.ToList(); + foreach (var originalPathInfo in list) + { + if (string.Equals(pathInfo.Path, originalPathInfo.Path, StringComparison.Ordinal)) + { + originalPathInfo.NetworkPath = pathInfo.NetworkPath; + break; + } + } + libraryOptions.PathInfos = list.ToArray(); + + CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); + } + public void RemoveVirtualFolder(string name, bool refreshLibrary) { if (string.IsNullOrWhiteSpace(name)) @@ -2938,19 +2964,28 @@ namespace MediaBrowser.Server.Implementations.Library } var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - var path = Path.Combine(rootFolderPath, virtualFolderName); + var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - if (!_fileSystem.DirectoryExists(path)) + if (!_fileSystem.DirectoryExists(virtualFolderPath)) { throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName)); } - var shortcut = Directory.EnumerateFiles(path, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase)); + var shortcut = Directory.EnumerateFiles(virtualFolderPath, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(shortcut)) { _fileSystem.DeleteFile(shortcut); } + + var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); + + libraryOptions.PathInfos = libraryOptions + .PathInfos + .Where(i => !string.Equals(i.Path, mediaPath, StringComparison.Ordinal)) + .ToArray(); + + CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index dcef6e84a..9b340a9f9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -699,7 +699,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.HomePageUrl = info.HomePageUrl; item.ProductionYear = info.ProductionYear; - item.PremiereDate = info.OriginalAirDate; + + if (!info.IsSeries || info.IsRepeat) + { + item.PremiereDate = info.OriginalAirDate; + } item.IndexNumber = info.EpisodeNumber; item.ParentIndexNumber = info.SeasonNumber; -- cgit v1.2.3 From 48d7f686eb2212a19ee988c18c39d9fe1027d483 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 24 Sep 2016 13:58:17 -0400 Subject: update network share settings --- MediaBrowser.Api/StartupWizardService.cs | 6 --- .../MediaBrowser.Controller.csproj | 1 - .../RelatedMedia/IRelatedMediaProvider.cs | 11 ------ MediaBrowser.Dlna/PlayTo/Device.cs | 2 +- .../Library/LibraryManager.cs | 44 ++++++++++++++++++++++ .../LiveTv/LiveTvManager.cs | 14 ------- .../Session/SessionManager.cs | 6 +-- MediaBrowser.Server.Mono/app.config | 2 +- .../ApplicationPathHelper.cs | 8 +++- 9 files changed, 55 insertions(+), 39 deletions(-) delete mode 100644 MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index ef898eb53..176b497d7 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -148,12 +148,6 @@ namespace MediaBrowser.Api { var user = _userManager.Users.First(); - // TODO: This should be handled internally by xbmc metadata - const string metadataKey = "xbmcmetadata"; - var metadata = _config.GetConfiguration(metadataKey); - metadata.UserId = user.Id.ToString("N"); - _config.SaveConfiguration(metadataKey, metadata); - user.Name = request.Name; await _userManager.UpdateUser(user).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 8fae46906..cb36afa5f 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -318,7 +318,6 @@ - diff --git a/MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs b/MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs deleted file mode 100644 index bb2a0cd89..000000000 --- a/MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MediaBrowser.Controller.RelatedMedia -{ - public interface IRelatedMediaProvider - { - /// - /// Gets the name. - /// - /// The name. - string Name { get; } - } -} diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index d1802b3ad..b656bc66e 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -474,7 +474,7 @@ namespace MediaBrowser.Dlna.PlayTo if (_disposed) return; - _logger.ErrorException("Error updating device info for {0}", ex, Properties.Name); + //_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name); _successiveStopCount++; _connectFailureCount++; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index f62847bcc..7c3196065 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2829,6 +2829,16 @@ namespace MediaBrowser.Server.Implementations.Library throw new DirectoryNotFoundException("The path does not exist."); } + if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !_fileSystem.DirectoryExists(pathInfo.NetworkPath)) + { + throw new DirectoryNotFoundException("The network path does not exist."); + } + + if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !_fileSystem.DirectoryExists(pathInfo.NetworkPath)) + { + throw new DirectoryNotFoundException("The network path does not exist."); + } + var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); @@ -2850,6 +2860,8 @@ namespace MediaBrowser.Server.Implementations.Library { var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); + SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions); + var list = libraryOptions.PathInfos.ToList(); list.Add(pathInfo); libraryOptions.PathInfos = list.ToArray(); @@ -2865,11 +2877,18 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("path"); } + if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !_fileSystem.DirectoryExists(pathInfo.NetworkPath)) + { + throw new DirectoryNotFoundException("The network path does not exist."); + } + var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); + SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions); + var list = libraryOptions.PathInfos.ToList(); foreach (var originalPathInfo in list) { @@ -2879,11 +2898,36 @@ namespace MediaBrowser.Server.Implementations.Library break; } } + libraryOptions.PathInfos = list.ToArray(); CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); } + private void SyncLibraryOptionsToLocations(string virtualFolderPath, LibraryOptions options) + { + var topLibraryFolders = GetUserRootFolder().Children.ToList(); + var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders); + + if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length) + { + var list = options.PathInfos.ToList(); + + foreach (var location in info.Locations) + { + if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal))) + { + list.Add(new MediaPathInfo + { + Path = location + }); + } + } + + options.PathInfos = list.ToArray(); + } + } + public void RemoveVirtualFolder(string name, bool refreshLibrary) { if (string.IsNullOrWhiteSpace(name)) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 9b340a9f9..8a4572813 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2855,20 +2855,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } - if (string.Equals(feature, "dvr", StringComparison.OrdinalIgnoreCase)) - { - var config = GetConfiguration(); - if (config.TunerHosts.Count(i => i.IsEnabled) > 0 && - config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0) - { - return Task.FromResult(new MBRegistrationRecord - { - IsRegistered = true, - IsValid = true - }); - } - } - return _security.GetRegistrationStatus(feature); } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index f56af5b61..48f48cdcc 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -294,11 +294,9 @@ namespace MediaBrowser.Server.Implementations.Session var key = GetSessionKey(session.Client, session.DeviceId); SessionInfo removed; + _activeConnections.TryRemove(key, out removed); - if (_activeConnections.TryRemove(key, out removed)) - { - OnSessionEnded(removed); - } + OnSessionEnded(session); } } finally diff --git a/MediaBrowser.Server.Mono/app.config b/MediaBrowser.Server.Mono/app.config index e14b908ad..e8c7a9326 100644 --- a/MediaBrowser.Server.Mono/app.config +++ b/MediaBrowser.Server.Mono/app.config @@ -8,7 +8,7 @@ - + diff --git a/MediaBrowser.Server.Startup.Common/ApplicationPathHelper.cs b/MediaBrowser.Server.Startup.Common/ApplicationPathHelper.cs index 285806791..254a782db 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationPathHelper.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationPathHelper.cs @@ -18,10 +18,16 @@ namespace MediaBrowser.Server.Startup.Common useDebugPath = true; #endif - var programDataPath = useDebugPath ? ConfigurationManager.AppSettings["DebugProgramDataPath"] : ConfigurationManager.AppSettings["ReleaseProgramDataPath"]; + var programDataPath = useDebugPath ? + ConfigurationManager.AppSettings["DebugProgramDataPath"] : + ConfigurationManager.AppSettings["ReleaseProgramDataPath"]; programDataPath = programDataPath.Replace("%ApplicationData%", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + programDataPath = programDataPath + .Replace('/', Path.DirectorySeparatorChar) + .Replace('\\', Path.DirectorySeparatorChar); + // If it's a relative path, e.g. "..\" if (!Path.IsPathRooted(programDataPath)) { -- cgit v1.2.3 From d596053ec7830d89a83723b0ae2f8439c6319f6f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 25 Sep 2016 14:39:13 -0400 Subject: rework live stream handling --- MediaBrowser.Api/ApiEntryPoint.cs | 39 +++++- MediaBrowser.Api/LiveTv/LiveTvService.cs | 33 ++++- MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 44 +++--- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 7 +- .../Progressive/BaseProgressiveStreamingService.cs | 23 ++- .../Progressive/ProgressiveStreamWriter.cs | 4 +- MediaBrowser.Api/Playback/StreamState.cs | 10 +- MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 18 +-- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 +- MediaBrowser.Controller/LiveTv/LiveStream.cs | 30 ++++ .../MediaBrowser.Controller.csproj | 1 + .../IO/LibraryMonitor.cs | 8 +- .../Library/LibraryManager.cs | 20 ++- .../LiveTv/EmbyTV/DirectRecorder.cs | 18 ++- .../LiveTv/EmbyTV/EmbyTV.cs | 100 +++++++------ .../LiveTv/EmbyTV/EncodedRecorder.cs | 84 +---------- .../LiveTv/TunerHosts/BaseTunerHost.cs | 145 +++++++------------ .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 33 ++++- .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 156 +++++++++++++++++++++ .../LiveTv/TunerHosts/M3UTunerHost.cs | 13 +- .../LiveTv/TunerHosts/SatIp/SatIpHost.cs | 10 +- .../MediaBrowser.Server.Implementations.csproj | 1 + .../MediaBrowser.WebDashboard.csproj | 18 +-- MediaBrowser.XbmcMetadata/EntryPoint.cs | 10 ++ 24 files changed, 520 insertions(+), 307 deletions(-) create mode 100644 MediaBrowser.Controller/LiveTv/LiveStream.cs create mode 100644 MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 214fb7488..2f5b9e1e0 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -8,6 +8,7 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Session; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -44,7 +45,13 @@ namespace MediaBrowser.Api private readonly IFileSystem _fileSystem; private readonly IMediaSourceManager _mediaSourceManager; - public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1); + /// + /// The active transcoding jobs + /// + private readonly List _activeTranscodingJobs = new List(); + + private readonly Dictionary _transcodingLocks = + new Dictionary(); /// /// Initializes a new instance of the class. @@ -67,6 +74,21 @@ namespace MediaBrowser.Api _sessionManager.PlaybackStart += _sessionManager_PlaybackStart; } + public SemaphoreSlim GetTranscodingLock(string outputPath) + { + lock (_transcodingLocks) + { + SemaphoreSlim result; + if (!_transcodingLocks.TryGetValue(outputPath, out result)) + { + result = new SemaphoreSlim(1, 1); + _transcodingLocks[outputPath] = result; + } + + return result; + } + } + private void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e) { if (!string.IsNullOrWhiteSpace(e.PlaySessionId)) @@ -148,11 +170,6 @@ namespace MediaBrowser.Api } } - /// - /// The active transcoding jobs - /// - private readonly List _activeTranscodingJobs = new List(); - /// /// Called when [transcode beginning]. /// @@ -258,6 +275,11 @@ namespace MediaBrowser.Api } } + lock (_transcodingLocks) + { + _transcodingLocks.Remove(path); + } + if (!string.IsNullOrWhiteSpace(state.Request.DeviceId)) { _sessionManager.ClearTranscodingInfo(state.Request.DeviceId); @@ -497,6 +519,11 @@ namespace MediaBrowser.Api } } + lock (_transcodingLocks) + { + _transcodingLocks.Remove(job.Path); + } + lock (job.ProcessLock) { if (job.TranscodingThrottler != null) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 3ad0ec1ba..a5f8fce6e 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -12,9 +12,13 @@ using ServiceStack; using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using CommonIO; +using MediaBrowser.Api.Playback.Progressive; +using MediaBrowser.Controller.Configuration; namespace MediaBrowser.Api.LiveTv { @@ -613,16 +617,24 @@ namespace MediaBrowser.Api.LiveTv } + [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")] + public class GetLiveStreamFile + { + public string Id { get; set; } + public string Container { get; set; } + } + public class LiveTvService : BaseApiService { private readonly ILiveTvManager _liveTvManager; private readonly IUserManager _userManager; - private readonly IConfigurationManager _config; + private readonly IServerConfigurationManager _config; private readonly IHttpClient _httpClient; private readonly ILibraryManager _libraryManager; private readonly IDtoService _dtoService; + private readonly IFileSystem _fileSystem; - public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService) + public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem) { _liveTvManager = liveTvManager; _userManager = userManager; @@ -630,6 +642,23 @@ namespace MediaBrowser.Api.LiveTv _httpClient = httpClient; _libraryManager = libraryManager; _dtoService = dtoService; + _fileSystem = fileSystem; + } + + public object Get(GetLiveStreamFile request) + { + var filePath = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, request.Id + ".ts"); + + var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + + outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath); + + var streamSource = new ProgressiveFileCopier(_fileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None) + { + AllowEndOfFile = false + }; + + return ResultFactory.GetAsyncStreamWriter(streamSource); } public object Get(GetDefaultListingProvider request) diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 761b1eb4e..319e4bbb6 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -87,7 +87,8 @@ namespace MediaBrowser.Api.Playback.Hls if (!FileSystem.FileExists(playlist)) { - await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); + var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlist); + await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { if (!FileSystem.FileExists(playlist)) @@ -104,13 +105,13 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 4); + var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3); await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false); } } finally { - ApiEntryPoint.Instance.TranscodingStartLock.Release(); + transcodingLock.Release(); } } @@ -182,32 +183,41 @@ namespace MediaBrowser.Api.Playback.Hls { Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist); - while (true) + while (!cancellationToken.IsCancellationRequested) { - // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written - using (var fileStream = GetPlaylistFileStream(playlist)) + try { - using (var reader = new StreamReader(fileStream)) + // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written + using (var fileStream = GetPlaylistFileStream(playlist)) { - var count = 0; - - while (!reader.EndOfStream) + using (var reader = new StreamReader(fileStream)) { - var line = await reader.ReadLineAsync().ConfigureAwait(false); + var count = 0; - if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1) + while (!reader.EndOfStream) { - count++; - if (count >= segmentCount) + var line = await reader.ReadLineAsync().ConfigureAwait(false); + + if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1) { - Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist); - return; + count++; + if (count >= segmentCount) + { + Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist); + return; + } } } + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } - await Task.Delay(100, cancellationToken).ConfigureAwait(false); } } + catch (IOException) + { + // May get an error if the file is locked + } + + await Task.Delay(50, cancellationToken).ConfigureAwait(false); } } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 9cd55528d..d4ddbd7c5 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -171,14 +171,15 @@ namespace MediaBrowser.Api.Playback.Hls return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false); } - await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); + var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(playlistPath); + await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); var released = false; try { if (FileSystem.FileExists(segmentPath)) { job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); - ApiEntryPoint.Instance.TranscodingStartLock.Release(); + transcodingLock.Release(); released = true; return await GetSegmentResult(state, playlistPath, segmentPath, requestedIndex, job, cancellationToken).ConfigureAwait(false); } @@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls { if (!released) { - ApiEntryPoint.Instance.TranscodingStartLock.Release(); + transcodingLock.Release(); } } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index b8cb6b14f..a68319109 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -17,6 +17,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using CommonIO; +using ServiceStack; namespace MediaBrowser.Api.Playback.Progressive { @@ -129,6 +130,23 @@ namespace MediaBrowser.Api.Playback.Progressive using (state) { + if (state.MediaPath.IndexOf("/livestreamfiles/", StringComparison.OrdinalIgnoreCase) != -1) + { + var parts = state.MediaPath.Split('/'); + var filename = parts[parts.Length - 2] + Path.GetExtension(parts[parts.Length - 1]); + var filePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, filename); + + var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + + outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath); + + var streamSource = new ProgressiveFileCopier(FileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None) + { + AllowEndOfFile = false + }; + return ResultFactory.GetAsyncStreamWriter(streamSource); + } + return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource) .ConfigureAwait(false); } @@ -345,7 +363,8 @@ namespace MediaBrowser.Api.Playback.Progressive return streamResult; } - await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); + var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath); + await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); try { TranscodingJob job; @@ -376,7 +395,7 @@ namespace MediaBrowser.Api.Playback.Progressive } finally { - ApiEntryPoint.Instance.TranscodingStartLock.Release(); + transcodingLock.Release(); } } diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 0a9a44641..80b5e357d 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -24,6 +24,8 @@ namespace MediaBrowser.Api.Playback.Progressive private long _bytesWritten = 0; + public bool AllowEndOfFile = true; + public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) { _fileSystem = fileSystem; @@ -50,7 +52,7 @@ namespace MediaBrowser.Api.Playback.Progressive using (var fs = _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { - while (eofCount < 15) + while (eofCount < 15 || !AllowEndOfFile) { var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 109aa85de..ef0282abc 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -73,10 +73,6 @@ namespace MediaBrowser.Api.Playback { get { - if (!RunTimeTicks.HasValue) - { - return 6; - } if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var userAgent = UserAgent ?? string.Empty; @@ -92,12 +88,16 @@ namespace MediaBrowser.Api.Playback return 10; } + if (!RunTimeTicks.HasValue) + { + return 3; + } return 6; } if (!RunTimeTicks.HasValue) { - return 6; + return 3; } return 3; } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index a8e42749b..41c5dbdbb 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -37,7 +37,7 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task{SeriesTimerInfoDto}. Task GetNewTimerDefaults(string programId, CancellationToken cancellationToken); - + /// /// Deletes the recording. /// @@ -51,7 +51,7 @@ namespace MediaBrowser.Controller.LiveTv /// The recording. /// Task. Task DeleteRecording(BaseItem recording); - + /// /// Cancels the timer. /// @@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.LiveTv /// The user. /// Task{RecordingInfoDto}. Task GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null); - + /// /// Gets the timer. /// @@ -125,14 +125,14 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task{QueryResult{SeriesTimerInfoDto}}. Task> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken); - + /// /// Gets the channel. /// /// The identifier. /// Channel. LiveTvChannel GetInternalChannel(string id); - + /// /// Gets the recording. /// @@ -157,7 +157,7 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task{StreamResponseInfo}. Task GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken); - + /// /// Gets the program. /// @@ -331,8 +331,8 @@ namespace MediaBrowser.Controller.LiveTv /// The fields. /// The user. /// Task. - Task AddInfoToProgramDto(List> programs, List fields, User user = null); - + Task AddInfoToProgramDto(List> programs, List fields, User user = null); + /// /// Saves the tuner host. /// @@ -395,7 +395,7 @@ namespace MediaBrowser.Controller.LiveTv Task> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken); Task> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken); - List ListingProviders { get;} + List ListingProviders { get; } event EventHandler> SeriesTimerCancelled; event EventHandler> TimerCancelled; diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 1e7aa3de5..3c8b964a2 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.LiveTv /// The stream identifier. /// The cancellation token. /// Task<MediaSourceInfo>. - Task> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken); + Task GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken); /// /// Gets the channel stream media sources. /// diff --git a/MediaBrowser.Controller/LiveTv/LiveStream.cs b/MediaBrowser.Controller/LiveTv/LiveStream.cs new file mode 100644 index 000000000..15d09d857 --- /dev/null +++ b/MediaBrowser.Controller/LiveTv/LiveStream.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Controller.LiveTv +{ + public class LiveStream + { + public MediaSourceInfo OriginalMediaSource { get; set; } + public MediaSourceInfo PublicMediaSource { get; set; } + public string Id { get; set; } + + public LiveStream(MediaSourceInfo mediaSource) + { + OriginalMediaSource = mediaSource; + PublicMediaSource = mediaSource; + Id = mediaSource.Id; + } + + public virtual Task Open(CancellationToken cancellationToken) + { + return Task.FromResult(true); + } + + public virtual Task Close() + { + return Task.FromResult(true); + } + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index cb36afa5f..d70fba742 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -201,6 +201,7 @@ + diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index bcc4e5dcf..76f0e6a1d 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -43,16 +43,14 @@ namespace MediaBrowser.Server.Implementations.IO // WMC temp recording directories that will constantly be written to "TempRec", - "TempSBE", - "@eaDir", - "eaDir", - "#recycle" + "TempSBE" }; private readonly IReadOnlyList _alwaysIgnoreSubstrings = new List { // Synology - "@eaDir", + "eaDir", + "#recycle", ".wd_tv", ".actors" }; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 7c3196065..b076996df 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2803,6 +2803,17 @@ namespace MediaBrowser.Server.Implementations.Library } } + private bool ValidateNetworkPath(string path) + { + if (Environment.OSVersion.Platform == PlatformID.Win32NT || !path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) + { + return Directory.Exists(path); + } + + // Without native support for unc, we cannot validate this when running under mono + return true; + } + private const string ShortcutFileExtension = ".mblink"; private const string ShortcutFileSearch = "*" + ShortcutFileExtension; public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo) @@ -2829,12 +2840,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new DirectoryNotFoundException("The path does not exist."); } - if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !_fileSystem.DirectoryExists(pathInfo.NetworkPath)) - { - throw new DirectoryNotFoundException("The network path does not exist."); - } - - if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !_fileSystem.DirectoryExists(pathInfo.NetworkPath)) + if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath)) { throw new DirectoryNotFoundException("The network path does not exist."); } @@ -2877,7 +2883,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("path"); } - if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !_fileSystem.DirectoryExists(pathInfo.NetworkPath)) + if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath)) { throw new DirectoryNotFoundException("The network path does not exist."); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 2e3edf3e9..0d043669a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -69,11 +69,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } private const int BufferSize = 81920; - public static async Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken) + public static Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken) + { + return CopyUntilCancelled(source, target, null, cancellationToken); + } + public static async Task CopyUntilCancelled(Stream source, Stream target, Action onStarted, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { - var bytesRead = await CopyToAsyncInternal(source, target, BufferSize, cancellationToken).ConfigureAwait(false); + var bytesRead = await CopyToAsyncInternal(source, target, BufferSize, onStarted, cancellationToken).ConfigureAwait(false); + + onStarted = null; //var position = fs.Position; //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); @@ -85,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private static async Task CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken) + private static async Task CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, Action onStarted, CancellationToken cancellationToken) { byte[] buffer = new byte[bufferSize]; int bytesRead; @@ -96,6 +102,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); totalBytesRead += bytesRead; + + if (onStarted != null) + { + onStarted(); + } + onStarted = null; } return totalBytesRead; diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index e358f9d25..6585e92be 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -746,33 +746,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new NotImplementedException(); } + private readonly SemaphoreSlim _liveStreamsSemaphore = new SemaphoreSlim(1, 1); + private readonly Dictionary _liveStreams = new Dictionary(); + public async Task GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) { - _logger.Info("Streaming Channel " + channelId); + var result = await GetChannelStreamInternal(channelId, streamId, cancellationToken).ConfigureAwait(false); - foreach (var hostInstance in _liveTvManager.TunerHosts) - { - try - { - var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); - - result.Item2.Release(); - - return result.Item1; - } - catch (FileNotFoundException) - { - } - catch (Exception e) - { - _logger.ErrorException("Error getting channel stream", e); - } - } - - throw new ApplicationException("Tuner not found."); + return result.Item1.PublicMediaSource; } - private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) + private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); @@ -782,7 +766,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); - return new Tuple(result.Item1, hostInstance, result.Item2); + await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false); + _liveStreams[result.Id] = result; + _liveStreamsSemaphore.Release(); + + return new Tuple(result, hostInstance); } catch (FileNotFoundException) { @@ -823,9 +811,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new NotImplementedException(); } - public Task CloseLiveStream(string id, CancellationToken cancellationToken) + public async Task CloseLiveStream(string id, CancellationToken cancellationToken) { - return Task.FromResult(0); + await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false); + + try + { + LiveStream stream; + if (_liveStreams.TryGetValue(id, out stream)) + { + _liveStreams.Remove(id); + + try + { + await stream.Close().ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error closing live stream", ex); + } + } + } + finally + { + _liveStreamsSemaphore.Release(); + } } public Task RecordLiveStream(string id, CancellationToken cancellationToken) @@ -999,15 +1009,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV string seriesPath = null; var recordPath = GetRecordingPath(timer, out seriesPath); var recordingStatus = RecordingStatus.New; - var isResourceOpen = false; - SemaphoreSlim semaphore = null; + + LiveStream liveStream = null; try { - var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false); - isResourceOpen = true; - semaphore = result.Item3; - var mediaStreamInfo = result.Item1; + var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); + + 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; // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg //await Task.Delay(3000, cancellationToken).ConfigureAwait(false); @@ -1034,13 +1046,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV timer.Status = RecordingStatus.InProgress; _timerProvider.AddOrUpdate(timer, false); - result.Item3.Release(); - isResourceOpen = false; - SaveNfo(timer, recordPath, seriesPath); }; - var pathWithDuration = result.Item2.ApplyDuration(mediaStreamInfo.Path, duration); + var pathWithDuration = tunerHost.ApplyDuration(mediaStreamInfo.Path, duration); // If it supports supplying duration via url if (!string.Equals(pathWithDuration, mediaStreamInfo.Path, StringComparison.OrdinalIgnoreCase)) @@ -1064,19 +1073,24 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.ErrorException("Error recording to {0}", ex, recordPath); recordingStatus = RecordingStatus.Error; } - finally + + if (liveStream != null) { - if (isResourceOpen && semaphore != null) + try + { + await CloseLiveStream(liveStream.Id, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) { - semaphore.Release(); + _logger.ErrorException("Error closing live stream", ex); } + } - _libraryManager.UnRegisterIgnoredPath(recordPath); - _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true); + _libraryManager.UnRegisterIgnoredPath(recordPath); + _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true); - ActiveRecordingInfo removed; - _activeRecordings.TryRemove(timer.Id, out removed); - } + ActiveRecordingInfo removed; + _activeRecordings.TryRemove(timer.Id, out removed); if (recordingStatus == RecordingStatus.Completed) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 4e7f637b1..f74a76e3f 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -68,18 +68,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { - if (mediaSource.Path.IndexOf("m3u8", StringComparison.OrdinalIgnoreCase) != -1) - { - await RecordWithoutTempFile(mediaSource, targetFile, duration, onStarted, cancellationToken) - .ConfigureAwait(false); - - return; - } + var durationToken = new CancellationTokenSource(duration); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts"); + await RecordFromFile(mediaSource, mediaSource.Path, targetFile, false, duration, onStarted, cancellationToken).ConfigureAwait(false); - await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken) - .ConfigureAwait(false); + _logger.Info("Recording completed to file {0}", targetFile); } private async void DeleteTempFile(string path) @@ -108,76 +102,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private async Task RecordWithoutTempFile(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) - { - var durationToken = new CancellationTokenSource(duration); - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - - await RecordFromFile(mediaSource, mediaSource.Path, targetFile, false, duration, onStarted, cancellationToken).ConfigureAwait(false); - - _logger.Info("Recording completed to file {0}", targetFile); - } - - private async Task RecordWithTempFile(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) - { - var httpRequestOptions = new HttpRequestOptions() - { - Url = mediaSource.Path - }; - - httpRequestOptions.BufferContent = false; - - using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false)) - { - _logger.Info("Opened recording stream from tuner provider"); - - Directory.CreateDirectory(Path.GetDirectoryName(tempFile)); - - using (var output = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read)) - { - //onStarted(); - - _logger.Info("Copying recording stream to file {0}", tempFile); - - var bufferMs = 5000; - - 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.Add(TimeSpan.FromMilliseconds(bufferMs))); - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - } - - var tempFileTask = DirectRecorder.CopyUntilCancelled(response.Content, output, cancellationToken); - - // Give the temp file a little time to build up - await Task.Delay(bufferMs, cancellationToken).ConfigureAwait(false); - - var recordTask = Task.Run(() => RecordFromFile(mediaSource, tempFile, targetFile, true, duration, onStarted, cancellationToken), CancellationToken.None); - - try - { - await tempFileTask.ConfigureAwait(false); - } - catch (OperationCanceledException) - { - - } - - await recordTask.ConfigureAwait(false); - } - } - - _logger.Info("Recording completed to file {0}", targetFile); - } - private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, bool deleteInputFileAfterCompletion, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { _targetPath = targetFile; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 3f6bb140b..6beea352a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -10,6 +10,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Serialization; @@ -18,7 +19,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { public abstract class BaseTunerHost { - protected readonly IConfigurationManager Config; + protected readonly IServerConfigurationManager Config; protected readonly ILogger Logger; protected IJsonSerializer JsonSerializer; protected readonly IMediaEncoder MediaEncoder; @@ -26,7 +27,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts private readonly ConcurrentDictionary _channelCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - protected BaseTunerHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder) + protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder) { Config = config; Logger = logger; @@ -125,12 +126,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts foreach (var host in hostsWithChannel) { - var resourcePool = GetLock(host.Url); - Logger.Debug("GetChannelStreamMediaSources - Waiting on tuner resource pool"); - - await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - Logger.Debug("GetChannelStreamMediaSources - Unlocked resource pool"); - try { // Check to make sure the tuner is available @@ -156,93 +151,63 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { Logger.Error("Error opening tuner", ex); } - finally - { - resourcePool.Release(); - } } } return new List(); } - protected abstract Task GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken); + protected abstract Task GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken); - public async Task> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) + public async Task GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken) { - if (IsValidChannelId(channelId)) + if (!IsValidChannelId(channelId)) { - var hosts = GetTunerHosts(); - - var hostsWithChannel = new List(); + throw new FileNotFoundException(); + } - foreach (var host in hosts) - { - if (string.IsNullOrWhiteSpace(streamId)) - { - try - { - var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false); + var hosts = GetTunerHosts(); - if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase))) - { - hostsWithChannel.Add(host); - } - } - catch (Exception ex) - { - Logger.Error("Error getting channels", ex); - } - } - else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase)) - { - hostsWithChannel = new List {host}; - streamId = streamId.Substring(host.Id.Length); - break; - } - } + var hostsWithChannel = new List(); - foreach (var host in hostsWithChannel) + foreach (var host in hosts) + { + if (string.IsNullOrWhiteSpace(streamId)) { - var resourcePool = GetLock(host.Url); - Logger.Debug("GetChannelStream - Waiting on tuner resource pool"); - await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - Logger.Debug("GetChannelStream - Unlocked resource pool"); try { - // Check to make sure the tuner is available - // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error - // If a streamId is specified then availibility has already been checked in GetChannelStreamMediaSources - if (string.IsNullOrWhiteSpace(streamId) && hostsWithChannel.Count > 1) - { - if (!await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false)) - { - Logger.Error("Tuner is not currently available"); - resourcePool.Release(); - continue; - } - } - - var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); + var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false); - if (EnableMediaProbing) + if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase))) { - await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); + hostsWithChannel.Add(host); } - - return new Tuple(stream, resourcePool); } catch (Exception ex) { - Logger.Error("Error opening tuner", ex); - - resourcePool.Release(); + Logger.Error("Error getting channels", ex); } } + else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase)) + { + hostsWithChannel = new List { host }; + streamId = streamId.Substring(host.Id.Length); + break; + } } - else + + foreach (var host in hostsWithChannel) { - throw new FileNotFoundException(); + try + { + var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); + await liveStream.Open(cancellationToken).ConfigureAwait(false); + return liveStream; + } + catch (Exception ex) + { + Logger.Error("Error opening tuner", ex); + } } throw new LiveTvConflictException(); @@ -268,37 +233,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts protected abstract Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken); - /// - /// The _semaphoreLocks - /// - private readonly ConcurrentDictionary _semaphoreLocks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - /// - /// Gets the lock. - /// - /// The filename. - /// System.Object. - private SemaphoreSlim GetLock(string url) - { - return _semaphoreLocks.GetOrAdd(url, key => new SemaphoreSlim(1, 1)); - } - - private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + private async Task AddMediaInfo(LiveStream stream, bool isAudio, CancellationToken cancellationToken) { - await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + //await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - await AddMediaInfoInternal(mediaSource, isAudio, cancellationToken).ConfigureAwait(false); + //try + //{ + // await AddMediaInfoInternal(mediaSource, isAudio, cancellationToken).ConfigureAwait(false); - // Leave the resource locked. it will be released upstream - } - catch (Exception) - { - // Release the resource if there's some kind of failure. - resourcePool.Release(); + // // Leave the resource locked. it will be released upstream + //} + //catch (Exception) + //{ + // // Release the resource if there's some kind of failure. + // resourcePool.Release(); - throw; - } + // throw; + //} } private async Task AddMediaInfoInternal(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index c5bd648cf..b40b74436 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -14,7 +14,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using CommonIO; using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Net; @@ -24,11 +27,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost { private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; + private readonly IServerApplicationHost _appHost; - public HdHomerunHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient) + public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost) : base(config, logger, jsonSerializer, mediaEncoder) { _httpClient = httpClient; + _fileSystem = fileSystem; + _appHost = appHost; } public string Name @@ -355,6 +362,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun url += "?transcode=" + profile; } + var id = profile; + if (string.IsNullOrWhiteSpace(id)) + { + id = "native"; + } + id += "_" + url.GetMD5().ToString("N"); + var mediaSource = new MediaSourceInfo { Path = url, @@ -387,9 +401,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun RequiresClosing = false, BufferMs = 0, Container = "ts", - Id = profile, - SupportsDirectPlay = true, - SupportsDirectStream = false, + Id = id, + SupportsDirectPlay = false, + SupportsDirectStream = true, SupportsTranscoding = true }; @@ -452,9 +466,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase); } - protected override async Task GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) + protected override async Task GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { - Logger.Info("GetChannelStream: channel id: {0}. stream id: {1}", channelId, streamId ?? string.Empty); + var profile = streamId.Split('_')[0]; + + Logger.Info("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelId, streamId, profile); if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase)) { @@ -462,7 +478,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun } var hdhrId = GetHdHrIdFromChannelId(channelId); - return await GetMediaSource(info, hdhrId, streamId).ConfigureAwait(false); + var mediaSource = await GetMediaSource(info, hdhrId, profile).ConfigureAwait(false); + + var liveStream = new HdHomerunLiveStream(mediaSource, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + return liveStream; } public async Task Validate(TunerHostInfo info) diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs new file mode 100644 index 000000000..6078c4a70 --- /dev/null +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -0,0 +1,156 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using CommonIO; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller; +using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Server.Implementations.LiveTv.EmbyTV; + +namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun +{ + public class HdHomerunLiveStream : LiveStream + { + private readonly ILogger _logger; + private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; + private readonly IServerApplicationPaths _appPaths; + private readonly IServerApplicationHost _appHost; + + private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); + + public HdHomerunLiveStream(MediaSourceInfo mediaSource, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) + : base(mediaSource) + { + _fileSystem = fileSystem; + _httpClient = httpClient; + _logger = logger; + _appPaths = appPaths; + _appHost = appHost; + } + + public override async Task Open(CancellationToken openCancellationToken) + { + _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); + + var mediaSource = OriginalMediaSource; + + var url = mediaSource.Path; + var tempFile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts"); + Directory.CreateDirectory(Path.GetDirectoryName(tempFile)); + + _logger.Info("Opening HDHR Live stream from {0} to {1}", url, tempFile); + + var output = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read); + + var taskCompletionSource = new TaskCompletionSource(); + + StartStreamingToTempFile(output, tempFile, url, taskCompletionSource, _liveStreamCancellationTokenSource.Token); + + await taskCompletionSource.Task.ConfigureAwait(false); + + PublicMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts"; + + PublicMediaSource.Protocol = MediaProtocol.Http; + } + + public override Task Close() + { + _liveStreamCancellationTokenSource.Cancel(); + + return base.Close(); + } + + private async Task StartStreamingToTempFile(Stream outputStream, string tempFilePath, string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) + { + await Task.Run(async () => + { + using (outputStream) + { + var isFirstAttempt = true; + + while (!cancellationToken.IsCancellationRequested) + { + try + { + using (var response = await _httpClient.SendAsync(new HttpRequestOptions + { + Url = url, + CancellationToken = cancellationToken, + BufferContent = false + + }, "GET").ConfigureAwait(false)) + { + _logger.Info("Opened HDHR stream from {0}", url); + + if (!cancellationToken.IsCancellationRequested) + { + _logger.Info("Beginning DirectRecorder.CopyUntilCancelled"); + + Action onStarted = null; + if (isFirstAttempt) + { + onStarted = () => openTaskCompletionSource.TrySetResult(true); + } + await DirectRecorder.CopyUntilCancelled(response.Content, outputStream, onStarted, cancellationToken).ConfigureAwait(false); + } + } + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + if (isFirstAttempt) + { + _logger.ErrorException("Error opening live stream:", ex); + openTaskCompletionSource.TrySetException(ex); + break; + } + + _logger.ErrorException("Error copying live stream, will reopen", ex); + } + + isFirstAttempt = false; + } + } + + await Task.Delay(5000).ConfigureAwait(false); + + DeleteTempFile(tempFilePath); + + }).ConfigureAwait(false); + } + + 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 temp file {0}", ex, path); + } + + await Task.Delay(1000).ConfigureAwait(false); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 5c508aacd..d9c0bb8bf 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -13,8 +13,10 @@ using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Serialization; +using MediaBrowser.Server.Implementations.LiveTv.EmbyTV; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { @@ -23,7 +25,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; - public M3UTunerHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) + public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) : base(config, logger, jsonSerializer, mediaEncoder) { _fileSystem = fileSystem; @@ -63,11 +65,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(list); } - protected override async Task GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) + protected override async Task GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false); - return sources.First(); + var liveStream = new LiveStream(sources.First()); + return liveStream; } public async Task Validate(TunerHostInfo info) @@ -136,7 +139,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts RequiresOpening = false, RequiresClosing = false, - ReadAtNativeFramerate = false + ReadAtNativeFramerate = false, + + Id = channel.Path.GetMD5().ToString("N") }; return new List { mediaSource }; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs index b1e349a86..81deb2995 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs @@ -8,6 +8,7 @@ using CommonIO; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; @@ -16,6 +17,7 @@ using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; +using MediaBrowser.Server.Implementations.LiveTv.EmbyTV; namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { @@ -24,7 +26,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; - public SatIpHost(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) + public SatIpHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) : base(config, logger, jsonSerializer, mediaEncoder) { _fileSystem = fileSystem; @@ -113,11 +115,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp return new List(); } - protected override async Task GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken) + protected override async Task GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken) { var sources = await GetChannelStreamMediaSources(tuner, channelId, cancellationToken).ConfigureAwait(false); - return sources.First(); + var liveStream = new LiveStream(sources.First()); + + return liveStream; } protected override async Task IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index eb3da1a12..12691a69b 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -241,6 +241,7 @@ + diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 00279fb05..b57416fab 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -104,6 +104,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -437,15 +443,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - PreserveNewest @@ -470,9 +467,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs index 0844f1f74..bf3d3c303 100644 --- a/MediaBrowser.XbmcMetadata/EntryPoint.cs +++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs @@ -91,6 +91,16 @@ namespace MediaBrowser.XbmcMetadata return; } + if (!item.SupportsLocalMetadata) + { + return; + } + + if (!item.IsSaveLocalMetadataEnabled()) + { + return; + } + try { await _providerManager.SaveMetadata(item, updateReason, new[] { BaseNfoSaver.SaverName }).ConfigureAwait(false); -- cgit v1.2.3 From 68268cfb73d83c21df939a7992f3e4dcee9234f5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 27 Sep 2016 13:51:01 -0400 Subject: update closing of live streams --- .../Configuration/ServerConfiguration.cs | 1 + .../Library/LibraryManager.cs | 18 +++++++++++++++++- .../LiveTv/EmbyTV/EmbyTV.cs | 1 + .../LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 5 +++-- 4 files changed, 22 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index e45aa58c5..1d2928f67 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -73,6 +73,7 @@ namespace MediaBrowser.Model.Configuration /// /// The metadata path. public string MetadataPath { get; set; } + public string MetadataNetworkPath { get; set; } public string LastVersion { get; set; } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index b076996df..a3930f36a 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2550,9 +2550,25 @@ namespace MediaBrowser.Server.Implementations.Library } } + var metadataPath = ConfigurationManager.Configuration.MetadataPath; + var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath; + + if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath)) + { + var metadataSubstitutionResult = SubstitutePathInternal(path, metadataPath, metadataNetworkPath); + if (metadataSubstitutionResult.Item2) + { + return metadataSubstitutionResult.Item1; + } + } + foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) { - path = SubstitutePath(path, map.From, map.To); + var substitutionResult = SubstitutePathInternal(path, map.From, map.To); + if (substitutionResult.Item2) + { + return substitutionResult.Item1; + } } return path; diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 6ea3fbc5e..f53ec3ee8 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -908,6 +908,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { await stream.Close().ConfigureAwait(false); + _logger.Info("Live stream {0} closed successfully", id); } catch (Exception ex) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index 6078c4a70..d3540d180 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -22,6 +22,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly IServerApplicationHost _appHost; private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource(); + private readonly TaskCompletionSource _liveStreamTaskCompletionSource = new TaskCompletionSource(); public HdHomerunLiveStream(MediaSourceInfo mediaSource, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost) : base(mediaSource) @@ -62,7 +63,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { _liveStreamCancellationTokenSource.Cancel(); - return base.Close(); + return _liveStreamTaskCompletionSource.Task; } private async Task StartStreamingToTempFile(Stream outputStream, string tempFilePath, string url, TaskCompletionSource openTaskCompletionSource, CancellationToken cancellationToken) @@ -120,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun } } - await Task.Delay(5000).ConfigureAwait(false); + _liveStreamTaskCompletionSource.TrySetResult(true); DeleteTempFile(tempFilePath); -- cgit v1.2.3 From f5d37ed659d88bfa00483deddbbf93c33c2e10d6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 28 Sep 2016 01:11:41 -0400 Subject: add guide settings --- .../Channels/RefreshChannelsScheduledTask.cs | 2 +- MediaBrowser.Server.Implementations/Library/LibraryManager.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 005bbb852..5ac3da3db 100644 --- a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Channels public string Category { - get { return "Channels"; } + get { return "Internet Channels"; } } public async Task Execute(System.Threading.CancellationToken cancellationToken, IProgress progress) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index a3930f36a..f6fb158ae 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2882,12 +2882,12 @@ namespace MediaBrowser.Server.Implementations.Library { var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); - SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions); - var list = libraryOptions.PathInfos.ToList(); list.Add(pathInfo); libraryOptions.PathInfos = list.ToArray(); + SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions); + CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); } } -- cgit v1.2.3 From 76c7bfcb6795771cb06ef354fbf76d6e39de8948 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 29 Sep 2016 08:55:49 -0400 Subject: update closing of streams --- MediaBrowser.Api/ApiEntryPoint.cs | 8 +- MediaBrowser.Api/LiveTv/LiveTvService.cs | 46 ++++++- MediaBrowser.Api/Playback/BaseStreamingService.cs | 16 +-- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- .../Progressive/BaseProgressiveStreamingService.cs | 2 - MediaBrowser.Api/Playback/StreamState.cs | 2 +- .../Entities/InternalItemsQuery.cs | 2 + .../Library/IMediaSourceManager.cs | 3 +- .../Library/IMediaSourceProvider.cs | 3 +- MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 3 +- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 5 +- MediaBrowser.Controller/LiveTv/LiveStream.cs | 23 +++- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 53 +++++++- MediaBrowser.Controller/LiveTv/TimerInfo.cs | 1 + MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs | 2 +- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 7 +- MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs | 24 ++++ MediaBrowser.Model/LiveTv/ProgramQuery.cs | 7 + .../LiveTv/RecommendedProgramQuery.cs | 6 + MediaBrowser.Model/LiveTv/RecordingQuery.cs | 1 + .../Channels/ChannelDynamicMediaSourceProvider.cs | 2 +- .../Library/MediaSourceManager.cs | 59 +++++---- .../LiveTv/EmbyTV/DirectRecorder.cs | 16 +-- .../LiveTv/EmbyTV/EmbyTV.cs | 143 +++++++++++++-------- .../LiveTv/EmbyTV/EncodedRecorder.cs | 39 +----- .../LiveTv/EmbyTV/RecordingHelper.cs | 1 + .../LiveTv/LiveTvManager.cs | 90 ++++++++----- .../LiveTv/LiveTvMediaSourceProvider.cs | 4 +- .../LiveTv/TunerHosts/BaseTunerHost.cs | 4 +- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 8 -- .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 7 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 5 - .../Persistence/SqliteItemRepository.cs | 113 +++++++++++++--- .../Session/SessionManager.cs | 2 +- .../Sync/SyncedMediaSourceProvider.cs | 2 +- MediaBrowser.WebDashboard/Api/DashboardService.cs | 90 +------------ 36 files changed, 475 insertions(+), 326 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 2f5b9e1e0..c6e45c61a 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/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); } } diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index a5f8fce6e..9c1105082 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/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; } + /// /// The maximum number of items to return /// @@ -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 diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index fe9869664..8e57650b4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/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) { diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 656b2ee08..761538c83 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/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); } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index a68319109..4bb62f47f 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/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; } /// diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index ef0282abc..019f378c5 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/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) { diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index deea63112..d917b7d6d 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/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; } diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 1df77cdc9..c06470c5e 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -92,8 +92,7 @@ namespace MediaBrowser.Controller.Library /// Closes the media source. /// /// The live stream identifier. - /// The cancellation token. /// Task. - Task CloseLiveStream(string id, CancellationToken cancellationToken); + Task CloseLiveStream(string id); } } diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs index 5b033af4a..56366e5a8 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs @@ -28,8 +28,7 @@ namespace MediaBrowser.Controller.Library /// Closes the media source. /// /// The live stream identifier. - /// The cancellation token. /// Task. - Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken); + Task CloseMediaSource(string liveStreamId); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 41c5dbdbb..d65d1ae30 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -220,9 +220,8 @@ namespace MediaBrowser.Controller.LiveTv /// Closes the live stream. /// /// The identifier. - /// The cancellation token. /// Task. - Task CloseLiveStream(string id, CancellationToken cancellationToken); + Task CloseLiveStream(string id); /// /// Gets the guide information. diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 3c8b964a2..89d035649 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -22,9 +22,8 @@ namespace MediaBrowser.Controller.LiveTv /// /// Gets the channels. /// - /// The cancellation token. /// Task<IEnumerable<ChannelInfo>>. - Task> GetChannels(CancellationToken cancellationToken); + Task> GetChannels(bool enableCache, CancellationToken cancellationToken); /// /// Gets the tuner infos. /// @@ -46,8 +45,6 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); - - string ApplyDuration(string streamPath, TimeSpan duration); } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Controller/LiveTv/LiveStream.cs b/MediaBrowser.Controller/LiveTv/LiveStream.cs index 15d09d857..1bb198632 100644 --- a/MediaBrowser.Controller/LiveTv/LiveStream.cs +++ b/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); } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 50aeed27d..69a1c24ea 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/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 GetUserDataKeys() { @@ -137,5 +137,56 @@ namespace MediaBrowser.Controller.LiveTv { return false; } + + [IgnoreDataMember] + public bool IsMovie { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is sports. + /// + /// true if this instance is sports; otherwise, false. + [IgnoreDataMember] + public bool IsSports { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is series. + /// + /// true if this instance is series; otherwise, false. + [IgnoreDataMember] + public bool IsSeries { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is live. + /// + /// true if this instance is live; otherwise, false. + [IgnoreDataMember] + public bool IsLive { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is news. + /// + /// true if this instance is news; otherwise, false. + [IgnoreDataMember] + public bool IsNews { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is kids. + /// + /// true if this instance is kids; otherwise, false. + [IgnoreDataMember] + public bool IsKids { get; set; } + + [IgnoreDataMember] + public bool IsPremiere { get; set; } + + [IgnoreDataMember] + public bool IsRepeat { get; set; } + + /// + /// Gets or sets the episode title. + /// + /// The episode title. + [IgnoreDataMember] + public string EpisodeTitle { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index 42c3480ce..978e9e1a8 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/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; } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index 490a51128..d3738d903 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/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) { diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index bb07d9cb6..2be69c8e4 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/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(); diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs index 0ece1e4a0..f76368a7b 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs @@ -61,6 +61,30 @@ namespace MediaBrowser.Model.LiveTv public bool AddCurrentProgram { get; set; } public bool EnableUserData { get; set; } + /// + /// Used to specific whether to return news or not + /// + /// If set to null, all programs will be returned + public bool? IsNews { get; set; } + + /// + /// Used to specific whether to return movies or not + /// + /// If set to null, all programs will be returned + public bool? IsMovie { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is kids. + /// + /// null if [is kids] contains no value, true if [is kids]; otherwise, false. + public bool? IsKids { get; set; } + /// + /// Gets or sets a value indicating whether this instance is sports. + /// + /// null if [is sports] contains no value, true if [is sports]; otherwise, false. + public bool? IsSports { get; set; } + public bool? IsSeries { get; set; } + public LiveTvChannelQuery() { EnableUserData = true; diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index bf459237a..7886342e7 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -62,6 +62,12 @@ namespace MediaBrowser.Model.LiveTv /// public DateTime? MaxEndDate { get; set; } + /// + /// Used to specific whether to return news or not + /// + /// If set to null, all programs will be returned + public bool? IsNews { get; set; } + /// /// Used to specific whether to return movies or not /// @@ -83,6 +89,7 @@ namespace MediaBrowser.Model.LiveTv /// Skips over a given number of items within the results. Use for paging. /// public int? StartIndex { get; set; } + public bool? IsSeries { get; set; } /// /// Gets or sets a value indicating whether this instance has aired. diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs index 0e6d081a1..4bc506bf6 100644 --- a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs @@ -45,11 +45,17 @@ namespace MediaBrowser.Model.LiveTv /// The limit. public int? Limit { get; set; } + /// + /// Gets or sets a value indicating whether this instance is movie. + /// + /// null if [is movie] contains no value, true if [is movie]; otherwise, false. + public bool? IsNews { get; set; } /// /// Gets or sets a value indicating whether this instance is movie. /// /// null if [is movie] contains no value, true if [is movie]; otherwise, false. public bool? IsMovie { get; set; } + public bool? IsSeries { get; set; } /// /// Gets or sets a value indicating whether this instance is kids. /// diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs index 0ba5f1779..265aad335 100644 --- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -68,6 +68,7 @@ namespace MediaBrowser.Model.LiveTv /// The fields. 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; } diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index 3239b20b2..6cba1b441 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/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(); } diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index c20245a6e..c7650102f 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -355,7 +355,7 @@ namespace MediaBrowser.Server.Implementations.Library .ToList(); } - private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _openStreams = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); public async Task 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 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(); } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 0d043669a..0f8c15e71 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/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); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index f53ec3ee8..ef19dcbc9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -340,22 +340,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _timerProvider.Delete(timer); } - private List _channelCache = null; private async Task> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken) { - if (enableCache && _channelCache != null) - { - - return _channelCache.ToList(); - } - var list = new List(); 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(json); + + mediaSource.Id = consumerId.ToString(CultureInfo.InvariantCulture) + "_" + mediaSource.Id; + + return mediaSource; } - private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) + private async Task> 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(result, hostInstance); - } - catch (FileNotFoundException) - { - } - catch (Exception e) + //_liveStreamsSemaphore.Release(); + //return new Tuple(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(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 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) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index f74a76e3f..ce5f14f4b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/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 /// /// Processes the exited. /// - 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() diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index f9c04abc5..bb6935e8e 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/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; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 8a4572813..7c61d2396 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -62,9 +62,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv private readonly List _services = new List(); - private readonly ConcurrentDictionary _openStreams = - new ConcurrentDictionary(); - private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1); private readonly List _tunerHosts = new List(); @@ -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; /// /// 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(); - } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index cdba1873e..aacc0c22b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/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); } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 6beea352a..a4236763f 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts .ToList(); } - public async Task> GetChannels(CancellationToken cancellationToken) + public async Task> GetChannels(bool enableCache, CancellationToken cancellationToken) { var list = new List(); @@ -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); diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index b40b74436..9f71940e1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/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> GetLineup(TunerHostInfo info, CancellationToken cancellationToken) { var options = new HttpRequestOptions diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index d3540d180..d6574db22 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/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; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index d9c0bb8bf..e5d6102b1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/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; - } } } diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 05c282687..097118418 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/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(); - if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name)) + if (query.IsMovie.HasValue) + { + var alternateTypes = new List(); + 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(); + if (query.IsMovie ?? false) + { + var alternateTypes = new List(); + 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"); diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 48f48cdcc..5857e9dbc 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/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) { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index a2b5851ac..cb666b6ac 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/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(); } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 2f8b34879..4850b6fe0 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -289,7 +289,7 @@ namespace MediaBrowser.WebDashboard.Api return list; } - private List> GetDeployIgnoreFilenames() + private List> GetDeployIgnoreFilenames() { var list = new List>(); @@ -313,8 +313,11 @@ namespace MediaBrowser.WebDashboard.Api public async Task 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)) { -- cgit v1.2.3 From 911d9f4598e4cafe24a8eb6d40a0d95356d3437c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 2 Oct 2016 00:31:47 -0400 Subject: move more metadata settings to per library --- MediaBrowser.Api/StartupWizardService.cs | 6 ----- MediaBrowser.Controller/Entities/BaseItem.cs | 6 +++-- .../Entities/CollectionFolder.cs | 2 +- MediaBrowser.Model/Configuration/ChapterOptions.cs | 6 ----- MediaBrowser.Model/Configuration/LibraryOptions.cs | 4 ++++ .../MediaInfo/FFProbeVideoInfo.cs | 4 ++-- .../TV/MissingEpisodeProvider.cs | 2 +- .../TV/TheTVDB/TvdbPrescanTask.cs | 17 ++++++++------ .../Library/LibraryManager.cs | 27 ++++++++++++++-------- .../MediaEncoder/EncodingManager.cs | 26 ++------------------- .../Session/SessionManager.cs | 3 +-- 11 files changed, 42 insertions(+), 61 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 176b497d7..ebb3204a4 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -88,8 +88,6 @@ namespace MediaBrowser.Api var result = new StartupConfiguration { UICulture = _config.Configuration.UICulture, - EnableInternetProviders = _config.Configuration.EnableInternetProviders, - SaveLocalMeta = _config.Configuration.SaveLocalMeta, MetadataCountryCode = _config.Configuration.MetadataCountryCode, PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage }; @@ -123,8 +121,6 @@ namespace MediaBrowser.Api public void Post(UpdateStartupConfiguration request) { _config.Configuration.UICulture = request.UICulture; - _config.Configuration.EnableInternetProviders = request.EnableInternetProviders; - _config.Configuration.SaveLocalMeta = request.SaveLocalMeta; _config.Configuration.MetadataCountryCode = request.MetadataCountryCode; _config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage; _config.SaveConfiguration(); @@ -215,8 +211,6 @@ namespace MediaBrowser.Api public class StartupConfiguration { public string UICulture { get; set; } - public bool EnableInternetProviders { get; set; } - public bool SaveLocalMeta { get; set; } public string MetadataCountryCode { get; set; } public string PreferredMetadataLanguage { get; set; } public string LiveTvTunerType { get; set; } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 492058f98..be88c535e 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -422,7 +422,7 @@ namespace MediaBrowser.Controller.Entities public virtual bool IsInternetMetadataEnabled() { - return ConfigurationManager.Configuration.EnableInternetProviders; + return LibraryManager.GetLibraryOptions(this).EnableInternetProviders; } public virtual bool CanDelete() @@ -1341,7 +1341,9 @@ namespace MediaBrowser.Controller.Entities return false; } - return ConfigurationManager.Configuration.SaveLocalMeta; + var libraryOptions = LibraryManager.GetLibraryOptions(this); + + return libraryOptions.SaveLocalMetadata; } /// diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 77d7ca7f2..04ba53263 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -111,7 +111,7 @@ namespace MediaBrowser.Controller.Entities { LibraryOptions[path] = options; - options.SchemaVersion = 2; + options.SchemaVersion = 3; XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path)); } } diff --git a/MediaBrowser.Model/Configuration/ChapterOptions.cs b/MediaBrowser.Model/Configuration/ChapterOptions.cs index f9ff6b4f9..c7bb6f861 100644 --- a/MediaBrowser.Model/Configuration/ChapterOptions.cs +++ b/MediaBrowser.Model/Configuration/ChapterOptions.cs @@ -2,17 +2,11 @@ { public class ChapterOptions { - public bool EnableMovieChapterImageExtraction { get; set; } - public bool EnableEpisodeChapterImageExtraction { get; set; } - public bool EnableOtherVideoChapterImageExtraction { get; set; } - public bool DownloadMovieChapters { get; set; } public bool DownloadEpisodeChapters { get; set; } public string[] FetcherOrder { get; set; } public string[] DisabledFetchers { get; set; } - - public bool ExtractDuringLibraryScan { get; set; } public ChapterOptions() { diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 460ee0918..5f8c77ccd 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -11,11 +11,15 @@ public bool DownloadImagesInAdvance { get; set; } public MediaPathInfo[] PathInfos { get; set; } + public bool SaveLocalMetadata { get; set; } + public bool EnableInternetProviders { get; set; } + public LibraryOptions() { EnablePhotos = true; EnableRealtimeMonitor = true; PathInfos = new MediaPathInfo[] { }; + EnableInternetProviders = true; } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 0f8cf93fb..66fe7ea81 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -262,8 +262,8 @@ namespace MediaBrowser.Providers.MediaInfo NormalizeChapterNames(chapters); var libraryOptions = _libraryManager.GetLibraryOptions(video); - var extractDuringScan = chapterOptions.ExtractDuringLibraryScan; - if (libraryOptions != null && libraryOptions.SchemaVersion >= 2) + var extractDuringScan = false; + if (libraryOptions != null) { extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan; } diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index a12402f4f..9c212e8a0 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -118,7 +118,7 @@ namespace MediaBrowser.Providers.TV var hasNewEpisodes = false; - if (_config.Configuration.EnableInternetProviders && addNewItems) + if (addNewItems && !group.Any(i => !i.IsInternetMetadataEnabled())) { var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index 215e0640f..d9e1037d8 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -74,12 +74,6 @@ namespace MediaBrowser.Providers.TV /// Task. public async Task Run(IProgress progress, CancellationToken cancellationToken) { - if (!_config.Configuration.EnableInternetProviders) - { - progress.Report(100); - return; - } - var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase)); if (seriesConfig != null && seriesConfig.DisabledMetadataFetchers.Contains(TvdbSeriesProvider.Current.Name, StringComparer.OrdinalIgnoreCase)) @@ -116,7 +110,9 @@ namespace MediaBrowser.Providers.TV IncludeItemTypes = new[] { typeof(Series).Name }, Recursive = true, GroupByPresentationUniqueKey = false - }).Cast(); + + }).Cast() + .ToList(); var seriesIdsInLibrary = seriesList .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb))) @@ -126,6 +122,13 @@ namespace MediaBrowser.Providers.TV var missingSeries = seriesIdsInLibrary.Except(existingDirectories, StringComparer.OrdinalIgnoreCase) .ToList(); + var enableInternetProviders = seriesList.Count == 0 ? false : seriesList[0].IsInternetMetadataEnabled(); + if (!enableInternetProviders) + { + progress.Report(100); + return; + } + // If this is our first time, update all series if (string.IsNullOrEmpty(lastUpdateTime)) { diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index f6fb158ae..93ee91c21 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1216,12 +1216,7 @@ namespace MediaBrowser.Server.Implementations.Library if (libraryFolder != null) { info.ItemId = libraryFolder.Id.ToString("N"); - } - - var collectionFolder = libraryFolder as CollectionFolder; - if (collectionFolder != null) - { - info.LibraryOptions = collectionFolder.GetLibraryOptions(); + info.LibraryOptions = GetLibraryOptions(libraryFolder); } return info; @@ -1889,11 +1884,23 @@ namespace MediaBrowser.Server.Implementations.Library public LibraryOptions GetLibraryOptions(BaseItem item) { - var collectionFolder = GetCollectionFolders(item) - .OfType() - .FirstOrDefault(); + var collectionFolder = item as CollectionFolder; + if (collectionFolder == null) + { + collectionFolder = GetCollectionFolders(item) + .OfType() + .FirstOrDefault(); + } + + var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); + + if (options.SchemaVersion < 3) + { + options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta; + options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders; + } - return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); + return options; } public string GetContentType(BaseItem item) diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index 06c109dfc..21e847c68 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } var libraryOptions = _libraryManager.GetLibraryOptions(video); - if (libraryOptions != null && libraryOptions.SchemaVersion >= 2) + if (libraryOptions != null) { if (!libraryOptions.EnableChapterImageExtraction) { @@ -70,29 +70,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } else { - var options = _chapterManager.GetConfiguration(); - - if (video is Movie) - { - if (!options.EnableMovieChapterImageExtraction) - { - return false; - } - } - else if (video is Episode) - { - if (!options.EnableEpisodeChapterImageExtraction) - { - return false; - } - } - else - { - if (!options.EnableOtherVideoChapterImageExtraction) - { - return false; - } - } + return false; } // Can't extract images if there are no video streams diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index d2bdee9fa..2fcc76588 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -242,8 +242,7 @@ namespace MediaBrowser.Server.Implementations.Session var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue; user.LastActivityDate = activityDate; - // Don't log in the db anymore frequently than 10 seconds - if ((activityDate - userLastActivityDate).TotalSeconds > 10) + if ((activityDate - userLastActivityDate).TotalSeconds > 60) { try { -- cgit v1.2.3 From cf4f5039bffa504b65569e2be5b2711e803e105b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 2 Oct 2016 02:13:04 -0400 Subject: update components --- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 761538c83..7fe7d5a21 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -107,7 +107,7 @@ namespace MediaBrowser.Api.Playback { var authInfo = AuthorizationContext.GetAuthorizationInfo(Request); - var result = await _mediaSourceManager.OpenLiveStream(request, false, CancellationToken.None).ConfigureAwait(false); + var result = await _mediaSourceManager.OpenLiveStream(request, true, CancellationToken.None).ConfigureAwait(false); var profile = request.DeviceProfile; if (profile == null) diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index c7650102f..ae32bdaf7 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -456,7 +456,7 @@ namespace MediaBrowser.Server.Implementations.Library } else { - _logger.Error("Failed to update MediaSource timestamp for {0}", id); + _logger.Error("Failed to ping live stream {0}", id); } } finally @@ -540,7 +540,7 @@ namespace MediaBrowser.Server.Implementations.Library } private Timer _closeTimer; - private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(60); + private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(180); private void StartCloseTimer() { -- cgit v1.2.3 From b9cacd8076c00bfc371898c50315304927efaff8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 5 Oct 2016 03:15:29 -0400 Subject: update live streams --- MediaBrowser.Api/Library/ChapterService.cs | 30 --- MediaBrowser.Api/LiveTv/LiveTvService.cs | 2 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 1 - MediaBrowser.Api/Playback/BaseStreamingService.cs | 4 +- .../Progressive/BaseProgressiveStreamingService.cs | 35 ++-- .../Progressive/ProgressiveStreamWriter.cs | 29 ++- MediaBrowser.Api/Playback/StreamState.cs | 1 + .../Chapters/ChapterResponse.cs | 19 -- .../Chapters/ChapterSearchRequest.cs | 31 --- .../Chapters/IChapterManager.cs | 47 +---- .../Chapters/IChapterProvider.cs | 39 ---- .../Library/IMediaSourceManager.cs | 8 + .../Library/IMediaSourceProvider.cs | 3 +- MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 3 +- MediaBrowser.Controller/LiveTv/ILiveTvService.cs | 6 + .../MediaBrowser.Controller.csproj | 3 - .../MediaBrowser.Model.Portable.csproj | 9 - .../MediaBrowser.Model.net35.csproj | 9 - MediaBrowser.Model/Chapters/ChapterProviderInfo.cs | 8 - MediaBrowser.Model/Chapters/RemoteChapterInfo.cs | 18 -- MediaBrowser.Model/Chapters/RemoteChapterResult.cs | 48 ----- MediaBrowser.Model/Configuration/ChapterOptions.cs | 16 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 3 - MediaBrowser.Providers/Chapters/ChapterManager.cs | 220 --------------------- .../MediaInfo/FFProbeVideoInfo.cs | 62 ------ .../MediaInfo/SubtitleScheduledTask.cs | 90 +++++++-- .../Channels/ChannelDynamicMediaSourceProvider.cs | 2 +- .../Library/LibraryManager.cs | 19 ++ .../Library/MediaSourceManager.cs | 19 +- .../LiveTv/EmbyTV/EmbyTV.cs | 11 +- .../LiveTv/LiveTvManager.cs | 28 ++- .../LiveTv/LiveTvMediaSourceProvider.cs | 11 +- .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 79 +++++++- .../Sync/SyncedMediaSourceProvider.cs | 4 +- .../ApplicationHost.cs | 1 - 35 files changed, 293 insertions(+), 625 deletions(-) delete mode 100644 MediaBrowser.Api/Library/ChapterService.cs delete mode 100644 MediaBrowser.Controller/Chapters/ChapterResponse.cs delete mode 100644 MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs delete mode 100644 MediaBrowser.Controller/Chapters/IChapterProvider.cs delete mode 100644 MediaBrowser.Model/Chapters/ChapterProviderInfo.cs delete mode 100644 MediaBrowser.Model/Chapters/RemoteChapterInfo.cs delete mode 100644 MediaBrowser.Model/Chapters/RemoteChapterResult.cs (limited to 'MediaBrowser.Server.Implementations/Library') diff --git a/MediaBrowser.Api/Library/ChapterService.cs b/MediaBrowser.Api/Library/ChapterService.cs deleted file mode 100644 index 6b8dd18f1..000000000 --- a/MediaBrowser.Api/Library/ChapterService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MediaBrowser.Controller.Chapters; -using MediaBrowser.Controller.Net; -using ServiceStack; -using System.Linq; - -namespace MediaBrowser.Api.Library -{ - [Route("/Providers/Chapters", "GET")] - public class GetChapterProviders : IReturnVoid - { - } - - [Authenticated] - public class ChapterService : BaseApiService - { - private readonly IChapterManager _chapterManager; - - public ChapterService(IChapterManager chapterManager) - { - _chapterManager = chapterManager; - } - - public object Get(GetChapterProviders request) - { - var result = _chapterManager.GetProviders().ToList(); - - return ToOptimizedResult(result); - } - } -} diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 4baae031f..ed7a0990f 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -705,7 +705,7 @@ namespace MediaBrowser.Api.LiveTv var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath); + outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType(filePath); long startPosition = 0; diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 77949d179..a98637650 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -79,7 +79,6 @@ - diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index bcf3bd28e..0b2fad580 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1888,7 +1888,9 @@ namespace MediaBrowser.Api.Playback } else { - mediaSource = await MediaSourceManager.GetLiveStream(request.LiveStreamId, cancellationToken).ConfigureAwait(false); + var liveStreamInfo = await MediaSourceManager.GetLiveStreamWithDirectStreamProvider(request.LiveStreamId, cancellationToken).ConfigureAwait(false); + mediaSource = liveStreamInfo.Item1; + state.DirectStreamProvider = liveStreamInfo.Item2; } var videoRequest = request as VideoStreamRequest; diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 4adf6fbca..809eabef8 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -121,32 +121,33 @@ namespace MediaBrowser.Api.Playback.Progressive var responseHeaders = new Dictionary(); - // Static remote stream - if (request.Static && state.InputProtocol == MediaProtocol.Http) + if (request.Static && state.DirectStreamProvider != null) { AddDlnaHeaders(state, responseHeaders, true); using (state) { - if (state.MediaPath.IndexOf("/livestreamfiles/", StringComparison.OrdinalIgnoreCase) != -1) - { - var parts = state.MediaPath.Split('/'); - var filename = parts[parts.Length - 2] + Path.GetExtension(parts[parts.Length - 1]); - var filePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, filename); + var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - var outputHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + // TODO: Don't hardcode this + outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts"); - outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath); + var streamSource = new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None) + { + AllowEndOfFile = false + }; + return ResultFactory.GetAsyncStreamWriter(streamSource); + } + } - var streamSource = new ProgressiveFileCopier(FileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None) - { - AllowEndOfFile = false - }; - return ResultFactory.GetAsyncStreamWriter(streamSource); - } + // Static remote stream + if (request.Static && state.InputProtocol == MediaProtocol.Http) + { + AddDlnaHeaders(state, responseHeaders, true); - return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource) - .ConfigureAwait(false); + using (state) + { + return await GetStaticRemoteStreamResult(state, responseHeaders, isHeadRequest, cancellationTokenSource).ConfigureAwait(false); } } diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index f601f4aa3..3477ad57b 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -7,6 +7,7 @@ using CommonIO; using MediaBrowser.Controller.Net; using System.Collections.Generic; using ServiceStack.Web; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Api.Playback.Progressive { @@ -26,6 +27,8 @@ namespace MediaBrowser.Api.Playback.Progressive public long StartPosition { get; set; } public bool AllowEndOfFile = true; + private IDirectStreamProvider _directStreamProvider; + public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) { _fileSystem = fileSystem; @@ -36,6 +39,15 @@ namespace MediaBrowser.Api.Playback.Progressive _cancellationToken = cancellationToken; } + public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken) + { + _directStreamProvider = directStreamProvider; + _outputHeaders = outputHeaders; + _job = job; + _logger = logger; + _cancellationToken = cancellationToken; + } + public IDictionary Options { get @@ -44,22 +56,33 @@ namespace MediaBrowser.Api.Playback.Progressive } } + private Stream GetInputStream() + { + return _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true); + } + public async Task WriteToAsync(Stream outputStream) { try { + if (_directStreamProvider != null) + { + await _directStreamProvider.CopyToAsync(outputStream, _cancellationToken).ConfigureAwait(false); + return; + } + var eofCount = 0; - using (var fs = _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + using (var inputStream = GetInputStream()) { if (StartPosition > 0) { - fs.Position = StartPosition; + inputStream.Position = StartPosition; } while (eofCount < 15 || !AllowEndOfFile) { - var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false); + var bytesRead = await CopyToAsyncInternal(inputStream, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false); //var position = fs.Position; //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 863bc0193..a59a7fe09 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -37,6 +37,7 @@ namespace MediaBrowser.Api.Playback /// /// The log file stream. public Stream LogFileStream { get; set; } + public IDirectStreamProvider DirectStreamProvider { get; set; } public string InputContainer { get; set; } diff --git a/MediaBrowser.Controller/Chapters/ChapterResponse.cs b/MediaBrowser.Controller/Chapters/ChapterResponse.cs deleted file mode 100644 index 3c1b8ed07..000000000 --- a/MediaBrowser.Controller/Chapters/ChapterResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MediaBrowser.Model.Chapters; -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Chapters -{ - public class ChapterResponse - { - /// - /// Gets or sets the chapters. - /// - /// The chapters. - public List Chapters { get; set; } - - public ChapterResponse() - { - Chapters = new List(); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs b/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs deleted file mode 100644 index 982dc35bb..000000000 --- a/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Chapters -{ - public class ChapterSearchRequest : IHasProviderIds - { - public string Language { get; set; } - - public VideoContentType ContentType { get; set; } - - public string MediaPath { get; set; } - public string SeriesName { get; set; } - public string Name { get; set; } - public int? IndexNumber { get; set; } - public int? IndexNumberEnd { get; set; } - public int? ParentIndexNumber { get; set; } - public int? ProductionYear { get; set; } - public long? RuntimeTicks { get; set; } - public Dictionary ProviderIds { get; set; } - - public bool SearchAllProviders { get; set; } - - public ChapterSearchRequest() - { - ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs index 27e06fb8d..4b39e66cc 100644 --- a/MediaBrowser.Controller/Chapters/IChapterManager.cs +++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs @@ -1,6 +1,4 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Chapters; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Configuration; @@ -13,12 +11,6 @@ namespace MediaBrowser.Controller.Chapters /// public interface IChapterManager { - /// - /// Adds the parts. - /// - /// The chapter providers. - void AddParts(IEnumerable chapterProviders); - /// /// Gets the chapters. /// @@ -35,43 +27,6 @@ namespace MediaBrowser.Controller.Chapters /// Task. Task SaveChapters(string itemId, List chapters, CancellationToken cancellationToken); - /// - /// Searches the specified video. - /// - /// The video. - /// The cancellation token. - /// Task{IEnumerable{RemoteChapterResult}}. - Task> Search(Video video, CancellationToken cancellationToken); - - /// - /// Searches the specified request. - /// - /// The request. - /// The cancellation token. - /// Task{IEnumerable{RemoteChapterResult}}. - Task> Search(ChapterSearchRequest request, CancellationToken cancellationToken); - - /// - /// Gets the chapters. - /// - /// The identifier. - /// The cancellation token. - /// Task{ChapterResponse}. - Task GetChapters(string id, CancellationToken cancellationToken); - - /// - /// Gets the providers. - /// - /// The item identifier. - /// IEnumerable{ChapterProviderInfo}. - IEnumerable GetProviders(string itemId); - - /// - /// Gets the providers. - /// - /// IEnumerable{ChapterProviderInfo}. - IEnumerable GetProviders(); - /// /// Gets the configuration. /// diff --git a/MediaBrowser.Controller/Chapters/IChapterProvider.cs b/MediaBrowser.Controller/Chapters/IChapterProvider.cs deleted file mode 100644 index a7505347b..000000000 --- a/MediaBrowser.Controller/Chapters/IChapterProvider.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Chapters; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Chapters -{ - public interface IChapterProvider - { - /// - /// Gets the name. - /// - /// The name. - string Name { get; } - - /// - /// Gets the supported media types. - /// - /// The supported media types. - IEnumerable SupportedMediaTypes { get; } - - /// - /// Searches the specified request. - /// - /// The request. - /// The cancellation token. - /// Task{IEnumerable{RemoteChapterResult}}. - Task> Search(ChapterSearchRequest request, CancellationToken cancellationToken); - - /// - /// Gets the chapters. - /// - /// The identifier. - /// The cancellation token. - /// Task{ChapterResponse}. - Task GetChapters(string id, CancellationToken cancellationToken); - } -} diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index c06470c5e..1ab0e4cb0 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using System.IO; namespace MediaBrowser.Controller.Library { @@ -79,6 +80,8 @@ namespace MediaBrowser.Controller.Library /// The cancellation token. /// Task<MediaSourceInfo>. Task GetLiveStream(string id, CancellationToken cancellationToken); + + Task> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken); /// /// Pings the media source. @@ -95,4 +98,9 @@ namespace MediaBrowser.Controller.Library /// Task. Task CloseLiveStream(string id); } + + public interface IDirectStreamProvider + { + Task CopyToAsync(Stream stream, CancellationToken cancellationToken); + } } diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs index 56366e5a8..b0881ba7c 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs @@ -3,6 +3,7 @@ using MediaBrowser.Model.Dto; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using System; namespace MediaBrowser.Controller.Library { @@ -22,7 +23,7 @@ namespace MediaBrowser.Controller.Library /// The open token. /// The cancellation token. /// Task<MediaSourceInfo>. - Task OpenMediaSource(string openToken, CancellationToken cancellationToken); + Task> OpenMediaSource(string openToken, CancellationToken cancellationToken); /// /// Closes the media source. diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index d65d1ae30..a381c7980 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Events; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.LiveTv { @@ -156,7 +157,7 @@ namespace MediaBrowser.Controller.LiveTv /// The media source identifier. /// The cancellation token. /// Task{StreamResponseInfo}. - Task GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken); + Task> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken); /// /// Gets the program. diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index d7d8336d0..94082b42e 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.LiveTv { @@ -245,4 +246,9 @@ namespace MediaBrowser.Controller.LiveTv /// Task. Task CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken); } + + public interface ISupportsDirectStreamProvider + { + Task> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, CancellationToken cancellationToken); + } } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index d70fba742..e9d2054da 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -91,10 +91,7 @@ - - - diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index ad7dea0a5..59e29087c 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -160,15 +160,6 @@ Channels\ChannelQuery.cs - - Chapters\ChapterProviderInfo.cs - - - Chapters\RemoteChapterInfo.cs - - - Chapters\RemoteChapterResult.cs - Collections\CollectionCreationResult.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 61f2f3f13..47ebb3a92 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -132,15 +132,6 @@ Channels\ChannelQuery.cs - - Chapters\ChapterProviderInfo.cs - - - Chapters\RemoteChapterInfo.cs - - - Chapters\RemoteChapterResult.cs - Collections\CollectionCreationResult.cs diff --git a/MediaBrowser.Model/Chapters/ChapterProviderInfo.cs b/MediaBrowser.Model/Chapters/ChapterProviderInfo.cs deleted file mode 100644 index 570407c57..000000000 --- a/MediaBrowser.Model/Chapters/ChapterProviderInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MediaBrowser.Model.Chapters -{ - public class ChapterProviderInfo - { - public string Name { get; set; } - public string Id { get; set; } - } -} \ No newline at end of file diff --git a/MediaBrowser.Model/Chapters/RemoteChapterInfo.cs b/MediaBrowser.Model/Chapters/RemoteChapterInfo.cs deleted file mode 100644 index f2674c842..000000000 --- a/MediaBrowser.Model/Chapters/RemoteChapterInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ - -namespace MediaBrowser.Model.Chapters -{ - public class RemoteChapterInfo - { - /// - /// Gets or sets the start position ticks. - /// - /// The start position ticks. - public long StartPositionTicks { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - } -} diff --git a/MediaBrowser.Model/Chapters/RemoteChapterResult.cs b/MediaBrowser.Model/Chapters/RemoteChapterResult.cs deleted file mode 100644 index 425c3ded8..000000000 --- a/MediaBrowser.Model/Chapters/RemoteChapterResult.cs +++ /dev/null @@ -1,48 +0,0 @@ - -namespace MediaBrowser.Model.Chapters -{ - public class RemoteChapterResult - { - /// - /// Gets or sets the identifier. - /// - /// The identifier. - public string Id { get; set; } - - /// - /// Gets or sets the run time ticks. - /// - /// The run time ticks. - public long? RunTimeTicks { get; set; } - - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - - /// - /// Gets or sets the name of the provider. - /// - /// The name of the provider. - public string ProviderName { get; set; } - - /// - /// Gets or sets the community rating. - /// - /// The community rating. - public float? CommunityRating { get; set; } - - /// - /// Gets or sets the chapter count. - /// - /// The chapter count. - public int? ChapterCount { get; set; } - - /// - /// Gets or sets the name of the three letter iso language. - /// - /// The name of the three letter iso language. - public string ThreeLetterISOLanguageName { get; set; } - } -} diff --git a/MediaBrowser.Model/Configuration/ChapterOptions.cs b/MediaBrowser.Model/Configuration/ChapterOptions.cs index c7bb6f861..1c2b0c527 100644 --- a/MediaBrowser.Model/Configuration/ChapterOptions.cs +++ b/MediaBrowser.Model/Configuration/ChapterOptions.cs @@ -2,18 +2,10 @@ { public class ChapterOptions { - public bool DownloadMovieChapters { get; set; } - public bool DownloadEpisodeChapters { get; set; } + public bool EnableMovieChapterImageExtraction { get; set; } + public bool EnableEpisodeChapterImageExtraction { get; set; } + public bool EnableOtherVideoChapterImageExtraction { get; set; } - public string[] FetcherOrder { get; set; } - public string[] DisabledFetchers { get; set; } - - public ChapterOptions() - { - DownloadMovieChapters = true; - - DisabledFetchers = new string[] { }; - FetcherOrder = new string[] { }; - } + public bool ExtractDuringLibraryScan { get; set; } } } \ No newline at end of file diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index c1a01680d..69754204e 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -84,9 +84,6 @@ - - - diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs index 88811c850..87aaafb39 100644 --- a/MediaBrowser.Providers/Chapters/ChapterManager.cs +++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs @@ -8,7 +8,6 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Chapters; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -22,7 +21,6 @@ namespace MediaBrowser.Providers.Chapters { public class ChapterManager : IChapterManager { - private IChapterProvider[] _providers; private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; private readonly IServerConfigurationManager _config; @@ -36,224 +34,6 @@ namespace MediaBrowser.Providers.Chapters _itemRepo = itemRepo; } - public void AddParts(IEnumerable chapterProviders) - { - _providers = chapterProviders.ToArray(); - } - - public Task> Search(Video video, CancellationToken cancellationToken) - { - VideoContentType mediaType; - - if (video is Episode) - { - mediaType = VideoContentType.Episode; - } - else if (video is Movie) - { - mediaType = VideoContentType.Movie; - } - else - { - // These are the only supported types - return Task.FromResult>(new List()); - } - - var request = new ChapterSearchRequest - { - ContentType = mediaType, - IndexNumber = video.IndexNumber, - Language = video.GetPreferredMetadataLanguage(), - MediaPath = video.Path, - Name = video.Name, - ParentIndexNumber = video.ParentIndexNumber, - ProductionYear = video.ProductionYear, - ProviderIds = video.ProviderIds, - RuntimeTicks = video.RunTimeTicks, - SearchAllProviders = false - }; - - var episode = video as Episode; - - if (episode != null) - { - request.IndexNumberEnd = episode.IndexNumberEnd; - request.SeriesName = episode.SeriesName; - } - - return Search(request, cancellationToken); - } - - public async Task> Search(ChapterSearchRequest request, CancellationToken cancellationToken) - { - var contentType = request.ContentType; - var providers = GetInternalProviders(false) - .Where(i => i.SupportedMediaTypes.Contains(contentType)) - .ToList(); - - // If not searching all, search one at a time until something is found - if (!request.SearchAllProviders) - { - foreach (var provider in providers) - { - try - { - var currentResults = await Search(request, provider, cancellationToken).ConfigureAwait(false); - - if (currentResults.Count > 0) - { - return currentResults; - } - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading subtitles from {0}", ex, provider.Name); - } - } - return new List(); - } - - var tasks = providers.Select(async i => - { - try - { - return await Search(request, i, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading subtitles from {0}", ex, i.Name); - return new List(); - } - }); - - var results = await Task.WhenAll(tasks).ConfigureAwait(false); - - return results.SelectMany(i => i); - } - - private async Task> Search(ChapterSearchRequest request, - IChapterProvider provider, - CancellationToken cancellationToken) - { - var searchResults = await provider.Search(request, cancellationToken).ConfigureAwait(false); - - var list = searchResults.ToList(); - - foreach (var result in list) - { - result.Id = GetProviderId(provider.Name) + "_" + result.Id; - result.ProviderName = provider.Name; - } - - return list; - } - - public Task GetChapters(string id, CancellationToken cancellationToken) - { - var parts = id.Split(new[] { '_' }, 2); - - var provider = GetProvider(parts.First()); - id = parts.Last(); - - return provider.GetChapters(id, cancellationToken); - } - - public IEnumerable GetProviders(string itemId) - { - var video = _libraryManager.GetItemById(itemId) as Video; - VideoContentType mediaType; - - if (video is Episode) - { - mediaType = VideoContentType.Episode; - } - else if (video is Movie) - { - mediaType = VideoContentType.Movie; - } - else - { - // These are the only supported types - return new List(); - } - - var providers = GetInternalProviders(false) - .Where(i => i.SupportedMediaTypes.Contains(mediaType)); - - return GetInfos(providers); - } - - public IEnumerable GetProviders() - { - return GetInfos(GetInternalProviders(true)); - } - - private IEnumerable GetInternalProviders(bool includeDisabledProviders) - { - var providers = _providers; - - if (!includeDisabledProviders) - { - var options = GetConfiguration(); - - providers = providers - .Where(i => !options.DisabledFetchers.Contains(i.Name)) - .ToArray(); - } - - return providers - .OrderBy(GetConfiguredOrder) - .ThenBy(GetDefaultOrder) - .ToArray(); - } - - private IEnumerable GetInfos(IEnumerable providers) - { - return providers.Select(i => new ChapterProviderInfo - { - Name = i.Name, - Id = GetProviderId(i.Name) - }); - } - - private string GetProviderId(string name) - { - return name.ToLower().GetMD5().ToString("N"); - } - - private IChapterProvider GetProvider(string id) - { - return _providers.First(i => string.Equals(id, GetProviderId(i.Name))); - } - - private int GetConfiguredOrder(IChapterProvider provider) - { - var options = GetConfiguration(); - - // See if there's a user-defined order - var index = Array.IndexOf(options.FetcherOrder, provider.Name); - - if (index != -1) - { - return index; - } - - // Not configured. Just return some high number to put it at the end. - return 100; - } - - private int GetDefaultOrder(IChapterProvider provider) - { - var hasOrder = provider as IHasOrder; - - if (hasOrder != null) - { - return hasOrder.Order; - } - - return 0; - } - public IEnumerable GetChapters(string itemId) { return _itemRepo.GetChapters(new Guid(itemId)); diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 66fe7ea81..8c87e991e 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -238,22 +238,6 @@ namespace MediaBrowser.Providers.MediaInfo if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || options.MetadataRefreshMode == MetadataRefreshMode.Default) { - var chapterOptions = _chapterManager.GetConfiguration(); - - try - { - var remoteChapters = await DownloadChapters(video, chapters, chapterOptions, cancellationToken).ConfigureAwait(false); - - if (remoteChapters.Count > 0) - { - chapters = remoteChapters; - } - } - catch (Exception ex) - { - _logger.ErrorException("Error downloading chapters", ex); - } - if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) { AddDummyChapters(video, chapters); @@ -561,52 +545,6 @@ namespace MediaBrowser.Providers.MediaInfo currentStreams.AddRange(externalSubtitleStreams); } - private async Task> DownloadChapters(Video video, List currentChapters, ChapterOptions options, CancellationToken cancellationToken) - { - if ((options.DownloadEpisodeChapters && - video is Episode) || - (options.DownloadMovieChapters && - video is Movie)) - { - var results = await _chapterManager.Search(video, cancellationToken).ConfigureAwait(false); - - var result = results.FirstOrDefault(); - - if (result != null) - { - var chapters = await _chapterManager.GetChapters(result.Id, cancellationToken).ConfigureAwait(false); - - var chapterInfos = chapters.Chapters.Select(i => new ChapterInfo - { - Name = i.Name, - StartPositionTicks = i.StartPositionTicks - - }).ToList(); - - if (chapterInfos.All(i => i.StartPositionTicks == 0)) - { - if (currentChapters.Count >= chapterInfos.Count) - { - var index = 0; - foreach (var info in chapterInfos) - { - info.StartPositionTicks = currentChapters[index].StartPositionTicks; - index++; - } - } - else - { - chapterInfos.Clear(); - } - } - - return chapterInfos; - } - } - - return new List(); - } - /// /// The dummy chapter duration /// diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index 79da291b7..2490f7145 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -14,6 +14,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.IO; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.MediaInfo { @@ -24,14 +26,16 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ISubtitleManager _subtitleManager; private readonly IMediaSourceManager _mediaSourceManager; private readonly ILogger _logger; + private IJsonSerializer _json; - public SubtitleScheduledTask(ILibraryManager libraryManager, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger, IMediaSourceManager mediaSourceManager) + public SubtitleScheduledTask(ILibraryManager libraryManager, IJsonSerializer json, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger logger, IMediaSourceManager mediaSourceManager) { _libraryManager = libraryManager; _config = config; _subtitleManager = subtitleManager; _logger = logger; _mediaSourceManager = mediaSourceManager; + _json = json; } public string Name @@ -58,38 +62,65 @@ namespace MediaBrowser.Providers.MediaInfo { var options = GetOptions(); - var videos = _libraryManager.RootFolder - .GetRecursiveChildren(i => - { - if (!(i is Video)) - { - return false; - } + var types = new List(); - if (i.LocationType == LocationType.Remote || i.LocationType == LocationType.Virtual) - { - return false; - } + if (options.DownloadEpisodeSubtitles) + { + types.Add("Episode"); + } + if (options.DownloadMovieSubtitles) + { + types.Add("Movie"); + } - return (options.DownloadEpisodeSubtitles && - i is Episode) || - (options.DownloadMovieSubtitles && - i is Movie); - }) - .Cast