|  | @@ -483,7 +483,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if (existingTimer != null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                if (existingTimer.Status == RecordingStatus.Cancelled)
 | 
	
		
			
				|  |  | +                if (existingTimer.Status == RecordingStatus.Cancelled ||
 | 
	
		
			
				|  |  | +                    existingTimer.Status == RecordingStatus.Completed)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      existingTimer.Status = RecordingStatus.New;
 | 
	
		
			
				|  |  |                      _timerProvider.Update(existingTimer);
 | 
	
	
		
			
				|  | @@ -832,12 +833,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 | 
	
		
			
				|  |  |              return result.Item2;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId)
 | 
	
		
			
				|  |  | +        private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId, bool enableStreamSharing)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var json = _jsonSerializer.SerializeToString(mediaSource);
 | 
	
		
			
				|  |  |              mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            mediaSource.Id = consumerId.ToString(CultureInfo.InvariantCulture) + "_" + mediaSource.Id;
 | 
	
		
			
				|  |  | +            mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var ticks = (DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value).Ticks - TimeSpan.FromSeconds(10).Ticks;
 | 
	
		
			
				|  |  | +                ticks = Math.Max(0, ticks);
 | 
	
		
			
				|  |  | +                mediaSource.Path += "?t=" + ticks.ToString(CultureInfo.InvariantCulture) + "&s=" + mediaSource.DateLiveStreamOpened.Value.Ticks.ToString(CultureInfo.InvariantCulture);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              return mediaSource;
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -850,14 +858,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var result = _liveStreams.Values.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if (result != null)
 | 
	
		
			
				|  |  | +            if (result != null && result.EnableStreamSharing)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                //result.ConsumerCount++;
 | 
	
		
			
				|  |  | +                result.ConsumerCount++;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                //_logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
 | 
	
		
			
				|  |  | +                _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                //_liveStreamsSemaphore.Release();
 | 
	
		
			
				|  |  | -                //return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1), result.TunerHost);
 | 
	
		
			
				|  |  | +                var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1, result.EnableStreamSharing);
 | 
	
		
			
				|  |  | +                _liveStreamsSemaphore.Release();
 | 
	
		
			
				|  |  | +                return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              try
 | 
	
	
		
			
				|  | @@ -868,16 +877,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 | 
	
		
			
				|  |  |                      {
 | 
	
		
			
				|  |  |                          result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        _liveStreams[result.OpenedMediaSource.Id] = result;
 | 
	
		
			
				|  |  | +                        var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, 0, result.EnableStreamSharing);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        _liveStreams[openedMediaSource.Id] = result;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                          result.ConsumerCount++;
 | 
	
		
			
				|  |  |                          result.TunerHost = hostInstance;
 | 
	
		
			
				|  |  |                          result.OriginalStreamId = streamId;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                          _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
 | 
	
		
			
				|  |  | -                            streamId, result.OpenedMediaSource.Id, result.OpenedMediaSource.LiveStreamId);
 | 
	
		
			
				|  |  | +                            streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, CloneMediaSource(result.OpenedMediaSource, 0), hostInstance);
 | 
	
		
			
				|  |  | +                        return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, hostInstance);
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |                      catch (FileNotFoundException)
 | 
	
		
			
				|  |  |                      {
 | 
	
	
		
			
				|  | @@ -925,7 +936,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 | 
	
		
			
				|  |  |          public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              // Ignore the consumer id
 | 
	
		
			
				|  |  | -            id = id.Substring(id.IndexOf('_') + 1);
 | 
	
		
			
				|  |  | +            //id = id.Substring(id.IndexOf('_') + 1);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1143,8 +1154,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              try
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                var allMediaSources =
 | 
	
		
			
				|  |  | -                    await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
 | 
	
		
			
				|  |  |                              .ConfigureAwait(false);
 |