From 0ebb82dc57fefcdf765855e3fda0dd0f90c43f33 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 18 Apr 2016 13:43:00 -0400 Subject: update NAL usage --- MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs | 9 ++++++++- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index d40f2a141d..ab2b59d482 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -511,8 +511,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (!(service is EmbyTV.EmbyTV)) { // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says - mediaSource.SupportsDirectStream = true; + mediaSource.SupportsDirectStream = false; mediaSource.SupportsTranscoding = true; + foreach (var stream in mediaSource.MediaStreams) + { + if (stream.Type == MediaStreamType.Video && string.IsNullOrWhiteSpace(stream.NalLengthSize)) + { + stream.NalLengthSize = "0"; + } + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 4c140602d2..469767c658 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -317,6 +317,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun videoCodec = "mpeg2video"; } + string nal = null; + if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) + { + nal = "0"; + } + var url = GetApiUrl(info, true) + "/auto/v" + channelId; if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) @@ -339,7 +345,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun Codec = videoCodec, Width = width, Height = height, - BitRate = videoBitrate + BitRate = videoBitrate, + NalLengthSize = nal }, new MediaStream -- cgit v1.2.3 From f3c3c74d84d81ada293ff10c2eabcd1be418f54f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 21 Apr 2016 14:00:46 -0400 Subject: add probe property --- .../LiveTv/TunerHosts/BaseTunerHost.cs | 30 +++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 02a8d69387..9bb5b4fd7d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -224,7 +224,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts var stream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false); - //await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); + if (EnableMediaProbing) + { + await AddMediaInfo(stream, false, resourcePool, cancellationToken).ConfigureAwait(false); + } + return new Tuple(stream, resourcePool); } catch (Exception ex) @@ -239,6 +243,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts throw new LiveTvConflictException(); } + protected virtual bool EnableMediaProbing + { + get { return false; } + } + protected async Task IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken) { try @@ -268,6 +277,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return _semaphoreLocks.GetOrAdd(url, key => new SemaphoreSlim(1, 1)); } + private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, SemaphoreSlim resourcePool, CancellationToken cancellationToken) + { + await resourcePool.WaitAsync(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(); + + throw; + } + } + private async Task AddMediaInfoInternal(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) { var originalRuntime = mediaSource.RunTimeTicks; -- cgit v1.2.3 From 54e04dd027f75066981524f7db02664b3afed2ab Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 25 Apr 2016 22:16:46 -0400 Subject: support duration on recording url --- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 ++ .../LiveTv/EmbyTV/DirectRecorder.cs | 9 ++++++--- .../LiveTv/EmbyTV/EmbyTV.cs | 23 ++++++++++++++++------ .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 8 ++++++++ .../LiveTv/TunerHosts/M3UTunerHost.cs | 5 +++++ .../LiveTv/TunerHosts/SatIp/SatIpHost.cs | 5 +++++ 6 files changed, 43 insertions(+), 9 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 498602ddf9..1e7aa3de5a 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -46,6 +46,8 @@ 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.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 3c7ee55b7d..98249f5ac9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -42,10 +42,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Info("Copying recording stream to file {0}", targetFile); - var durationToken = new CancellationTokenSource(duration); - var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + if (!mediaSource.RunTimeTicks.HasValue) + { + var durationToken = new CancellationTokenSource(duration); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + } - await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, linkedToken).ConfigureAwait(false); + await response.Content.CopyToAsync(output, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 60ff23b04b..d85e8ba3f9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -591,7 +591,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, string streamId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); @@ -599,7 +599,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { try { - return await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + + return new Tuple(result.Item1, hostInstance, result.Item2); } catch (Exception e) { @@ -797,8 +799,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg //await Task.Delay(3000, cancellationToken).ConfigureAwait(false); - var duration = recordingEndDate - DateTime.UtcNow; - var recorder = await GetRecorder().ConfigureAwait(false); if (recorder is EncodedRecorder) @@ -816,6 +816,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV recording.DateLastUpdated = DateTime.UtcNow; _recordingProvider.AddOrUpdate(recording); + var duration = recordingEndDate - DateTime.UtcNow; + _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture)); _logger.Info("Writing file to path: " + recordPath); @@ -823,10 +825,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV Action onStarted = () => { - result.Item2.Release(); + result.Item3.Release(); isResourceOpen = false; }; + var pathWithDuration = result.Item2.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); recording.Status = RecordingStatus.Completed; @@ -836,7 +847,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { if (isResourceOpen) { - result.Item2.Release(); + result.Item3.Release(); } _libraryMonitor.ReportFileSystemChangeComplete(recordPath, false); diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 469767c658..a3e5589e88 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -59,6 +59,14 @@ 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/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 523f14dfc8..c874e51b61 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -146,5 +146,10 @@ 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/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs index ffd85fd18a..1e571c84fb 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs @@ -164,5 +164,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp return list; } + + public string ApplyDuration(string streamPath, TimeSpan duration) + { + return streamPath; + } } } -- cgit v1.2.3 From 747518decc3e59a9c6b01f31e5360ce194919d8d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 27 Apr 2016 17:26:28 -0400 Subject: added option to preserve original audio when recording --- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 1 + MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 6 ++++-- .../LiveTv/EmbyTV/EncodedRecorder.cs | 7 +++++-- 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 46f630fe04..fc2b155ecd 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -9,6 +9,7 @@ namespace MediaBrowser.Model.LiveTv public string RecordingPath { get; set; } public bool EnableAutoOrganize { get; set; } public bool EnableRecordingEncoding { get; set; } + public bool EnableOriginalAudioWithEncodedRecordings { get; set; } public List TunerHosts { get; set; } public List ListingProviders { get; set; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index d85e8ba3f9..351bc05af6 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -927,13 +927,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private async Task GetRecorder() { - if (GetConfiguration().EnableRecordingEncoding) + var config = GetConfiguration(); + + if (config.EnableRecordingEncoding) { var regInfo = await _security.GetRegistrationStatus("embytvrecordingconversion").ConfigureAwait(false); if (regInfo.IsValid) { - return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer); + return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config); } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 442f151dd0..63d0846fc1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -12,6 +12,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; @@ -23,6 +24,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly IFileSystem _fileSystem; private readonly IMediaEncoder _mediaEncoder; private readonly IApplicationPaths _appPaths; + private readonly LiveTvOptions _liveTvOptions; private bool _hasExited; private Stream _logFileStream; private string _targetPath; @@ -30,13 +32,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly IJsonSerializer _json; private readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); - public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IApplicationPaths appPaths, IJsonSerializer json) + public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions) { _logger = logger; _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _appPaths = appPaths; _json = json; + _liveTvOptions = liveTvOptions; } public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) @@ -129,7 +132,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var copyAudio = new[] { "aac", "mp3" }; var mediaStreams = mediaSource.MediaStreams ?? new List(); - if (mediaStreams.Any(i => i.Type == MediaStreamType.Audio && copyAudio.Contains(i.Codec, StringComparer.OrdinalIgnoreCase))) + if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings || mediaStreams.Any(i => i.Type == MediaStreamType.Audio && copyAudio.Contains(i.Codec, StringComparer.OrdinalIgnoreCase))) { return "-codec:a:0 copy"; } -- cgit v1.2.3 From 8537cbe6c2e1b6a5508d3f13bf0ec0224e88b452 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 29 Apr 2016 12:54:26 -0400 Subject: add audio sync to converted recordings --- MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 63d0846fc1..fa4f821364 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -143,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { audioChannels = audioStream.Channels ?? audioChannels; } - return "-codec:a:0 aac -strict experimental -ab 320000"; + return "-codec:a:0 aac -strict experimental -ab 320000 -af \"async=1000\""; } private bool EncodeVideo(MediaSourceInfo mediaSource) -- cgit v1.2.3 From 5d28c65783f850db58ea990a04ec09da38c24442 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 30 Apr 2016 00:00:28 -0400 Subject: update recordings --- .../LiveTv/EmbyTV/DirectRecorder.cs | 10 +++++++++- .../LiveTv/EmbyTV/EncodedRecorder.cs | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 98249f5ac9..54443d9ca3 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -42,8 +42,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Info("Copying recording stream to file {0}", targetFile); - if (!mediaSource.RunTimeTicks.HasValue) + 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; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index fa4f821364..d589b2bc38 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -44,6 +44,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { + 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; + } + _targetPath = targetFile; _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile)); -- cgit v1.2.3 From 1f9d32afc52e9bd133ddf22d12bdc468adaf9ebe Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 30 Apr 2016 18:05:13 -0400 Subject: limit use of GetUserDataKey --- MediaBrowser.Api/Movies/MoviesService.cs | 4 ++-- .../UserLibrary/BaseItemsByNameService.cs | 8 +++---- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 8 ++----- MediaBrowser.Controller/Entities/BaseItem.cs | 14 +++++------- MediaBrowser.Controller/Entities/Folder.cs | 2 +- .../Entities/UserViewBuilder.cs | 10 ++++----- .../Library/IUserDataManager.cs | 5 +++++ .../ContentDirectory/ControlHandler.cs | 2 +- .../Channels/ChannelManager.cs | 12 +++++------ .../Dto/DtoService.cs | 4 ++-- .../Library/MediaSourceManager.cs | 2 +- .../Library/UserDataManager.cs | 15 +++++++++++++ .../LiveTv/LiveTvManager.cs | 12 +++++------ .../Session/SessionManager.cs | 25 ++++++++-------------- .../Sorting/DatePlayedComparer.cs | 2 +- .../Sorting/PlayCountComparer.cs | 2 +- .../Sync/SyncManager.cs | 2 +- .../TV/TVSeriesManager.cs | 4 ++-- MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 2 +- 19 files changed, 70 insertions(+), 65 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 065f882687..bfbb3a6d75 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -247,7 +247,7 @@ namespace MediaBrowser.Api.Movies var recentlyPlayedMovies = allMoviesForCategories .Select(i => { - var userdata = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey()); + var userdata = _userDataRepository.GetUserData(user, i); return new Tuple(i, userdata.Played, userdata.LastPlayedDate ?? DateTime.MinValue); }) .Where(i => i.Item2) @@ -260,7 +260,7 @@ namespace MediaBrowser.Api.Movies .Select(i => { var score = 0; - var userData = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey()); + var userData = _userDataRepository.GetUserData(user, i); if (userData.IsFavorite) { diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 6ae2b08322..b3164ce3f7 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -274,7 +274,7 @@ namespace MediaBrowser.Api.UserLibrary { items = items.Where(i => { - var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); + var userdata = UserDataRepository.GetUserData(user, i); return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; }); @@ -284,7 +284,7 @@ namespace MediaBrowser.Api.UserLibrary { items = items.Where(i => { - var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); + var userdata = UserDataRepository.GetUserData(user, i); return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; }); @@ -294,7 +294,7 @@ namespace MediaBrowser.Api.UserLibrary { items = items.Where(i => { - var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); + var userdata = UserDataRepository.GetUserData(user, i); var likes = userdata.Likes ?? false; var favorite = userdata.IsFavorite; @@ -307,7 +307,7 @@ namespace MediaBrowser.Api.UserLibrary { items = items.Where(i => { - var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); + var userdata = UserDataRepository.GetUserData(user, i); return userdata != null && userdata.IsFavorite; }); diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index c2c481cb6f..8cc5cab358 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -519,10 +519,8 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); - var key = item.GetUserDataKey(); - // Get the user data for this item - var data = _userDataRepository.GetUserData(user.Id, key); + var data = _userDataRepository.GetUserData(user, item); // Set favorite status data.IsFavorite = isFavorite; @@ -567,10 +565,8 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); - var key = item.GetUserDataKey(); - // Get the user data for this item - var data = _userDataRepository.GetUserData(user.Id, key); + var data = _userDataRepository.GetUserData(user, item); data.Likes = likes; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 9171c2a716..4828426fdc 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1617,9 +1617,7 @@ namespace MediaBrowser.Controller.Entities throw new ArgumentNullException(); } - var key = GetUserDataKey(); - - var data = UserDataManager.GetUserData(user.Id, key); + var data = UserDataManager.GetUserData(user, this); if (datePlayed.HasValue) { @@ -1654,9 +1652,7 @@ namespace MediaBrowser.Controller.Entities throw new ArgumentNullException(); } - var key = GetUserDataKey(); - - var data = UserDataManager.GetUserData(user.Id, key); + var data = UserDataManager.GetUserData(user, this); //I think it is okay to do this here. // if this is only called when a user is manually forcing something to un-played @@ -1987,14 +1983,14 @@ namespace MediaBrowser.Controller.Entities public virtual bool IsPlayed(User user) { - var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); + var userdata = UserDataManager.GetUserData(user, this); return userdata != null && userdata.Played; } public bool IsFavoriteOrLiked(User user) { - var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); + var userdata = UserDataManager.GetUserData(user, this); return userdata != null && (userdata.IsFavorite || (userdata.Likes ?? false)); } @@ -2006,7 +2002,7 @@ namespace MediaBrowser.Controller.Entities throw new ArgumentNullException("user"); } - var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); + var userdata = UserDataManager.GetUserData(user, this); return userdata == null || !userdata.Played; } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index f4cdc8fa1b..41f0c02bdc 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1635,7 +1635,7 @@ namespace MediaBrowser.Controller.Entities var isUnplayed = true; - var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey()); + var itemUserData = UserDataManager.GetUserData(user, child); // Incrememt totalPercentPlayed if (itemUserData != null) diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index db669ca37d..5a0e0b614b 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -348,7 +348,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => !i.IsFolder) .OfType(); - var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite); + var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user, i).IsFavorite); return GetResult(artists, parent, query); } @@ -1218,7 +1218,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsLiked.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value) { @@ -1228,7 +1228,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsFavoriteOrLiked.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false); if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value) @@ -1239,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsFavorite.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); if (userData.IsFavorite != query.IsFavorite.Value) { @@ -1249,7 +1249,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsResumable.HasValue) { - userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + userData = userData ?? userDataManager.GetUserData(user, item); var isResumable = userData.PlaybackPositionTicks > 0; if (isResumable != query.IsResumable.Value) diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 56ac14e9df..4ff0f6439f 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -45,6 +45,11 @@ namespace MediaBrowser.Controller.Library /// Task{UserItemData}. UserItemData GetUserData(Guid userId, string key); + UserItemData GetUserData(IHasUserData user, IHasUserData item); + + UserItemData GetUserData(string userId, IHasUserData item); + UserItemData GetUserData(Guid userId, IHasUserData item); + /// /// Gets the user data dto. /// diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 34fb2a6df7..bc9fa1d7e7 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -111,7 +111,7 @@ namespace MediaBrowser.Dlna.ContentDirectory var newbookmark = int.Parse(sparams["PosSecond"], _usCulture); - var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + var userdata = _userDataManager.GetUserData(user, item); userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks; diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index c9956c68a5..6a9842cb2e 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -133,7 +133,7 @@ namespace MediaBrowser.Server.Implementations.Channels if (query.IsFavorite.HasValue) { var val = query.IsFavorite.Value; - channels = channels.Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val) + channels = channels.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val) .ToList(); } @@ -1437,7 +1437,7 @@ namespace MediaBrowser.Server.Implementations.Channels case ItemFilter.IsFavoriteOrLikes: return items.Where(item => { - var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + var userdata = _userDataManager.GetUserData(user, item); if (userdata == null) { @@ -1453,7 +1453,7 @@ namespace MediaBrowser.Server.Implementations.Channels case ItemFilter.Likes: return items.Where(item => { - var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + var userdata = _userDataManager.GetUserData(user, item); return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; }); @@ -1461,7 +1461,7 @@ namespace MediaBrowser.Server.Implementations.Channels case ItemFilter.Dislikes: return items.Where(item => { - var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + var userdata = _userDataManager.GetUserData(user, item); return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; }); @@ -1469,7 +1469,7 @@ namespace MediaBrowser.Server.Implementations.Channels case ItemFilter.IsFavorite: return items.Where(item => { - var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + var userdata = _userDataManager.GetUserData(user, item); return userdata != null && userdata.IsFavorite; }); @@ -1477,7 +1477,7 @@ namespace MediaBrowser.Server.Implementations.Channels case ItemFilter.IsResumable: return items.Where(item => { - var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + var userdata = _userDataManager.GetUserData(user, item); return userdata != null && userdata.PlaybackPositionTicks > 0; }); diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 7adde5c273..b19f5727c8 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -487,7 +487,7 @@ namespace MediaBrowser.Server.Implementations.Dto { if (item.IsFolder) { - var userData = _userDataRepository.GetUserData(user.Id, item.GetUserDataKey()); + var userData = _userDataRepository.GetUserData(user, item); // Skip the user data manager because we've already looped through the recursive tree and don't want to do it twice // TODO: Improve in future @@ -1686,7 +1686,7 @@ namespace MediaBrowser.Server.Implementations.Dto dateLastMediaAdded = new[] { dateLastMediaAdded.Value, child.DateCreated }.Max(); } - var userdata = _userDataRepository.GetUserData(user.Id, child.GetUserDataKey()); + var userdata = _userDataRepository.GetUserData(user, child); recursiveItemCount++; diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 092b797ce7..a47fcdf4f8 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -267,7 +267,7 @@ namespace MediaBrowser.Server.Implementations.Library private void SetUserProperties(IHasUserData item, MediaSourceInfo source, User user) { - var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item); var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections; diff --git a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs index ae737d2446..c16beed8cc 100644 --- a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs @@ -172,6 +172,21 @@ namespace MediaBrowser.Server.Implementations.Library return userId + key; } + public UserItemData GetUserData(IHasUserData user, IHasUserData item) + { + return GetUserData(user.Id, item.GetUserDataKey()); + } + + public UserItemData GetUserData(string userId, IHasUserData item) + { + return GetUserData(userId, item.GetUserDataKey()); + } + + public UserItemData GetUserData(Guid userId, IHasUserData item) + { + return GetUserData(userId, item.GetUserDataKey()); + } + public UserItemDataDto GetUserDataDto(IHasUserData item, User user) { var userData = GetUserData(user.Id, item.GetUserDataKey()); diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index ab2b59d482..eec8328f89 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -164,7 +164,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var val = query.IsFavorite.Value; channels = channels - .Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val); + .Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val); } if (query.IsLiked.HasValue) @@ -174,7 +174,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv channels = channels .Where(i => { - var likes = _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).Likes; + var likes = _userDataManager.GetUserData(user, i).Likes; return likes.HasValue && likes.Value == val; }); @@ -187,7 +187,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv channels = channels .Where(i => { - var likes = _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).Likes; + var likes = _userDataManager.GetUserData(user, i).Likes; return likes.HasValue && likes.Value != val; }); @@ -200,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { if (enableFavoriteSorting) { - var userData = _userDataManager.GetUserData(user.Id, i.GetUserDataKey()); + var userData = _userDataManager.GetUserData(user, i); if (userData.IsFavorite) { @@ -1005,7 +1005,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var channel = GetInternalChannel(program.ChannelId); - var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey()); + var channelUserdata = _userDataManager.GetUserData(userId, channel); if (channelUserdata.Likes ?? false) { @@ -1036,7 +1036,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (genres.TryGetValue(i, out genre)) { - var genreUserdata = _userDataManager.GetUserData(userId, genre.GetUserDataKey()); + var genreUserdata = _userDataManager.GetUserData(userId, genre); if (genreUserdata.Likes ?? false) { diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 88f11c3685..014e8babf9 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -601,11 +601,9 @@ namespace MediaBrowser.Server.Implementations.Session if (libraryItem != null) { - var key = libraryItem.GetUserDataKey(); - foreach (var user in users) { - await OnPlaybackStart(user.Id, key, libraryItem).ConfigureAwait(false); + await OnPlaybackStart(user.Id, libraryItem).ConfigureAwait(false); } } @@ -632,12 +630,11 @@ namespace MediaBrowser.Server.Implementations.Session /// Called when [playback start]. /// /// The user identifier. - /// The user data key. /// The item. /// Task. - private async Task OnPlaybackStart(Guid userId, string userDataKey, IHasUserData item) + private async Task OnPlaybackStart(Guid userId, IHasUserData item) { - var data = _userDataRepository.GetUserData(userId, userDataKey); + var data = _userDataRepository.GetUserData(userId, item); data.PlayCount++; data.LastPlayedDate = DateTime.UtcNow; @@ -676,11 +673,9 @@ namespace MediaBrowser.Server.Implementations.Session if (libraryItem != null) { - var key = libraryItem.GetUserDataKey(); - foreach (var user in users) { - await OnPlaybackProgress(user, key, libraryItem, info).ConfigureAwait(false); + await OnPlaybackProgress(user, libraryItem, info).ConfigureAwait(false); } } @@ -714,9 +709,9 @@ namespace MediaBrowser.Server.Implementations.Session StartIdleCheckTimer(); } - private async Task OnPlaybackProgress(User user, string userDataKey, BaseItem item, PlaybackProgressInfo info) + private async Task OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info) { - var data = _userDataRepository.GetUserData(user.Id, userDataKey); + var data = _userDataRepository.GetUserData(user.Id, item); var positionTicks = info.PositionTicks; @@ -811,11 +806,9 @@ namespace MediaBrowser.Server.Implementations.Session if (libraryItem != null) { - var key = libraryItem.GetUserDataKey(); - foreach (var user in users) { - playedToCompletion = await OnPlaybackStopped(user.Id, key, libraryItem, info.PositionTicks, info.Failed).ConfigureAwait(false); + playedToCompletion = await OnPlaybackStopped(user.Id, libraryItem, info.PositionTicks, info.Failed).ConfigureAwait(false); } } @@ -848,13 +841,13 @@ namespace MediaBrowser.Server.Implementations.Session await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false); } - private async Task OnPlaybackStopped(Guid userId, string userDataKey, BaseItem item, long? positionTicks, bool playbackFailed) + private async Task OnPlaybackStopped(Guid userId, BaseItem item, long? positionTicks, bool playbackFailed) { bool playedToCompletion = false; if (!playbackFailed) { - var data = _userDataRepository.GetUserData(userId, userDataKey); + var data = _userDataRepository.GetUserData(userId, item); if (positionTicks.HasValue) { diff --git a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs index c881591beb..3edf23020a 100644 --- a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs @@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.Sorting /// DateTime. private DateTime GetDate(BaseItem x) { - var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()); + var userdata = UserDataRepository.GetUserData(User, x); if (userdata != null && userdata.LastPlayedDate.HasValue) { diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs index 1bc5261b44..8b14efffcf 100644 --- a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Sorting /// DateTime. private int GetValue(BaseItem x) { - var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()); + var userdata = UserDataRepository.GetUserData(User, x); return userdata == null ? 0 : userdata.PlayCount; } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index fe1a7fb28f..38edc30240 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -687,7 +687,7 @@ namespace MediaBrowser.Server.Implementations.Sync private Task ReportOfflinePlayedItem(UserAction action) { var item = _libraryManager.GetItemById(action.ItemId); - var userData = _userDataManager.GetUserData(new Guid(action.UserId), item.GetUserDataKey()); + var userData = _userDataManager.GetUserData(action.UserId, item); userData.LastPlayedDate = action.Date; _userDataManager.UpdatePlayState(item, userData, action.PositionTicks); diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs index 3e43ebe9b8..dc880d84b5 100644 --- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs +++ b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs @@ -85,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.TV { var episode = i.Item1; - var seriesUserData = _userDataManager.GetUserData(user.Id, episode.Series.GetUserDataKey()); + var seriesUserData = _userDataManager.GetUserData(user, episode.Series); if (seriesUserData.IsFavorite) { @@ -128,7 +128,7 @@ namespace MediaBrowser.Server.Implementations.TV // Go back starting with the most recent episodes foreach (var episode in allEpisodes) { - var userData = _userDataManager.GetUserData(user.Id, episode.GetUserDataKey()); + var userData = _userDataManager.GetUserData(user, episode); if (userData.Played) { diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 6d15d168d1..d2e09d4ebf 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -936,7 +936,7 @@ namespace MediaBrowser.XbmcMetadata.Savers return; } - var userdata = userDataRepo.GetUserData(user.Id, item.GetUserDataKey()); + var userdata = userDataRepo.GetUserData(user, item); writer.WriteElementString("isuserfavorite", userdata.IsFavorite.ToString().ToLower()); -- cgit v1.2.3 From fdc37a2b5879f4c96c59fa00d330385d919f8b8b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 3 May 2016 00:17:57 -0400 Subject: update recording audio sync --- MediaBrowser.Model/Connect/ConnectUser.cs | 1 - MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 6 ++++++ .../LiveTv/EmbyTV/EncodedRecorder.cs | 4 ++-- .../LiveTv/TunerHosts/M3UTunerHost.cs | 4 +++- Nuget/MediaBrowser.Common.Internal.nuspec | 4 ++-- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 ++-- 8 files changed, 17 insertions(+), 10 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Model/Connect/ConnectUser.cs b/MediaBrowser.Model/Connect/ConnectUser.cs index 383261a6b7..da290da120 100644 --- a/MediaBrowser.Model/Connect/ConnectUser.cs +++ b/MediaBrowser.Model/Connect/ConnectUser.cs @@ -8,6 +8,5 @@ namespace MediaBrowser.Model.Connect public string Email { get; set; } public bool IsActive { get; set; } public string ImageUrl { get; set; } - public bool IsSupporter { get; set; } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 351bc05af6..df15c38fdd 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -733,6 +733,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV else if (info.IsSeries) { recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name).Trim()); + + if (info.SeasonNumber.HasValue) + { + var folderName = string.Format("Season {0}", info.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); + recordPath = Path.Combine(recordPath, folderName); + } } else if (info.IsKids) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index d589b2bc38..dec8ba83d2 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -121,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var maxBitrate = 25000000; videoArgs = string.Format( - "-codec:v:0 libx264 -force_key_frames expr:gte(t,n_forced*5) {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync vfr -profile:v high -level 41", + "-codec:v:0 libx264 -force_key_frames expr:gte(t,n_forced*5) {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41", GetOutputSizeParam(), maxBitrate.ToString(CultureInfo.InvariantCulture)); } @@ -157,7 +157,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { audioChannels = audioStream.Channels ?? audioChannels; } - return "-codec:a:0 aac -strict experimental -ab 320000 -af \"async=1000\""; + return "-codec:a:0 aac -strict experimental -ab 320000"; } private bool EncodeVideo(MediaSourceInfo mediaSource) diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index c874e51b61..2a974b5459 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -134,7 +134,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts } }, RequiresOpening = false, - RequiresClosing = false + RequiresClosing = false, + + ReadAtNativeFramerate = true }; return new List { mediaSource }; diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index a561dbf4b3..b9da464dd5 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.645 + 3.0.646 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption. Copyright © Emby 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index b9bce00d02..e55b0f9af9 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.645 + 3.0.646 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 3af1b91e26..7ad4d7dac3 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.645 + 3.0.646 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index d48b421c56..e62cfd37e6 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.645 + 3.0.646 Media Browser.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - + -- cgit v1.2.3 From 33c002684e30f910312eef7f796e2efc0607d4a5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 4 May 2016 16:50:47 -0400 Subject: update recording saving --- MediaBrowser.Api/Library/LibraryHelpers.cs | 54 --- .../Library/LibraryStructureService.cs | 43 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 1 - MediaBrowser.Controller/Library/ILibraryManager.cs | 2 + MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 4 + .../Library/LibraryManager.cs | 68 +++ .../LiveTv/EmbyTV/EmbyTV.cs | 512 +++++++++++++-------- 7 files changed, 403 insertions(+), 281 deletions(-) delete mode 100644 MediaBrowser.Api/Library/LibraryHelpers.cs (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Api/Library/LibraryHelpers.cs b/MediaBrowser.Api/Library/LibraryHelpers.cs deleted file mode 100644 index 22a88e2bf1..0000000000 --- a/MediaBrowser.Api/Library/LibraryHelpers.cs +++ /dev/null @@ -1,54 +0,0 @@ -using MediaBrowser.Controller; -using System; -using System.IO; -using System.Linq; -using CommonIO; - -namespace MediaBrowser.Api.Library -{ - /// - /// Class LibraryHelpers - /// - public static class LibraryHelpers - { - /// - /// The shortcut file extension - /// - private const string ShortcutFileExtension = ".mblink"; - /// - /// The shortcut file search - /// - private const string ShortcutFileSearch = "*" + ShortcutFileExtension; - - /// - /// Deletes a shortcut from within a virtual folder, within either the default view or a user view - /// - /// The file system. - /// Name of the virtual folder. - /// The media path. - /// The app paths. - /// The media folder does not exist - public static void RemoveMediaPath(IFileSystem fileSystem, string virtualFolderName, string mediaPath, IServerApplicationPaths appPaths) - { - if (string.IsNullOrWhiteSpace(mediaPath)) - { - throw new ArgumentNullException("mediaPath"); - } - - var rootFolderPath = appPaths.DefaultUserViewsPath; - var path = Path.Combine(rootFolderPath, virtualFolderName); - - if (!fileSystem.DirectoryExists(path)) - { - 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)); - - if (!string.IsNullOrEmpty(shortcut)) - { - fileSystem.DeleteFile(shortcut); - } - } - } -} diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index 817fd9ce00..3cf0d5d937 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -268,46 +268,7 @@ namespace MediaBrowser.Api.Library /// The request. public void Delete(RemoveVirtualFolder request) { - if (string.IsNullOrWhiteSpace(request.Name)) - { - throw new ArgumentNullException("request"); - } - - var rootFolderPath = _appPaths.DefaultUserViewsPath; - - var path = Path.Combine(rootFolderPath, request.Name); - - if (!_fileSystem.DirectoryExists(path)) - { - throw new DirectoryNotFoundException("The media folder does not exist"); - } - - _libraryMonitor.Stop(); - - try - { - _fileSystem.DeleteDirectory(path, true); - } - finally - { - Task.Run(() => - { - // No need to start if scanning the library because it will handle it - if (request.RefreshLibrary) - { - _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); - } - else - { - // Need to add a delay here or directory watchers may still pick up the changes - var task = Task.Delay(1000); - // Have to block here to allow exceptions to bubble - Task.WaitAll(task); - - _libraryMonitor.Start(); - } - }); - } + _libraryManager.RemoveVirtualFolder(request.Name, request.RefreshLibrary); } /// @@ -364,7 +325,7 @@ namespace MediaBrowser.Api.Library try { - LibraryHelpers.RemoveMediaPath(_fileSystem, request.Name, request.Path, _appPaths); + _libraryManager.RemoveMediaPath(request.Name, request.Path); } finally { diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index db8961a66c..77949d1793 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -129,7 +129,6 @@ - diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 07ba41b3d7..936b97c6e2 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -571,6 +571,8 @@ namespace MediaBrowser.Controller.Library bool IgnoreFile(FileSystemMetadata file, BaseItem parent); void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, bool refreshLibrary); + void RemoveVirtualFolder(string name, bool refreshLibrary); void AddMediaPath(string virtualFolderName, string path); + void RemoveMediaPath(string virtualFolderName, string path); } } \ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index fc2b155ecd..4211fbd59f 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -7,8 +7,11 @@ namespace MediaBrowser.Model.LiveTv public int? GuideDays { get; set; } public bool EnableMovieProviders { get; set; } public string RecordingPath { get; set; } + public string MovieRecordingPath { get; set; } + public string SeriesRecordingPath { get; set; } public bool EnableAutoOrganize { get; set; } public bool EnableRecordingEncoding { get; set; } + public bool EnableRecordingSubfolders { get; set; } public bool EnableOriginalAudioWithEncodedRecordings { get; set; } public List TunerHosts { get; set; } @@ -20,6 +23,7 @@ namespace MediaBrowser.Model.LiveTv public LiveTvOptions() { EnableMovieProviders = true; + EnableRecordingSubfolders = true; TunerHosts = new List(); ListingProviders = new List(); } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 72132c4be1..95d47b380c 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2640,7 +2640,52 @@ namespace MediaBrowser.Server.Implementations.Library } } + public void RemoveVirtualFolder(string name, bool refreshLibrary) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException("name"); + } + + var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + + var path = Path.Combine(rootFolderPath, name); + + if (!_fileSystem.DirectoryExists(path)) + { + throw new DirectoryNotFoundException("The media folder does not exist"); + } + + _libraryMonitorFactory().Stop(); + + try + { + _fileSystem.DeleteDirectory(path, true); + } + finally + { + Task.Run(() => + { + // No need to start if scanning the library because it will handle it + if (refreshLibrary) + { + ValidateMediaLibrary(new Progress(), CancellationToken.None); + } + else + { + // Need to add a delay here or directory watchers may still pick up the changes + var task = Task.Delay(1000); + // Have to block here to allow exceptions to bubble + Task.WaitAll(task); + + _libraryMonitorFactory().Start(); + } + }); + } + } + private const string ShortcutFileExtension = ".mblink"; + private const string ShortcutFileSearch = "*" + ShortcutFileExtension; public void AddMediaPath(string virtualFolderName, string path) { if (string.IsNullOrWhiteSpace(path)) @@ -2668,5 +2713,28 @@ namespace MediaBrowser.Server.Implementations.Library _fileSystem.CreateShortcut(lnk, path); } + + public void RemoveMediaPath(string virtualFolderName, string mediaPath) + { + if (string.IsNullOrWhiteSpace(mediaPath)) + { + throw new ArgumentNullException("mediaPath"); + } + + var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var path = Path.Combine(rootFolderPath, virtualFolderName); + + if (!_fileSystem.DirectoryExists(path)) + { + 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)); + + if (!string.IsNullOrEmpty(shortcut)) + { + _fileSystem.DeleteFile(shortcut); + } + } } } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index df15c38fdd..c5920d3d6a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -26,7 +26,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Power; using Microsoft.Win32; @@ -40,7 +43,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; - private readonly ItemDataProvider _recordingProvider; private readonly ItemDataProvider _seriesTimerProvider; private readonly TimerManager _timerProvider; @@ -56,6 +58,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public static EmbyTV Current; + public event EventHandler DataSourceChanged; + public event EventHandler RecordingStatusChanged; + + private readonly ConcurrentDictionary _activeRecordings = + new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ISecurityManager security, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, IPowerManagement powerManagement) { Current = this; @@ -74,10 +82,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _liveTvManager = (LiveTvManager)liveTvManager; _jsonSerializer = jsonSerializer; - _recordingProvider = new ItemDataProvider(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "recordings"), (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase)); _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers")); _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), powerManagement, _logger); _timerProvider.TimerFired += _timerProvider_TimerFired; + + _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; + } + + private void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) + { + if (string.Equals(e.Key, "livetv", StringComparison.OrdinalIgnoreCase)) + { + OnRecordingFoldersChanged(); + } } public void Start() @@ -85,6 +102,95 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _timerProvider.RestartTimers(); SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; + + CreateRecordingFolders(); + } + + private void OnRecordingFoldersChanged() + { + CreateRecordingFolders(); + } + + private void CreateRecordingFolders() + { + var recordingFolders = GetRecordingFolders(); + + var defaultRecordingPath = DefaultRecordingPath; + if (!recordingFolders.Any(i => i.Locations.Contains(defaultRecordingPath, StringComparer.OrdinalIgnoreCase))) + { + RemovePathFromLibrary(defaultRecordingPath); + } + + var virtualFolders = _libraryManager.GetVirtualFolders() + .ToList(); + + var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList(); + + foreach (var recordingFolder in recordingFolders) + { + var pathsToCreate = recordingFolder.Locations + .Where(i => !allExistingPaths.Contains(i, StringComparer.OrdinalIgnoreCase)) + .ToList(); + + if (pathsToCreate.Count == 0) + { + continue; + } + + try + { + _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, pathsToCreate.ToArray(), true); + } + catch (Exception ex) + { + _logger.ErrorException("Error creating virtual folder", ex); + } + } + } + + private void RemovePathFromLibrary(string path) + { + var requiresRefresh = false; + var virtualFolders = _libraryManager.GetVirtualFolders() + .ToList(); + + foreach (var virtualFolder in virtualFolders) + { + if (!virtualFolder.Locations.Contains(path, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + + if (virtualFolder.Locations.Count == 1) + { + // remove entire virtual folder + try + { + _libraryManager.RemoveVirtualFolder(virtualFolder.Name, true); + } + catch (Exception ex) + { + _logger.ErrorException("Error removing virtual folder", ex); + } + } + else + { + try + { + _libraryManager.RemoveMediaPath(virtualFolder.Name, path); + requiresRefresh = true; + } + catch (Exception ex) + { + _logger.ErrorException("Error removing media path", ex); + } + } + } + + if (requiresRefresh) + { + _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None); + } } void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) @@ -97,13 +203,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - public event EventHandler DataSourceChanged; - - public event EventHandler RecordingStatusChanged; - - private readonly ConcurrentDictionary _activeRecordings = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - public string Name { get { return "Emby"; } @@ -114,6 +213,26 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv"); } } + private string DefaultRecordingPath + { + get + { + return Path.Combine(DataPath, "recordings"); + } + } + + private string RecordingPath + { + get + { + var path = GetConfiguration().RecordingPath; + + return string.IsNullOrWhiteSpace(path) + ? DefaultRecordingPath + : path; + } + } + public string HomePageUrl { get { return "http://emby.media"; } @@ -280,49 +399,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return Task.FromResult(true); } - public async Task DeleteRecordingAsync(string recordingId, CancellationToken cancellationToken) + public Task DeleteRecordingAsync(string recordingId, CancellationToken cancellationToken) { - var remove = _recordingProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, recordingId, StringComparison.OrdinalIgnoreCase)); - if (remove != null) - { - if (!string.IsNullOrWhiteSpace(remove.TimerId)) - { - var enableDelay = _activeRecordings.ContainsKey(remove.TimerId); - - CancelTimerInternal(remove.TimerId); - - if (enableDelay) - { - // A hack yes, but need to make sure the file is closed before attempting to delete it - await Task.Delay(3000, cancellationToken).ConfigureAwait(false); - } - } - - if (!string.IsNullOrWhiteSpace(remove.Path)) - { - try - { - _fileSystem.DeleteFile(remove.Path); - } - catch (DirectoryNotFoundException) - { - - } - catch (FileNotFoundException) - { - - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting recording file {0}", ex, remove.Path); - } - } - _recordingProvider.Delete(remove); - } - else - { - throw new ResourceNotFoundException("Recording not found: " + recordingId); - } + return Task.FromResult(true); } public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken) @@ -424,29 +503,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public async Task> GetRecordingsAsync(CancellationToken cancellationToken) { - var recordings = _recordingProvider.GetAll().ToList(); - var updated = false; - - foreach (var recording in recordings) - { - if (recording.Status == RecordingStatus.InProgress) - { - if (string.IsNullOrWhiteSpace(recording.TimerId) || !_activeRecordings.ContainsKey(recording.TimerId)) - { - recording.Status = RecordingStatus.Cancelled; - recording.DateLastUpdated = DateTime.UtcNow; - _recordingProvider.Update(recording); - updated = true; - } - } - } - - if (updated) - { - recordings = _recordingProvider.GetAll().ToList(); - } - - return recordings; + return new List(); } public Task> GetTimersAsync(CancellationToken cancellationToken) @@ -695,104 +752,124 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) + private string GetRecordingPath(TimerInfo timer, ProgramInfo info) { - if (timer == null) - { - throw new ArgumentNullException("timer"); - } - - ProgramInfo info = null; - - if (string.IsNullOrWhiteSpace(timer.ProgramId)) - { - _logger.Info("Timer {0} has null programId", timer.Id); - } - else - { - info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId); - } - - if (info == null) - { - _logger.Info("Unable to find program with Id {0}. Will search using start date", timer.ProgramId); - info = GetProgramInfoFromCache(timer.ChannelId, timer.StartDate); - } - - if (info == null) - { - throw new InvalidOperationException(string.Format("Program with Id {0} not found", timer.ProgramId)); - } - var recordPath = RecordingPath; + var config = GetConfiguration(); if (info.IsMovie) { - recordPath = Path.Combine(recordPath, "Movies", _fileSystem.GetValidFilename(info.Name).Trim()); + var customRecordingPath = config.MovieRecordingPath; + if ((string.IsNullOrWhiteSpace(customRecordingPath) || string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase)) && config.EnableRecordingSubfolders) + { + recordPath = Path.Combine(recordPath, "Movies"); + } + + var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); + if (info.ProductionYear.HasValue) + { + folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; + } + recordPath = Path.Combine(recordPath, folderName); } else if (info.IsSeries) { - recordPath = Path.Combine(recordPath, "Series", _fileSystem.GetValidFilename(info.Name).Trim()); + var customRecordingPath = config.SeriesRecordingPath; + if ((string.IsNullOrWhiteSpace(customRecordingPath) || string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase)) && config.EnableRecordingSubfolders) + { + recordPath = Path.Combine(recordPath, "Series"); + } + + var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); + var folderNameWithYear = folderName; + if (info.ProductionYear.HasValue) + { + folderNameWithYear += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; + } + + if (Directory.Exists(Path.Combine(recordPath, folderName))) + { + recordPath = Path.Combine(recordPath, folderName); + } + else + { + recordPath = Path.Combine(recordPath, folderNameWithYear); + } if (info.SeasonNumber.HasValue) { - var folderName = string.Format("Season {0}", info.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); + folderName = string.Format("Season {0}", info.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); recordPath = Path.Combine(recordPath, folderName); } } else if (info.IsKids) { - recordPath = Path.Combine(recordPath, "Kids", _fileSystem.GetValidFilename(info.Name).Trim()); + if (config.EnableRecordingSubfolders) + { + recordPath = Path.Combine(recordPath, "Kids"); + } + + var folderName = _fileSystem.GetValidFilename(info.Name).Trim(); + if (info.ProductionYear.HasValue) + { + folderName += " (" + info.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")"; + } + recordPath = Path.Combine(recordPath, folderName); } else if (info.IsSports) { - recordPath = Path.Combine(recordPath, "Sports", _fileSystem.GetValidFilename(info.Name).Trim()); + if (config.EnableRecordingSubfolders) + { + recordPath = Path.Combine(recordPath, "Sports"); + } + recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(info.Name).Trim()); } else { - recordPath = Path.Combine(recordPath, "Other", _fileSystem.GetValidFilename(info.Name).Trim()); + if (config.EnableRecordingSubfolders) + { + recordPath = Path.Combine(recordPath, "Other"); + } + recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(info.Name).Trim()); } var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts"; - recordPath = Path.Combine(recordPath, recordingFileName); + return Path.Combine(recordPath, recordingFileName); + } - var recordingId = info.Id.GetMD5().ToString("N"); - var recording = _recordingProvider.GetAll().FirstOrDefault(x => string.Equals(x.Id, recordingId, StringComparison.OrdinalIgnoreCase)); + private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken) + { + if (timer == null) + { + throw new ArgumentNullException("timer"); + } - if (recording == null) + ProgramInfo info = null; + + if (string.IsNullOrWhiteSpace(timer.ProgramId)) { - recording = new RecordingInfo - { - ChannelId = info.ChannelId, - Id = recordingId, - StartDate = info.StartDate, - EndDate = info.EndDate, - Genres = info.Genres, - IsKids = info.IsKids, - IsLive = info.IsLive, - IsMovie = info.IsMovie, - IsHD = info.IsHD, - IsNews = info.IsNews, - IsPremiere = info.IsPremiere, - IsSeries = info.IsSeries, - IsSports = info.IsSports, - IsRepeat = !info.IsPremiere, - Name = info.Name, - EpisodeTitle = info.EpisodeTitle, - ProgramId = info.Id, - ImagePath = info.ImagePath, - ImageUrl = info.ImageUrl, - OriginalAirDate = info.OriginalAirDate, - Status = RecordingStatus.Scheduled, - Overview = info.Overview, - SeriesTimerId = timer.SeriesTimerId, - TimerId = timer.Id, - ShowId = info.ShowId - }; - _recordingProvider.AddOrUpdate(recording); + _logger.Info("Timer {0} has null programId", timer.Id); + } + else + { + info = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId); + } + + if (info == null) + { + _logger.Info("Unable to find program with Id {0}. Will search using start date", timer.ProgramId); + info = GetProgramInfoFromCache(timer.ChannelId, timer.StartDate); } + if (info == null) + { + throw new InvalidOperationException(string.Format("Program with Id {0} not found", timer.ProgramId)); + } + + var recordPath = GetRecordingPath(timer, info); + var recordingStatus = RecordingStatus.New; + try { var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false); @@ -817,11 +894,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _libraryMonitor.ReportFileSystemChangeBeginning(recordPath); - recording.Path = recordPath; - recording.Status = RecordingStatus.InProgress; - recording.DateLastUpdated = DateTime.UtcNow; - _recordingProvider.AddOrUpdate(recording); - var duration = recordingEndDate - DateTime.UtcNow; _logger.Info("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture)); @@ -846,7 +918,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken).ConfigureAwait(false); - recording.Status = RecordingStatus.Completed; + recordingStatus = RecordingStatus.Completed; _logger.Info("Recording completed: {0}", recordPath); } finally @@ -862,12 +934,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV catch (OperationCanceledException) { _logger.Info("Recording stopped: {0}", recordPath); - recording.Status = RecordingStatus.Completed; + recordingStatus = RecordingStatus.Completed; } catch (Exception ex) { _logger.ErrorException("Error recording to {0}", ex, recordPath); - recording.Status = RecordingStatus.Error; + recordingStatus = RecordingStatus.Error; } finally { @@ -875,12 +947,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _activeRecordings.TryRemove(timer.Id, out removed); } - recording.DateLastUpdated = DateTime.UtcNow; - _recordingProvider.AddOrUpdate(recording); - - if (recording.Status == RecordingStatus.Completed) + if (recordingStatus == RecordingStatus.Completed) { - OnSuccessfulRecording(recording); + OnSuccessfulRecording(info.IsSeries, recordPath); _timerProvider.Delete(timer); } else if (DateTime.UtcNow < timer.EndDate) @@ -893,7 +962,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV else { _timerProvider.Delete(timer); - _recordingProvider.Delete(recording); } } @@ -948,11 +1016,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return new DirectRecorder(_logger, _httpClient, _fileSystem); } - private async void OnSuccessfulRecording(RecordingInfo recording) + private async void OnSuccessfulRecording(bool isSeries, string path) { if (GetConfiguration().EnableAutoOrganize) { - if (recording.IsSeries) + if (isSeries) { try { @@ -962,12 +1030,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); - var result = await organize.OrganizeEpisodeFile(recording.Path, CancellationToken.None).ConfigureAwait(false); - - if (result.Status == FileSortingStatus.Success) - { - _recordingProvider.Delete(recording); - } + var result = await organize.OrganizeEpisodeFile(path, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -991,18 +1054,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return epgData.FirstOrDefault(p => Math.Abs(startDateTicks - p.StartDate.Ticks) <= TimeSpan.FromMinutes(3).Ticks); } - private string RecordingPath - { - get - { - var path = GetConfiguration().RecordingPath; - - return string.IsNullOrWhiteSpace(path) - ? Path.Combine(DataPath, "recordings") - : path; - } - } - private LiveTvOptions GetConfiguration() { return _config.GetConfiguration("livetv"); @@ -1010,7 +1061,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private async Task UpdateTimersForSeriesTimer(List epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers) { - var newTimers = GetTimersForSeries(seriesTimer, epgData, _recordingProvider.GetAll()).ToList(); + var newTimers = GetTimersForSeries(seriesTimer, epgData, true).ToList(); var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false); @@ -1024,7 +1075,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (deleteInvalidTimers) { - var allTimers = GetTimersForSeries(seriesTimer, epgData, new List()) + var allTimers = GetTimersForSeries(seriesTimer, epgData, false) .Select(i => i.Id) .ToList(); @@ -1040,7 +1091,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private IEnumerable GetTimersForSeries(SeriesTimerInfo seriesTimer, IEnumerable allPrograms, IReadOnlyList currentRecordings) + private IEnumerable GetTimersForSeries(SeriesTimerInfo seriesTimer, + IEnumerable allPrograms, + bool filterByCurrentRecordings) { if (seriesTimer == null) { @@ -1050,23 +1103,71 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { throw new ArgumentNullException("allPrograms"); } - if (currentRecordings == null) - { - throw new ArgumentNullException("currentRecordings"); - } // Exclude programs that have already ended allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow && i.StartDate > DateTime.UtcNow); allPrograms = GetProgramsForSeries(seriesTimer, allPrograms); - var recordingShowIds = currentRecordings.Select(i => i.ProgramId).Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); - - allPrograms = allPrograms.Where(i => !recordingShowIds.Contains(i.Id, StringComparer.OrdinalIgnoreCase)); + if (filterByCurrentRecordings) + { + allPrograms = allPrograms.Where(i => !IsProgramAlreadyInLibrary(i)); + } return allPrograms.Select(i => RecordingHelper.CreateTimer(i, seriesTimer)); } + private bool IsProgramAlreadyInLibrary(ProgramInfo program) + { + if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle)) + { + var seriesIds = _libraryManager.GetItemIds(new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(Series).Name }, + Name = program.Name + + }).Select(i => i.ToString("N")).ToArray(); + + if (seriesIds.Length == 0) + { + return false; + } + + if (program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) + { + var result = _libraryManager.GetItemsResult(new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(Episode).Name }, + ParentIndexNumber = program.SeasonNumber.Value, + IndexNumber = program.EpisodeNumber.Value, + AncestorIds = seriesIds + }); + + if (result.TotalRecordCount > 0) + { + return true; + } + } + + if (!string.IsNullOrWhiteSpace(program.EpisodeTitle)) + { + var result = _libraryManager.GetItemsResult(new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(Episode).Name }, + Name = program.EpisodeTitle, + AncestorIds = seriesIds + }); + + if (result.TotalRecordCount > 0) + { + return true; + } + } + } + + return false; + } + private IEnumerable GetProgramsForSeries(SeriesTimerInfo seriesTimer, IEnumerable allPrograms) { if (!seriesTimer.RecordAnyTime) @@ -1151,6 +1252,47 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV }); } + public List GetRecordingFolders() + { + var list = new List(); + + var defaultFolder = RecordingPath; + var defaultName = "Recordings"; + + if (Directory.Exists(defaultFolder)) + { + list.Add(new VirtualFolderInfo + { + Locations = new List { defaultFolder }, + Name = defaultName + }); + } + + var customPath = GetConfiguration().MovieRecordingPath; + if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath)) + { + list.Add(new VirtualFolderInfo + { + Locations = new List { customPath }, + Name = "Recorded Movies", + CollectionType = CollectionType.Movies + }); + } + + customPath = GetConfiguration().SeriesRecordingPath; + if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath)) + { + list.Add(new VirtualFolderInfo + { + Locations = new List { customPath }, + Name = "Recorded Series", + CollectionType = CollectionType.TvShows + }); + } + + return list; + } + class ActiveRecordingInfo { public string Path { get; set; } -- cgit v1.2.3 From 99084edabeb1787f28496dffa55fbb260e34ae81 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 8 May 2016 23:13:38 -0400 Subject: update windows ffmpeg --- MediaBrowser.Api/ApiEntryPoint.cs | 8 ++- MediaBrowser.Api/LiveTv/LiveTvService.cs | 20 ++++++- MediaBrowser.Api/Movies/MoviesService.cs | 1 - MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs | 5 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 3 +- MediaBrowser.Controller/Entities/Audio/Audio.cs | 6 --- .../Entities/Audio/MusicAlbum.cs | 9 ++++ .../Entities/Audio/MusicArtist.cs | 9 ++++ MediaBrowser.Controller/Entities/BaseItem.cs | 11 ++++ MediaBrowser.Controller/Entities/Folder.cs | 49 +++++++++++------ MediaBrowser.Controller/Entities/IHasMetadata.cs | 2 + .../Entities/InternalItemsQuery.cs | 2 + MediaBrowser.Controller/Entities/MusicVideo.cs | 6 --- MediaBrowser.Controller/Entities/TV/Season.cs | 9 ++++ MediaBrowser.Controller/Entities/TV/Series.cs | 9 ++++ MediaBrowser.Controller/Entities/UserRootFolder.cs | 5 -- MediaBrowser.Controller/Playlists/Playlist.cs | 9 ++++ MediaBrowser.Model/LiveTv/ProgramQuery.cs | 3 ++ .../LiveTv/RecommendedProgramQuery.cs | 9 +++- MediaBrowser.Providers/Manager/MetadataService.cs | 61 +++++++++++++++++++-- .../Music/MusicBrainzArtistProvider.cs | 29 +++++++--- .../Dto/DtoService.cs | 33 ++++-------- .../Library/LibraryManager.cs | 14 +++-- .../LiveTv/EmbyTV/EmbyTV.cs | 18 ++++++- .../LiveTv/LiveTvManager.cs | 10 ++-- .../Persistence/SqliteItemRepository.cs | 62 ++++++++++++++++++++-- .../Sorting/DateLastMediaAddedComparer.cs | 8 +-- .../MediaBrowser.ServerApplication.csproj | 2 - .../Native/WindowsApp.cs | 17 ++++-- .../ffmpeg/ffmpegx64.7z.REMOVED.git-id | 1 - .../ffmpeg/ffmpegx86.7z.REMOVED.git-id | 1 - 31 files changed, 330 insertions(+), 101 deletions(-) delete mode 100644 MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id delete mode 100644 MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 66ca8c25d4..a677bc6004 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -488,13 +488,17 @@ namespace MediaBrowser.Api { try { - Logger.Info("Killing ffmpeg process for {0}", job.Path); + Logger.Info("Stopping ffmpeg process with q command for {0}", job.Path); //process.Kill(); process.StandardInput.WriteLine("q"); // Need to wait because killing is asynchronous - process.WaitForExit(5000); + if (!process.WaitForExit(5000)) + { + Logger.Info("Killing ffmpeg process for {0}", job.Path); + process.Kill(); + } } catch (Exception ex) { diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index ebcf8fbeac..d3a4558c88 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -254,6 +254,8 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableImages { get; set; } + public bool EnableTotalRecordCount { get; set; } + [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? ImageTypeLimit { get; set; } @@ -266,12 +268,24 @@ namespace MediaBrowser.Api.LiveTv /// The fields. [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } + + public GetPrograms() + { + EnableTotalRecordCount = true; + } } [Route("/LiveTv/Programs/Recommended", "GET", Summary = "Gets available live tv epgs..")] [Authenticated] public class GetRecommendedPrograms : IReturn>, IHasDtoOptions { + public bool EnableTotalRecordCount { get; set; } + + public GetRecommendedPrograms() + { + EnableTotalRecordCount = true; + } + [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string UserId { get; set; } @@ -662,7 +676,8 @@ namespace MediaBrowser.Api.LiveTv { ChannelIds = (request.ChannelIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray(), UserId = request.UserId, - HasAired = request.HasAired + HasAired = request.HasAired, + EnableTotalRecordCount = request.EnableTotalRecordCount }; if (!string.IsNullOrEmpty(request.MinStartDate)) @@ -709,7 +724,8 @@ namespace MediaBrowser.Api.LiveTv HasAired = request.HasAired, IsMovie = request.IsMovie, IsKids = request.IsKids, - IsSports = request.IsSports + IsSports = request.IsSports, + EnableTotalRecordCount = request.EnableTotalRecordCount }; var result = await _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 9590971d06..786615cc3e 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -196,7 +196,6 @@ namespace MediaBrowser.Api.Movies var parentIds = new string[] { }; var list = _libraryManager.GetItemList(query, parentIds) - .DistinctBy(i => i.PresentationUniqueKey, StringComparer.OrdinalIgnoreCase) .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) .ToList(); diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 6867f6308c..aee1a8d545 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Api.UserLibrary protected BaseItemsRequest() { EnableImages = true; + EnableTotalRecordCount = true; } /// @@ -104,7 +105,9 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IsInBoxSet", Description = "Optional filter by items that are in boxsets, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsInBoxSet { get; set; } - + + public bool EnableTotalRecordCount { get; set; } + /// /// Skips over a given number of items within the results. Use for paging. /// diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 7d2029cabe..ff937078e6 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -230,7 +230,8 @@ namespace MediaBrowser.Api.UserLibrary ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId), ParentIndexNumber = request.ParentIndexNumber, AiredDuringSeason = request.AiredDuringSeason, - AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater + AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater, + EnableTotalRecordCount = request.EnableTotalRecordCount }; if (!string.IsNullOrWhiteSpace(request.Ids)) diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index fd56a6746a..c34a884ff5 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -40,12 +40,6 @@ namespace MediaBrowser.Controller.Entities.Audio public List AlbumArtists { get; set; } - /// - /// Gets or sets the album. - /// - /// The album. - public string Album { get; set; } - [IgnoreDataMember] public bool IsThemeMedia { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 5cb4e8c9d1..615276e837 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -48,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + [IgnoreDataMember] public List AllArtists { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 6f6f124dbb..6104976615 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -34,6 +34,15 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + [IgnoreDataMember] public override bool SupportsAddingToPlaylist { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 2e968c8803..cd021d2ab5 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -69,6 +69,12 @@ namespace MediaBrowser.Controller.Entities public List ImageInfos { get; set; } + /// + /// Gets or sets the album. + /// + /// The album. + public string Album { get; set; } + /// /// Gets or sets the channel identifier. /// @@ -1175,6 +1181,11 @@ namespace MediaBrowser.Controller.Entities get { return Id.ToString("N"); } } + public virtual bool RequiresRefresh() + { + return false; + } + private string _userDataKey; /// /// Gets the user data key. diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 978fd7fedf..457a0b3abd 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -28,6 +28,9 @@ namespace MediaBrowser.Controller.Entities public List ThemeSongIds { get; set; } public List ThemeVideoIds { get; set; } + [IgnoreDataMember] + public DateTime? DateLastMediaAdded { get; set; } + public Folder() { LinkedChildren = new List(); @@ -55,6 +58,36 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public virtual bool SupportsCumulativeRunTimeTicks + { + get + { + return false; + } + } + + [IgnoreDataMember] + public virtual bool SupportsDateLastMediaAdded + { + get + { + return false; + } + } + + public override bool RequiresRefresh() + { + var baseResult = base.RequiresRefresh(); + + if (SupportsCumulativeRunTimeTicks && !RunTimeTicks.HasValue) + { + baseResult = true; + } + + return baseResult; + } + [IgnoreDataMember] public override string FileNameWithoutExtension { @@ -789,11 +822,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.AiredEpisodeOrder"); return true; } - if (query.SortBy.Contains(ItemSortBy.Album, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.Album"); - return true; - } if (query.SortBy.Contains(ItemSortBy.AlbumArtist, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.AlbumArtist"); @@ -809,11 +837,6 @@ namespace MediaBrowser.Controller.Entities Logger.Debug("Query requires post-filtering due to ItemSortBy.Budget"); return true; } - if (query.SortBy.Contains(ItemSortBy.DateLastContentAdded, StringComparer.OrdinalIgnoreCase)) - { - Logger.Debug("Query requires post-filtering due to ItemSortBy.DateLastContentAdded"); - return true; - } if (query.SortBy.Contains(ItemSortBy.GameSystem, StringComparer.OrdinalIgnoreCase)) { Logger.Debug("Query requires post-filtering due to ItemSortBy.GameSystem"); @@ -1086,12 +1109,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.AlbumNames.Length > 0) - { - Logger.Debug("Query requires post-filtering due to AlbumNames"); - return true; - } - if (query.ArtistNames.Length > 0) { Logger.Debug("Query requires post-filtering due to ArtistNames"); diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index 1f680b35f3..c7940c8a97 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -49,5 +49,7 @@ namespace MediaBrowser.Controller.Entities /// /// true if [supports people]; otherwise, false. bool SupportsPeople { get; } + + bool RequiresRefresh(); } } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 385ee81e9d..5236b0a278 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -137,10 +137,12 @@ namespace MediaBrowser.Controller.Entities public string AncestorWithPresentationUniqueKey { get; set; } public bool GroupByPresentationUniqueKey { get; set; } + public bool EnableTotalRecordCount { get; set; } public InternalItemsQuery() { GroupByPresentationUniqueKey = true; + EnableTotalRecordCount = true; AlbumNames = new string[] { }; ArtistNames = new string[] { }; diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index bf4c2559c5..7119828e26 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -9,12 +9,6 @@ namespace MediaBrowser.Controller.Entities { public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasProductionLocations, IHasBudget, IHasLookupInfo { - /// - /// Gets or sets the album. - /// - /// The album. - public string Album { get; set; } - /// /// Gets or sets the budget. /// diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 4e6128527c..ac8cc0ee26 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -32,6 +32,15 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } + [IgnoreDataMember] public override Guid? DisplayParentId { diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 680af1843c..f8cdab8ce6 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -48,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override bool SupportsDateLastMediaAdded + { + get + { + return true; + } + } + public bool DisplaySpecialsWithSeasons { get; set; } public List LocalTrailerIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 8ce39c6979..b9e997d175 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -102,10 +102,5 @@ namespace MediaBrowser.Controller.Entities LibraryManager.RegisterItem(item); } } - - public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) - { - // Nothing meaninful here and will only waste resources - } } } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index cd0c5dc1c7..003cbcfcda 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -39,6 +39,15 @@ namespace MediaBrowser.Controller.Playlists } } + [IgnoreDataMember] + public override bool SupportsCumulativeRunTimeTicks + { + get + { + return true; + } + } + public override bool IsAuthorizedToDelete(User user) { return true; diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index 7a877e3561..0141191c18 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -14,8 +14,11 @@ namespace MediaBrowser.Model.LiveTv ChannelIds = new string[] { }; SortBy = new string[] { }; Genres = new string[] { }; + EnableTotalRecordCount = true; } + public bool EnableTotalRecordCount { get; set; } + /// /// Fields to return within the items, in addition to basic information /// diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs index e83a8fda65..0e6d081a1d 100644 --- a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs @@ -13,7 +13,14 @@ namespace MediaBrowser.Model.LiveTv public bool? EnableImages { get; set; } public int? ImageTypeLimit { get; set; } public ImageType[] EnableImageTypes { get; set; } - + + public bool EnableTotalRecordCount { get; set; } + + public RecommendedProgramQuery() + { + EnableTotalRecordCount = true; + } + /// /// Gets or sets the user identifier. /// diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index ae48c996a0..218127ab9e 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -310,6 +310,11 @@ namespace MediaBrowser.Providers.Manager return true; } + if (item is MusicVideo) + { + return true; + } + return false; } @@ -390,7 +395,6 @@ namespace MediaBrowser.Providers.Manager return _cachedTask; } - private readonly Task _cachedResult = Task.FromResult(ItemUpdateType.None); /// /// Befores the save. /// @@ -398,9 +402,58 @@ namespace MediaBrowser.Providers.Manager /// if set to true [is full refresh]. /// Type of the current update. /// ItemUpdateType. - protected virtual Task BeforeSave(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) + protected virtual async Task BeforeSave(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) + { + var updateType = ItemUpdateType.None; + + updateType |= SaveCumulativeRunTimeTicks(item, isFullRefresh, currentUpdateType); + updateType |= SaveDateLastMediaAdded(item, isFullRefresh, currentUpdateType); + + return updateType; + } + + private ItemUpdateType SaveCumulativeRunTimeTicks(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) + { + var updateType = ItemUpdateType.None; + + if (isFullRefresh || currentUpdateType > ItemUpdateType.None) + { + var folder = item as Folder; + if (folder != null && folder.SupportsCumulativeRunTimeTicks) + { + var ticks = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.RunTimeTicks ?? 0).Sum(); + + if (!folder.RunTimeTicks.HasValue || folder.RunTimeTicks.Value != ticks) + { + folder.RunTimeTicks = ticks; + updateType = ItemUpdateType.MetadataEdit; + } + } + } + + return updateType; + } + + private ItemUpdateType SaveDateLastMediaAdded(TItemType item, bool isFullRefresh, ItemUpdateType currentUpdateType) { - return _cachedResult; + var updateType = ItemUpdateType.None; + + if (isFullRefresh || currentUpdateType > ItemUpdateType.None) + { + var folder = item as Folder; + if (folder != null && folder.SupportsDateLastMediaAdded) + { + var date = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.DateCreated).Max(); + + if (!folder.DateLastMediaAdded.HasValue || folder.DateLastMediaAdded.Value != date) + { + folder.DateLastMediaAdded = date; + updateType = ItemUpdateType.MetadataEdit; + } + } + } + + return updateType; } /// @@ -420,7 +473,7 @@ namespace MediaBrowser.Providers.Manager : status.DateLastMetadataRefresh ?? default(DateTime); // Run all if either of these flags are true - var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || dateLastRefresh == default(DateTime); + var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || dateLastRefresh == default(DateTime) || item.RequiresRefresh(); if (!runAllProviders) { diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index ad900123ed..2eb65f4e57 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -62,12 +62,25 @@ namespace MediaBrowser.Providers.Music private IEnumerable GetResultsFromResponse(XmlDocument doc) { - var ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#"); + //var ns = new XmlNamespaceManager(doc.NameTable); + //ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#"); var list = new List(); - var nodes = doc.SelectNodes("//mb:artist-list/mb:artist", ns); + var docElem = doc.DocumentElement; + + if (docElem == null) + { + return list; + } + + var artistList = docElem.FirstChild; + if (artistList == null) + { + return list; + } + + var nodes = artistList.ChildNodes; if (nodes != null) { @@ -79,11 +92,13 @@ namespace MediaBrowser.Providers.Music string mbzId = node.Attributes["id"].Value; - var nameNode = node.SelectSingleNode("//mb:name", ns); - - if (nameNode != null) + foreach (var child in node.ChildNodes.Cast()) { - name = nameNode.InnerText; + if (string.Equals(child.Name, "name", StringComparison.OrdinalIgnoreCase)) + { + name = node.InnerText; + break; + } } if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name)) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 0aec3230da..32610a6ad9 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -499,6 +499,16 @@ namespace MediaBrowser.Server.Implementations.Dto } } + if (fields.Contains(ItemFields.CumulativeRunTimeTicks)) + { + dto.CumulativeRunTimeTicks = dto.RunTimeTicks; + } + + if (fields.Contains(ItemFields.DateLastMediaAdded)) + { + dto.DateLastMediaAdded = folder.DateLastMediaAdded; + } + dto.UserData.Played = dto.UserData.PlayedPercentage.HasValue && dto.UserData.PlayedPercentage.Value >= 100; } @@ -1613,9 +1623,7 @@ namespace MediaBrowser.Server.Implementations.Dto { var recursiveItemCount = 0; var unplayed = 0; - long runtime = 0; - DateTime? dateLastMediaAdded = null; double totalPercentPlayed = 0; double totalSyncPercent = 0; var addSyncInfo = fields.Contains(ItemFields.SyncInfo); @@ -1632,15 +1640,6 @@ namespace MediaBrowser.Server.Implementations.Dto // Loop through each recursive child foreach (var child in children) { - if (!dateLastMediaAdded.HasValue) - { - dateLastMediaAdded = child.DateCreated; - } - else - { - dateLastMediaAdded = new[] { dateLastMediaAdded.Value, child.DateCreated }.Max(); - } - var userdata = _userDataRepository.GetUserData(user, child); recursiveItemCount++; @@ -1669,8 +1668,6 @@ namespace MediaBrowser.Server.Implementations.Dto unplayed++; } - runtime += child.RunTimeTicks ?? 0; - if (addSyncInfo) { double percent = 0; @@ -1709,16 +1706,6 @@ namespace MediaBrowser.Server.Implementations.Dto } } } - - if (runtime > 0 && fields.Contains(ItemFields.CumulativeRunTimeTicks)) - { - dto.CumulativeRunTimeTicks = runtime; - } - - if (fields.Contains(ItemFields.DateLastMediaAdded)) - { - dto.DateLastMediaAdded = dateLastMediaAdded; - } } /// diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index f9bf3446f4..c95b301722 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1354,12 +1354,20 @@ namespace MediaBrowser.Server.Implementations.Library AddUserToQuery(query, query.User); } - var initialResult = ItemRepository.GetItemIds(query); + if (query.EnableTotalRecordCount) + { + var initialResult = ItemRepository.GetItemIds(query); + + return new QueryResult + { + TotalRecordCount = initialResult.TotalRecordCount, + Items = initialResult.Items.Select(GetItemById).Where(i => i != null).ToArray() + }; + } return new QueryResult { - TotalRecordCount = initialResult.TotalRecordCount, - Items = initialResult.Items.Select(GetItemById).Where(i => i != null).ToArray() + Items = ItemRepository.GetItemIdsList(query).Select(GetItemById).Where(i => i != null).ToArray() }; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index c5920d3d6a..b5e8ad79a5 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -760,7 +760,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (info.IsMovie) { var customRecordingPath = config.MovieRecordingPath; - if ((string.IsNullOrWhiteSpace(customRecordingPath) || string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase)) && config.EnableRecordingSubfolders) + var allowSubfolder = true; + if (!string.IsNullOrWhiteSpace(customRecordingPath)) + { + allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); + recordPath = customRecordingPath; + } + + if (allowSubfolder && config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Movies"); } @@ -775,7 +782,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV else if (info.IsSeries) { var customRecordingPath = config.SeriesRecordingPath; - if ((string.IsNullOrWhiteSpace(customRecordingPath) || string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase)) && config.EnableRecordingSubfolders) + var allowSubfolder = true; + if (!string.IsNullOrWhiteSpace(customRecordingPath)) + { + allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase); + recordPath = customRecordingPath; + } + + if (allowSubfolder && config.EnableRecordingSubfolders) { recordPath = Path.Combine(recordPath, "Series"); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index eec8328f89..175eed66c0 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -886,7 +886,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv StartIndex = query.StartIndex, Limit = query.Limit, SortBy = query.SortBy, - SortOrder = query.SortOrder ?? SortOrder.Ascending + SortOrder = query.SortOrder ?? SortOrder.Ascending, + EnableTotalRecordCount = query.EnableTotalRecordCount }; if (query.HasAired.HasValue) @@ -924,7 +925,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv IsAiring = query.IsAiring, IsMovie = query.IsMovie, IsSports = query.IsSports, - IsKids = query.IsKids + IsKids = query.IsKids, + EnableTotalRecordCount = query.EnableTotalRecordCount }; if (query.HasAired.HasValue) @@ -1263,11 +1265,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv private async Task CleanDatabaseInternal(List currentIdList, string[] validTypes, IProgress progress, CancellationToken cancellationToken) { - var list = _itemRepo.GetItemIds(new InternalItemsQuery + var list = _itemRepo.GetItemIdsList(new InternalItemsQuery { IncludeItemTypes = validTypes - }).Items.ToList(); + }).ToList(); var numComplete = 0; diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 09739f8a9d..180bd35479 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -85,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _updateInheritedRatingCommand; private IDbCommand _updateInheritedTagsCommand; - public const int LatestSchemaVersion = 76; + public const int LatestSchemaVersion = 77; /// /// Initializes a new instance of the class. @@ -235,6 +235,9 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(Logger, "TypedBaseItems", "SlugName", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "OriginalTitle", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "PrimaryVersionId", "Text"); + _connection.AddColumn(Logger, "TypedBaseItems", "DateLastMediaAdded", "DATETIME"); + _connection.AddColumn(Logger, "TypedBaseItems", "Album", "Text"); + _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT"); string[] postQueries = @@ -351,7 +354,9 @@ namespace MediaBrowser.Server.Implementations.Persistence "TrailerTypes", "DateModifiedDuringLastRefresh", "OriginalTitle", - "PrimaryVersionId" + "PrimaryVersionId", + "DateLastMediaAdded", + "Album" }; private readonly string[] _mediaStreamSaveColumns = @@ -463,7 +468,9 @@ namespace MediaBrowser.Server.Implementations.Persistence "PresentationUniqueKey", "SlugName", "OriginalTitle", - "PrimaryVersionId" + "PrimaryVersionId", + "DateLastMediaAdded", + "Album" }; _saveItemCommand = _connection.CreateCommand(); _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; @@ -824,6 +831,18 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = null; } + var folder = item as Folder; + if (folder != null && folder.DateLastMediaAdded.HasValue) + { + _saveItemCommand.GetParameter(index++).Value = folder.DateLastMediaAdded.Value; + } + else + { + _saveItemCommand.GetParameter(index++).Value = null; + } + + _saveItemCommand.GetParameter(index++).Value = item.Album; + _saveItemCommand.Transaction = transaction; _saveItemCommand.ExecuteNonQuery(); @@ -1217,6 +1236,17 @@ namespace MediaBrowser.Server.Implementations.Persistence } } + var folder = item as Folder; + if (folder != null && !reader.IsDBNull(54)) + { + folder.DateLastMediaAdded = reader.GetDateTime(54).ToUniversalTime(); + } + + if (!reader.IsDBNull(55)) + { + item.Album = reader.GetString(55); + } + return item; } @@ -1777,6 +1807,10 @@ namespace MediaBrowser.Server.Implementations.Persistence { return new Tuple("played", false); } + if (string.Equals(name, ItemSortBy.DateLastContentAdded, StringComparison.OrdinalIgnoreCase)) + { + return new Tuple("DateLastMediaAdded", false); + } return new Tuple(name, false); } @@ -2484,7 +2518,7 @@ namespace MediaBrowser.Server.Implementations.Persistence if (query.MediaTypes.Length == 1) { whereClauses.Add("MediaType=@MediaTypes"); - cmd.Parameters.Add(cmd, "@MediaTypes", DbType.String).Value = query.MediaTypes[0].ToString(); + cmd.Parameters.Add(cmd, "@MediaTypes", DbType.String).Value = query.MediaTypes[0]; } if (query.MediaTypes.Length > 1) { @@ -2493,6 +2527,26 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add("MediaType in (" + val + ")"); } + if (query.AlbumNames.Length > 0) + { + var clause = "("; + + var index = 0; + foreach (var name in query.AlbumNames) + { + if (index > 0) + { + clause += " OR "; + } + clause += "Album=@AlbumName" + index; + index++; + cmd.Parameters.Add(cmd, "@AlbumName" + index, DbType.String).Value = name; + } + + clause += ")"; + whereClauses.Add(clause); + } + //var enableItemsByName = query.IncludeItemsByName ?? query.IncludeItemTypes.Length > 0; var enableItemsByName = query.IncludeItemsByName ?? false; diff --git a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs index 68cd44ec94..5c51f5e0fc 100644 --- a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs @@ -49,10 +49,10 @@ namespace MediaBrowser.Server.Implementations.Sorting if (folder != null) { - return folder.GetRecursiveChildren(User, i => !i.IsFolder) - .Select(i => i.DateCreated) - .OrderByDescending(i => i) - .FirstOrDefault(); + if (folder.DateLastMediaAdded.HasValue) + { + return folder.DateLastMediaAdded.Value; + } } return x.DateCreated; diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index f544a149d8..366d4b6083 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -142,8 +142,6 @@ - - diff --git a/MediaBrowser.ServerApplication/Native/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs index 808c7558ec..271b02d9a7 100644 --- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs +++ b/MediaBrowser.ServerApplication/Native/WindowsApp.cs @@ -158,9 +158,9 @@ namespace MediaBrowser.ServerApplication.Native info.FFMpegFilename = "ffmpeg.exe"; info.FFProbeFilename = "ffprobe.exe"; - info.Version = "20160401"; + info.Version = "20160508"; info.ArchiveType = "7z"; - info.IsEmbedded = true; + info.IsEmbedded = false; info.DownloadUrls = GetDownloadUrls(); return info; @@ -212,11 +212,18 @@ namespace MediaBrowser.ServerApplication.Native switch (Environment.SystemArchitecture) { case Architecture.X86_X64: - return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx64.7z" }; + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160508-win64.7z", + "https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-20160508-git-caee88d-win64-static.7z" + }; case Architecture.X86: - return new[] { "MediaBrowser.ServerApplication.ffmpeg.ffmpegx86.7z" }; + return new[] + { + "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160508-win32.7z", + "https://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20160508-git-caee88d-win32-static.7z" + }; } - return new string[] { }; } } diff --git a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id deleted file mode 100644 index b0542b75f3..0000000000 --- a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx64.7z.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -9dc10b022537738edce7eb71aa8dd4adbfee2c7b \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id b/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id deleted file mode 100644 index 3939ec44d6..0000000000 --- a/MediaBrowser.ServerApplication/ffmpeg/ffmpegx86.7z.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -00fa1afa35fbd0a7e97ad7956e42ae17f6882f64 \ No newline at end of file -- cgit v1.2.3 From 287f797d996c2080c8b9d7c438ad977ec6e5c45f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 10 May 2016 01:00:50 -0400 Subject: update recording editor --- MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index b5e8ad79a5..ac8c99acec 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1154,7 +1154,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV IncludeItemTypes = new[] { typeof(Episode).Name }, ParentIndexNumber = program.SeasonNumber.Value, IndexNumber = program.EpisodeNumber.Value, - AncestorIds = seriesIds + AncestorIds = seriesIds, + ExcludeLocationTypes = new[] { LocationType.Virtual } }); if (result.TotalRecordCount > 0) @@ -1169,7 +1170,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { IncludeItemTypes = new[] { typeof(Episode).Name }, Name = program.EpisodeTitle, - AncestorIds = seriesIds + AncestorIds = seriesIds, + ExcludeLocationTypes = new[] { LocationType.Virtual } }); if (result.TotalRecordCount > 0) -- cgit v1.2.3 From 9edda5782b6c6221e1b1def4e209b08c2f94ad9c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 10 May 2016 01:15:06 -0400 Subject: update recording path --- .../LiveTv/EmbyTV/DirectRecorder.cs | 5 +++++ MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 5 +---- .../LiveTv/EmbyTV/EncodedRecorder.cs | 10 ++++++++++ MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs | 2 ++ 4 files changed, 18 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 54443d9ca3..b21aa904b9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -23,6 +23,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _fileSystem = fileSystem; } + public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile) + { + return targetFile; + } + public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { var httpRequestOptions = new HttpRequestOptions() diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index ac8c99acec..2de51479f2 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -898,10 +898,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var recorder = await GetRecorder().ConfigureAwait(false); - if (recorder is EncodedRecorder) - { - recordPath = Path.ChangeExtension(recordPath, ".mp4"); - } + recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath); recordPath = EnsureFileUnique(recordPath, timer.Id); _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); activeRecordingInfo.Path = recordPath; diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index dec8ba83d2..85b6ead07b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -42,6 +42,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _liveTvOptions = liveTvOptions; } + public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile) + { + if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings) + { + return Path.ChangeExtension(targetFile, ".mkv"); + } + + return Path.ChangeExtension(targetFile, ".mp4"); + } + public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { if (mediaSource.RunTimeTicks.HasValue) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs index 268a4f7512..5706b6ae9e 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs @@ -17,5 +17,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV /// The cancellation token. /// Task. Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken); + + string GetOutputPath(MediaSourceInfo mediaSource, string targetFile); } } -- cgit v1.2.3 From 761a476ea09bfe657ca824d15b1a71a8f9f3206e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 11 May 2016 13:46:44 -0400 Subject: use shared itemHelper --- MediaBrowser.Model/Dto/BaseItemDto.cs | 3 ++- MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 49ec9f8ef4..146fcc74e8 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -297,7 +297,8 @@ namespace MediaBrowser.Model.Dto /// /// The number. public string Number { get; set; } - + public string ChannelNumber { get; set; } + /// /// Gets or sets the index number. /// diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 175eed66c0..b3972a6778 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1515,6 +1515,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { dto.ChannelName = channel.Name; dto.MediaType = channel.MediaType; + dto.ChannelNumber = channel.Number; if (channel.HasImage(ImageType.Primary)) { @@ -1854,6 +1855,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var channel = tuple.Item2; dto.Number = channel.Number; + dto.ChannelNumber = channel.Number; dto.ChannelType = channel.ChannelType; dto.ServiceName = GetService(channel).Name; -- cgit v1.2.3 From c4e3bbaf4a9446250463f79c2b93728e8dfcd739 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 11 May 2016 18:08:19 -0400 Subject: import shared media info component --- .../Entities/Audio/MusicArtist.cs | 24 ++++++++++++++++++++-- .../LiveTv/EmbyTV/EncodedRecorder.cs | 7 ++++++- .../LiveTv/LiveTvManager.cs | 3 ++- 3 files changed, 30 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 6104976615..fb8a24061a 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -61,11 +61,31 @@ namespace MediaBrowser.Controller.Entities.Audio if (query.User != null) { return query.User.RootFolder - .GetRecursiveChildren(query.User, i => !i.IsFolder && itemByNameFilter(i)); + .GetRecursiveChildren(query.User, i => + { + if (query.IsFolder.HasValue) + { + if (query.IsFolder.Value != i.IsFolder) + { + return false; + } + } + return itemByNameFilter(i); + }); } return LibraryManager.RootFolder - .GetRecursiveChildren(i => !i.IsFolder && itemByNameFilter(i)); + .GetRecursiveChildren(i => + { + if (query.IsFolder.HasValue) + { + if (query.IsFolder.Value != i.IsFolder) + { + return false; + } + } + return itemByNameFilter(i); + }); } protected override IEnumerable ActualChildren diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 85b6ead07b..e9ea49fa32 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -46,7 +46,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings) { - return Path.ChangeExtension(targetFile, ".mkv"); + // if the audio is aac_latm, stream copying to mp4 will fail + var streams = mediaSource.MediaStreams ?? new List(); + if (streams.Any(i => i.Type == MediaStreamType.Audio && (i.Codec ?? string.Empty).IndexOf("aac", StringComparison.OrdinalIgnoreCase) != -1)) + { + return Path.ChangeExtension(targetFile, ".mkv"); + } } return Path.ChangeExtension(targetFile, ".mp4"); diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index b3972a6778..f85be5100b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -135,7 +135,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv var channels = _libraryManager.GetItemList(new InternalItemsQuery { - IncludeItemTypes = new[] { typeof(LiveTvChannel).Name } + IncludeItemTypes = new[] { typeof(LiveTvChannel).Name }, + SortBy = new[] { ItemSortBy.SortName } }).Cast(); -- cgit v1.2.3 From 995c34437e65b40576881a26c18f2ecf8b84ac89 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 15 May 2016 12:30:32 -0400 Subject: update tabs --- MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs | 393 ++++++++++----------- MediaBrowser.Dlna/Main/DlnaEntryPoint.cs | 4 + .../Probing/ProbeResultNormalizer.cs | 3 +- .../LiveTv/LiveTvManager.cs | 33 +- 4 files changed, 225 insertions(+), 208 deletions(-) (limited to 'MediaBrowser.Server.Implementations/LiveTv') diff --git a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs index b6ee3d4341..a8e778751d 100644 --- a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs +++ b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs @@ -1,37 +1,190 @@ -namespace MediaBrowser.Dlna.Channels +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Dlna.ContentDirectory; +using MediaBrowser.Dlna.PlayTo; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Dlna.Channels { - //public class DlnaChannelFactory : IChannelFactory, IDisposable + //public class DlnaChannel : IChannel, IDisposable //{ - // private readonly IServerConfigurationManager _config; // private readonly ILogger _logger; // private readonly IHttpClient _httpClient; + // private readonly IServerConfigurationManager _config; + // private List _servers = new List(); // private readonly IDeviceDiscovery _deviceDiscovery; - // private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1); - // private List _servers = new List(); - - // public static DlnaChannelFactory Instance; - // private Func> _localServersLookup; - // public DlnaChannelFactory(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IDeviceDiscovery deviceDiscovery) + // public static DlnaChannel Current; + + // public DlnaChannel(ILogger logger, IHttpClient httpClient, IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config) // { - // _config = config; - // _httpClient = httpClient; // _logger = logger; + // _httpClient = httpClient; // _deviceDiscovery = deviceDiscovery; - // Instance = this; + // _config = config; + // Current = this; + // } + + // public string Name + // { + // get { return "Devices"; } + // } + + // public string Description + // { + // get { return string.Empty; } + // } + + // public string DataVersion + // { + // get { return DateTime.UtcNow.Ticks.ToString(); } + // } + + // public string HomePageUrl + // { + // get { return string.Empty; } + // } + + // public ChannelParentalRating ParentalRating + // { + // get { return ChannelParentalRating.GeneralAudience; } + // } + + // public InternalChannelFeatures GetChannelFeatures() + // { + // return new InternalChannelFeatures + // { + // ContentTypes = new List + // { + // ChannelMediaContentType.Song, + // ChannelMediaContentType.Clip + // }, + + // MediaTypes = new List + // { + // ChannelMediaType.Audio, + // ChannelMediaType.Video, + // ChannelMediaType.Photo + // } + // }; + // } + + // public bool IsEnabledFor(string userId) + // { + // return true; + // } + + // public Task GetChannelImage(ImageType type, CancellationToken cancellationToken) + // { + // throw new NotImplementedException(); + // } + + // public IEnumerable GetSupportedChannelImages() + // { + // return new List + // { + // ImageType.Primary + // }; // } - // internal void Start(Func> localServersLookup) + // public void Start(Func> localServersLookup) // { // _localServersLookup = localServersLookup; - // //deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered; + // _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered; + // _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft; + + // _deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered; // _deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft; // } + // public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) + // { + // if (string.IsNullOrWhiteSpace(query.FolderId)) + // { + // return await GetServers(query, cancellationToken).ConfigureAwait(false); + // } + + // return new ChannelItemResult(); + + // //var idParts = query.FolderId.Split('|'); + // //var folderId = idParts.Length == 2 ? idParts[1] : null; + + // //var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest + // //{ + // // Limit = query.Limit, + // // StartIndex = query.StartIndex, + // // ParentId = folderId, + // // ContentDirectoryUrl = ControlUrl + + // //}, cancellationToken).ConfigureAwait(false); + + // //items = result.Items.ToList(); + + // //var list = items.ToList(); + // //var count = list.Count; + + // //list = ApplyPaging(list, query).ToList(); + + // //return new ChannelItemResult + // //{ + // // Items = list, + // // TotalRecordCount = count + // //}; + // } + + // public async Task GetServers(InternalChannelItemQuery query, CancellationToken cancellationToken) + // { + // await _syncLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + // try + // { + // var items = _servers.Select(i => + // { + // var service = i.Properties.Services + // .FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase)); + + // var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/')); + + // if (string.IsNullOrWhiteSpace(controlUrl)) + // { + // return null; + // } + + // return new ChannelItemInfo + // { + // Id = i.Properties.UUID, + // Name = i.Properties.Name, + // Type = ChannelItemType.Folder + // }; + + // }).Where(i => i != null).ToList(); + + // return new ChannelItemResult + // { + // TotalRecordCount = items.Count, + // Items = items + // }; + // } + // finally + // { + // _syncLock.Release(); + // } + // } + // async void deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e) // { // string usn; @@ -57,16 +210,13 @@ // } // } - // if (GetExistingServers(usn).Any()) - // { - // return; - // } - // await _syncLock.WaitAsync().ConfigureAwait(false); + // var serverList = _servers.ToList(); + // try // { - // if (GetExistingServers(usn).Any()) + // if (GetExistingServers(serverList, usn).Any()) // { // return; // } @@ -74,9 +224,9 @@ // var device = await Device.CreateuPnpDeviceAsync(new Uri(location), _httpClient, _config, _logger) // .ConfigureAwait(false); - // if (!_servers.Any(i => string.Equals(i.Properties.UUID, device.Properties.UUID, StringComparison.OrdinalIgnoreCase))) + // if (!serverList.Any(i => string.Equals(i.Properties.UUID, device.Properties.UUID, StringComparison.OrdinalIgnoreCase))) // { - // _servers.Add(device); + // serverList.Add(device); // } // } // catch (Exception ex) @@ -102,23 +252,22 @@ // return; // } - // if (!GetExistingServers(usn).Any()) - // { - // return; - // } - // await _syncLock.WaitAsync().ConfigureAwait(false); // try // { - // var list = _servers.ToList(); + // var serverList = _servers.ToList(); - // foreach (var device in GetExistingServers(usn).ToList()) + // var matchingServers = GetExistingServers(serverList, usn); + // if (matchingServers.Count > 0) // { - // list.Remove(device); - // } + // foreach (var device in matchingServers) + // { + // serverList.Remove(device); + // } - // _servers = list; + // _servers = serverList; + // } // } // finally // { @@ -140,185 +289,17 @@ // return true; // } - // private IEnumerable GetExistingServers(string usn) - // { - // return _servers - // .Where(i => usn.IndexOf(i.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1); - // } - - // public IEnumerable GetChannels() + // private List GetExistingServers(List allDevices, string usn) // { - // //if (_servers.Count > 0) - // //{ - // // var service = _servers[0].Properties.Services - // // .FirstOrDefault(i => string.Equals(i.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase)); - - // // var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/')); - - // // if (!string.IsNullOrEmpty(controlUrl)) - // // { - // // return new List - // // { - // // new ServerChannel(_servers.ToList(), _httpClient, _logger, controlUrl) - // // }; - // // } - // //} - - // return new List(); + // return allDevices + // .Where(i => usn.IndexOf(i.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1) + // .ToList(); // } // public void Dispose() // { - // if (_deviceDiscovery != null) - // { - // _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered; - // _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft; - // } - // } - //} - - //public class ServerChannel : IChannel, IFactoryChannel - //{ - // private readonly IHttpClient _httpClient; - // private readonly ILogger _logger; - // public string ControlUrl { get; set; } - // public List Servers { get; set; } - - // public ServerChannel(IHttpClient httpClient, ILogger logger) - // { - // _httpClient = httpClient; - // _logger = logger; - // Servers = new List(); - // } - - // public string Name - // { - // get { return "Devices"; } - // } - - // public string Description - // { - // get { return string.Empty; } - // } - - // public string DataVersion - // { - // get { return DateTime.UtcNow.Ticks.ToString(); } - // } - - // public string HomePageUrl - // { - // get { return string.Empty; } - // } - - // public ChannelParentalRating ParentalRating - // { - // get { return ChannelParentalRating.GeneralAudience; } - // } - - // public InternalChannelFeatures GetChannelFeatures() - // { - // return new InternalChannelFeatures - // { - // ContentTypes = new List - // { - // ChannelMediaContentType.Song, - // ChannelMediaContentType.Clip - // }, - - // MediaTypes = new List - // { - // ChannelMediaType.Audio, - // ChannelMediaType.Video, - // ChannelMediaType.Photo - // } - // }; - // } - - // public bool IsEnabledFor(string userId) - // { - // return true; - // } - - // public async Task GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken) - // { - // IEnumerable items; - - // if (string.IsNullOrWhiteSpace(query.FolderId)) - // { - // items = Servers.Select(i => new ChannelItemInfo - // { - // FolderType = ChannelFolderType.Container, - // Id = GetServerId(i), - // Name = i.Properties.Name, - // Overview = i.Properties.ModelDescription, - // Type = ChannelItemType.Folder - // }); - // } - // else - // { - // var idParts = query.FolderId.Split('|'); - // var folderId = idParts.Length == 2 ? idParts[1] : null; - - // var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest - // { - // Limit = query.Limit, - // StartIndex = query.StartIndex, - // ParentId = folderId, - // ContentDirectoryUrl = ControlUrl - - // }, cancellationToken).ConfigureAwait(false); - - // items = result.Items.ToList(); - // } - - // var list = items.ToList(); - // var count = list.Count; - - // list = ApplyPaging(list, query).ToList(); - - // return new ChannelItemResult - // { - // Items = list, - // TotalRecordCount = count - // }; - // } - - // private string GetServerId(Device device) - // { - // return device.Properties.UUID.GetMD5().ToString("N"); - // } - - // private IEnumerable ApplyPaging(IEnumerable items, InternalChannelItemQuery query) - // { - // if (query.StartIndex.HasValue) - // { - // items = items.Skip(query.StartIndex.Value); - // } - - // if (query.Limit.HasValue) - // { - // items = items.Take(query.Limit.Value); - // } - - // return items; - // } - - // public Task GetChannelImage(ImageType type, CancellationToken cancellationToken) - // { - // // TODO: Implement - // return Task.FromResult(new DynamicImageResponse - // { - // HasImage = false - // }); - // } - - // public IEnumerable GetSupportedChannelImages() - // { - // return new List - // { - // ImageType.Primary - // }; + // _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered; + // _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft; // } //} } diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index a2e9cd60e7..22f308979f 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -14,7 +14,9 @@ using MediaBrowser.Dlna.Ssdp; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; +using System.Linq; using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Dlna.Channels; namespace MediaBrowser.Dlna.Main { @@ -167,6 +169,8 @@ namespace MediaBrowser.Dlna.Main try { ((DeviceDiscovery)_deviceDiscovery).Start(_ssdpHandler); + + //DlnaChannel.Current.Start(() => _registeredServerIds.ToList()); } catch (Exception ex) { diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 1a95bdf9d7..44c69d4c1b 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -548,7 +548,8 @@ namespace MediaBrowser.MediaEncoding.Probing private void NormalizeStreamTitle(MediaStream stream) { - if (string.Equals(stream.Title, "sdh", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(stream.Title, "sdh", StringComparison.OrdinalIgnoreCase) || + string.Equals(stream.Title, "cc", StringComparison.OrdinalIgnoreCase)) { stream.Title = null; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index f85be5100b..99ab07648d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1383,6 +1383,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } + private QueryResult GetEmbyRecordings(RecordingQuery query, User user) + { + var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders() + .SelectMany(i => i.Locations) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Select(i => _libraryManager.FindByPath(i, true)) + .Where(i => i != null) + .Where(i => i.IsVisibleStandalone(user)) + .ToList(); + + var items = _libraryManager.GetItemsResult(new InternalItemsQuery(user) + { + MediaTypes = new[] { MediaType.Video }, + Recursive = true, + AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(), + ExcludeLocationTypes = new[] { LocationType.Virtual }, + Limit = Math.Min(10, query.Limit ?? int.MaxValue) + }); + + return items; + } + public async Task> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken) { var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId); @@ -1391,6 +1413,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv return new QueryResult(); } + if (user != null && !(query.IsInProgress ?? false)) + { + var initialResult = GetEmbyRecordings(query, user); + if (initialResult.TotalRecordCount > 0) + { + return initialResult; + } + } + await RefreshRecordings(cancellationToken).ConfigureAwait(false); var internalQuery = new InternalItemsQuery(user) @@ -2060,7 +2091,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv }, cancellationToken).ConfigureAwait(false); - var recordings = recordingResult.Items.Cast().ToList(); + var recordings = recordingResult.Items.OfType().ToList(); var groups = new List(); -- cgit v1.2.3