diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index ef415ec57c..281f764b5f 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -132,6 +132,7 @@ namespace MediaBrowser.Api
/// Called when [transcode beginning].
///
/// The path.
+ /// The stream identifier.
/// The transcoding job identifier.
/// The type.
/// The process.
@@ -140,6 +141,7 @@ namespace MediaBrowser.Api
/// The cancellation token source.
/// TranscodingJob.
public TranscodingJob OnTranscodeBeginning(string path,
+ string streamId,
string transcodingJobId,
TranscodingJobType type,
Process process,
@@ -157,7 +159,8 @@ namespace MediaBrowser.Api
ActiveRequestCount = 1,
DeviceId = deviceId,
CancellationTokenSource = cancellationTokenSource,
- Id = transcodingJobId
+ Id = transcodingJobId,
+ StreamId = streamId
};
_activeTranscodingJobs.Add(job);
@@ -316,17 +319,26 @@ namespace MediaBrowser.Api
/// Kills the single transcoding job.
///
/// The device id.
+ /// The stream identifier.
/// The delete files.
/// Task.
- /// deviceId
- internal void KillTranscodingJobs(string deviceId, Func deleteFiles)
+ internal void KillTranscodingJobs(string deviceId, string streamId, Func deleteFiles)
{
if (string.IsNullOrEmpty(deviceId))
{
throw new ArgumentNullException("deviceId");
}
- KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles);
+ KillTranscodingJobs(j =>
+ {
+ if (string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase))
+ {
+ return string.IsNullOrWhiteSpace(streamId) || string.Equals(streamId, j.StreamId, StringComparison.OrdinalIgnoreCase);
+ }
+
+ return false;
+
+ }, deleteFiles);
}
///
@@ -335,7 +347,7 @@ namespace MediaBrowser.Api
/// The kill job.
/// The delete files.
/// Task.
- internal void KillTranscodingJobs(Func killJob, Func deleteFiles)
+ private void KillTranscodingJobs(Func killJob, Func deleteFiles)
{
var jobs = new List();
@@ -516,6 +528,11 @@ namespace MediaBrowser.Api
///
public class TranscodingJob
{
+ ///
+ /// Gets or sets the stream identifier.
+ ///
+ /// The stream identifier.
+ public string StreamId { get; set; }
///
/// Gets or sets the path.
///
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 3a10881821..7115ddffdb 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -134,7 +134,7 @@ namespace MediaBrowser.Api.Playback
var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false);
data += "-" + (state.Request.DeviceId ?? string.Empty);
- data += "-" + (state.Request.ClientTime ?? string.Empty);
+ data += "-" + (state.Request.StreamId ?? state.Request.ClientTime ?? string.Empty);
var dataHash = data.GetMD5().ToString("N");
@@ -1054,6 +1054,7 @@ namespace MediaBrowser.Api.Playback
}
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
+ state.Request.StreamId ?? state.Request.ClientTime,
transcodingId,
TranscodingJobType,
process,
@@ -1523,7 +1524,7 @@ namespace MediaBrowser.Api.Playback
}
else if (i == 16)
{
- request.ClientTime = val;
+ request.StreamId = val;
}
else if (i == 17)
{
diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
index 2655ff8ad1..38b0d35d11 100644
--- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
+++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
@@ -162,7 +162,7 @@ namespace MediaBrowser.Api.Playback.Dash
// If the playlist doesn't already exist, startup ffmpeg
try
{
- KillTranscodingJobs(request.DeviceId, playlistPath);
+ ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId, p => false);
if (currentTranscodingIndex.HasValue)
{
@@ -204,11 +204,6 @@ namespace MediaBrowser.Api.Playback.Dash
return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType), cancellationToken).ConfigureAwait(false);
}
- private void KillTranscodingJobs(string deviceId, string playlistPath)
- {
- ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType && string.Equals(j.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase));
- }
-
private long GetPositionTicks(StreamState state, int requestedIndex)
{
if (requestedIndex <= 0)
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 32bbda8dbc..43a9db1312 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -136,7 +136,7 @@ namespace MediaBrowser.Api.Playback.Hls
// If the playlist doesn't already exist, startup ffmpeg
try
{
- ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase));
+ ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId ?? request.ClientTime, p => false);
if (currentTranscodingIndex.HasValue)
{
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index 9f80fcd0ad..da4ffb55dc 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -47,6 +47,9 @@ namespace MediaBrowser.Api.Playback.Hls
{
[ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
public string DeviceId { get; set; }
+
+ [ApiMember(Name = "StreamId", Description = "The stream id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string StreamId { get; set; }
}
public class HlsSegmentService : BaseApiService
@@ -69,7 +72,7 @@ namespace MediaBrowser.Api.Playback.Hls
public void Delete(StopEncodingProcess request)
{
- ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, path => true);
+ ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.StreamId, path => true);
}
///
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index 5adb76cc58..943d9fe481 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Library;
+using System;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 907b17b244..b52260b506 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -71,6 +71,7 @@ namespace MediaBrowser.Api.Playback
public string Params { get; set; }
public string ClientTime { get; set; }
+ public string StreamId { get; set; }
public string TranscodingJobId { get; set; }
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 41d8604de1..b383bbb87b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -1,7 +1,6 @@
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Channels;
@@ -24,7 +23,6 @@ using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -747,8 +745,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
programs = programs.Where(p => p.IsMovie == query.IsMovie.Value);
}
- var serviceName = ActiveService.Name;
-
var programList = programs.ToList();
var genres = programList.SelectMany(i => i.Genres)
@@ -757,7 +753,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
programs = programList.OrderBy(i => i.HasImage(ImageType.Primary) ? 0 : 1)
- .ThenByDescending(i => GetRecommendationScore(i, user.Id, serviceName, genres))
+ .ThenByDescending(i => GetRecommendationScore(i, user.Id, genres))
.ThenBy(i => i.StartDate);
if (query.Limit.HasValue)
@@ -807,7 +803,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return result;
}
- private int GetRecommendationScore(LiveTvProgram program, Guid userId, string serviceName, Dictionary genres)
+ private int GetRecommendationScore(LiveTvProgram program, Guid userId, Dictionary genres)
{
var score = 0;
@@ -821,7 +817,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
score++;
}
- var internalChannelId = _tvDtoService.GetInternalChannelId(serviceName, program.ExternalChannelId);
+ var internalChannelId = _tvDtoService.GetInternalChannelId(program.ServiceName, program.ExternalChannelId);
var channel = GetInternalChannel(internalChannelId);
var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey());
@@ -879,11 +875,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private async Task AddRecordingInfo(IEnumerable programs, CancellationToken cancellationToken)
{
- var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false);
- var timerList = timers.ToList();
+ var timers = new Dictionary>();
foreach (var program in programs)
{
+ List timerList;
+ if (!timers.TryGetValue(program.ServiceName, out timerList))
+ {
+ var tempTimers = await GetService(program.ServiceName).GetTimersAsync(cancellationToken).ConfigureAwait(false);
+ timers[program.ServiceName] = timerList = tempTimers.ToList();
+ }
+
var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, program.ExternalId, StringComparison.OrdinalIgnoreCase));
if (timer != null)
@@ -922,15 +924,43 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private async Task RefreshChannelsInternal(IProgress progress, CancellationToken cancellationToken)
{
- // Avoid implicitly captured closure
- var service = ActiveService;
+ var numComplete = 0;
+ double progressPerService = _services.Count == 0
+ ? 0
+ : 1 / _services.Count;
- if (service == null)
+ foreach (var service in _services)
{
- progress.Report(100);
- return;
+ cancellationToken.ThrowIfCancellationRequested();
+
+ try
+ {
+ var innerProgress = new ActionableProgress();
+ innerProgress.RegisterAction(p => progress.Report(p * progressPerService));
+
+ await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error refreshing channels for service", ex);
+ }
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= _services.Count;
+
+ progress.Report(100 * percent);
}
+ progress.Report(100);
+ }
+
+ private async Task RefreshChannelsInternal(ILiveTvService service, IProgress progress, CancellationToken cancellationToken)
+ {
progress.Report(10);
var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false);
@@ -1192,16 +1222,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task> GetRecordings(RecordingQuery query, CancellationToken cancellationToken)
{
- var service = ActiveService;
-
- if (service == null)
- {
- return new QueryResult
- {
- Items = new RecordingInfoDto[] { }
- };
- }
-
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
var internalResult = await GetInternalRecordings(query, cancellationToken).ConfigureAwait(false);
@@ -1209,6 +1229,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var returnArray = internalResult.Items.Cast()
.Select(i =>
{
+ var service = GetService(i);
+
var channel = string.IsNullOrEmpty(i.RecordingInfo.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, i.RecordingInfo.ChannelId));
return _tvDtoService.GetRecordingInfoDto(i, channel, service, user);
})
@@ -1413,8 +1435,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return program;
}
- private async Task GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
+ private async Task> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
{
+ var service = program != null && !string.IsNullOrWhiteSpace(program.ServiceName) ?
+ GetService(program) :
+ _services.FirstOrDefault();
+
ProgramInfo programInfo = null;
if (program != null)
@@ -1448,18 +1474,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
};
}
- var info = await ActiveService.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
+ var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
info.Id = null;
- return info;
+ return new Tuple(info, service);
}
public async Task GetNewTimerDefaults(CancellationToken cancellationToken)
{
var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
- var obj = _tvDtoService.GetSeriesTimerInfoDto(info, ActiveService, null);
+ var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
return obj;
}
@@ -1470,7 +1496,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var programDto = await GetProgram(programId, cancellationToken).ConfigureAwait(false);
var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
- var info = _tvDtoService.GetSeriesTimerInfoDto(defaults, ActiveService, null);
+ var info = _tvDtoService.GetSeriesTimerInfoDto(defaults.Item1, defaults.Item2, null);
info.Days = new List
{