mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-04 06:48:35 +01:00
update display of active recordings
This commit is contained in:
@@ -598,7 +598,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
||||
dto.Altitude = item.Altitude;
|
||||
dto.IsoSpeedRating = item.IsoSpeedRating;
|
||||
|
||||
var album = item.Album;
|
||||
var album = item.AlbumEntity;
|
||||
|
||||
if (album != null)
|
||||
{
|
||||
|
||||
@@ -269,9 +269,10 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
positionTicks = 0;
|
||||
}
|
||||
|
||||
if (item is Audio)
|
||||
if (!item.SupportsPlayedStatus)
|
||||
{
|
||||
positionTicks = 0;
|
||||
data.Played = false;
|
||||
}
|
||||
|
||||
data.PlaybackPositionTicks = positionTicks;
|
||||
|
||||
@@ -27,6 +27,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Events;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
@@ -59,8 +60,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
public static EmbyTV Current;
|
||||
|
||||
public event EventHandler DataSourceChanged { add { } remove { } }
|
||||
public event EventHandler<RecordingStatusChangedEventArgs> RecordingStatusChanged { add { } remove { } }
|
||||
public event EventHandler DataSourceChanged;
|
||||
public event EventHandler<RecordingStatusChangedEventArgs> RecordingStatusChanged;
|
||||
|
||||
private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
|
||||
new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
|
||||
@@ -1009,7 +1010,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
|
||||
public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
|
||||
{
|
||||
ActiveRecordingInfo info;
|
||||
|
||||
@@ -1017,22 +1018,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
if (_activeRecordings.TryGetValue(recordingId, out info))
|
||||
{
|
||||
return Task.FromResult(new List<MediaSourceInfo>
|
||||
var stream = new MediaSourceInfo
|
||||
{
|
||||
new MediaSourceInfo
|
||||
{
|
||||
Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream",
|
||||
Id = recordingId,
|
||||
SupportsDirectPlay = false,
|
||||
SupportsDirectStream = true,
|
||||
SupportsTranscoding = true,
|
||||
IsInfiniteStream = true,
|
||||
RequiresOpening = false,
|
||||
RequiresClosing = false,
|
||||
Protocol = Model.MediaInfo.MediaProtocol.Http,
|
||||
BufferMs = 0
|
||||
}
|
||||
});
|
||||
Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream",
|
||||
Id = recordingId,
|
||||
SupportsDirectPlay = false,
|
||||
SupportsDirectStream = true,
|
||||
SupportsTranscoding = true,
|
||||
IsInfiniteStream = true,
|
||||
RequiresOpening = false,
|
||||
RequiresClosing = false,
|
||||
Protocol = Model.MediaInfo.MediaProtocol.Http,
|
||||
BufferMs = 0
|
||||
};
|
||||
|
||||
var isAudio = false;
|
||||
await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new List<MediaSourceInfo>
|
||||
{
|
||||
stream
|
||||
};
|
||||
}
|
||||
|
||||
throw new FileNotFoundException();
|
||||
@@ -1258,6 +1264,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
|
||||
string liveStreamId = null;
|
||||
|
||||
OnRecordingStatusChanged();
|
||||
|
||||
try
|
||||
{
|
||||
var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
|
||||
@@ -1353,6 +1361,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||
{
|
||||
_timerProvider.Delete(timer);
|
||||
}
|
||||
|
||||
OnRecordingStatusChanged();
|
||||
}
|
||||
|
||||
private void OnRecordingStatusChanged()
|
||||
{
|
||||
EventHelper.FireEventIfNotNull(RecordingStatusChanged, this, new RecordingStatusChangedEventArgs
|
||||
{
|
||||
|
||||
}, _logger);
|
||||
}
|
||||
|
||||
private async void EnforceKeepUpTo(TimerInfo timer)
|
||||
|
||||
110
MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs
Normal file
110
MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
{
|
||||
public class LiveStreamHelper
|
||||
{
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger)
|
||||
{
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
|
||||
{
|
||||
var originalRuntime = mediaSource.RunTimeTicks;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
||||
{
|
||||
InputPath = mediaSource.Path,
|
||||
Protocol = mediaSource.Protocol,
|
||||
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
||||
ExtractChapters = false,
|
||||
AnalyzeDurationSections = 2
|
||||
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
_logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
mediaSource.Bitrate = info.Bitrate;
|
||||
mediaSource.Container = info.Container;
|
||||
mediaSource.Formats = info.Formats;
|
||||
mediaSource.MediaStreams = info.MediaStreams;
|
||||
mediaSource.RunTimeTicks = info.RunTimeTicks;
|
||||
mediaSource.Size = info.Size;
|
||||
mediaSource.Timestamp = info.Timestamp;
|
||||
mediaSource.Video3DFormat = info.Video3DFormat;
|
||||
mediaSource.VideoType = info.VideoType;
|
||||
|
||||
mediaSource.DefaultSubtitleStreamIndex = null;
|
||||
|
||||
// Null this out so that it will be treated like a live stream
|
||||
if (!originalRuntime.HasValue)
|
||||
{
|
||||
mediaSource.RunTimeTicks = null;
|
||||
}
|
||||
|
||||
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
|
||||
|
||||
if (audioStream == null || audioStream.Index == -1)
|
||||
{
|
||||
mediaSource.DefaultAudioStreamIndex = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
mediaSource.DefaultAudioStreamIndex = audioStream.Index;
|
||||
}
|
||||
|
||||
var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video);
|
||||
if (videoStream != null)
|
||||
{
|
||||
if (!videoStream.BitRate.HasValue)
|
||||
{
|
||||
var width = videoStream.Width ?? 1920;
|
||||
|
||||
if (width >= 1900)
|
||||
{
|
||||
videoStream.BitRate = 8000000;
|
||||
}
|
||||
|
||||
else if (width >= 1260)
|
||||
{
|
||||
videoStream.BitRate = 3000000;
|
||||
}
|
||||
|
||||
else if (width >= 700)
|
||||
{
|
||||
videoStream.BitRate = 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
// This is coming up false and preventing stream copy
|
||||
videoStream.IsAVC = null;
|
||||
}
|
||||
|
||||
// Try to estimate this
|
||||
if (!mediaSource.Bitrate.HasValue)
|
||||
{
|
||||
var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
|
||||
|
||||
if (total > 0)
|
||||
{
|
||||
mediaSource.Bitrate = total;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,9 +121,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
foreach (var service in _services)
|
||||
{
|
||||
service.DataSourceChanged += service_DataSourceChanged;
|
||||
service.RecordingStatusChanged += Service_RecordingStatusChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void Service_RecordingStatusChanged(object sender, RecordingStatusChangedEventArgs e)
|
||||
{
|
||||
_lastRecordingRefreshTime = DateTime.MinValue;
|
||||
}
|
||||
|
||||
public List<ITunerHost> TunerHosts
|
||||
{
|
||||
get { return _tunerHosts; }
|
||||
@@ -2299,6 +2305,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
|
||||
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
|
||||
|
||||
info.RecordAnyChannel = true;
|
||||
info.RecordAnyTime = true;
|
||||
info.Days = new List<DayOfWeek>
|
||||
{
|
||||
DayOfWeek.Sunday,
|
||||
DayOfWeek.Monday,
|
||||
DayOfWeek.Tuesday,
|
||||
DayOfWeek.Wednesday,
|
||||
DayOfWeek.Thursday,
|
||||
DayOfWeek.Friday,
|
||||
DayOfWeek.Saturday
|
||||
};
|
||||
|
||||
info.Id = null;
|
||||
|
||||
return new Tuple<SeriesTimerInfo, ILiveTvService>(info, service);
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
}
|
||||
else
|
||||
{
|
||||
await AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
|
||||
await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -216,92 +216,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
|
||||
{
|
||||
var originalRuntime = mediaSource.RunTimeTicks;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
|
||||
{
|
||||
InputPath = mediaSource.Path,
|
||||
Protocol = mediaSource.Protocol,
|
||||
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
|
||||
ExtractChapters = false
|
||||
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
_logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
mediaSource.Bitrate = info.Bitrate;
|
||||
mediaSource.Container = info.Container;
|
||||
mediaSource.Formats = info.Formats;
|
||||
mediaSource.MediaStreams = info.MediaStreams;
|
||||
mediaSource.RunTimeTicks = info.RunTimeTicks;
|
||||
mediaSource.Size = info.Size;
|
||||
mediaSource.Timestamp = info.Timestamp;
|
||||
mediaSource.Video3DFormat = info.Video3DFormat;
|
||||
mediaSource.VideoType = info.VideoType;
|
||||
|
||||
mediaSource.DefaultSubtitleStreamIndex = null;
|
||||
|
||||
// Null this out so that it will be treated like a live stream
|
||||
if (!originalRuntime.HasValue)
|
||||
{
|
||||
mediaSource.RunTimeTicks = null;
|
||||
}
|
||||
|
||||
var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
|
||||
|
||||
if (audioStream == null || audioStream.Index == -1)
|
||||
{
|
||||
mediaSource.DefaultAudioStreamIndex = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
mediaSource.DefaultAudioStreamIndex = audioStream.Index;
|
||||
}
|
||||
|
||||
var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video);
|
||||
if (videoStream != null)
|
||||
{
|
||||
if (!videoStream.BitRate.HasValue)
|
||||
{
|
||||
var width = videoStream.Width ?? 1920;
|
||||
|
||||
if (width >= 1900)
|
||||
{
|
||||
videoStream.BitRate = 8000000;
|
||||
}
|
||||
|
||||
else if (width >= 1260)
|
||||
{
|
||||
videoStream.BitRate = 3000000;
|
||||
}
|
||||
|
||||
else if (width >= 700)
|
||||
{
|
||||
videoStream.BitRate = 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
// This is coming up false and preventing stream copy
|
||||
videoStream.IsAVC = null;
|
||||
}
|
||||
|
||||
// Try to estimate this
|
||||
if (!mediaSource.Bitrate.HasValue)
|
||||
{
|
||||
var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
|
||||
|
||||
if (total > 0)
|
||||
{
|
||||
mediaSource.Bitrate = total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task CloseMediaSource(string liveStreamId)
|
||||
{
|
||||
return _liveTvManager.CloseLiveStream(liveStreamId);
|
||||
|
||||
@@ -234,6 +234,7 @@
|
||||
<Compile Include="LiveTv\EmbyTV\TimerManager.cs" />
|
||||
<Compile Include="LiveTv\Listings\SchedulesDirect.cs" />
|
||||
<Compile Include="LiveTv\Listings\XmlTvListingsProvider.cs" />
|
||||
<Compile Include="LiveTv\LiveStreamHelper.cs" />
|
||||
<Compile Include="LiveTv\LiveTvConfigurationFactory.cs" />
|
||||
<Compile Include="LiveTv\LiveTvDtoService.cs" />
|
||||
<Compile Include="LiveTv\LiveTvManager.cs" />
|
||||
|
||||
@@ -633,10 +633,14 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||
data.PlayCount++;
|
||||
data.LastPlayedDate = DateTime.UtcNow;
|
||||
|
||||
if (!(item is Video))
|
||||
if (!(item is Video) && item.SupportsPlayedStatus)
|
||||
{
|
||||
data.Played = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Played = false;
|
||||
}
|
||||
|
||||
await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
@@ -847,11 +851,11 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||
{
|
||||
playedToCompletion = _userDataManager.UpdatePlayState(item, data, positionTicks.Value);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
// If the client isn't able to report this, then we'll just have to make an assumption
|
||||
data.PlayCount++;
|
||||
data.Played = true;
|
||||
data.Played = item.SupportsPlayedStatus;
|
||||
data.PlaybackPositionTicks = 0;
|
||||
playedToCompletion = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user