channel fixes

pull/702/head
Luke Pulverenti 10 years ago
parent ca5989cb17
commit 3ccecd3ca3

@ -1,5 +1,6 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
@ -25,7 +26,6 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Api.Playback
{
@ -71,6 +71,7 @@ namespace MediaBrowser.Api.Playback
protected IItemRepository ItemRepository { get; private set; }
protected ILiveTvManager LiveTvManager { get; private set; }
protected IDlnaManager DlnaManager { get; private set; }
protected IChannelManager ChannelManager { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
@ -83,8 +84,9 @@ namespace MediaBrowser.Api.Playback
/// <param name="dtoService">The dto service.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="itemRepository">The item repository.</param>
protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager)
protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager)
{
ChannelManager = channelManager;
DlnaManager = dlnaManager;
EncodingManager = encodingManager;
LiveTvManager = liveTvManager;
@ -169,13 +171,28 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
protected virtual string GetMapArgs(StreamState state)
{
var args = string.Empty;
// If we don't have known media info
// If input is video, use -sn to drop subtitles
// Otherwise just return empty
if (state.VideoStream == null && state.AudioStream == null)
{
return state.IsInputVideo ? "-sn" : string.Empty;
}
if (!state.HasMediaStreams)
// We have media info, but we don't know the stream indexes
if (state.VideoStream != null && state.VideoStream.Index == -1)
{
return "-sn";
}
// We have media info, but we don't know the stream indexes
if (state.AudioStream != null && state.AudioStream.Index == -1)
{
return state.IsInputVideo ? "-sn" : string.Empty;
}
var args = string.Empty;
if (state.VideoStream != null)
{
args += string.Format("-map 0:{0}", state.VideoStream.Index);
@ -1329,13 +1346,14 @@ namespace MediaBrowser.Api.Playback
throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name));
}
List<MediaStream> mediaStreams = null;
if (item is ILiveTvRecording)
{
var recording = await LiveTvManager.GetInternalRecording(request.Id, cancellationToken).ConfigureAwait(false);
state.VideoType = VideoType.VideoFile;
state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
state.PlayableStreamFileNames = new List<string>();
var path = recording.RecordingInfo.Path;
var mediaUrl = recording.RecordingInfo.Url;
@ -1345,6 +1363,7 @@ namespace MediaBrowser.Api.Playback
var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
mediaStreams = streamInfo.MediaStreams;
path = streamInfo.Path;
mediaUrl = streamInfo.Url;
@ -1381,11 +1400,11 @@ namespace MediaBrowser.Api.Playback
state.VideoType = VideoType.VideoFile;
state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
state.PlayableStreamFileNames = new List<string>();
var streamInfo = await LiveTvManager.GetChannelStream(request.Id, cancellationToken).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
mediaStreams = streamInfo.MediaStreams;
if (!string.IsNullOrEmpty(streamInfo.Path))
{
@ -1406,6 +1425,16 @@ namespace MediaBrowser.Api.Playback
state.InputVideoSync = "-1";
state.InputAudioSync = "1";
}
else if (item is IChannelMediaItem)
{
var channelMediaSources = await ChannelManager.GetChannelItemMediaSources(request.Id, CancellationToken.None).ConfigureAwait(false);
var source = channelMediaSources.First();
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
state.IsRemote = source.IsRemote;
state.MediaPath = source.Path;
state.RunTimeTicks = item.RunTimeTicks;
}
else
{
state.MediaPath = item.Path;
@ -1424,7 +1453,11 @@ namespace MediaBrowser.Api.Playback
: video.PlayableStreamFileNames.ToList();
state.DeInterlace = string.Equals(video.Container, "wtv", StringComparison.OrdinalIgnoreCase);
state.InputTimestamp = video.Timestamp ?? TransportStreamTimestamp.None;
if (video.Timestamp.HasValue)
{
state.InputTimestamp = video.Timestamp.Value;
}
state.InputContainer = video.Container;
}
@ -1440,7 +1473,7 @@ namespace MediaBrowser.Api.Playback
var videoRequest = request as VideoStreamRequest;
var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery
mediaStreams = mediaStreams ?? ItemRepository.GetMediaStreams(new MediaStreamQuery
{
ItemId = item.Id
@ -1469,8 +1502,6 @@ namespace MediaBrowser.Api.Playback
state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
}
state.HasMediaStreams = mediaStreams.Count > 0;
state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10;
state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440;

@ -1,6 +1,7 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
@ -24,8 +25,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public abstract class BaseHlsService : BaseStreamingService
{
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager)
protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
{
}

@ -1,4 +1,5 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
@ -59,7 +60,7 @@ namespace MediaBrowser.Api.Playback.Hls
public class DynamicHlsService : BaseHlsService
{
public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager)
public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
{
}

@ -1,4 +1,5 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
@ -53,7 +54,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public class VideoHlsService : BaseHlsService
{
public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager)
public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
{
}

@ -1,5 +1,6 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@ -43,8 +44,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class AudioService : BaseProgressiveStreamingService
{
public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor)
public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient)
{
}

@ -1,5 +1,6 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@ -26,11 +27,10 @@ namespace MediaBrowser.Api.Playback.Progressive
protected readonly IImageProcessor ImageProcessor;
protected readonly IHttpClient HttpClient;
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor)
: base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager)
protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager)
{
HttpClient = httpClient;
ImageProcessor = imageProcessor;
HttpClient = httpClient;
}
/// <summary>

@ -1,5 +1,6 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
@ -59,7 +60,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class VideoService : BaseProgressiveStreamingService
{
public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IHttpClient httpClient, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, httpClient, imageProcessor)
public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService, IFileSystem fileSystem, IItemRepository itemRepository, ILiveTvManager liveTvManager, IEncodingManager encodingManager, IDlnaManager dlnaManager, IChannelManager channelManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, dtoService, fileSystem, itemRepository, liveTvManager, encodingManager, dlnaManager, channelManager, imageProcessor, httpClient)
{
}
@ -183,7 +184,7 @@ namespace MediaBrowser.Api.Playback.Progressive
private string GetAudioArguments(StreamState state)
{
// If the video doesn't have an audio stream, return a default.
if (state.AudioStream == null && state.HasMediaStreams)
if (state.AudioStream == null && state.VideoStream != null)
{
return string.Empty;
}

@ -28,6 +28,11 @@ namespace MediaBrowser.Api.Playback
get { return Request as VideoStreamRequest; }
}
public StreamState()
{
PlayableStreamFileNames = new List<string>();
}
/// <summary>
/// Gets or sets the log file stream.
/// </summary>
@ -57,8 +62,6 @@ namespace MediaBrowser.Api.Playback
public List<string> PlayableStreamFileNames { get; set; }
public bool HasMediaStreams { get; set; }
public string LiveTvStreamId { get; set; }
public int SegmentLength = 10;

@ -218,6 +218,9 @@ namespace MediaBrowser.Api
[ApiMember(Name = "SupportedCommands", Description = "A list of supported remote control commands, comma delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string SupportedCommands { get; set; }
[ApiMember(Name = "MessageCallbackUrl", Description = "A url to post messages to, including remote control commands.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string MessageCallbackUrl { get; set; }
[ApiMember(Name = "SupportsMediaControl", Description = "Determines whether media can be played remotely.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsMediaControl { get; set; }
}
@ -414,7 +417,9 @@ namespace MediaBrowser.Api
SupportedCommands = request.SupportedCommands.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
SupportsMediaControl = request.SupportsMediaControl
SupportsMediaControl = request.SupportsMediaControl,
MessageCallbackUrl = request.MessageCallbackUrl
});
}

@ -1,6 +1,8 @@
using System.Linq;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Channels
{
@ -18,6 +20,8 @@ namespace MediaBrowser.Controller.Channels
public string OriginalImageUrl { get; set; }
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
protected override bool GetBlockUnratedValue(UserConfiguration config)
{
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
@ -30,5 +34,23 @@ namespace MediaBrowser.Controller.Channels
return false;
}
}
public ChannelAudioItem()
{
ChannelMediaSources = new List<ChannelMediaInfo>();
}
public override LocationType LocationType
{
get
{
if (string.IsNullOrEmpty(Path))
{
return LocationType.Remote;
}
return base.LocationType;
}
}
}
}

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Channels
{
@ -8,10 +9,11 @@ namespace MediaBrowser.Controller.Channels
public string ExternalId { get; set; }
public string ChannelId { get; set; }
public ChannelItemType ChannelItemType { get; set; }
public string OriginalImageUrl { get; set; }
public List<string> Tags { get; set; }
protected override bool GetBlockUnratedValue(UserConfiguration config)
{
@ -26,5 +28,10 @@ namespace MediaBrowser.Controller.Channels
return false;
}
}
public ChannelCategoryItem()
{
Tags = new List<string>();
}
}
}

@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.Channels
public List<string> Genres { get; set; }
public List<string> Studios { get; set; }
public List<string> Tags { get; set; }
public List<PersonInfo> People { get; set; }
@ -49,6 +50,7 @@ namespace MediaBrowser.Controller.Channels
Genres = new List<string>();
Studios = new List<string>();
People = new List<PersonInfo>();
Tags = new List<string>();
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}

@ -18,9 +18,12 @@ namespace MediaBrowser.Controller.Channels
public int? Height { get; set; }
public int? AudioChannels { get; set; }
public bool IsRemote { get; set; }
public ChannelMediaInfo()
{
RequiredHttpHeaders = new Dictionary<string, string>();
IsRemote = true;
}
}
}

@ -1,6 +1,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@ -20,6 +21,8 @@ namespace MediaBrowser.Controller.Channels
public string OriginalImageUrl { get; set; }
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
public override string GetUserDataKey()
{
if (ContentType == ChannelMediaContentType.Trailer)
@ -55,5 +58,23 @@ namespace MediaBrowser.Controller.Channels
return false;
}
}
public ChannelVideoItem()
{
ChannelMediaSources = new List<ChannelMediaInfo>();
}
public override LocationType LocationType
{
get
{
if (string.IsNullOrEmpty(Path))
{
return LocationType.Remote;
}
return base.LocationType;
}
}
}
}

@ -59,4 +59,15 @@ namespace MediaBrowser.Controller.Channels
/// <returns>IEnumerable{ImageType}.</returns>
IEnumerable<ImageType> GetSupportedChannelImages();
}
public interface IRequiresMediaInfoCallback
{
/// <summary>
/// Gets the channel item media information.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{ChannelMediaInfo}}.</returns>
Task<IEnumerable<ChannelMediaInfo>> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken);
}
}

@ -2,7 +2,7 @@
namespace MediaBrowser.Controller.Channels
{
public interface IChannelItem : IHasImages
public interface IChannelItem : IHasImages, IHasTags
{
string ChannelId { get; set; }

@ -31,5 +31,13 @@ namespace MediaBrowser.Controller.Channels
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{QueryResult{BaseItemDto}}.</returns>
Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken);
/// <summary>
/// Gets the channel item media sources.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{ChannelMediaInfo}}.</returns>
Task<IEnumerable<ChannelMediaInfo>> GetChannelItemMediaSources(string id, CancellationToken cancellationToken);
}
}

@ -1,9 +1,13 @@
namespace MediaBrowser.Controller.Channels
using System.Collections.Generic;
namespace MediaBrowser.Controller.Channels
{
public interface IChannelMediaItem : IChannelItem
{
bool IsInfiniteStream { get; set; }
ChannelMediaContentType ContentType { get; set; }
List<ChannelMediaInfo> ChannelMediaSources { get; set; }
}
}

@ -10,16 +10,18 @@ namespace MediaBrowser.Controller.Entities.Audio
/// <summary>
/// Class Audio
/// </summary>
public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<SongInfo>
public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<SongInfo>, IHasTags
{
public string FormatName { get; set; }
public long? Size { get; set; }
public string Container { get; set; }
public int? TotalBitrate { get; set; }
public List<string> Tags { get; set; }
public Audio()
{
Artists = new List<string>();
Tags = new List<string>();
}
/// <summary>

@ -1,5 +1,6 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.System;
using System.Threading;
using System.Threading.Tasks;
@ -54,9 +55,10 @@ namespace MediaBrowser.Controller.Session
/// <summary>
/// Sends the restart required message.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendRestartRequiredNotification(CancellationToken cancellationToken);
Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken);
/// <summary>
/// Sends the user data change info.

@ -19,6 +19,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.System;
namespace MediaBrowser.Dlna.PlayTo
{
@ -320,7 +321,7 @@ namespace MediaBrowser.Dlna.PlayTo
return Task.FromResult(true);
}
public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}

@ -1,91 +0,0 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class AudioEncoder
{
private readonly string _ffmpegPath;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IApplicationPaths _appPaths;
private readonly IIsoManager _isoManager;
private readonly ILiveTvManager _liveTvManager;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public AudioEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths, IIsoManager isoManager, ILiveTvManager liveTvManager)
{
_ffmpegPath = ffmpegPath;
_logger = logger;
_fileSystem = fileSystem;
_appPaths = appPaths;
_isoManager = isoManager;
_liveTvManager = liveTvManager;
}
public Task BeginEncoding(InternalEncodingTask task)
{
return new FFMpegProcess(_ffmpegPath, _logger, _fileSystem, _appPaths, _isoManager, _liveTvManager).Start(task, GetArguments);
}
private string GetArguments(InternalEncodingTask task, string mountedPath)
{
var options = task.Request;
return string.Format("{0} -i {1} {2} -id3v2_version 3 -write_id3v1 1 \"{3}\"",
GetInputModifier(task),
GetInputArgument(task),
GetOutputModifier(task),
options.OutputPath).Trim();
}
private string GetInputModifier(InternalEncodingTask task)
{
return EncodingUtils.GetInputModifier(task);
}
private string GetInputArgument(InternalEncodingTask task)
{
return EncodingUtils.GetInputArgument(new List<string> { task.MediaPath }, task.IsInputRemote);
}
private string GetOutputModifier(InternalEncodingTask task)
{
var options = task.Request;
var audioTranscodeParams = new List<string>
{
"-threads " + EncodingUtils.GetNumberOfThreads(task, false).ToString(_usCulture),
"-vn"
};
var bitrate = EncodingUtils.GetAudioBitrateParam(task);
if (bitrate.HasValue)
{
audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(_usCulture));
}
var channels = EncodingUtils.GetNumAudioChannelsParam(options, task.AudioStream);
if (channels.HasValue)
{
audioTranscodeParams.Add("-ac " + channels.Value);
}
if (options.AudioSampleRate.HasValue)
{
audioTranscodeParams.Add("-ar " + options.AudioSampleRate.Value);
}
return string.Join(" ", audioTranscodeParams.ToArray());
}
}
}

@ -64,77 +64,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
return string.Format("\"{0}\"", url);
}
public static string GetAudioInputModifier(InternalEncodingTask options)
{
return GetCommonInputModifier(options);
}
public static string GetInputModifier(InternalEncodingTask options)
{
var inputModifier = GetCommonInputModifier(options);
//if (state.VideoRequest != null)
//{
// inputModifier += " -fflags genpts";
//}
//if (!string.IsNullOrEmpty(state.InputVideoCodec))
//{
// inputModifier += " -vcodec " + state.InputVideoCodec;
//}
//if (!string.IsNullOrEmpty(state.InputVideoSync))
//{
// inputModifier += " -vsync " + state.InputVideoSync;
//}
return inputModifier;
}
private static string GetCommonInputModifier(InternalEncodingTask options)
{
var inputModifier = string.Empty;
if (options.EnableDebugLogging)
{
inputModifier += "-loglevel debug";
}
var probeSize = GetProbeSizeArgument(options.InputVideoType.HasValue && options.InputVideoType.Value == VideoType.Dvd);
inputModifier += " " + probeSize;
inputModifier = inputModifier.Trim();
if (!string.IsNullOrWhiteSpace(options.UserAgent))
{
inputModifier += " -user-agent \"" + options.UserAgent + "\"";
}
inputModifier += " " + GetFastSeekValue(options.Request);
inputModifier = inputModifier.Trim();
if (!string.IsNullOrEmpty(options.InputFormat))
{
inputModifier += " -f " + options.InputFormat;
}
if (!string.IsNullOrEmpty(options.InputAudioCodec))
{
inputModifier += " -acodec " + options.InputAudioCodec;
}
if (!string.IsNullOrEmpty(options.InputAudioSync))
{
inputModifier += " -async " + options.InputAudioSync;
}
if (options.ReadInputAtNativeFramerate)
{
inputModifier += " -re";
}
return inputModifier;
}
private static string GetFastSeekValue(EncodingOptions options)
{
var time = options.StartTimeTicks;
@ -157,19 +86,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
return isDvd ? "-probesize 1G -analyzeduration 200M" : string.Empty;
}
public static int? GetAudioBitrateParam(InternalEncodingTask task)
{
if (task.Request.AudioBitRate.HasValue)
{
// Make sure we don't request a bitrate higher than the source
var currentBitrate = task.AudioStream == null ? task.Request.AudioBitRate.Value : task.AudioStream.BitRate ?? task.Request.AudioBitRate.Value;
return Math.Min(currentBitrate, task.Request.AudioBitRate.Value);
}
return null;
}
/// <summary>
/// Gets the number of audio channels to specify on the command line
/// </summary>
@ -201,35 +117,5 @@ namespace MediaBrowser.MediaEncoding.Encoder
return request.AudioChannels;
}
public static int GetNumberOfThreads(InternalEncodingTask state, bool isWebm)
{
// Use more when this is true. -re will keep cpu usage under control
if (state.ReadInputAtNativeFramerate)
{
if (isWebm)
{
return Math.Max(Environment.ProcessorCount - 1, 2);
}
return 0;
}
// Webm: http://www.webmproject.org/docs/encoder-parameters/
// The decoder will usually automatically use an appropriate number of threads according to how many cores are available but it can only use multiple threads
// for the coefficient data if the encoder selected --token-parts > 0 at encode time.
switch (state.QualitySetting)
{
case EncodingQuality.HighSpeed:
return 2;
case EncodingQuality.HighQuality:
return 2;
case EncodingQuality.MaxQuality:
return isWebm ? Math.Max(Environment.ProcessorCount - 1, 2) : 0;
default:
throw new Exception("Unrecognized MediaEncodingQuality value.");
}
}
}
}

@ -1,168 +0,0 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class FFMpegProcess : IDisposable
{
private readonly string _ffmpegPath;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IApplicationPaths _appPaths;
private readonly IIsoManager _isoManager;
private readonly ILiveTvManager _liveTvManager;
private Stream _logFileStream;
private InternalEncodingTask _task;
private IIsoMount _isoMount;
public FFMpegProcess(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths, IIsoManager isoManager, ILiveTvManager liveTvManager)
{
_ffmpegPath = ffmpegPath;
_logger = logger;
_fileSystem = fileSystem;
_appPaths = appPaths;
_isoManager = isoManager;
_liveTvManager = liveTvManager;
}
public async Task Start(InternalEncodingTask task, Func<InternalEncodingTask,string,string> argumentsFactory)
{
_task = task;
if (!File.Exists(_ffmpegPath))
{
throw new InvalidOperationException("ffmpeg was not found at " + _ffmpegPath);
}
Directory.CreateDirectory(Path.GetDirectoryName(task.Request.OutputPath));
string mountedPath = null;
if (task.InputVideoType.HasValue && task.InputVideoType == VideoType.Iso && task.IsoType.HasValue)
{
if (_isoManager.CanMount(task.MediaPath))
{
_isoMount = await _isoManager.Mount(task.MediaPath, CancellationToken.None).ConfigureAwait(false);
mountedPath = _isoMount.MountedPath;
}
}
var process = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
// Must consume both stdout and stderr or deadlocks may occur
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = _ffmpegPath,
WorkingDirectory = Path.GetDirectoryName(_ffmpegPath),
Arguments = argumentsFactory(task, mountedPath),
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false
},
EnableRaisingEvents = true
};
_logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-" + task.Id + ".txt");
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
_logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
process.Exited += process_Exited;
try
{
process.Start();
}
catch (Exception ex)
{
_logger.ErrorException("Error starting ffmpeg", ex);
task.OnError();
DisposeLogFileStream();
process.Dispose();
throw;
}
task.OnBegin();
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
process.BeginOutputReadLine();
#pragma warning disable 4014
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
process.StandardError.BaseStream.CopyToAsync(_logFileStream);
#pragma warning restore 4014
}
async void process_Exited(object sender, EventArgs e)
{
var process = (Process)sender;
if (_isoMount != null)
{
_isoMount.Dispose();
_isoMount = null;
}
DisposeLogFileStream();
try
{
_logger.Info("FFMpeg exited with code {0} for {1}", process.ExitCode, _task.Request.OutputPath);
}
catch
{
_logger.Info("FFMpeg exited with an error for {0}", _task.Request.OutputPath);
}
_task.OnCompleted();
if (!string.IsNullOrEmpty(_task.LiveTvStreamId))
{
try
{
await _liveTvManager.CloseLiveStream(_task.LiveTvStreamId, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error closing live tv stream", ex);
}
}
}
public void Dispose()
{
DisposeLogFileStream();
}
private void DisposeLogFileStream()
{
if (_logFileStream != null)
{
_logFileStream.Dispose();
_logFileStream = null;
}
}
}
}

@ -1,235 +0,0 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Logging;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class ImageEncoder
{
private readonly string _ffmpegPath;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IApplicationPaths _appPaths;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(10, 10);
public ImageEncoder(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths)
{
_ffmpegPath = ffmpegPath;
_logger = logger;
_fileSystem = fileSystem;
_appPaths = appPaths;
}
public async Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken)
{
ValidateInput(options);
await ResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
return await EncodeImageInternal(options, cancellationToken).ConfigureAwait(false);
}
finally
{
ResourcePool.Release();
}
}
private async Task<Stream> EncodeImageInternal(ImageEncodingOptions options, CancellationToken cancellationToken)
{
ValidateInput(options);
var inputPath = options.InputPath;
var filename = Path.GetFileName(inputPath);
if (HasDiacritics(filename))
{
inputPath = GetTempFile(inputPath);
filename = Path.GetFileName(inputPath);
}
var process = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
FileName = _ffmpegPath,
Arguments = GetArguments(options, filename),
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = Path.GetDirectoryName(inputPath)
}
};
_logger.Debug("ffmpeg " + process.StartInfo.Arguments);
process.Start();
var memoryStream = new MemoryStream();
#pragma warning disable 4014
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
process.StandardOutput.BaseStream.CopyToAsync(memoryStream);
#pragma warning restore 4014
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
process.BeginErrorReadLine();
var ranToCompletion = process.WaitForExit(5000);
if (!ranToCompletion)
{
try
{
_logger.Info("Killing ffmpeg process");
process.Kill();
process.WaitForExit(1000);
}
catch (Exception ex)
{
_logger.ErrorException("Error killing process", ex);
}
}
var exitCode = ranToCompletion ? process.ExitCode : -1;
process.Dispose();
if (exitCode == -1 || memoryStream.Length == 0)
{
memoryStream.Dispose();
var msg = string.Format("ffmpeg image encoding failed for {0}", options.InputPath);
_logger.Error(msg);
throw new ApplicationException(msg);
}
memoryStream.Position = 0;
return memoryStream;
}
private string GetTempFile(string path)
{
var extension = Path.GetExtension(path) ?? string.Empty;
var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N") + extension);
File.Copy(path, tempPath);
return tempPath;
}
private string GetArguments(ImageEncodingOptions options, string inputFilename)
{
var vfScale = GetFilterGraph(options);
var outputFormat = GetOutputFormat(options.Format);
var quality = (options.Quality ?? 100) * .3;
quality = 31 - quality;
var qualityValue = Convert.ToInt32(Math.Max(quality, 1));
return string.Format("-f image2 -i file:\"{3}\" -q:v {0} {1} -f image2pipe -vcodec {2} -",
qualityValue.ToString(_usCulture),
vfScale,
outputFormat,
inputFilename);
}
private string GetFilterGraph(ImageEncodingOptions options)
{
if (!options.Width.HasValue &&
!options.Height.HasValue &&
!options.MaxHeight.HasValue &&
!options.MaxWidth.HasValue)
{
return string.Empty;
}
var widthScale = "-1";
var heightScale = "-1";
if (options.MaxWidth.HasValue)
{
widthScale = "min(iw\\," + options.MaxWidth.Value.ToString(_usCulture) + ")";
}
else if (options.Width.HasValue)
{
widthScale = options.Width.Value.ToString(_usCulture);
}
if (options.MaxHeight.HasValue)
{
heightScale = "min(ih\\," + options.MaxHeight.Value.ToString(_usCulture) + ")";
}
else if (options.Height.HasValue)
{
heightScale = options.Height.Value.ToString(_usCulture);
}
var scaleMethod = "lanczos";
return string.Format("-vf scale=\"{0}:{1}\"",
widthScale,
heightScale);
}
private string GetOutputFormat(string format)
{
if (string.Equals(format, "jpeg", StringComparison.OrdinalIgnoreCase) ||
string.Equals(format, "jpg", StringComparison.OrdinalIgnoreCase))
{
return "mjpeg";
}
return format;
}
private void ValidateInput(ImageEncodingOptions options)
{
}
/// <summary>
/// Determines whether the specified text has diacritics.
/// </summary>
/// <param name="text">The text.</param>
/// <returns><c>true</c> if the specified text has diacritics; otherwise, <c>false</c>.</returns>
private bool HasDiacritics(string text)
{
return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal);
}
/// <summary>
/// Removes the diacritics.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>System.String.</returns>
private string RemoveDiacritics(string text)
{
return String.Concat(
text.Normalize(NormalizationForm.FormD)
.Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
UnicodeCategory.NonSpacingMark)
).Normalize(NormalizationForm.FormC);
}
}
}

@ -1,95 +0,0 @@
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Threading;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class InternalEncodingTask
{
public string Id { get; set; }
public CancellationTokenSource CancellationTokenSource { get; set; }
public double ProgressPercentage { get; set; }
public EncodingOptions Request { get; set; }
public VideoEncodingOptions VideoRequest
{
get { return Request as VideoEncodingOptions; }
}
public string MediaPath { get; set; }
public List<string> StreamFileNames { get; set; }
public bool IsInputRemote { get; set; }
public VideoType? InputVideoType { get; set; }
public IsoType? IsoType { get; set; }
public long? InputRunTimeTicks;
public string AudioSync = "1";
public string VideoSync = "vfr";
public string InputAudioSync { get; set; }
public string InputVideoSync { get; set; }
public bool DeInterlace { get; set; }
public bool ReadInputAtNativeFramerate { get; set; }
public string InputFormat { get; set; }
public string InputVideoCodec { get; set; }
public string InputAudioCodec { get; set; }
public string LiveTvStreamId { get; set; }
public MediaStream AudioStream { get; set; }
public MediaStream VideoStream { get; set; }
public MediaStream SubtitleStream { get; set; }
public bool HasMediaStreams { get; set; }
public int SegmentLength = 10;
public int HlsListSize;
public string MimeType { get; set; }
public string OrgPn { get; set; }
public bool EnableMpegtsM2TsMode { get; set; }
/// <summary>
/// Gets or sets the user agent.
/// </summary>
/// <value>The user agent.</value>
public string UserAgent { get; set; }
public EncodingQuality QualitySetting { get; set; }
public InternalEncodingTask()
{
Id = Guid.NewGuid().ToString("N");
CancellationTokenSource = new CancellationTokenSource();
StreamFileNames = new List<string>();
}
public bool EnableDebugLogging { get; set; }
internal void OnBegin()
{
}
internal void OnCompleted()
{
}
internal void OnError()
{
}
}
}

@ -1,311 +0,0 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class InternalEncodingTaskFactory
{
private readonly ILibraryManager _libraryManager;
private readonly ILiveTvManager _liveTvManager;
private readonly IItemRepository _itemRepo;
private readonly IServerConfigurationManager _config;
public InternalEncodingTaskFactory(ILibraryManager libraryManager, ILiveTvManager liveTvManager, IItemRepository itemRepo, IServerConfigurationManager config)
{
_libraryManager = libraryManager;
_liveTvManager = liveTvManager;
_itemRepo = itemRepo;
_config = config;
}
public async Task<InternalEncodingTask> Create(EncodingOptions request, CancellationToken cancellationToken)
{
ValidateInput(request);
var state = new InternalEncodingTask
{
Request = request
};
var item = string.IsNullOrEmpty(request.MediaSourceId) ?
_libraryManager.GetItemById(new Guid(request.ItemId)) :
_libraryManager.GetItemById(new Guid(request.MediaSourceId));
if (item is ILiveTvRecording)
{
var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false);
if (string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
state.InputVideoType = VideoType.VideoFile;
}
var path = recording.RecordingInfo.Path;
var mediaUrl = recording.RecordingInfo.Url;
if (string.IsNullOrWhiteSpace(path) && string.IsNullOrWhiteSpace(mediaUrl))
{
var streamInfo = await _liveTvManager.GetRecordingStream(request.ItemId, cancellationToken).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
path = streamInfo.Path;
mediaUrl = streamInfo.Url;
}
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
state.MediaPath = path;
state.IsInputRemote = false;
}
else if (!string.IsNullOrEmpty(mediaUrl))
{
state.MediaPath = mediaUrl;
state.IsInputRemote = true;
}
state.InputRunTimeTicks = recording.RunTimeTicks;
if (recording.RecordingInfo.Status == RecordingStatus.InProgress && !state.IsInputRemote)
{
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
}
state.ReadInputAtNativeFramerate = recording.RecordingInfo.Status == RecordingStatus.InProgress;
state.AudioSync = "1000";
state.DeInterlace = true;
state.InputVideoSync = "-1";
state.InputAudioSync = "1";
}
else if (item is LiveTvChannel)
{
var channel = _liveTvManager.GetInternalChannel(request.ItemId);
if (string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
state.InputVideoType = VideoType.VideoFile;
}
var streamInfo = await _liveTvManager.GetChannelStream(request.ItemId, cancellationToken).ConfigureAwait(false);
state.LiveTvStreamId = streamInfo.Id;
if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path))
{
state.MediaPath = streamInfo.Path;
state.IsInputRemote = false;
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(streamInfo.Url))
{
state.MediaPath = streamInfo.Url;
state.IsInputRemote = true;
}
state.ReadInputAtNativeFramerate = true;
state.AudioSync = "1000";
state.DeInterlace = true;
state.InputVideoSync = "-1";
state.InputAudioSync = "1";
}
else
{
state.MediaPath = item.Path;
state.IsInputRemote = item.LocationType == LocationType.Remote;
var video = item as Video;
if (video != null)
{
state.InputVideoType = video.VideoType;
state.IsoType = video.IsoType;
state.StreamFileNames = video.PlayableStreamFileNames.ToList();
}
state.InputRunTimeTicks = item.RunTimeTicks;
}
var videoRequest = request as VideoEncodingOptions;
var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
{
ItemId = item.Id
}).ToList();
if (videoRequest != null)
{
state.VideoStream = GetMediaStream(mediaStreams, videoRequest.VideoStreamIndex, MediaStreamType.Video);
state.SubtitleStream = GetMediaStream(mediaStreams, videoRequest.SubtitleStreamIndex, MediaStreamType.Subtitle, false);
state.AudioStream = GetMediaStream(mediaStreams, videoRequest.AudioStreamIndex, MediaStreamType.Audio);
if (state.VideoStream != null && state.VideoStream.IsInterlaced)
{
state.DeInterlace = true;
}
}
else
{
state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
}
state.HasMediaStreams = mediaStreams.Count > 0;
state.SegmentLength = state.ReadInputAtNativeFramerate ? 5 : 10;
state.HlsListSize = state.ReadInputAtNativeFramerate ? 100 : 1440;
state.QualitySetting = GetQualitySetting();
ApplyDeviceProfileSettings(state);
return state;
}
private void ValidateInput(EncodingOptions request)
{
if (string.IsNullOrWhiteSpace(request.ItemId))
{
throw new ArgumentException("ItemId is required.");
}
if (string.IsNullOrWhiteSpace(request.OutputPath))
{
throw new ArgumentException("OutputPath is required.");
}
if (string.IsNullOrWhiteSpace(request.Container))
{
throw new ArgumentException("Container is required.");
}
if (string.IsNullOrWhiteSpace(request.AudioCodec))
{
throw new ArgumentException("AudioCodec is required.");
}
var videoRequest = request as VideoEncodingOptions;
if (videoRequest == null)
{
return;
}
}
/// <summary>
/// Determines which stream will be used for playback
/// </summary>
/// <param name="allStream">All stream.</param>
/// <param name="desiredIndex">Index of the desired.</param>
/// <param name="type">The type.</param>
/// <param name="returnFirstIfNoIndex">if set to <c>true</c> [return first if no index].</param>
/// <returns>MediaStream.</returns>
private MediaStream GetMediaStream(IEnumerable<MediaStream> allStream, int? desiredIndex, MediaStreamType type, bool returnFirstIfNoIndex = true)
{
var streams = allStream.Where(s => s.Type == type).OrderBy(i => i.Index).ToList();
if (desiredIndex.HasValue)
{
var stream = streams.FirstOrDefault(s => s.Index == desiredIndex.Value);
if (stream != null)
{
return stream;
}
}
if (returnFirstIfNoIndex && type == MediaStreamType.Audio)
{
return streams.FirstOrDefault(i => i.Channels.HasValue && i.Channels.Value > 0) ??
streams.FirstOrDefault();
}
// Just return the first one
return returnFirstIfNoIndex ? streams.FirstOrDefault() : null;
}
private void ApplyDeviceProfileSettings(InternalEncodingTask state)
{
var profile = state.Request.DeviceProfile;
if (profile == null)
{
// Don't use settings from the default profile.
// Only use a specific profile if it was requested.
return;
}
var container = state.Request.Container;
var audioCodec = state.Request.AudioCodec;
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
{
videoCodec = state.VideoStream.Codec;
}
//var mediaProfile = state.VideoRequest == null ?
// profile.GetAudioMediaProfile(container, audioCodec) :
// profile.GetVideoMediaProfile(container, audioCodec, videoCodec, state.AudioStream, state.VideoStream);
//if (mediaProfile != null)
//{
// state.MimeType = mediaProfile.MimeType;
// state.OrgPn = mediaProfile.OrgPn;
//}
//var transcodingProfile = state.VideoRequest == null ?
// profile.GetAudioTranscodingProfile(container, audioCodec) :
// profile.GetVideoTranscodingProfile(container, audioCodec, videoCodec);
//if (transcodingProfile != null)
//{
// //state.EstimateContentLength = transcodingProfile.EstimateContentLength;
// state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
// //state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
// if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.VideoProfile))
// {
// state.VideoRequest.VideoProfile = transcodingProfile.VideoProfile;
// }
//}
}
private EncodingQuality GetQualitySetting()
{
var quality = _config.Configuration.MediaEncodingQuality;
if (quality == EncodingQuality.Auto)
{
var cpuCount = Environment.ProcessorCount;
if (cpuCount >= 4)
{
//return EncodingQuality.HighQuality;
}
return EncodingQuality.HighSpeed;
}
return quality;
}
}
}

@ -853,7 +853,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken)
{
return new ImageEncoder(FFMpegPath, _logger, _fileSystem, _appPaths).EncodeImage(options, cancellationToken);
throw new NotImplementedException();
}
/// <summary>

@ -53,12 +53,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="BdInfo\BdInfoExaminer.cs" />
<Compile Include="Encoder\AudioEncoder.cs" />
<Compile Include="Encoder\EncodingUtils.cs" />
<Compile Include="Encoder\FFMpegProcess.cs" />
<Compile Include="Encoder\ImageEncoder.cs" />
<Compile Include="Encoder\InternalEncodingTask.cs" />
<Compile Include="Encoder\InternalEncodingTaskFactory.cs" />
<Compile Include="Encoder\MediaEncoder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Subtitles\ISubtitleParser.cs" />

@ -10,6 +10,8 @@ namespace MediaBrowser.Model.Session
public bool SupportsMediaControl { get; set; }
public string MessageCallbackUrl { get; set; }
public SessionCapabilities()
{
PlayableMediaTypes = new List<string>();

@ -98,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
all = all.Take(query.Limit.Value).ToList();
}
// Get everything
var fields = Enum.GetNames(typeof(ItemFields))
.Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
@ -156,6 +156,23 @@ namespace MediaBrowser.Server.Implementations.Channels
progress.Report(100);
}
public Task<IEnumerable<ChannelMediaInfo>> GetChannelItemMediaSources(string id, CancellationToken cancellationToken)
{
var item = (IChannelMediaItem)_libraryManager.GetItemById(id);
var channelGuid = new Guid(item.ChannelId);
var channel = _channelEntities.First(i => i.Id == channelGuid);
var requiresCallback = channel as IRequiresMediaInfoCallback;
if (requiresCallback != null)
{
return requiresCallback.GetChannelItemMediaInfo(item.ExternalId, cancellationToken);
}
return Task.FromResult<IEnumerable<ChannelMediaInfo>>(item.ChannelMediaSources);
}
private async Task<Channel> GetChannel(IChannel channelInfo, CancellationToken cancellationToken)
{
var path = Path.Combine(_config.ApplicationPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(channelInfo.Name));
@ -303,10 +320,16 @@ namespace MediaBrowser.Server.Implementations.Channels
var query = new InternalChannelItemQuery
{
User = user,
CategoryId = categoryId
User = user
};
if (!string.IsNullOrWhiteSpace(categoryId))
{
var categoryItem = (IChannelItem)_libraryManager.GetItemById(new Guid(categoryId));
query.CategoryId = categoryItem.ExternalId;
}
var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
CacheResponse(result, cachePath);
@ -380,7 +403,7 @@ namespace MediaBrowser.Server.Implementations.Channels
private string GetIdToHash(string externalId)
{
// Increment this as needed to force new downloads
return externalId + "4";
return externalId + "7";
}
private async Task<BaseItem> GetChannelItemEntity(ChannelItemInfo info, string internalChannnelId, CancellationToken cancellationToken)
@ -434,10 +457,6 @@ namespace MediaBrowser.Server.Implementations.Channels
item.Id = id;
item.RunTimeTicks = info.RunTimeTicks;
var mediaSource = info.MediaSources.FirstOrDefault();
item.Path = mediaSource == null ? null : mediaSource.Path;
if (isNew)
{
item.Name = info.Name;
@ -464,12 +483,22 @@ namespace MediaBrowser.Server.Implementations.Channels
channelItem.ChannelId = internalChannnelId;
channelItem.ChannelItemType = info.Type;
if (isNew)
{
channelItem.Tags = info.Tags;
}
var channelMediaItem = item as IChannelMediaItem;
if (channelMediaItem != null)
{
channelMediaItem.IsInfiniteStream = info.IsInfiniteStream;
channelMediaItem.ContentType = info.ContentType;
channelMediaItem.ChannelMediaSources = info.MediaSources;
var mediaSource = info.MediaSources.FirstOrDefault();
item.Path = mediaSource == null ? null : mediaSource.Path;
}
if (isNew)

@ -1,5 +1,8 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Net;
@ -16,23 +19,18 @@ namespace MediaBrowser.Server.Implementations.Session
{
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _json;
private readonly IServerApplicationHost _appHost;
public SessionInfo Session { get; private set; }
//var postUrl = string.Format("http://{0}/mediabrowser/message", session.RemoteEndPoint);
private readonly string _postUrl;
public HttpSessionController(IHttpClient httpClient,
IJsonSerializer json,
IServerApplicationHost appHost,
SessionInfo session,
public HttpSessionController(IHttpClient httpClient,
IJsonSerializer json,
SessionInfo session,
string postUrl)
{
_httpClient = httpClient;
_json = json;
_appHost = appHost;
Session = session;
_postUrl = postUrl;
}
@ -63,6 +61,21 @@ namespace MediaBrowser.Server.Implementations.Session
});
}
private Task SendMessage(string name, CancellationToken cancellationToken)
{
return SendMessage(name, new NameValueCollection(), cancellationToken);
}
private Task SendMessage(string name, NameValueCollection args, CancellationToken cancellationToken)
{
return SendMessage(new WebSocketMessage<string>
{
MessageType = name,
Data = string.Empty
}, cancellationToken);
}
public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
{
return Task.FromResult(true);
@ -80,22 +93,25 @@ namespace MediaBrowser.Server.Implementations.Session
public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
{
return SendMessage(new WebSocketMessage<PlayRequest>
{
MessageType = "Play",
Data = command
return Task.FromResult(true);
//return SendMessage(new WebSocketMessage<PlayRequest>
//{
// MessageType = "Play",
// Data = command
}, cancellationToken);
//}, cancellationToken);
}
public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
{
return SendMessage(new WebSocketMessage<PlaystateRequest>
var args = new Dictionary<string, string>();
if (command.Command == PlaystateCommand.Seek)
{
MessageType = "Playstate",
Data = command
}, cancellationToken);
}
return SendMessage(command.Command.ToString(), cancellationToken);
}
public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
@ -103,14 +119,9 @@ namespace MediaBrowser.Server.Implementations.Session
return Task.FromResult(true);
}
public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
{
return SendMessage(new WebSocketMessage<SystemInfo>
{
MessageType = "RestartRequired",
Data = _appHost.GetSystemInfo()
}, cancellationToken);
return SendMessage("RestartRequired", cancellationToken);
}
public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
@ -120,22 +131,12 @@ namespace MediaBrowser.Server.Implementations.Session
public Task SendServerShutdownNotification(CancellationToken cancellationToken)
{
return SendMessage(new WebSocketMessage<string>
{
MessageType = "ServerShuttingDown",
Data = string.Empty
}, cancellationToken);
return SendMessage("ServerShuttingDown", cancellationToken);
}
public Task SendServerRestartNotification(CancellationToken cancellationToken)
{
return SendMessage(new WebSocketMessage<string>
{
MessageType = "ServerRestarting",
Data = string.Empty
}, cancellationToken);
return SendMessage("ServerRestarting", cancellationToken);
}
public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
@ -147,5 +148,13 @@ namespace MediaBrowser.Server.Implementations.Session
}, cancellationToken);
}
private string ToQueryString(Dictionary<string, string> nvc)
{
var array = (from item in nvc
select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)))
.ToArray();
return "?" + string.Join("&", array);
}
}
}

@ -1,6 +1,7 @@
using System.Globalization;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
@ -14,10 +15,12 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -51,6 +54,10 @@ namespace MediaBrowser.Server.Implementations.Session
private readonly IImageProcessor _imageProcessor;
private readonly IItemRepository _itemRepo;
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
/// <summary>
/// Gets or sets the configuration manager.
/// </summary>
@ -93,7 +100,7 @@ namespace MediaBrowser.Server.Implementations.Session
/// <param name="logger">The logger.</param>
/// <param name="userRepository">The user repository.</param>
/// <param name="libraryManager">The library manager.</param>
public SessionManager(IUserDataManager userDataRepository, IServerConfigurationManager configurationManager, ILogger logger, IUserRepository userRepository, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IItemRepository itemRepo)
public SessionManager(IUserDataManager userDataRepository, IServerConfigurationManager configurationManager, ILogger logger, IUserRepository userRepository, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IItemRepository itemRepo, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IHttpClient httpClient)
{
_userDataRepository = userDataRepository;
_configurationManager = configurationManager;
@ -105,6 +112,9 @@ namespace MediaBrowser.Server.Implementations.Session
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_itemRepo = itemRepo;
_jsonSerializer = jsonSerializer;
_appHost = appHost;
_httpClient = httpClient;
}
/// <summary>
@ -908,11 +918,13 @@ namespace MediaBrowser.Server.Implementations.Session
{
var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
var info = _appHost.GetSystemInfo();
var tasks = sessions.Select(session => Task.Run(async () =>
{
try
{
await session.SessionController.SendRestartRequiredNotification(cancellationToken).ConfigureAwait(false);
await session.SessionController.SendRestartRequiredNotification(info, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -1135,6 +1147,18 @@ namespace MediaBrowser.Server.Implementations.Session
session.PlayableMediaTypes = capabilities.PlayableMediaTypes;
session.SupportedCommands = capabilities.SupportedCommands;
if (!string.IsNullOrWhiteSpace(capabilities.MessageCallbackUrl))
{
var postUrl = string.Format("http://{0}{1}", session.RemoteEndPoint, capabilities.MessageCallbackUrl);
var controller = session.SessionController as HttpSessionController;
if (controller == null)
{
session.SessionController = new HttpSessionController(_httpClient, _jsonSerializer, session, postUrl);
}
}
EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs
{
SessionInfo = session

@ -35,19 +35,17 @@ namespace MediaBrowser.Server.Implementations.Session
/// The _dto service
/// </summary>
private readonly IJsonSerializer _json;
private readonly IServerApplicationHost _appHost;
/// <summary>
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
/// </summary>
/// <param name="sessionManager">The session manager.</param>
/// <param name="logManager">The log manager.</param>
/// <param name="appHost">The application host.</param>
public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IServerApplicationHost appHost, IJsonSerializer json)
/// <param name="json">The json.</param>
public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IJsonSerializer json)
{
_sessionManager = sessionManager;
_logger = logManager.GetLogger(GetType().Name);
_appHost = appHost;
_json = json;
}
@ -138,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.Session
if (controller == null)
{
controller = new WebSocketController(session, _appHost, _logger, _sessionManager);
controller = new WebSocketController(session, _logger, _sessionManager);
}
controller.AddWebSocket(message.Connection);

@ -1,5 +1,4 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@ -19,15 +18,13 @@ namespace MediaBrowser.Server.Implementations.Session
public SessionInfo Session { get; private set; }
public IReadOnlyList<IWebSocketConnection> Sockets { get; private set; }
private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger;
private readonly ISessionManager _sessionManager;
public WebSocketController(SessionInfo session, IServerApplicationHost appHost, ILogger logger, ISessionManager sessionManager)
public WebSocketController(SessionInfo session, ILogger logger, ISessionManager sessionManager)
{
Session = session;
_appHost = appHost;
_logger = logger;
_sessionManager = sessionManager;
Sockets = new List<IWebSocketConnection>();
@ -121,14 +118,15 @@ namespace MediaBrowser.Server.Implementations.Session
/// <summary>
/// Sends the restart required message.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
{
return SendMessages(new WebSocketMessage<SystemInfo>
{
MessageType = "RestartRequired",
Data = _appHost.GetSystemInfo()
Data = info
}, cancellationToken);
}

@ -496,7 +496,7 @@ namespace MediaBrowser.ServerApplication
DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager);
RegisterSingleInstance(DtoService);
SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, ItemRepository);
SessionManager = new SessionManager(UserDataManager, ServerConfigurationManager, Logger, UserRepository, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, ItemRepository, JsonSerializer, this, HttpClient);
RegisterSingleInstance(SessionManager);
var newsService = new Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.367</version>
<version>3.0.369</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.367" />
<dependency id="MediaBrowser.Common" version="3.0.369" />
<dependency id="NLog" version="2.1.0" />
<dependency id="SimpleInjector" version="2.5.0" />
<dependency id="sharpcompress" version="0.10.2" />

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
<version>3.0.367</version>
<version>3.0.369</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
<version>3.0.367</version>
<version>3.0.369</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.367" />
<dependency id="MediaBrowser.Common" version="3.0.369" />
</dependencies>
</metadata>
<files>

Loading…
Cancel
Save