tikuf 11 years ago
commit 8882925dab

@ -39,6 +39,9 @@ namespace MediaBrowser.Api.LiveTv
/// <value>The limit.</value> /// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Limit { get; set; } public int? Limit { get; set; }
[ApiMember(Name = "IsFavorite", Description = "Filter by channels that are favorites, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsFavorite { get; set; }
} }
[Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")] [Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")]
@ -290,7 +293,8 @@ namespace MediaBrowser.Api.LiveTv
ChannelType = request.Type, ChannelType = request.Type,
UserId = request.UserId, UserId = request.UserId,
StartIndex = request.StartIndex, StartIndex = request.StartIndex,
Limit = request.Limit Limit = request.Limit,
IsFavorite = request.IsFavorite
}, CancellationToken.None).Result; }, CancellationToken.None).Result;

@ -31,6 +31,14 @@ namespace MediaBrowser.Api
{ {
} }
/// <summary>
/// Class ParentalRatings
/// </summary>
[Route("/Localization/Options", "GET", Summary = "Gets localization options")]
public class GetLocalizationOptions : IReturn<List<LocalizatonOption>>
{
}
/// <summary> /// <summary>
/// Class CulturesService /// Class CulturesService
/// </summary> /// </summary>
@ -62,6 +70,13 @@ namespace MediaBrowser.Api
return ToOptimizedSerializedResultUsingCache(result); return ToOptimizedSerializedResultUsingCache(result);
} }
public object Get(GetLocalizationOptions request)
{
var result = _localization.GetLocalizationOptions().ToList();
return ToOptimizedSerializedResultUsingCache(result);
}
/// <summary> /// <summary>
/// Gets the specified request. /// Gets the specified request.
/// </summary> /// </summary>

@ -103,6 +103,7 @@
<Compile Include="Playback\Hls\DynamicHlsService.cs" /> <Compile Include="Playback\Hls\DynamicHlsService.cs" />
<Compile Include="Playback\Hls\HlsSegmentService.cs" /> <Compile Include="Playback\Hls\HlsSegmentService.cs" />
<Compile Include="Playback\Hls\VideoHlsService.cs" /> <Compile Include="Playback\Hls\VideoHlsService.cs" />
<Compile Include="Playback\ProgressiveStreamService.cs" />
<Compile Include="Playback\Progressive\AudioService.cs" /> <Compile Include="Playback\Progressive\AudioService.cs" />
<Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" /> <Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
<Compile Include="Playback\BaseStreamingService.cs" /> <Compile Include="Playback\BaseStreamingService.cs" />

@ -1,9 +1,9 @@
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using ServiceStack; using ServiceStack;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -36,103 +36,74 @@ namespace MediaBrowser.Api.Music
public class InstantMixService : BaseApiService public class InstantMixService : BaseApiService
{ {
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
private readonly IMusicManager _musicManager;
public InstantMixService(IUserManager userManager, ILibraryManager libraryManager, IDtoService dtoService) public InstantMixService(IUserManager userManager, IDtoService dtoService, IMusicManager musicManager)
{ {
_userManager = userManager; _userManager = userManager;
_libraryManager = libraryManager;
_dtoService = dtoService; _dtoService = dtoService;
_musicManager = musicManager;
} }
public object Get(GetInstantMixFromSong request) public object Get(GetInstantMixFromSong request)
{ {
var item = _dtoService.GetItemByDtoId(request.Id); var item = (Audio)_dtoService.GetItemByDtoId(request.Id);
var result = GetInstantMixResult(request, item.Genres); var user = _userManager.GetUserById(request.UserId.Value);
return ToOptimizedSerializedResultUsingCache(result); var items = _musicManager.GetInstantMixFromSong(item, user);
return GetResult(items, user, request);
} }
public object Get(GetInstantMixFromAlbum request) public object Get(GetInstantMixFromAlbum request)
{ {
var album = (MusicAlbum)_dtoService.GetItemByDtoId(request.Id); var album = (MusicAlbum)_dtoService.GetItemByDtoId(request.Id);
var genres = album var user = _userManager.GetUserById(request.UserId.Value);
.RecursiveChildren
.OfType<Audio>()
.SelectMany(i => i.Genres)
.Concat(album.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase);
var result = GetInstantMixResult(request, genres); var items = _musicManager.GetInstantMixFromAlbum(album, user);
return ToOptimizedSerializedResultUsingCache(result); return GetResult(items, user, request);
} }
public object Get(GetInstantMixFromMusicGenre request) public object Get(GetInstantMixFromMusicGenre request)
{ {
var genre = GetMusicGenre(request.Name, _libraryManager); var user = _userManager.GetUserById(request.UserId.Value);
var result = GetInstantMixResult(request, new[] { genre.Name }); var items = _musicManager.GetInstantMixFromGenres(new[] { request.Name }, user);
return ToOptimizedSerializedResultUsingCache(result); return GetResult(items, user, request);
} }
public object Get(GetInstantMixFromArtist request) public object Get(GetInstantMixFromArtist request)
{ {
var artist = GetArtist(request.Name, _libraryManager); var user = _userManager.GetUserById(request.UserId.Value);
var genres = _libraryManager.RootFolder
.RecursiveChildren
.OfType<Audio>()
.Where(i => i.HasArtist(artist.Name))
.SelectMany(i => i.Genres)
.Concat(artist.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase);
var result = GetInstantMixResult(request, genres); var items = _musicManager.GetInstantMixFromArtist(request.Name, user);
return ToOptimizedSerializedResultUsingCache(result); return GetResult(items, user, request);
} }
private ItemsResult GetInstantMixResult(BaseGetSimilarItems request, IEnumerable<string> genres) private object GetResult(IEnumerable<Audio> items, User user, BaseGetSimilarItems request)
{ {
var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
var fields = request.GetItemFields().ToList(); var fields = request.GetItemFields().ToList();
var inputItems = user == null var list = items.ToList();
? _libraryManager.RootFolder.RecursiveChildren
: user.RootFolder.GetRecursiveChildren(user);
var genresDictionary = genres.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
var limit = request.Limit.HasValue ? request.Limit.Value * 2 : 100;
var items = inputItems
.OfType<Audio>()
.Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey)))
.OrderByDescending(i => i.Item2)
.ThenBy(i => Guid.NewGuid())
.Select(i => i.Item1)
.Take(limit)
.OrderBy(i => Guid.NewGuid())
.ToList();
var result = new ItemsResult var result = new ItemsResult
{ {
TotalRecordCount = items.Count TotalRecordCount = list.Count
}; };
var dtos = items.Take(request.Limit ?? items.Count) var dtos = list.Take(request.Limit ?? list.Count)
.Select(i => _dtoService.GetBaseItemDto(i, fields, user)); .Select(i => _dtoService.GetBaseItemDto(i, fields, user));
result.Items = dtos.ToArray(); result.Items = dtos.ToArray();
return result; return ToOptimizedResult(result);
} }
} }

@ -734,7 +734,9 @@ namespace MediaBrowser.Api.Playback
{ {
if (audioStream != null) if (audioStream != null)
{ {
if (audioStream.Channels > 2 && string.Equals(request.AudioCodec, "wma", StringComparison.OrdinalIgnoreCase)) var codec = request.AudioCodec ?? string.Empty;
if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
{ {
// wmav2 currently only supports two channel output // wmav2 currently only supports two channel output
return 2; return 2;
@ -835,11 +837,6 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
protected string GetInputArgument(StreamState state) protected string GetInputArgument(StreamState state)
{ {
if (state.SendInputOverStandardInput)
{
return "-";
}
var type = InputType.File; var type = InputType.File;
var inputPath = new[] { state.MediaPath }; var inputPath = new[] { state.MediaPath };
@ -898,9 +895,7 @@ namespace MediaBrowser.Api.Playback
Arguments = commandLineArgs, Arguments = commandLineArgs,
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false, ErrorDialog = false
RedirectStandardInput = state.SendInputOverStandardInput
}, },
EnableRaisingEvents = true EnableRaisingEvents = true
@ -933,11 +928,6 @@ namespace MediaBrowser.Api.Playback
throw; throw;
} }
if (state.SendInputOverStandardInput)
{
StreamToStandardInput(process, state);
}
// MUST read both stdout and stderr asynchronously or a deadlock may occurr // MUST read both stdout and stderr asynchronously or a deadlock may occurr
process.BeginOutputReadLine(); process.BeginOutputReadLine();
@ -965,32 +955,6 @@ namespace MediaBrowser.Api.Playback
} }
} }
private async void StreamToStandardInput(Process process, StreamState state)
{
try
{
await StreamToStandardInputInternal(process, state).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
Logger.Debug("Stream to standard input closed normally.");
}
catch (Exception ex)
{
Logger.ErrorException("Error writing to standard input", ex);
}
}
private async Task StreamToStandardInputInternal(Process process, StreamState state)
{
state.StandardInputCancellationTokenSource = new CancellationTokenSource();
using (var fileStream = FileSystem.GetFileStream(state.MediaPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
{
await new EndlessStreamCopy().CopyStream(fileStream, process.StandardInput.BaseStream, state.StandardInputCancellationTokenSource.Token).ConfigureAwait(false);
}
}
protected int? GetVideoBitrateParamValue(StreamState state) protected int? GetVideoBitrateParamValue(StreamState state)
{ {
var bitrate = state.VideoRequest.VideoBitRate; var bitrate = state.VideoRequest.VideoBitRate;
@ -1279,20 +1243,10 @@ namespace MediaBrowser.Api.Playback
} }
} }
else if (i == 14) else if (i == 14)
{
if (videoRequest != null)
{
videoRequest.Framerate = int.Parse(val, UsCulture);
}
}
else if (i == 15)
{
if (videoRequest != null)
{ {
request.StartTimeTicks = long.Parse(val, UsCulture); request.StartTimeTicks = long.Parse(val, UsCulture);
} }
} else if (i == 15)
else if (i == 16)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
@ -1315,11 +1269,6 @@ namespace MediaBrowser.Api.Playback
ParseParams(request); ParseParams(request);
} }
if (request.ThrowDebugError)
{
throw new InvalidOperationException("You asked for a debug error, you got one.");
}
var user = AuthorizationRequestFilterAttribute.GetCurrentUser(Request, UserManager); var user = AuthorizationRequestFilterAttribute.GetCurrentUser(Request, UserManager);
var url = Request.PathInfo; var url = Request.PathInfo;
@ -1369,8 +1318,6 @@ namespace MediaBrowser.Api.Playback
{ {
state.MediaPath = path; state.MediaPath = path;
state.IsRemote = false; state.IsRemote = false;
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
} }
else if (!string.IsNullOrEmpty(mediaUrl)) else if (!string.IsNullOrEmpty(mediaUrl))
{ {
@ -1378,7 +1325,8 @@ namespace MediaBrowser.Api.Playback
state.IsRemote = true; state.IsRemote = true;
} }
//state.RunTimeTicks = recording.RunTimeTicks; state.RunTimeTicks = recording.RunTimeTicks;
if (recording.RecordingInfo.Status == RecordingStatus.InProgress && !state.IsRemote) if (recording.RecordingInfo.Status == RecordingStatus.InProgress && !state.IsRemote)
{ {
await Task.Delay(1000, cancellationToken).ConfigureAwait(false); await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
@ -1544,21 +1492,9 @@ namespace MediaBrowser.Api.Playback
state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
foreach (var setting in transcodingProfile.Settings)
{
switch (setting.Name)
{
case TranscodingSettingType.VideoProfile:
{
if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile)) if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile))
{ {
state.VideoRequest.Profile = setting.Value; state.VideoRequest.Profile = transcodingProfile.VideoProfile;
}
break;
}
default:
throw new ArgumentException("Unrecognized TranscodingSettingType");
}
} }
} }
} }
@ -1575,12 +1511,6 @@ namespace MediaBrowser.Api.Playback
{ {
var timeSeek = GetHeader("TimeSeekRange.dlna.org"); var timeSeek = GetHeader("TimeSeekRange.dlna.org");
if (!string.IsNullOrEmpty(timeSeek))
{
ResultFactory.ThrowError(406, "Time seek not supported during encoding.", responseHeaders);
return;
}
var transferMode = GetHeader("transferMode.dlna.org"); var transferMode = GetHeader("transferMode.dlna.org");
responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode; responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*"; responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
@ -1589,7 +1519,13 @@ namespace MediaBrowser.Api.Playback
var extension = GetOutputFileExtension(state); var extension = GetOutputFileExtension(state);
// first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
var orgOp = isStaticallyStreamed || state.TranscodeSeekInfo == TranscodeSeekInfo.Bytes ? ";DLNA.ORG_OP=01" : ";DLNA.ORG_OP=00"; var orgOp = ";DLNA.ORG_OP=";
// Time-based seeking currently only possible when transcoding
orgOp += isStaticallyStreamed ? "0" : "1";
// Byte-based seeking only possible when not transcoding
orgOp += isStaticallyStreamed || state.TranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0";
// 0 = native, 1 = transcoded // 0 = native, 1 = transcoded
var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";

@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Playback.Hls
? 0 ? 0
: ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs; : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds); var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
var threads = GetNumberOfThreads(state, false); var threads = GetNumberOfThreads(state, false);

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Api.Playback.Progressive;
namespace MediaBrowser.Api.Playback
{
//public class GetProgressiveAudioStream : StreamRequest
//{
//}
//public class ProgressiveStreamService : BaseApiService
//{
// public object Get(GetProgressiveAudioStream request)
// {
// return ProcessRequest(request, false);
// }
// /// <summary>
// /// Gets the specified request.
// /// </summary>
// /// <param name="request">The request.</param>
// /// <returns>System.Object.</returns>
// public object Head(GetProgressiveAudioStream request)
// {
// return ProcessRequest(request, true);
// }
// protected object ProcessRequest(StreamRequest request, bool isHeadRequest)
// {
// var state = GetState(request, CancellationToken.None).Result;
// var responseHeaders = new Dictionary<string, string>();
// if (request.Static && state.IsRemote)
// {
// AddDlnaHeaders(state, responseHeaders, true);
// return GetStaticRemoteStreamResult(state.MediaPath, responseHeaders, isHeadRequest).Result;
// }
// var outputPath = GetOutputFilePath(state);
// var outputPathExists = File.Exists(outputPath);
// var isStatic = request.Static ||
// (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive));
// AddDlnaHeaders(state, responseHeaders, isStatic);
// if (request.Static)
// {
// var contentType = state.GetMimeType(state.MediaPath);
// return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
// }
// if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
// {
// var contentType = state.GetMimeType(outputPath);
// return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
// }
// return GetStreamResult(state, responseHeaders, isHeadRequest).Result;
// }
//}
}

@ -68,11 +68,6 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "DeviceProfileId", Description = "Optional. The dlna device profile id to utilize.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "DeviceProfileId", Description = "Optional. The dlna device profile id to utilize.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceProfileId { get; set; } public string DeviceProfileId { get; set; }
/// <summary>
/// For testing purposes
/// </summary>
public bool ThrowDebugError { get; set; }
public string Params { get; set; } public string Params { get; set; }
} }

@ -51,8 +51,6 @@ namespace MediaBrowser.Api.Playback
public bool HasMediaStreams { get; set; } public bool HasMediaStreams { get; set; }
public bool SendInputOverStandardInput { get; set; }
public CancellationTokenSource StandardInputCancellationTokenSource { get; set; } public CancellationTokenSource StandardInputCancellationTokenSource { get; set; }
public string LiveTvStreamId { get; set; } public string LiveTvStreamId { get; set; }

@ -146,7 +146,36 @@ namespace MediaBrowser.Api
/// </summary> /// </summary>
/// <value>The play command.</value> /// <value>The play command.</value>
[ApiMember(Name = "Command", Description = "The command to send.", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "Command", Description = "The command to send.", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public SystemCommand Command { get; set; } public string Command { get; set; }
}
[Route("/Sessions/{Id}/Command/{Command}", "POST", Summary = "Issues a system command to a client")]
public class SendGeneralCommand : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the command.
/// </summary>
/// <value>The play command.</value>
[ApiMember(Name = "Command", Description = "The command to send.", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Command { get; set; }
}
[Route("/Sessions/{Id}/Command", "POST", Summary = "Issues a system command to a client")]
public class SendFullGeneralCommand : GeneralCommand, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public Guid Id { get; set; }
} }
[Route("/Sessions/{Id}/Message", "POST", Summary = "Issues a command to a client to display a message to the user")] [Route("/Sessions/{Id}/Message", "POST", Summary = "Issues a command to a client to display a message to the user")]
@ -301,10 +330,23 @@ namespace MediaBrowser.Api
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
public void Post(SendSystemCommand request) public void Post(SendSystemCommand request)
{ {
var task = _sessionManager.SendSystemCommand(GetSession().Id, request.Id, request.Command, CancellationToken.None); GeneralCommandType commandType;
if (Enum.TryParse(request.Command, true, out commandType))
{
var currentSession = GetSession();
var command = new GeneralCommand
{
Name = commandType.ToString(),
ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null
};
var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
Task.WaitAll(task); Task.WaitAll(task);
} }
}
/// <summary> /// <summary>
/// Posts the specified request. /// Posts the specified request.
@ -343,6 +385,32 @@ namespace MediaBrowser.Api
Task.WaitAll(task); Task.WaitAll(task);
} }
public void Post(SendGeneralCommand request)
{
var currentSession = GetSession();
var command = new GeneralCommand
{
Name = request.Command,
ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null
};
var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
public void Post(SendFullGeneralCommand request)
{
var currentSession = GetSession();
request.ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null;
var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, request, CancellationToken.None);
Task.WaitAll(task);
}
public void Post(AddUserToSession request) public void Post(AddUserToSession request)
{ {
_sessionManager.AddAdditionalUser(request.Id, request.UserId); _sessionManager.AddAdditionalUser(request.Id, request.UserId);

@ -74,13 +74,13 @@ namespace MediaBrowser.Controller.Dlna
public ContainerProfile[] ContainerProfiles { get; set; } public ContainerProfile[] ContainerProfiles { get; set; }
public CodecProfile[] CodecProfiles { get; set; } public CodecProfile[] CodecProfiles { get; set; }
public MediaProfile[] MediaProfiles { get; set; } public ResponseProfile[] ResponseProfiles { get; set; }
public DeviceProfile() public DeviceProfile()
{ {
DirectPlayProfiles = new DirectPlayProfile[] { }; DirectPlayProfiles = new DirectPlayProfile[] { };
TranscodingProfiles = new TranscodingProfile[] { }; TranscodingProfiles = new TranscodingProfile[] { };
MediaProfiles = new MediaProfile[] { }; ResponseProfiles = new ResponseProfile[] { };
CodecProfiles = new CodecProfile[] { }; CodecProfiles = new CodecProfile[] { };
ContainerProfiles = new ContainerProfile[] { }; ContainerProfiles = new ContainerProfile[] { };
@ -147,11 +147,11 @@ namespace MediaBrowser.Controller.Dlna
}); });
} }
public MediaProfile GetAudioMediaProfile(string container, string audioCodec, MediaStream audioStream) public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, MediaStream audioStream)
{ {
container = (container ?? string.Empty).TrimStart('.'); container = (container ?? string.Empty).TrimStart('.');
return MediaProfiles.FirstOrDefault(i => return ResponseProfiles.FirstOrDefault(i =>
{ {
if (i.Type != DlnaProfileType.Audio) if (i.Type != DlnaProfileType.Audio)
{ {
@ -174,11 +174,11 @@ namespace MediaBrowser.Controller.Dlna
}); });
} }
public MediaProfile GetVideoMediaProfile(string container, string audioCodec, string videoCodec, MediaStream audioStream, MediaStream videoStream) public ResponseProfile GetVideoMediaProfile(string container, string audioCodec, string videoCodec, MediaStream audioStream, MediaStream videoStream)
{ {
container = (container ?? string.Empty).TrimStart('.'); container = (container ?? string.Empty).TrimStart('.');
return MediaProfiles.FirstOrDefault(i => return ResponseProfiles.FirstOrDefault(i =>
{ {
if (i.Type != DlnaProfileType.Video) if (i.Type != DlnaProfileType.Video)
{ {
@ -207,11 +207,11 @@ namespace MediaBrowser.Controller.Dlna
}); });
} }
public MediaProfile GetPhotoMediaProfile(string container) public ResponseProfile GetPhotoMediaProfile(string container)
{ {
container = (container ?? string.Empty).TrimStart('.'); container = (container ?? string.Empty).TrimStart('.');
return MediaProfiles.FirstOrDefault(i => return ResponseProfiles.FirstOrDefault(i =>
{ {
if (i.Type != DlnaProfileType.Photo) if (i.Type != DlnaProfileType.Photo)
{ {

@ -4,7 +4,7 @@ using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna namespace MediaBrowser.Controller.Dlna
{ {
public class MediaProfile public class ResponseProfile
{ {
[XmlAttribute("container")] [XmlAttribute("container")]
public string Container { get; set; } public string Container { get; set; }
@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Dlna
public ProfileCondition[] Conditions { get; set; } public ProfileCondition[] Conditions { get; set; }
public MediaProfile() public ResponseProfile()
{ {
Conditions = new ProfileCondition[] {}; Conditions = new ProfileCondition[] {};
} }

@ -30,13 +30,8 @@ namespace MediaBrowser.Controller.Dlna
[XmlAttribute("transcodeSeekInfo")] [XmlAttribute("transcodeSeekInfo")]
public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
public TranscodingSetting[] Settings { get; set; } [XmlAttribute("videoProfile")]
public string VideoProfile { get; set; }
public TranscodingProfile()
{
Settings = new TranscodingSetting[] { };
}
public List<string> GetAudioCodecs() public List<string> GetAudioCodecs()
{ {
@ -44,20 +39,6 @@ namespace MediaBrowser.Controller.Dlna
} }
} }
public class TranscodingSetting
{
[XmlAttribute("name")]
public TranscodingSettingType Name { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
}
public enum TranscodingSettingType
{
VideoProfile = 0
}
public enum TranscodeSeekInfo public enum TranscodeSeekInfo
{ {
Auto = 0, Auto = 0,

@ -85,6 +85,13 @@ namespace MediaBrowser.Controller.Dto
/// <returns>ChapterInfoDto.</returns> /// <returns>ChapterInfoDto.</returns>
ChapterInfoDto GetChapterInfoDto(ChapterInfo chapterInfo, BaseItem item); ChapterInfoDto GetChapterInfoDto(ChapterInfo chapterInfo, BaseItem item);
/// <summary>
/// Gets the media sources.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>List{MediaSourceInfo}.</returns>
List<MediaSourceInfo> GetMediaSources(BaseItem item);
/// <summary> /// <summary>
/// Gets the item by name dto. /// Gets the item by name dto.
/// </summary> /// </summary>

@ -0,0 +1,38 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Library
{
public interface IMusicManager
{
/// <summary>
/// Gets the instant mix from song.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{Audio}.</returns>
IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user);
/// <summary>
/// Gets the instant mix from artist.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{Audio}.</returns>
IEnumerable<Audio> GetInstantMixFromArtist(string name, User user);
/// <summary>
/// Gets the instant mix from album.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{Audio}.</returns>
IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user);
/// <summary>
/// Gets the instant mix from genre.
/// </summary>
/// <param name="genres">The genres.</param>
/// <param name="user">The user.</param>
/// <returns>IEnumerable{Audio}.</returns>
IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user);
}
}

@ -1,8 +1,8 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Library;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Library;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -14,6 +14,8 @@ namespace MediaBrowser.Controller.LiveTv
RecordingInfo RecordingInfo { get; set; } RecordingInfo RecordingInfo { get; set; }
long? RunTimeTicks { get; set; }
string GetClientTypeName(); string GetClientTypeName();
string GetUserDataKey(); string GetUserDataKey();

@ -1,7 +1,7 @@
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Localization namespace MediaBrowser.Controller.Localization
{ {
@ -31,5 +31,42 @@ namespace MediaBrowser.Controller.Localization
/// <param name="rating">The rating.</param> /// <param name="rating">The rating.</param>
/// <returns>System.Int32.</returns> /// <returns>System.Int32.</returns>
int? GetRatingLevel(string rating); int? GetRatingLevel(string rating);
/// <summary>
/// Gets the localized string.
/// </summary>
/// <param name="phrase">The phrase.</param>
/// <param name="culture">The culture.</param>
/// <returns>System.String.</returns>
string GetLocalizedString(string phrase, string culture);
/// <summary>
/// Gets the localized string.
/// </summary>
/// <param name="phrase">The phrase.</param>
/// <returns>System.String.</returns>
string GetLocalizedString(string phrase);
/// <summary>
/// Localizes the document.
/// </summary>
/// <param name="document">The document.</param>
/// <param name="culture">The culture.</param>
/// <param name="tokenBuilder">The token builder.</param>
/// <returns>System.String.</returns>
string LocalizeDocument(string document, string culture, Func<string, string> tokenBuilder);
/// <summary>
/// Gets the localization options.
/// </summary>
/// <returns>IEnumerable{LocalizatonOption}.</returns>
IEnumerable<LocalizatonOption> GetLocalizationOptions();
/// <summary>
/// Gets the java script localization dictionary.
/// </summary>
/// <param name="culture">The culture.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
Dictionary<string, string> GetJavaScriptLocalizationDictionary(string culture);
} }
} }

@ -84,7 +84,7 @@
<Compile Include="Dlna\DirectPlayProfile.cs" /> <Compile Include="Dlna\DirectPlayProfile.cs" />
<Compile Include="Dlna\IDlnaManager.cs" /> <Compile Include="Dlna\IDlnaManager.cs" />
<Compile Include="Dlna\DeviceProfile.cs" /> <Compile Include="Dlna\DeviceProfile.cs" />
<Compile Include="Dlna\MediaProfile.cs" /> <Compile Include="Dlna\ResponseProfile.cs" />
<Compile Include="Dlna\TranscodingProfile.cs" /> <Compile Include="Dlna\TranscodingProfile.cs" />
<Compile Include="Drawing\IImageProcessor.cs" /> <Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" /> <Compile Include="Drawing\ImageFormat.cs" />
@ -134,6 +134,7 @@
<Compile Include="Library\DeleteOptions.cs" /> <Compile Include="Library\DeleteOptions.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" /> <Compile Include="Library\ILibraryPostScanTask.cs" />
<Compile Include="Library\IMetadataSaver.cs" /> <Compile Include="Library\IMetadataSaver.cs" />
<Compile Include="Library\IMusicManager.cs" />
<Compile Include="Library\ItemUpdateType.cs" /> <Compile Include="Library\ItemUpdateType.cs" />
<Compile Include="Library\IUserDataManager.cs" /> <Compile Include="Library\IUserDataManager.cs" />
<Compile Include="Library\UserDataSaveEventArgs.cs" /> <Compile Include="Library\UserDataSaveEventArgs.cs" />
@ -155,11 +156,14 @@
<Compile Include="LiveTv\SeriesTimerInfo.cs" /> <Compile Include="LiveTv\SeriesTimerInfo.cs" />
<Compile Include="LiveTv\TimerInfo.cs" /> <Compile Include="LiveTv\TimerInfo.cs" />
<Compile Include="Localization\ILocalizationManager.cs" /> <Compile Include="Localization\ILocalizationManager.cs" />
<Compile Include="MediaEncoding\EncodingOptions.cs" />
<Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" /> <Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" />
<Compile Include="MediaEncoding\EncodingResult.cs" />
<Compile Include="MediaEncoding\IEncodingManager.cs" /> <Compile Include="MediaEncoding\IEncodingManager.cs" />
<Compile Include="MediaEncoding\ImageEncodingOptions.cs" /> <Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
<Compile Include="MediaEncoding\IMediaEncoder.cs" /> <Compile Include="MediaEncoding\IMediaEncoder.cs" />
<Compile Include="MediaEncoding\InternalMediaInfoResult.cs" /> <Compile Include="MediaEncoding\InternalMediaInfoResult.cs" />
<Compile Include="MediaEncoding\VideoEncodingOptions.cs" />
<Compile Include="Net\IHasResultFactory.cs" /> <Compile Include="Net\IHasResultFactory.cs" />
<Compile Include="Net\IHttpResultFactory.cs" /> <Compile Include="Net\IHttpResultFactory.cs" />
<Compile Include="Net\IHttpServer.cs" /> <Compile Include="Net\IHttpServer.cs" />

@ -0,0 +1,79 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Controller.MediaEncoding
{
public class EncodingOptions
{
/// <summary>
/// Gets or sets the item identifier.
/// </summary>
/// <value>The item identifier.</value>
public string ItemId { get; set; }
/// <summary>
/// Gets or sets the media source identifier.
/// </summary>
/// <value>The media source identifier.</value>
public string MediaSourceId { get; set; }
/// <summary>
/// Gets or sets the device profile.
/// </summary>
/// <value>The device profile.</value>
public DeviceProfile DeviceProfile { get; set; }
/// <summary>
/// Gets or sets the output path.
/// </summary>
/// <value>The output path.</value>
public string OutputPath { get; set; }
/// <summary>
/// Gets or sets the container.
/// </summary>
/// <value>The container.</value>
public string Container { get; set; }
/// <summary>
/// Gets or sets the audio codec.
/// </summary>
/// <value>The audio codec.</value>
public string AudioCodec { get; set; }
/// <summary>
/// Gets or sets the start time ticks.
/// </summary>
/// <value>The start time ticks.</value>
public long? StartTimeTicks { get; set; }
/// <summary>
/// Gets or sets the maximum channels.
/// </summary>
/// <value>The maximum channels.</value>
public int? MaxAudioChannels { get; set; }
/// <summary>
/// Gets or sets the channels.
/// </summary>
/// <value>The channels.</value>
public int? AudioChannels { get; set; }
/// <summary>
/// Gets or sets the sample rate.
/// </summary>
/// <value>The sample rate.</value>
public int? AudioSampleRate { get; set; }
/// <summary>
/// Gets or sets the bit rate.
/// </summary>
/// <value>The bit rate.</value>
public int? AudioBitRate { get; set; }
/// <summary>
/// Gets or sets the maximum audio bit rate.
/// </summary>
/// <value>The maximum audio bit rate.</value>
public int? MaxAudioBitRate { get; set; }
}
}

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.MediaEncoding
{
public class EncodingResult
{
public string OutputPath { get; set; }
}
}

@ -0,0 +1,26 @@

namespace MediaBrowser.Controller.MediaEncoding
{
public class VideoEncodingOptions : EncodingOptions
{
public string VideoCodec { get; set; }
public string VideoProfile { get; set; }
public double? VideoLevel { get; set; }
public int? VideoStreamIndex { get; set; }
public int? AudioStreamIndex { get; set; }
public int? SubtitleStreamIndex { get; set; }
public int? MaxWidth { get; set; }
public int? MaxHeight { get; set; }
public int? Height { get; set; }
public int? Width { get; set; }
}
}

@ -19,14 +19,6 @@ namespace MediaBrowser.Controller.Session
/// <value><c>true</c> if this instance is session active; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is session active; otherwise, <c>false</c>.</value>
bool IsSessionActive { get; } bool IsSessionActive { get; }
/// <summary>
/// Sends the system command.
/// </summary>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendSystemCommand(SystemCommand command, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Sends the message command. /// Sends the message command.
/// </summary> /// </summary>
@ -65,7 +57,7 @@ namespace MediaBrowser.Controller.Session
/// <param name="command">The command.</param> /// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken); Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Sends the library update info. /// Sends the library update info.

@ -84,14 +84,14 @@ namespace MediaBrowser.Controller.Session
Task ReportSessionEnded(Guid sessionId); Task ReportSessionEnded(Guid sessionId);
/// <summary> /// <summary>
/// Sends the system command. /// Sends the general command.
/// </summary> /// </summary>
/// <param name="controllingSessionId">The controlling session identifier.</param> /// <param name="controllingSessionId">The controlling session identifier.</param>
/// <param name="sessionId">The session id.</param> /// <param name="sessionId">The session identifier.</param>
/// <param name="command">The command.</param> /// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SendSystemCommand(Guid controllingSessionId, Guid sessionId, SystemCommand command, CancellationToken cancellationToken); Task SendGeneralCommand(Guid controllingSessionId, Guid sessionId, GeneralCommand command, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Sends the message command. /// Sends the message command.

@ -20,13 +20,15 @@ namespace MediaBrowser.Dlna
private readonly IXmlSerializer _xmlSerializer; private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger) public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer)
{ {
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_appPaths = appPaths; _appPaths = appPaths;
_logger = logger; _logger = logger;
_jsonSerializer = jsonSerializer;
//DumpProfiles(); //DumpProfiles();
} }
@ -381,10 +383,66 @@ namespace MediaBrowser.Dlna
public void CreateProfile(DeviceProfile profile) public void CreateProfile(DeviceProfile profile)
{ {
profile = ReserializeProfile(profile);
if (string.IsNullOrWhiteSpace(profile.Name))
{
throw new ArgumentException("Profile is missing Name");
}
var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
var path = Path.Combine(UserProfilesPath, newFilename);
_xmlSerializer.SerializeToFile(profile, path);
} }
public void UpdateProfile(DeviceProfile profile) public void UpdateProfile(DeviceProfile profile)
{ {
profile = ReserializeProfile(profile);
if (string.IsNullOrWhiteSpace(profile.Id))
{
throw new ArgumentException("Profile is missing Id");
}
if (string.IsNullOrWhiteSpace(profile.Name))
{
throw new ArgumentException("Profile is missing Name");
}
var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profile.Id, StringComparison.OrdinalIgnoreCase));
if (current.Info.Type == DeviceProfileType.System)
{
throw new ArgumentException("System profiles are readonly");
}
var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
var path = Path.Combine(UserProfilesPath, newFilename);
if (!string.Equals(path, current.Path, StringComparison.Ordinal))
{
File.Delete(current.Path);
}
_xmlSerializer.SerializeToFile(profile, path);
}
/// <summary>
/// Recreates the object using serialization, to ensure it's not a subclass.
/// If it's a subclass it may not serlialize properly to xml (different root element tag name)
/// </summary>
/// <param name="profile"></param>
/// <returns></returns>
private DeviceProfile ReserializeProfile(DeviceProfile profile)
{
if (profile.GetType() == typeof(DeviceProfile))
{
return profile;
}
var json = _jsonSerializer.SerializeToString(profile);
return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
} }
class InternalProfileInfo class InternalProfileInfo

@ -308,25 +308,6 @@ namespace MediaBrowser.Dlna.PlayTo
return Task.FromResult(true); return Task.FromResult(true);
} }
public Task SendSystemCommand(SystemCommand command, CancellationToken cancellationToken)
{
switch (command)
{
case SystemCommand.VolumeDown:
return _device.VolumeDown();
case SystemCommand.VolumeUp:
return _device.VolumeUp();
case SystemCommand.Mute:
return _device.VolumeDown(true);
case SystemCommand.Unmute:
return _device.VolumeUp(true);
case SystemCommand.ToggleMute:
return _device.ToggleMute();
default:
return Task.FromResult(true);
}
}
public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken) public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
{ {
return Task.FromResult(true); return Task.FromResult(true);
@ -620,9 +601,30 @@ namespace MediaBrowser.Dlna.PlayTo
} }
} }
public Task SendGenericCommand(GenericCommand command, CancellationToken cancellationToken) public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
{
GeneralCommandType commandType;
if (!Enum.TryParse(command.Name, true, out commandType))
{
switch (commandType)
{ {
throw new NotImplementedException(); case GeneralCommandType.VolumeDown:
return _device.VolumeDown();
case GeneralCommandType.VolumeUp:
return _device.VolumeUp();
case GeneralCommandType.Mute:
return _device.VolumeDown(true);
case GeneralCommandType.Unmute:
return _device.VolumeUp(true);
case GeneralCommandType.ToggleMute:
return _device.ToggleMute();
default:
return Task.FromResult(true);
}
}
return Task.FromResult(true);
} }
} }
} }

@ -1,5 +1,4 @@
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using System.Collections.Generic;
namespace MediaBrowser.Dlna.PlayTo namespace MediaBrowser.Dlna.PlayTo
{ {
@ -27,8 +26,6 @@ namespace MediaBrowser.Dlna.PlayTo
public string AudioCodec { get; set; } public string AudioCodec { get; set; }
public List<TranscodingSetting> TranscodingSettings { get; set; }
public int? AudioStreamIndex { get; set; } public int? AudioStreamIndex { get; set; }
public int? SubtitleStreamIndex { get; set; } public int? SubtitleStreamIndex { get; set; }
@ -47,10 +44,5 @@ namespace MediaBrowser.Dlna.PlayTo
public int? MaxFramerate { get; set; } public int? MaxFramerate { get; set; }
public string DeviceProfileId { get; set; } public string DeviceProfileId { get; set; }
public PlaylistItem()
{
TranscodingSettings = new List<TranscodingSetting>();
}
} }
} }

@ -48,7 +48,6 @@ namespace MediaBrowser.Dlna.PlayTo
if (transcodingProfile != null) if (transcodingProfile != null)
{ {
playlistItem.Transcode = true; playlistItem.Transcode = true;
playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList();
playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.'); playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
playlistItem.AudioCodec = transcodingProfile.AudioCodec; playlistItem.AudioCodec = transcodingProfile.AudioCodec;
@ -88,7 +87,6 @@ namespace MediaBrowser.Dlna.PlayTo
if (transcodingProfile != null) if (transcodingProfile != null)
{ {
playlistItem.Transcode = true; playlistItem.Transcode = true;
playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList();
playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.'); playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
} }
@ -137,7 +135,6 @@ namespace MediaBrowser.Dlna.PlayTo
if (transcodingProfile != null) if (transcodingProfile != null)
{ {
playlistItem.Transcode = true; playlistItem.Transcode = true;
playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList();
playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.'); playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault(); playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault();
playlistItem.VideoCodec = transcodingProfile.VideoCodec; playlistItem.VideoCodec = transcodingProfile.VideoCodec;

@ -35,11 +35,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video, Type = DlnaProfileType.Video,
AudioCodec = "aac", AudioCodec = "aac",
VideoCodec = "h264", VideoCodec = "h264",
VideoProfile= "baseline"
Settings = new []
{
new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
}
} }
}; };

@ -302,16 +302,16 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "avi", Container = "avi",
MimeType = "video/x-msvideo", MimeType = "video/x-msvideo",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mkv", Container = "mkv",
MimeType = "video/x-mkv", MimeType = "video/x-mkv",

@ -206,9 +206,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec = "h264,mpeg4,vc1", VideoCodec = "h264,mpeg4,vc1",
@ -218,42 +218,42 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "avi", Container = "avi",
MimeType = "video/mpeg", MimeType = "video/mpeg",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mkv", Container = "mkv",
MimeType = "video/vnd.dlna.mpeg-tts", MimeType = "video/vnd.dlna.mpeg-tts",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
MimeType = "video/vnd.dlna.mpeg-tts", MimeType = "video/vnd.dlna.mpeg-tts",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mp4", Container = "mp4",
MimeType = "video/mpeg", MimeType = "video/mpeg",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mpeg", Container = "mpeg",
MimeType = "video/mpeg", MimeType = "video/mpeg",
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mp3", Container = "mp3",
MimeType = "audio/mpeg", MimeType = "audio/mpeg",

@ -89,9 +89,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -101,7 +101,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -111,7 +111,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -121,7 +121,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="mpeg2video", VideoCodec="mpeg2video",
@ -130,7 +130,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mpeg", Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video", VideoCodec="mpeg1video,mpeg2video",

@ -131,9 +131,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -143,7 +143,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -153,7 +153,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -163,7 +163,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="mpeg2video", VideoCodec="mpeg2video",
@ -172,7 +172,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mpeg", Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video", VideoCodec="mpeg1video,mpeg2video",

@ -119,9 +119,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -131,7 +131,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -141,7 +141,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -151,7 +151,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="mpeg2video", VideoCodec="mpeg2video",
@ -160,7 +160,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mpeg", Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video", VideoCodec="mpeg1video,mpeg2video",

@ -175,9 +175,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -187,7 +187,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -197,7 +197,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="h264", VideoCodec="h264",
@ -207,7 +207,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
VideoCodec="mpeg2video", VideoCodec="mpeg2video",
@ -216,7 +216,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "mpeg", Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video", VideoCodec="mpeg1video,mpeg2video",

@ -207,9 +207,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "mp4,mov", Container = "mp4,mov",
AudioCodec="aac", AudioCodec="aac",
@ -217,7 +217,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "avi", Container = "avi",
MimeType = "video/divx", MimeType = "video/divx",
@ -225,7 +225,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video Type = DlnaProfileType.Video
}, },
new MediaProfile new ResponseProfile
{ {
Container = "wav", Container = "wav",
MimeType = "audio/wav", MimeType = "audio/wav",

@ -43,11 +43,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video, Type = DlnaProfileType.Video,
VideoCodec = "h264", VideoCodec = "h264",
AudioCodec = "aac", AudioCodec = "aac",
VideoProfile= "baseline"
Settings = new []
{
new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
}
}, },
new TranscodingProfile new TranscodingProfile
{ {
@ -157,9 +153,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "ts", Container = "ts",
OrgPn = "MPEG_TS_SD_NA", OrgPn = "MPEG_TS_SD_NA",

@ -48,11 +48,7 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video, Type = DlnaProfileType.Video,
TranscodeSeekInfo = TranscodeSeekInfo.Bytes, TranscodeSeekInfo = TranscodeSeekInfo.Bytes,
EstimateContentLength = true, EstimateContentLength = true,
VideoProfile= "baseline"
Settings = new []
{
new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
}
}, },
new TranscodingProfile new TranscodingProfile
{ {
@ -110,9 +106,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "avi", Container = "avi",
MimeType = "video/avi", MimeType = "video/avi",

@ -42,9 +42,9 @@ namespace MediaBrowser.Dlna.Profiles
} }
}; };
MediaProfiles = new[] ResponseProfiles = new[]
{ {
new MediaProfile new ResponseProfile
{ {
Container = "avi", Container = "avi",
MimeType = "video/x-msvideo", MimeType = "video/x-msvideo",

@ -20,16 +20,10 @@
<DirectPlayProfile container="avi,mp4" type="Video" /> <DirectPlayProfile container="avi,mp4" type="Video" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" videoProfile="baseline" />
</TranscodingProfile>
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings>
<TranscodingSetting name="VideoProfile" value="baseline" />
</Settings>
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles /> <ContainerProfiles />
<CodecProfiles /> <CodecProfiles />
<MediaProfiles /> <ResponseProfiles />
</Profile> </Profile>

@ -24,16 +24,10 @@
<DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" /> <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" videoProfile="baseline" />
</TranscodingProfile>
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings>
<TranscodingSetting name="VideoProfile" value="baseline" />
</Settings>
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles /> <ContainerProfiles />
<CodecProfiles /> <CodecProfiles />
<MediaProfiles /> <ResponseProfiles />
</Profile> </Profile>

@ -29,15 +29,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -69,5 +63,5 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles /> <ResponseProfiles />
</Profile> </Profile>

@ -24,16 +24,10 @@
<DirectPlayProfile container="avi,mp4,mkv,ts" type="Video" /> <DirectPlayProfile container="avi,mp4,mkv,ts" type="Video" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" videoProfile="baseline" />
</TranscodingProfile>
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings>
<TranscodingSetting name="VideoProfile" value="baseline" />
</Settings>
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles /> <ContainerProfiles />
<CodecProfiles /> <CodecProfiles />
<MediaProfiles /> <ResponseProfiles />
</Profile> </Profile>

@ -35,15 +35,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -62,5 +56,5 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles /> <ResponseProfiles />
</Profile> </Profile>

@ -33,15 +33,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -91,12 +85,12 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="avi" type="Video" mimeType="video/x-msvideo"> <ResponseProfile container="avi" type="Video" mimeType="video/x-msvideo">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mkv" type="Video" mimeType="video/x-mkv"> <ResponseProfile container="mkv" type="Video" mimeType="video/x-mkv">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -33,15 +33,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -65,5 +59,5 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles /> <ResponseProfiles />
</Profile> </Profile>

@ -32,15 +32,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="mpeg2video" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="mpeg2video" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -71,27 +65,27 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4,vc1" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4,vc1" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="avi" type="Video" mimeType="video/mpeg"> <ResponseProfile container="avi" type="Video" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mkv" type="Video" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="mkv" type="Video" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" type="Video" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" type="Video" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mp4" type="Video" mimeType="video/mpeg"> <ResponseProfile container="mp4" type="Video" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mpeg" type="Video" mimeType="video/mpeg"> <ResponseProfile container="mpeg" type="Video" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mp3" type="Audio" mimeType="audio/mpeg"> <ResponseProfile container="mp3" type="Audio" mimeType="audio/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -30,15 +30,9 @@
<DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" /> <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -80,21 +74,21 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -33,15 +33,9 @@
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" /> <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -83,21 +77,21 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -35,15 +35,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -66,21 +60,21 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -40,15 +40,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -66,21 +60,21 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts"> <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg"> <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -29,15 +29,9 @@
<DirectPlayProfile container="avi,mp4" type="Video" /> <DirectPlayProfile container="avi,mp4" type="Video" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -80,15 +74,15 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="mp4,mov" audioCodec="aac" type="Video" mimeType="video/mp4"> <ResponseProfile container="mp4,mov" audioCodec="aac" type="Video" mimeType="video/mp4">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="avi" type="Video" orgPn="AVI" mimeType="video/divx"> <ResponseProfile container="avi" type="Video" orgPn="AVI" mimeType="video/divx">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
<MediaProfile container="wav" type="Audio" mimeType="audio/wav"> <ResponseProfile container="wav" type="Audio" mimeType="audio/wav">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -38,17 +38,9 @@
<DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" /> <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" videoProfile="baseline" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings>
<TranscodingSetting name="VideoProfile" value="baseline" />
</Settings>
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Photo"> <ContainerProfile type="Photo">
@ -72,9 +64,9 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="ts" type="Video" orgPn="MPEG_TS_SD_NA"> <ResponseProfile container="ts" type="Video" orgPn="MPEG_TS_SD_NA">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -33,17 +33,9 @@
<DirectPlayProfile container="jpeg" type="Photo" /> <DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="asf" type="Video" videoCodec="wmv2" audioCodec="wmav2" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" videoProfile="baseline" />
</TranscodingProfile> <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<TranscodingProfile container="asf" type="Video" videoCodec="wmv2" audioCodec="wmav2" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes">
<Settings>
<TranscodingSetting name="VideoProfile" value="baseline" />
</Settings>
</TranscodingProfile>
<TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles> <ContainerProfiles>
<ContainerProfile type="Video" container="mp4,mov"> <ContainerProfile type="Video" container="mp4,mov">
@ -95,9 +87,9 @@
</Conditions> </Conditions>
</CodecProfile> </CodecProfile>
</CodecProfiles> </CodecProfiles>
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="avi" type="Video" mimeType="video/avi"> <ResponseProfile container="avi" type="Video" mimeType="video/avi">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -24,18 +24,14 @@
<DirectPlayProfile container="mp3,wma" type="Audio" /> <DirectPlayProfile container="mp3,wma" type="Audio" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
</TranscodingProfile>
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings />
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles /> <ContainerProfiles />
<CodecProfiles /> <CodecProfiles />
<MediaProfiles> <ResponseProfiles>
<MediaProfile container="avi" type="Video" mimeType="video/x-msvideo"> <ResponseProfile container="avi" type="Video" mimeType="video/x-msvideo">
<Conditions /> <Conditions />
</MediaProfile> </ResponseProfile>
</MediaProfiles> </ResponseProfiles>
</Profile> </Profile>

@ -26,16 +26,10 @@
<DirectPlayProfile container="avi,mp4" type="Video" /> <DirectPlayProfile container="avi,mp4" type="Video" />
</DirectPlayProfiles> </DirectPlayProfiles>
<TranscodingProfiles> <TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto"> <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" />
<Settings /> <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" videoProfile="baseline" />
</TranscodingProfile>
<TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto">
<Settings>
<TranscodingSetting name="VideoProfile" value="baseline" />
</Settings>
</TranscodingProfile>
</TranscodingProfiles> </TranscodingProfiles>
<ContainerProfiles /> <ContainerProfiles />
<CodecProfiles /> <CodecProfiles />
<MediaProfiles /> <ResponseProfiles />
</Profile> </Profile>

@ -0,0 +1,91 @@
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());
}
}
}

@ -0,0 +1,235 @@
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace MediaBrowser.MediaEncoding.Encoder
{
public static class EncodingUtils
{
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
public static string GetInputArgument(List<string> inputFiles, bool isRemote)
{
if (isRemote)
{
return GetHttpInputArgument(inputFiles);
}
return GetConcatInputArgument(inputFiles);
}
/// <summary>
/// Gets the concat input argument.
/// </summary>
/// <param name="inputFiles">The input files.</param>
/// <returns>System.String.</returns>
private static string GetConcatInputArgument(List<string> inputFiles)
{
// Get all streams
// If there's more than one we'll need to use the concat command
if (inputFiles.Count > 1)
{
var files = string.Join("|", inputFiles);
return string.Format("concat:\"{0}\"", files);
}
// Determine the input path for video files
return GetFileInputArgument(inputFiles[0]);
}
/// <summary>
/// Gets the file input argument.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>System.String.</returns>
private static string GetFileInputArgument(string path)
{
return string.Format("file:\"{0}\"", path);
}
/// <summary>
/// Gets the HTTP input argument.
/// </summary>
/// <param name="inputFiles">The input files.</param>
/// <returns>System.String.</returns>
private static string GetHttpInputArgument(IEnumerable<string> inputFiles)
{
var url = inputFiles.First();
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;
if (time.HasValue)
{
var seconds = TimeSpan.FromTicks(time.Value).TotalSeconds;
if (seconds > 0)
{
return string.Format("-ss {0}", seconds.ToString(UsCulture));
}
}
return string.Empty;
}
public static string GetProbeSizeArgument(bool isDvd)
{
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>
/// <param name="request">The request.</param>
/// <param name="audioStream">The audio stream.</param>
/// <returns>System.Nullable{System.Int32}.</returns>
public static int? GetNumAudioChannelsParam(EncodingOptions request, MediaStream audioStream)
{
if (audioStream != null)
{
var codec = request.AudioCodec ?? string.Empty;
if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
{
// wmav2 currently only supports two channel output
return 2;
}
}
if (request.MaxAudioChannels.HasValue)
{
if (audioStream != null && audioStream.Channels.HasValue)
{
return Math.Min(request.MaxAudioChannels.Value, audioStream.Channels.Value);
}
return request.MaxAudioChannels.Value;
}
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, 1);
}
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 ? 2 : 0;
default:
throw new Exception("Unrecognized MediaEncodingQuality value.");
}
}
}
}

@ -0,0 +1,168 @@
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;
}
}
}
}

@ -0,0 +1,95 @@
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()
{
}
}
}

@ -0,0 +1,311 @@
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, state.AudioStream) :
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;
}
}
}

@ -6,10 +6,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -122,35 +122,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <exception cref="System.ArgumentException">Unrecognized InputType</exception> /// <exception cref="System.ArgumentException">Unrecognized InputType</exception>
public string GetInputArgument(string[] inputFiles, InputType type) public string GetInputArgument(string[] inputFiles, InputType type)
{ {
string inputPath; return EncodingUtils.GetInputArgument(inputFiles.ToList(), type == InputType.Url);
switch (type)
{
case InputType.Bluray:
case InputType.Dvd:
case InputType.File:
inputPath = GetConcatInputArgument(inputFiles);
break;
case InputType.Url:
inputPath = GetHttpInputArgument(inputFiles);
break;
default:
throw new ArgumentException("Unrecognized InputType");
}
return inputPath;
}
/// <summary>
/// Gets the HTTP input argument.
/// </summary>
/// <param name="inputFiles">The input files.</param>
/// <returns>System.String.</returns>
private string GetHttpInputArgument(string[] inputFiles)
{
var url = inputFiles[0];
return string.Format("\"{0}\"", url);
} }
/// <summary> /// <summary>
@ -160,7 +132,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
public string GetProbeSizeArgument(InputType type) public string GetProbeSizeArgument(InputType type)
{ {
return type == InputType.Dvd ? "-probesize 1G -analyzeduration 200M" : string.Empty; return EncodingUtils.GetProbeSizeArgument(type == InputType.Dvd);
} }
/// <summary> /// <summary>
@ -796,7 +768,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
} }
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=80\" -f image2 \"{1}\"", inputPath, "-", vf) : var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=50\" -f image2 \"{1}\"", inputPath, "-", vf) :
string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf);
var probeSize = GetProbeSizeArgument(type); var probeSize = GetProbeSizeArgument(type);
@ -879,36 +851,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
return memoryStream; return memoryStream;
} }
/// <summary>
/// Gets the file input argument.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>System.String.</returns>
private string GetFileInputArgument(string path)
{
return string.Format("file:\"{0}\"", path);
}
/// <summary>
/// Gets the concat input argument.
/// </summary>
/// <param name="playableStreamFiles">The playable stream files.</param>
/// <returns>System.String.</returns>
private string GetConcatInputArgument(string[] playableStreamFiles)
{
// Get all streams
// If there's more than one we'll need to use the concat command
if (playableStreamFiles.Length > 1)
{
var files = string.Join("|", playableStreamFiles);
return string.Format("concat:\"{0}\"", files);
}
// Determine the input path for video files
return GetFileInputArgument(playableStreamFiles[0]);
}
public Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken) public Task<Stream> EncodeImage(ImageEncodingOptions options, CancellationToken cancellationToken)
{ {
return new ImageEncoder(FFMpegPath, _logger, _fileSystem, _appPaths).EncodeImage(options, cancellationToken); return new ImageEncoder(FFMpegPath, _logger, _fileSystem, _appPaths).EncodeImage(options, cancellationToken);

@ -48,7 +48,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="BdInfo\BdInfoExaminer.cs" /> <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\ImageEncoder.cs" />
<Compile Include="Encoder\InternalEncodingTask.cs" />
<Compile Include="Encoder\InternalEncodingTaskFactory.cs" />
<Compile Include="Encoder\MediaEncoder.cs" /> <Compile Include="Encoder\MediaEncoder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

@ -428,8 +428,8 @@
<Compile Include="..\MediaBrowser.Model\Session\BrowseRequest.cs"> <Compile Include="..\MediaBrowser.Model\Session\BrowseRequest.cs">
<Link>Session\BrowseRequest.cs</Link> <Link>Session\BrowseRequest.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Session\GenericCommand.cs"> <Compile Include="..\MediaBrowser.Model\Session\GeneralCommand.cs">
<Link>Session\GenericCommand.cs</Link> <Link>Session\GeneralCommand.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs"> <Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
<Link>Session\MessageCommand.cs</Link> <Link>Session\MessageCommand.cs</Link>
@ -449,9 +449,6 @@
<Compile Include="..\MediaBrowser.Model\Session\SessionInfoDto.cs"> <Compile Include="..\MediaBrowser.Model\Session\SessionInfoDto.cs">
<Link>Session\SessionInfoDto.cs</Link> <Link>Session\SessionInfoDto.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Session\SystemCommand.cs">
<Link>Session\SystemCommand.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs"> <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
<Link>Session\UserDataChangeInfo.cs</Link> <Link>Session\UserDataChangeInfo.cs</Link>
</Compile> </Compile>

@ -415,8 +415,8 @@
<Compile Include="..\MediaBrowser.Model\Session\BrowseRequest.cs"> <Compile Include="..\MediaBrowser.Model\Session\BrowseRequest.cs">
<Link>Session\BrowseRequest.cs</Link> <Link>Session\BrowseRequest.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Session\GenericCommand.cs"> <Compile Include="..\MediaBrowser.Model\Session\GeneralCommand.cs">
<Link>Session\GenericCommand.cs</Link> <Link>Session\GeneralCommand.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs"> <Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
<Link>Session\MessageCommand.cs</Link> <Link>Session\MessageCommand.cs</Link>
@ -436,9 +436,6 @@
<Compile Include="..\MediaBrowser.Model\Session\SessionInfoDto.cs"> <Compile Include="..\MediaBrowser.Model\Session\SessionInfoDto.cs">
<Link>Session\SessionInfoDto.cs</Link> <Link>Session\SessionInfoDto.cs</Link>
</Compile> </Compile>
<Compile Include="..\MediaBrowser.Model\Session\SystemCommand.cs">
<Link>Session\SystemCommand.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs"> <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
<Link>Session\UserDataChangeInfo.cs</Link> <Link>Session\UserDataChangeInfo.cs</Link>
</Compile> </Compile>

@ -593,17 +593,9 @@ namespace MediaBrowser.Model.ApiClient
/// Sends the command asynchronous. /// Sends the command asynchronous.
/// </summary> /// </summary>
/// <param name="sessionId">The session identifier.</param> /// <param name="sessionId">The session identifier.</param>
/// <param name="request">The request.</param>
/// <returns>Task.</returns>
Task SendCommandAsync(string sessionId, GenericCommand request);
/// <summary>
/// Sends a system command to the client
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param> /// <param name="command">The command.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SendSystemCommandAsync(string sessionId, SystemCommand command); Task SendCommandAsync(string sessionId, GeneralCommand command);
/// <summary> /// <summary>
/// Instructs the client to display a message to the user /// Instructs the client to display a message to the user

@ -66,7 +66,7 @@ namespace MediaBrowser.Model.ApiClient
/// <summary> /// <summary>
/// Occurs when [system command]. /// Occurs when [system command].
/// </summary> /// </summary>
event EventHandler<SystemCommandEventArgs> SystemCommand; event EventHandler<GeneralCommandEventArgs> GeneralCommand;
/// <summary> /// <summary>
/// Occurs when [notification added]. /// Occurs when [notification added].
/// </summary> /// </summary>

@ -152,13 +152,19 @@ namespace MediaBrowser.Model.ApiClient
/// <summary> /// <summary>
/// Class SystemCommandEventArgs /// Class SystemCommandEventArgs
/// </summary> /// </summary>
public class SystemCommandEventArgs : EventArgs public class GeneralCommandEventArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets or sets the command. /// Gets or sets the command.
/// </summary> /// </summary>
/// <value>The command.</value> /// <value>The command.</value>
public SystemCommand Command { get; set; } public GeneralCommand Command { get; set; }
/// <summary>
/// Gets or sets the type of the known command.
/// </summary>
/// <value>The type of the known command.</value>
public GeneralCommandType? KnownCommandType { get; set; }
} }
/// <summary> /// <summary>

@ -218,6 +218,8 @@ namespace MediaBrowser.Model.Configuration
public string ServerName { get; set; } public string ServerName { get; set; }
public string WanDdns { get; set; } public string WanDdns { get; set; }
public string UICulture { get; set; }
public DlnaOptions DlnaOptions { get; set; } public DlnaOptions DlnaOptions { get; set; }
/// <summary> /// <summary>
@ -281,6 +283,8 @@ namespace MediaBrowser.Model.Configuration
MetadataOptions = options.ToArray(); MetadataOptions = options.ToArray();
DlnaOptions = new DlnaOptions(); DlnaOptions = new DlnaOptions();
UICulture = "en-us";
} }
} }

@ -30,4 +30,10 @@ namespace MediaBrowser.Model.Globalization
/// <value>The name of the three letter ISO region.</value> /// <value>The name of the three letter ISO region.</value>
public string ThreeLetterISORegionName { get; set; } public string ThreeLetterISORegionName { get; set; }
} }
public class LocalizatonOption
{
public string Name { get; set; }
public string Value { get; set; }
}
} }

@ -33,6 +33,8 @@ namespace MediaBrowser.Model.LiveTv
/// <value>The external identifier.</value> /// <value>The external identifier.</value>
public string ExternalId { get; set; } public string ExternalId { get; set; }
public List<MediaSourceInfo> MediaSources { get; set; }
/// <summary> /// <summary>
/// Gets or sets the image tags. /// Gets or sets the image tags.
/// </summary> /// </summary>
@ -112,6 +114,7 @@ namespace MediaBrowser.Model.LiveTv
public ChannelInfoDto() public ChannelInfoDto()
{ {
ImageTags = new Dictionary<ImageType, Guid>(); ImageTags = new Dictionary<ImageType, Guid>();
MediaSources = new List<MediaSourceInfo>();
} }
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;

@ -12,6 +12,12 @@ namespace MediaBrowser.Model.LiveTv
/// <value>The type of the channel.</value> /// <value>The type of the channel.</value>
public ChannelType? ChannelType { get; set; } public ChannelType? ChannelType { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is favorite.
/// </summary>
/// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value>
public bool? IsFavorite { get; set; }
/// <summary> /// <summary>
/// Gets or sets the user identifier. /// Gets or sets the user identifier.
/// </summary> /// </summary>

@ -1,11 +1,11 @@
using System.Diagnostics; using MediaBrowser.Model.Dto;
using System.Runtime.Serialization;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using MediaBrowser.Model.Library; using System.Diagnostics;
using System.Runtime.Serialization;
namespace MediaBrowser.Model.LiveTv namespace MediaBrowser.Model.LiveTv
{ {
@ -248,10 +248,13 @@ namespace MediaBrowser.Model.LiveTv
/// <value>The type.</value> /// <value>The type.</value>
public string Type { get; set; } public string Type { get; set; }
public List<MediaSourceInfo> MediaSources { get; set; }
public RecordingInfoDto() public RecordingInfoDto()
{ {
Genres = new List<string>(); Genres = new List<string>();
ImageTags = new Dictionary<ImageType, Guid>(); ImageTags = new Dictionary<ImageType, Guid>();
MediaSources = new List<MediaSourceInfo>();
} }
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;

@ -132,7 +132,7 @@
<Compile Include="Querying\UserQuery.cs" /> <Compile Include="Querying\UserQuery.cs" />
<Compile Include="Search\SearchQuery.cs" /> <Compile Include="Search\SearchQuery.cs" />
<Compile Include="Session\BrowseRequest.cs" /> <Compile Include="Session\BrowseRequest.cs" />
<Compile Include="Session\GenericCommand.cs" /> <Compile Include="Session\GeneralCommand.cs" />
<Compile Include="Session\MessageCommand.cs" /> <Compile Include="Session\MessageCommand.cs" />
<Compile Include="Session\PlaybackReports.cs" /> <Compile Include="Session\PlaybackReports.cs" />
<Compile Include="Session\PlayRequest.cs" /> <Compile Include="Session\PlayRequest.cs" />
@ -171,7 +171,6 @@
<Compile Include="Serialization\IXmlSerializer.cs" /> <Compile Include="Serialization\IXmlSerializer.cs" />
<Compile Include="Session\SessionCapabilities.cs" /> <Compile Include="Session\SessionCapabilities.cs" />
<Compile Include="Session\SessionInfoDto.cs" /> <Compile Include="Session\SessionInfoDto.cs" />
<Compile Include="Session\SystemCommand.cs" />
<Compile Include="Session\UserDataChangeInfo.cs" /> <Compile Include="Session\UserDataChangeInfo.cs" />
<Compile Include="Themes\AppTheme.cs" /> <Compile Include="Themes\AppTheme.cs" />
<Compile Include="Themes\ThemeImage.cs" /> <Compile Include="Themes\ThemeImage.cs" />

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Model.Session namespace MediaBrowser.Model.Session
{ {
public class GenericCommand public class GeneralCommand
{ {
public string Name { get; set; } public string Name { get; set; }
@ -11,7 +11,7 @@ namespace MediaBrowser.Model.Session
public Dictionary<string, string> Arguments { get; set; } public Dictionary<string, string> Arguments { get; set; }
public GenericCommand() public GeneralCommand()
{ {
Arguments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); Arguments = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} }
@ -20,7 +20,7 @@ namespace MediaBrowser.Model.Session
/// <summary> /// <summary>
/// This exists simply to identify a set of known commands. /// This exists simply to identify a set of known commands.
/// </summary> /// </summary>
public enum CoreGenericCommand public enum GeneralCommandType
{ {
MoveUp = 0, MoveUp = 0,
MoveDown = 1, MoveDown = 1,

@ -39,14 +39,22 @@ namespace MediaBrowser.Model.Session
/// <summary> /// <summary>
/// The play now /// The play now
/// </summary> /// </summary>
PlayNow, PlayNow = 0,
/// <summary> /// <summary>
/// The play next /// The play next
/// </summary> /// </summary>
PlayNext, PlayNext = 1,
/// <summary> /// <summary>
/// The play last /// The play last
/// </summary> /// </summary>
PlayLast PlayLast = 2,
/// <summary>
/// The play instant mix
/// </summary>
PlayInstantMix = 3,
/// <summary>
/// The play shuffle
/// </summary>
PlayShuffle = 4
} }
} }

@ -1,14 +0,0 @@

namespace MediaBrowser.Model.Session
{
public enum SystemCommand
{
GoHome,
GoToSettings,
VolumeUp,
VolumeDown,
Mute,
Unmute,
ToggleMute
}
}

@ -116,6 +116,12 @@ namespace MediaBrowser.Model.System
/// <value>The log path.</value> /// <value>The log path.</value>
public string LogPath { get; set; } public string LogPath { get; set; }
/// <summary>
/// Gets or sets the internal metadata path.
/// </summary>
/// <value>The internal metadata path.</value>
public string InternalMetadataPath { get; set; }
/// <summary> /// <summary>
/// Gets or sets the transcoding temporary path. /// Gets or sets the transcoding temporary path.
/// </summary> /// </summary>

@ -174,11 +174,21 @@ namespace MediaBrowser.Providers.TV
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task<bool> AddMissingEpisodes(List<Series> series, string seriesDataPath, IEnumerable<Tuple<int, int>> episodeLookup, CancellationToken cancellationToken) private async Task<bool> AddMissingEpisodes(List<Series> series, string seriesDataPath, IEnumerable<Tuple<int, int>> episodeLookup, CancellationToken cancellationToken)
{ {
var existingEpisodes = series.SelectMany(s => s.RecursiveChildren.OfType<Episode>()).ToList(); var existingEpisodes = (from s in series
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
from c in s.RecursiveChildren.OfType<Episode>()
select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0) + seasonOffset, c))
.ToList();
var lookup = episodeLookup as IList<Tuple<int, int>> ?? episodeLookup.ToList();
var seasonCounts = (from e in lookup
group e by e.Item1 into g select g)
.ToDictionary(g => g.Key, g => g.Count());
var hasChanges = false; var hasChanges = false;
foreach (var tuple in episodeLookup) foreach (var tuple in lookup)
{ {
if (tuple.Item1 <= 0) if (tuple.Item1 <= 0)
{ {
@ -192,7 +202,7 @@ namespace MediaBrowser.Providers.TV
continue; continue;
} }
var existingEpisode = GetExistingEpisode(existingEpisodes, tuple); var existingEpisode = GetExistingEpisode(existingEpisodes, seasonCounts, tuple);
if (existingEpisode != null) if (existingEpisode != null)
{ {
@ -208,13 +218,13 @@ namespace MediaBrowser.Providers.TV
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var targetSeries = DetermineAppropriateSeries(series, tuple.Item1); var targetSeries = DetermineAppropriateSeries(series, tuple.Item1);
var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(targetSeries.ProviderIds) ?? ((targetSeries.AnimeSeriesIndex ?? 1) - 1);
if (airDate.Value < now) if (airDate.Value < now)
{ {
// tvdb has a lot of nearly blank episodes // tvdb has a lot of nearly blank episodes
_logger.Info("Creating virtual missing episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2); _logger.Info("Creating virtual missing episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
await AddEpisode(targetSeries, tuple.Item1 - seasonOffset, tuple.Item2, cancellationToken).ConfigureAwait(false);
await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
hasChanges = true; hasChanges = true;
} }
@ -222,8 +232,7 @@ namespace MediaBrowser.Providers.TV
{ {
// tvdb has a lot of nearly blank episodes // tvdb has a lot of nearly blank episodes
_logger.Info("Creating virtual unaired episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2); _logger.Info("Creating virtual unaired episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
await AddEpisode(targetSeries, tuple.Item1 - seasonOffset, tuple.Item2, cancellationToken).ConfigureAwait(false);
await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
hasChanges = true; hasChanges = true;
} }
@ -232,11 +241,15 @@ namespace MediaBrowser.Providers.TV
return hasChanges; return hasChanges;
} }
private Series DetermineAppropriateSeries(List<Series> series, int seasonNumber) private Series DetermineAppropriateSeries(IEnumerable<Series> series, int seasonNumber)
{ {
return series.FirstOrDefault(s => s.RecursiveChildren.OfType<Season>().Any(season => season.IndexNumber == seasonNumber)) ?? var seriesAndOffsets = series.Select(s => new { Series = s, SeasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) }).ToList();
series.FirstOrDefault(s => s.RecursiveChildren.OfType<Season>().Any(season => season.IndexNumber == 1)) ??
series.OrderBy(s => s.RecursiveChildren.OfType<Season>().Select(season => season.IndexNumber).Min()).First(); var bestMatch = seriesAndOffsets.FirstOrDefault(s => s.Series.RecursiveChildren.OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == seasonNumber)) ??
seriesAndOffsets.FirstOrDefault(s => s.Series.RecursiveChildren.OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == 1)) ??
seriesAndOffsets.OrderBy(s => s.Series.RecursiveChildren.OfType<Season>().Select(season => season.IndexNumber + s.SeasonOffset).Min()).First();
return bestMatch.Series;
} }
/// <summary> /// <summary>
@ -244,28 +257,32 @@ namespace MediaBrowser.Providers.TV
/// </summary> /// </summary>
private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series, IEnumerable<Tuple<int, int>> episodeLookup, CancellationToken cancellationToken) private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series, IEnumerable<Tuple<int, int>> episodeLookup, CancellationToken cancellationToken)
{ {
var existingEpisodes = series.SelectMany(s => s.RecursiveChildren.OfType<Episode>()).ToList(); var existingEpisodes = (from s in series
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
from c in s.RecursiveChildren.OfType<Episode>()
select new { SeasonOffset = seasonOffset, Episode = c })
.ToList();
var physicalEpisodes = existingEpisodes var physicalEpisodes = existingEpisodes
.Where(i => i.LocationType != LocationType.Virtual) .Where(i => i.Episode.LocationType != LocationType.Virtual)
.ToList(); .ToList();
var virtualEpisodes = existingEpisodes var virtualEpisodes = existingEpisodes
.Where(i => i.LocationType == LocationType.Virtual) .Where(i => i.Episode.LocationType == LocationType.Virtual)
.ToList(); .ToList();
var episodesToRemove = virtualEpisodes var episodesToRemove = virtualEpisodes
.Where(i => .Where(i =>
{ {
if (i.IndexNumber.HasValue && i.ParentIndexNumber.HasValue) if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
{ {
var seasonNumber = i.ParentIndexNumber.Value; var seasonNumber = i.Episode.ParentIndexNumber.Value + i.SeasonOffset;
var episodeNumber = i.IndexNumber.Value; var episodeNumber = i.Episode.IndexNumber.Value;
// If there's a physical episode with the same season and episode number, delete it // If there's a physical episode with the same season and episode number, delete it
if (physicalEpisodes.Any(p => if (physicalEpisodes.Any(p =>
p.ParentIndexNumber.HasValue && p.ParentIndexNumber.Value == seasonNumber && p.Episode.ParentIndexNumber.HasValue && (p.Episode.ParentIndexNumber.Value + p.SeasonOffset) == seasonNumber &&
p.ContainsEpisodeNumber(episodeNumber))) p.Episode.ContainsEpisodeNumber(episodeNumber)))
{ {
return true; return true;
} }
@ -285,7 +302,7 @@ namespace MediaBrowser.Providers.TV
var hasChanges = false; var hasChanges = false;
foreach (var episodeToRemove in episodesToRemove) foreach (var episodeToRemove in episodesToRemove.Select(e => e.Episode))
{ {
_logger.Info("Removing missing/unaired episode {0} {1}x{2}", episodeToRemove.Series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber); _logger.Info("Removing missing/unaired episode {0} {1}x{2}", episodeToRemove.Series.Name, episodeToRemove.ParentIndexNumber, episodeToRemove.IndexNumber);
@ -306,25 +323,29 @@ namespace MediaBrowser.Providers.TV
/// <returns>Task{System.Boolean}.</returns> /// <returns>Task{System.Boolean}.</returns>
private async Task<bool> RemoveObsoleteOrMissingSeasons(IEnumerable<Series> series, IEnumerable<Tuple<int, int>> episodeLookup, CancellationToken cancellationToken) private async Task<bool> RemoveObsoleteOrMissingSeasons(IEnumerable<Series> series, IEnumerable<Tuple<int, int>> episodeLookup, CancellationToken cancellationToken)
{ {
var existingSeasons = series.SelectMany(s => s.Children.OfType<Season>()).ToList(); var existingSeasons = (from s in series
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
from c in s.Children.OfType<Season>()
select new { SeasonOffset = seasonOffset, Season = c })
.ToList();
var physicalSeasons = existingSeasons var physicalSeasons = existingSeasons
.Where(i => i.LocationType != LocationType.Virtual) .Where(i => i.Season.LocationType != LocationType.Virtual)
.ToList(); .ToList();
var virtualSeasons = existingSeasons var virtualSeasons = existingSeasons
.Where(i => i.LocationType == LocationType.Virtual) .Where(i => i.Season.LocationType == LocationType.Virtual)
.ToList(); .ToList();
var seasonsToRemove = virtualSeasons var seasonsToRemove = virtualSeasons
.Where(i => .Where(i =>
{ {
if (i.IndexNumber.HasValue) if (i.Season.IndexNumber.HasValue)
{ {
var seasonNumber = i.IndexNumber.Value; var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset;
// If there's a physical season with the same number, delete it // If there's a physical season with the same number, delete it
if (physicalSeasons.Any(p => p.IndexNumber.HasValue && p.IndexNumber.Value == seasonNumber)) if (physicalSeasons.Any(p => p.Season.IndexNumber.HasValue && (p.Season.IndexNumber.Value + p.SeasonOffset) == seasonNumber))
{ {
return true; return true;
} }
@ -344,7 +365,7 @@ namespace MediaBrowser.Providers.TV
var hasChanges = false; var hasChanges = false;
foreach (var seasonToRemove in seasonsToRemove) foreach (var seasonToRemove in seasonsToRemove.Select(s => s.Season))
{ {
_logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber); _logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber);
@ -417,9 +438,7 @@ namespace MediaBrowser.Providers.TV
await series.AddChild(season, cancellationToken).ConfigureAwait(false); await series.AddChild(season, cancellationToken).ConfigureAwait(false);
await season.RefreshMetadata(new MetadataRefreshOptions await season.RefreshMetadata(new MetadataRefreshOptions(), cancellationToken).ConfigureAwait(false);
{
}, cancellationToken).ConfigureAwait(false);
return season; return season;
} }
@ -428,12 +447,37 @@ namespace MediaBrowser.Providers.TV
/// Gets the existing episode. /// Gets the existing episode.
/// </summary> /// </summary>
/// <param name="existingEpisodes">The existing episodes.</param> /// <param name="existingEpisodes">The existing episodes.</param>
/// <param name="seasonCounts"></param>
/// <param name="tuple">The tuple.</param> /// <param name="tuple">The tuple.</param>
/// <returns>Episode.</returns> /// <returns>Episode.</returns>
private Episode GetExistingEpisode(IEnumerable<Episode> existingEpisodes, Tuple<int, int> tuple) private Episode GetExistingEpisode(IList<Tuple<int, Episode>> existingEpisodes, Dictionary<int, int> seasonCounts, Tuple<int, int> tuple)
{
var s = tuple.Item1;
var e = tuple.Item2;
while (true)
{
var episode = GetExistingEpisode(existingEpisodes, s, e);
if (episode != null)
return episode;
s--;
if (seasonCounts.ContainsKey(s))
e += seasonCounts[s];
else
break;
}
return null;
}
private static Episode GetExistingEpisode(IEnumerable<Tuple<int, Episode>> existingEpisodes, int season, int episode)
{ {
return existingEpisodes return existingEpisodes
.FirstOrDefault(i => (i.ParentIndexNumber ?? -1) == tuple.Item1 && i.ContainsEpisodeNumber(tuple.Item2)); .Where(i => i.Item1 == season && i.Item2.ContainsEpisodeNumber(episode))
.Select(i => i.Item2)
.FirstOrDefault();
} }
/// <summary> /// <summary>

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -11,6 +12,11 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
class SeriesGroup : List<Series>, IGrouping<string, Series>
{
public string Key { get; set; }
}
class SeriesPostScanTask : ILibraryPostScanTask, IHasOrder class SeriesPostScanTask : ILibraryPostScanTask, IHasOrder
{ {
/// <summary> /// <summary>
@ -39,11 +45,7 @@ namespace MediaBrowser.Providers.TV
.OfType<Series>() .OfType<Series>()
.ToList(); .ToList();
var seriesGroups = from series in seriesList var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
let tvdbId = series.GetProviderId(MetadataProviders.Tvdb)
where !string.IsNullOrEmpty(tvdbId)
group series by tvdbId into g
select g;
await new MissingEpisodeProvider(_logger, _config, _libraryManager).Run(seriesGroups, cancellationToken).ConfigureAwait(false); await new MissingEpisodeProvider(_logger, _config, _libraryManager).Run(seriesGroups, cancellationToken).ConfigureAwait(false);
@ -84,6 +86,51 @@ namespace MediaBrowser.Providers.TV
} }
} }
private IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList)
{
var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList());
var visited = new HashSet<Series>();
foreach (var series in seriesList)
{
if (!visited.Contains(series))
{
var group = new SeriesGroup();
FindAllLinked(series, visited, links, group);
group.Key = group.Select(s => s.GetProviderId(MetadataProviders.Tvdb)).FirstOrDefault(id => !string.IsNullOrEmpty(id));
yield return group;
}
}
}
private void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results)
{
results.Add(series);
visited.Add(series);
var links = linksMap[series];
foreach (var s in links)
{
if (!visited.Contains(s))
{
FindAllLinked(s, visited, linksMap, results);
}
}
}
private bool ShareProviderId(Series a, Series b)
{
return a.ProviderIds.Any(id =>
{
string value;
return b.ProviderIds.TryGetValue(id.Key, out value) && id.Value == value;
});
}
public int Order public int Order
{ {
get get

@ -25,7 +25,8 @@ namespace MediaBrowser.Providers.TV
{ {
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{ {
internal const string TvdbSeriesOffset = "TvdbSeriesOffset"; private const string TvdbSeriesOffset = "TvdbSeriesOffset";
private const string TvdbSeriesOffsetFormat = "{0}-{1}";
internal readonly SemaphoreSlim TvDbResourcePool = new SemaphoreSlim(2, 2); internal readonly SemaphoreSlim TvDbResourcePool = new SemaphoreSlim(2, 2);
internal static TvdbSeriesProvider Current { get; private set; } internal static TvdbSeriesProvider Current { get; private set; }
@ -109,17 +110,22 @@ namespace MediaBrowser.Providers.TV
return; return;
var offset = info.AnimeSeriesIndex - index; var offset = info.AnimeSeriesIndex - index;
series.SetProviderId(TvdbSeriesOffset, offset.ToString()); var id = string.Format(TvdbSeriesOffsetFormat, series.GetProviderId(MetadataProviders.Tvdb), offset);
series.SetProviderId(TvdbSeriesOffset, id);
} }
internal static int? GetSeriesOffset(Dictionary<string, string> seriesProviderIds) internal static int? GetSeriesOffset(Dictionary<string, string> seriesProviderIds)
{ {
string offsetString; string idString;
if (!seriesProviderIds.TryGetValue(TvdbSeriesOffset, out offsetString)) if (!seriesProviderIds.TryGetValue(TvdbSeriesOffset, out idString))
return null;
var parts = idString.Split('-');
if (parts.Length < 2)
return null; return null;
int offset; int offset;
if (int.TryParse(offsetString, out offset)) if (int.TryParse(parts[1], out offset))
return offset; return offset;
return null; return null;

@ -8,6 +8,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
@ -1064,7 +1065,7 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary); dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary);
} }
dto.MediaSources = GetMediaSources(audio); dto.MediaSources = GetAudioMediaSources(audio);
dto.MediaSourceCount = 1; dto.MediaSourceCount = 1;
} }
@ -1100,7 +1101,7 @@ namespace MediaBrowser.Server.Implementations.Dto
if (fields.Contains(ItemFields.MediaSources)) if (fields.Contains(ItemFields.MediaSources))
{ {
dto.MediaSources = GetMediaSources(video); dto.MediaSources = GetVideoMediaSources(video);
} }
if (fields.Contains(ItemFields.Chapters)) if (fields.Contains(ItemFields.Chapters))
@ -1266,9 +1267,48 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
SetBookProperties(dto, book); SetBookProperties(dto, book);
} }
var tvChannel = item as LiveTvChannel;
if (tvChannel != null)
{
dto.MediaSources = GetMediaSources(tvChannel);
}
}
public List<MediaSourceInfo> GetMediaSources(BaseItem item)
{
var video = item as Video;
if (video != null)
{
return GetVideoMediaSources(video);
}
var audio = item as Audio;
if (audio != null)
{
return GetAudioMediaSources(audio);
}
var result = new List<MediaSourceInfo>
{
new MediaSourceInfo
{
Id = item.Id.ToString("N"),
LocationType = item.LocationType,
Name = item.Name,
Path = GetMappedPath(item),
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList(),
RunTimeTicks = item.RunTimeTicks
}
};
return result;
} }
private List<MediaSourceInfo> GetMediaSources(Video item) private List<MediaSourceInfo> GetVideoMediaSources(Video item)
{ {
var result = item.GetAlternateVersions().Select(GetVersionInfo).ToList(); var result = item.GetAlternateVersions().Select(GetVersionInfo).ToList();
@ -1293,11 +1333,11 @@ namespace MediaBrowser.Server.Implementations.Dto
.ToList(); .ToList();
} }
private List<MediaSourceInfo> GetMediaSources(Audio item) private List<MediaSourceInfo> GetAudioMediaSources(Audio item)
{ {
var result = new List<MediaSourceInfo> var result = new List<MediaSourceInfo>
{ {
GetVersionInfo(item, true) GetVersionInfo(item)
}; };
return result; return result;
@ -1321,7 +1361,7 @@ namespace MediaBrowser.Server.Implementations.Dto
}; };
} }
private MediaSourceInfo GetVersionInfo(Audio i, bool isPrimary) private MediaSourceInfo GetVersionInfo(Audio i)
{ {
return new MediaSourceInfo return new MediaSourceInfo
{ {

@ -257,8 +257,12 @@ namespace MediaBrowser.Server.Implementations.IO
InternalBufferSize = 32767 InternalBufferSize = 32767
}; };
newWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | newWatcher.NotifyFilter = NotifyFilters.CreationTime |
NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size; NotifyFilters.DirectoryName |
NotifyFilters.FileName |
NotifyFilters.LastWrite |
NotifyFilters.Size |
NotifyFilters.Attributes;
newWatcher.Created += watcher_Changed; newWatcher.Created += watcher_Changed;
newWatcher.Deleted += watcher_Changed; newWatcher.Deleted += watcher_Changed;
@ -349,13 +353,13 @@ namespace MediaBrowser.Server.Implementations.IO
{ {
try try
{ {
Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath);
ReportFileSystemChanged(e.FullPath); ReportFileSystemChanged(e.FullPath);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("Exception in watcher changed. Path: {0}", ex, e.FullPath); Logger.ErrorException("Exception in ReportFileSystemChanged. Path: {0}", ex, e.FullPath);
} }
} }
@ -397,14 +401,6 @@ namespace MediaBrowser.Server.Implementations.IO
Logger.Debug("Ignoring change to {0}", path); Logger.Debug("Ignoring change to {0}", path);
return true; return true;
} }
// Go up another level
parent = Path.GetDirectoryName(i);
if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug("Ignoring change to {0}", path);
return true;
}
} }
return false; return false;

@ -0,0 +1,67 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Server.Implementations.Library
{
public class MusicManager : IMusicManager
{
private readonly ILibraryManager _libraryManager;
public MusicManager(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user)
{
return GetInstantMixFromGenres(item.Genres, user);
}
public IEnumerable<Audio> GetInstantMixFromArtist(string name, User user)
{
var artist = _libraryManager.GetArtist(name);
var genres = _libraryManager.RootFolder
.RecursiveChildren
.OfType<Audio>()
.Where(i => i.HasArtist(name))
.SelectMany(i => i.Genres)
.Concat(artist.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase);
return GetInstantMixFromGenres(genres, user);
}
public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user)
{
var genres = item
.RecursiveChildren
.OfType<Audio>()
.SelectMany(i => i.Genres)
.Concat(item.Genres)
.Distinct(StringComparer.OrdinalIgnoreCase);
return GetInstantMixFromGenres(genres, user);
}
public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user)
{
var inputItems = user.RootFolder.GetRecursiveChildren(user);
var genresDictionary = genres.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
return inputItems
.OfType<Audio>()
.Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey)))
.OrderByDescending(i => i.Item2)
.ThenBy(i => Guid.NewGuid())
.Select(i => i.Item1)
.Take(100)
.OrderBy(i => Guid.NewGuid());
}
}
}

@ -34,8 +34,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{ {
var collectionType = args.GetCollectionType(); var collectionType = args.GetCollectionType();
if (string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || if (string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
string.IsNullOrEmpty(collectionType))
{ {
return new Controller.Entities.Audio.Audio(); return new Controller.Entities.Audio.Audio();
} }

@ -115,22 +115,19 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
} }
// Find movies that are mixed in the same folder // Find movies that are mixed in the same folder
if (args.Path.IndexOf("[trailers]", StringComparison.OrdinalIgnoreCase) != -1 || if (string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
{ {
return ResolveVideo<Trailer>(args); return ResolveVideo<Trailer>(args);
} }
Video item = null; Video item = null;
if (args.Path.IndexOf("[musicvideos]", StringComparison.OrdinalIgnoreCase) != -1 || if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{ {
item = ResolveVideo<MusicVideo>(args); item = ResolveVideo<MusicVideo>(args);
} }
if (args.Path.IndexOf("[adultvideos]", StringComparison.OrdinalIgnoreCase) != -1 || if (string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase))
string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase))
{ {
item = ResolveVideo<AdultVideo>(args); item = ResolveVideo<AdultVideo>(args);
} }

@ -222,13 +222,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
RunTimeTicks = (info.EndDate - info.StartDate).Ticks, RunTimeTicks = (info.EndDate - info.StartDate).Ticks,
OriginalAirDate = info.OriginalAirDate, OriginalAirDate = info.OriginalAirDate,
MediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery MediaSources = _dtoService.GetMediaSources((BaseItem)recording)
{
ItemId = recording.Id
}).ToList()
}; };
dto.MediaStreams = dto.MediaSources.SelectMany(i => i.MediaStreams).ToList();
if (info.Status == RecordingStatus.InProgress) if (info.Status == RecordingStatus.InProgress)
{ {
var now = DateTime.UtcNow.Ticks; var now = DateTime.UtcNow.Ticks;
@ -317,7 +315,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
Type = info.GetClientTypeName(), Type = info.GetClientTypeName(),
Id = info.Id.ToString("N"), Id = info.Id.ToString("N"),
MediaType = info.MediaType, MediaType = info.MediaType,
ExternalId = info.ExternalId ExternalId = info.ExternalId,
MediaSources = _dtoService.GetMediaSources(info)
}; };
if (user != null) if (user != null)

@ -134,6 +134,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return number; return number;
}); });
if (query.IsFavorite.HasValue)
{
var val = query.IsFavorite.Value;
channels = channels
.Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val);
}
} }
channels = channels.OrderBy(i => channels = channels.OrderBy(i =>

@ -0,0 +1,31 @@
{
"SettingsSaved": "Einstellungen gespeichert",
"AddUser": "Benutzer hinzuf\u00fcgen",
"Users": "Benutzer",
"Delete": "L\u00f6schen",
"Administrator": "Administrator",
"Password": "Passwort",
"CreatePassword": "Passwort erstellen",
"DeleteImage": "Bild l\u00f6schen",
"DeleteImageConfirmation": "M\u00f6chten Sie das Bild wirklich l\u00f6schen?",
"FileReadCancelled": "Das Einlesen der Datei wurde abgebrochen.",
"FileNotFound": "Datei nicht gefunden",
"FileReadError": "Beim Lesen der Datei ist ein Fehler aufgetreten.",
"DeleteUser": "Benutzer l\u00f6schen",
"DeleteUserConfirmation": "M\u00f6chten Sie {0} wirklich l\u00f6schen?",
"PasswordResetHeader": "Passwort zur\u00fccksetzen",
"PasswordResetComplete": "Das Passwort wurde zur\u00fcckgesetzt.",
"PasswordResetConfirmation": "M\u00f6chten Sie das Passwort wirklich zur\u00fccksetzen?",
"PasswordSaved": "Passwort gespeichert",
"PasswordMatchError": "Passwort und Passwortbest\u00e4tigung stimmen nicht \u00fcberein.",
"OptionOff": "Aus",
"OptionOn": "Ein",
"OptionRelease": "Release",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "Deinstalliere Plugin",
"UninstallPluginConfirmation": "M\u00f6chten Sie {0} wirklich deinstallieren?",
"NoPluginConfigurationMessage": "Bei diesem Plugin kann nichts eingestellt werden.",
"NoPluginsInstalledMessage": "Sie haben keine Plugins installiert.",
"BrowsePluginCatalogMessage": "Durchsuchen Sie unsere Bibliothek um alle verf\u00fcgbaren Plugins anzuzeigen."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "Settings saved.",
"AddUser": "Add User",
"Users": "Users",
"Delete": "Delete",
"Administrator": "Administrator",
"Password": "Password",
"CreatePassword": "Create Password",
"DeleteImage": "Delete Image",
"DeleteImageConfirmation": "Are you sure you wish to delete this image?",
"FileReadCancelled": "The file read has been cancelled.",
"FileNotFound": "File not found.",
"FileReadError": "An error occurred while reading the file.",
"DeleteUser": "Delete User",
"DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
"PasswordResetHeader": "Password Reset",
"PasswordResetComplete": "The password has been reset.",
"PasswordResetConfirmation": "Are you sure you wish to reset the password?",
"PasswordSaved": "Password saved.",
"PasswordMatchError": "Password and password confirmation must match.",
"OptionOff": "Off",
"OptionOn": "On",
"OptionRelease": "Release",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "Uninstall Plugin",
"UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
"NoPluginConfigurationMessage": "This plugin has nothing to configure.",
"NoPluginsInstalledMessage": "You have no plugins installed.",
"BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "Configuracion guardada",
"AddUser": "Agregar usuario",
"Users": "Usuarios",
"Delete": "Borrar",
"Administrator": "Administrador",
"Password": "Contrase\u00f1a",
"CreatePassword": "Crear Contrase\u00f1a",
"DeleteImage": "Borrar Imagen",
"DeleteImageConfirmation": "Esta seguro que desea borrar esta imagen?",
"FileReadCancelled": "La lectura del archivo se ha cancelado.",
"FileNotFound": "Archivo no encontrado.",
"FileReadError": "Se encontr\u00f3 un error al leer el archivo.",
"DeleteUser": "Borrar Usuario",
"DeleteUserConfirmation": "Esta seguro que desea eliminar a {0}?",
"PasswordResetHeader": "Restablecer contrase\u00f1a",
"PasswordResetComplete": "La contrase\u00f1a se ha restablecido.",
"PasswordResetConfirmation": "Esta seguro que desea restablecer la contrase\u00f1a?",
"PasswordSaved": "Contrase\u00f1a guardada.",
"PasswordMatchError": "La contrase\u00f1a y la confirmaci\u00f3n de la contrase\u00f1a deben de ser iguales.",
"OptionOff": "Apagado",
"OptionOn": "Prendido",
"OptionRelease": "Liberar",
"OptionBeta": "Beta",
"OptionDev": "Desarrollo",
"UninstallPluginHeader": "Desinstalar Plugin",
"UninstallPluginConfirmation": "Esta seguro que desea desinstalar {0}?",
"NoPluginConfigurationMessage": "El plugin no requiere configuraci\u00f3n",
"NoPluginsInstalledMessage": "No tiene plugins instalados.",
"BrowsePluginCatalogMessage": "Navegar el catalogo de plugins para ver los plugins disponibles."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "Param\u00e8tres sauvegard\u00e9s.",
"AddUser": "Ajout\u00e9 Usager",
"Users": "Usagers",
"Delete": "Supprimer",
"Administrator": "Administrateur",
"Password": "Mot de passe",
"CreatePassword": "Cr\u00e9er mot de passe",
"DeleteImage": "Supprimer Image",
"DeleteImageConfirmation": "\u00cates-vous s\u00fbr de vouloir supprimer l'image?",
"FileReadCancelled": "La lecture du fichier a \u00e9t\u00e9 annul\u00e9e.",
"FileNotFound": "Fichier non trouv\u00e9",
"FileReadError": "Un erreur est survenue pendant la lecture du fichier.",
"DeleteUser": "Supprimer Usager",
"DeleteUserConfirmation": "\u00cates-vous s\u00fbr de vouloir supprimer {0}?",
"PasswordResetHeader": "Red\u00e9marrage du mot de passe",
"PasswordResetComplete": "Le mot de passe a \u00e9t\u00e9 red\u00e9marr\u00e9.",
"PasswordResetConfirmation": "\u00cates-vous s\u00fbr de vouloir red\u00e9marrer le mot de passe?",
"PasswordSaved": "Mot de passe sauvegard\u00e9.",
"PasswordMatchError": "Mot de passe et confirmation de mot de passe doivent correspondre.",
"OptionOff": "Off",
"OptionOn": "On",
"OptionRelease": "Lancement",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "D\u00e9sinstaller module d'extention",
"UninstallPluginConfirmation": "\u00cates-vous s\u00fbr de vouloir d\u00e9sinstaller {0}?",
"NoPluginConfigurationMessage": "Ce module d'extension n'a rien \u00e0 configurer.",
"NoPluginsInstalledMessage": "Vous n'avez aucun module d'extension install\u00e9.",
"BrowsePluginCatalogMessage": "Explorer notre catalogue de modules d'extension pour voir ce qui est disponible."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "Settings saved.",
"AddUser": "Add User",
"Users": "Users",
"Delete": "Delete",
"Administrator": "Administrator",
"Password": "Password",
"CreatePassword": "Create Password",
"DeleteImage": "Delete Image",
"DeleteImageConfirmation": "Are you sure you wish to delete this image?",
"FileReadCancelled": "The file read has been cancelled.",
"FileNotFound": "File not found.",
"FileReadError": "An error occurred while reading the file.",
"DeleteUser": "Delete User",
"DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
"PasswordResetHeader": "Password Reset",
"PasswordResetComplete": "The password has been reset.",
"PasswordResetConfirmation": "Are you sure you wish to reset the password?",
"PasswordSaved": "Password saved.",
"PasswordMatchError": "Password and password confirmation must match.",
"OptionOff": "Off",
"OptionOn": "On",
"OptionRelease": "Release",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "Uninstall Plugin",
"UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
"NoPluginConfigurationMessage": "This plugin has nothing to configure.",
"NoPluginsInstalledMessage": "You have no plugins installed.",
"BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "Instellingen opgeslagen.",
"AddUser": "Gebruiker toevoegen",
"Users": "Gebruikers",
"Delete": "Verwijderen",
"Administrator": "Beheerder",
"Password": "Wachtwoord",
"CreatePassword": "Maak wachtwoord",
"DeleteImage": "Verwijder afbeelding",
"DeleteImageConfirmation": "Weet je zeker dat je deze afbeelding wilt verwijderen?",
"FileReadCancelled": "Het lezen van het bestand is geannuleerd",
"FileNotFound": "Bestand niet gevonden.",
"FileReadError": "Er is een fout opgetreden bij het lezen van het bestand.",
"DeleteUser": "Verwijder gebruiker",
"DeleteUserConfirmation": "Weet je zeker dat je {0} wilt verwijderen?",
"PasswordResetHeader": "Wachtwoord opnieuw instellen",
"PasswordResetComplete": "Het wachtwoord is opnieuw ingesteld.",
"PasswordResetConfirmation": "Weet je zeker dat je het wachtwoord opnieuw in wilt stellen?",
"PasswordSaved": "Wachtwoord opgeslagen.",
"PasswordMatchError": "Wachtwoord en wachtwoord bevestiging moeten hetzelfde zijn.",
"OptionOff": "Uit",
"OptionOn": "Aan",
"OptionRelease": "Release",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "Deinstalleer Plugin",
"UninstallPluginConfirmation": "Weet u zeker dat u {0} wilt deinstalleren?",
"NoPluginConfigurationMessage": "Deze plugin heeft niets in te stellen",
"NoPluginsInstalledMessage": "U heeft geen plugins geinstalleerd",
"BrowsePluginCatalogMessage": "Blader door de Plugincatalogus voor beschikbare plugins."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "Prefer\u00eancias salvas.",
"AddUser": "Adicionar Usu\u00e1rio",
"Users": "Usu\u00e1rios",
"Delete": "Apagar",
"Administrator": "Administrador",
"Password": "Senha",
"CreatePassword": "Criar Senha",
"DeleteImage": "Apagar Imagem",
"DeleteImageConfirmation": "Tem certeza que deseja apagar esta imagem?",
"FileReadCancelled": "A leitura do arquivo foi cancelada.",
"FileNotFound": "Arquivo n\u00e3o encontrado.",
"FileReadError": "Ocorreu um erro ao ler o arquivo.",
"DeleteUser": "Apagar Usu\u00e1rio",
"DeleteUserConfirmation": "Tem certeza que deseja apagar {0}?",
"PasswordResetHeader": "Redefinir Senha",
"PasswordResetComplete": "A senha foi redefinida.",
"PasswordResetConfirmation": "Deseja realmente redefinir a senha?",
"PasswordSaved": "Senha salva.",
"PasswordMatchError": "A senha e confirma\u00e7\u00e3o da senha devem conferir.",
"OptionOff": "Off",
"OptionOn": "On",
"OptionRelease": "Release",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "Desintalar Plugin",
"UninstallPluginConfirmation": "Deseja realmente desinstalar {0}?",
"NoPluginConfigurationMessage": "Este plugin n\u00e3o necessita configurar.",
"NoPluginsInstalledMessage": "N\u00e3o existem plugins instalados.",
"BrowsePluginCatalogMessage": "Navegue pelo cat\u00e1logo de plugins para ver os dispon\u00edveis."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "Configura\u00e7\u00f5es guardadas.",
"AddUser": "Adicionar Utilizador",
"Users": "Utilizadores",
"Delete": "Apagar",
"Administrator": "Administrador",
"Password": "Senha",
"CreatePassword": "Criar Senha",
"DeleteImage": "Apagar Imagem",
"DeleteImageConfirmation": "Tem a certeza que pretende apagar a imagem?",
"FileReadCancelled": "A leitura do ficheiro foi cancelada.",
"FileNotFound": "Ficheiro n\u00e3o encontrado",
"FileReadError": "Ocorreu um erro ao ler o ficheiro.",
"DeleteUser": "Apagar Utilizador",
"DeleteUserConfirmation": "Tem a certeza que pretende apagar {0}?",
"PasswordResetHeader": "Redefinir Senha",
"PasswordResetComplete": "A senha foi redefinida.",
"PasswordResetConfirmation": "Tem a certeza que pretende redefinir a senha?",
"PasswordSaved": "Senha guardada.",
"PasswordMatchError": "A senha e a confirma\u00e7\u00e3o da senha devem coincidir.",
"OptionOff": "Desligado",
"OptionOn": "Ligado",
"OptionRelease": "Final",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "Desinstalar extens\u00e3o",
"UninstallPluginConfirmation": "Tem a certeza que pretende desinstalar {0}?",
"NoPluginConfigurationMessage": "Esta extens\u00e3o n\u00e3o \u00e9 configur\u00e1vel.",
"NoPluginsInstalledMessage": "N\u00e3o tem extens\u00f5es instaladas.",
"BrowsePluginCatalogMessage": "Navegue o nosso cat\u00e1logo de extens\u00f5es para ver as extens\u00f5es dispon\u00edveis."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b",
"AddUser": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
"Users": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438",
"Delete": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c",
"Administrator": "\u0410\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440",
"Password": "\u041f\u0430\u0440\u043e\u043b\u044c",
"CreatePassword": "\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c",
"DeleteImage": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435",
"DeleteImageConfirmation": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u044d\u0442\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435?",
"FileReadCancelled": "\u0427\u0442\u0435\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u0430 \u0431\u044b\u043b\u043e \u043e\u0442\u043c\u0435\u043d\u0435\u043d\u043e",
"FileNotFound": "\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d",
"FileReadError": "\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430",
"DeleteUser": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
"DeleteUserConfirmation": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c {0}?",
"PasswordResetHeader": "\u0421\u0431\u0440\u043e\u0441 \u043f\u0430\u0440\u043e\u043b\u044f",
"PasswordResetComplete": "\u041f\u0430\u0440\u043e\u043b\u044c \u0431\u044b\u043b \u0441\u0431\u0440\u043e\u0448\u0435\u043d",
"PasswordResetConfirmation": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0441\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u044c?",
"PasswordSaved": "\u041f\u0430\u0440\u043e\u043b\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d",
"PasswordMatchError": "\u041f\u043e\u043b\u044f \u041f\u0430\u0440\u043e\u043b\u044c \u0438 \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u043e\u043b\u044f \u0434\u043e\u043b\u0436\u043d\u044b \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0442\u044c",
"OptionOff": "\u0412\u044b\u043a\u043b.",
"OptionOn": "\u0412\u043a\u043b.",
"OptionRelease": "\u0412\u044b\u043f\u0443\u0441\u043a",
"OptionBeta": "\u0411\u0435\u0442\u0430",
"OptionDev": "\u0420\u0430\u0437\u0440\u0430\u0431.",
"UninstallPluginHeader": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u043b\u0430\u0433\u0438\u043d",
"UninstallPluginConfirmation": "\u0412\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c {0}?",
"NoPluginConfigurationMessage": "\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a",
"NoPluginsInstalledMessage": "\u0423 \u0412\u0430\u0441 \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0433\u0438\u043d\u0430.",
"BrowsePluginCatalogMessage": "\u041e\u0437\u043d\u0430\u043a\u043e\u043c\u044c\u0442\u0435\u0441\u044c \u0441 \u043d\u0430\u0448\u0438\u043c \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u043c \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432."
}

@ -0,0 +1,31 @@
{
"SettingsSaved": "\u8a2d\u7f6e\u5df2\u4fdd\u5b58",
"AddUser": "Add User",
"Users": "\u7528\u6236",
"Delete": "\u522a\u9664",
"Administrator": "\u7ba1\u7406\u54e1",
"Password": "\u5bc6\u78bc",
"CreatePassword": "\u5275\u5efa\u5bc6\u78bc",
"DeleteImage": "\u522a\u9664\u5716\u50cf",
"DeleteImageConfirmation": "\u4f60\u78ba\u5b9a\u8981\u522a\u9664\u9019\u5f35\u5716\u7247\uff1f",
"FileReadCancelled": "The file read has been cancelled.",
"FileNotFound": "File not found.",
"FileReadError": "An error occurred while reading the file.",
"DeleteUser": "\u522a\u9664\u7528\u6236",
"DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
"PasswordResetHeader": "\u91cd\u8a2d\u5bc6\u78bc",
"PasswordResetComplete": "\u5bc6\u78bc\u5df2\u91cd\u8a2d",
"PasswordResetConfirmation": "\u4f60\u78ba\u5b9a\u8981\u91cd\u8a2d\u5bc6\u78bc\uff1f",
"PasswordSaved": "\u5bc6\u78bc\u5df2\u4fdd\u5b58\u3002",
"PasswordMatchError": "\u5bc6\u78bc\u548c\u78ba\u8a8d\u5bc6\u78bc\u5fc5\u9808\u4e00\u81f4\u3002",
"OptionOff": "Off",
"OptionOn": "On",
"OptionRelease": "Release",
"OptionBeta": "Beta",
"OptionDev": "Dev",
"UninstallPluginHeader": "Uninstall Plugin",
"UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
"NoPluginConfigurationMessage": "This plugin has nothing to configure.",
"NoPluginsInstalledMessage": "You have no plugins installed.",
"BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
}

@ -1,9 +1,10 @@
using MediaBrowser.Common.IO; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
using MoreLinq; using MoreLinq;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -11,6 +12,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
namespace MediaBrowser.Server.Implementations.Localization namespace MediaBrowser.Server.Implementations.Localization
{ {
@ -33,15 +35,17 @@ namespace MediaBrowser.Server.Implementations.Localization
new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase); new ConcurrentDictionary<string, Dictionary<string, ParentalRating>>(StringComparer.OrdinalIgnoreCase);
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LocalizationManager"/> class. /// Initializes a new instance of the <see cref="LocalizationManager"/> class.
/// </summary> /// </summary>
/// <param name="configurationManager">The configuration manager.</param> /// <param name="configurationManager">The configuration manager.</param>
public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem) public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
{ {
_configurationManager = configurationManager; _configurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
ExtractAll(); ExtractAll();
} }
@ -241,5 +245,117 @@ namespace MediaBrowser.Server.Implementations.Localization
return value == null ? (int?)null : value.Value; return value == null ? (int?)null : value.Value;
} }
public string GetLocalizedString(string phrase)
{
return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture);
}
public string GetLocalizedString(string phrase, string culture)
{
var dictionary = GetLocalizationDictionary(culture);
string value;
if (dictionary.TryGetValue(phrase, out value))
{
return value;
}
return phrase;
}
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> GetLocalizationDictionary(string culture)
{
const string prefix = "Server";
var key = prefix + culture;
return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "server.json"));
}
public Dictionary<string, string> GetJavaScriptLocalizationDictionary(string culture)
{
const string prefix = "JavaScript";
var key = prefix + culture;
return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "javascript.json"));
}
private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
{
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var assembly = GetType().Assembly;
var namespaceName = GetType().Namespace + "." + prefix;
CopyInto(dictionary, namespaceName + "." + baseFilename, assembly);
CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture), assembly);
return dictionary;
}
private void CopyInto(IDictionary<string, string> dictionary, string resourcePath, Assembly assembly)
{
using (var stream = assembly.GetManifestResourceStream(resourcePath))
{
if (stream != null)
{
var dict = _jsonSerializer.DeserializeFromStream<Dictionary<string, string>>(stream);
foreach (var key in dict.Keys)
{
dictionary[key] = dict[key];
}
}
}
}
private string GetResourceFilename(string culture)
{
var parts = culture.Split('-');
if (parts.Length == 2)
{
culture = parts[0].ToLower() + "_" + parts[1].ToUpper();
}
else
{
culture = culture.ToLower();
}
return culture + ".json";
}
public IEnumerable<LocalizatonOption> GetLocalizationOptions()
{
return new List<LocalizatonOption>
{
new LocalizatonOption{ Name="English (United States)", Value="en-us"},
new LocalizatonOption{ Name="Chinese Traditional", Value="zh-TW"},
new LocalizatonOption{ Name="Dutch", Value="nl"},
new LocalizatonOption{ Name="French", Value="fr"},
new LocalizatonOption{ Name="German", Value="de"},
new LocalizatonOption{ Name="Portuguese (Brazil)", Value="pt-BR"},
new LocalizatonOption{ Name="Portuguese (Portugal)", Value="pt-PT"},
new LocalizatonOption{ Name="Russian", Value="ru"},
new LocalizatonOption{ Name="Spanish", Value="es"}
}.OrderBy(i => i.Name);
}
public string LocalizeDocument(string document, string culture, Func<string,string> tokenBuilder)
{
foreach (var pair in GetLocalizationDictionary(culture).ToList())
{
var token = tokenBuilder(pair.Key);
document = document.Replace(token, pair.Value, StringComparison.Ordinal);
}
return document;
}
} }
} }

@ -0,0 +1,50 @@
{
"LabelExit": "Ende",
"LabelVisitCommunity": "Besuche die Community",
"LabelGithubWiki": "Github Wiki",
"LabelSwagger": "Swagger",
"LabelStandard": "Standard",
"LabelViewApiDocumentation": "Zeige API Dokumentation",
"LabelBrowseLibrary": "Durchsuche Bibliothek",
"LabelConfigureMediaBrowser": "Konfiguriere Media Browser",
"LabelOpenLibraryViewer": "\u00d6ffne Bibliothekenansicht",
"LabelRestartServer": "Server neustarten",
"LabelShowLogWindow": "Zeige Log Fenster",
"LabelPrevious": "Vorheriges",
"LabelFinish": "Ende",
"LabelNext": "N\u00e4chstes",
"LabelYoureDone": "Du bist fertig!",
"WelcomeToMediaBrowser": "Willkommen zu Media Browser!",
"LabelMediaBrowser": "Media Browser",
"ThisWizardWillGuideYou": "Dieser Assistent wird Sie durch den Einrichtungsprozess f\u00fchren.",
"TellUsAboutYourself": "Sagen Sie uns etwas \u00fcber sich selbst",
"LabelYourFirstName": "Ihr Vorname:",
"MoreUsersCanBeAddedLater": "Weitere Benutzer k\u00f6nnen Sie sp\u00e4ter im Dashboard hinzuf\u00fcgen.",
"UserProfilesIntro": "Media Browser includes built-in support for user profiles, enabling each user to have their own display settings, playstate and parental controls.",
"LabelWindowsService": "Windows Service",
"AWindowsServiceHasBeenInstalled": "Ein Windows Service wurde installiert.",
"WindowsServiceIntro1": "Media Browser Server normally runs as a desktop application with a tray icon, but if you prefer to run it as a background service, it can be started from the windows services control panel instead.",
"WindowsServiceIntro2": "If using the windows service, please note that it cannot be run at the same time as the tray icon, so you'll need to exit the tray in order to run the service. The service will also need to be configured with administrative privileges via the control panel. Please note that at this time the service is unable to self-update, so new versions will require manual interaction.",
"WizardCompleted": "That's all we need for now. Media Browser has begun collecting information about your media library. Check out some of our apps, and then click <b>Finish<\/b> to view the <b>Dashboard<\/b>.",
"LabelConfigureSettings": "Konfiguriere Einstellungen",
"LabelEnableVideoImageExtraction": "Aktiviere Videobild-Extrahierung",
"VideoImageExtractionHelp": "F\u00fcr Videos die noch keien Bilder haben, und f\u00fcr die wir keine Internetbilder finden k\u00f6nnen. Hierdurch wird der erste Bibliothekenscan etwas mehr Zeit beanspruchen, f\u00fchrt aber zu einer ansprechenderen Pr\u00e4sentation.",
"LabelEnableChapterImageExtractionForMovies": "Extrahiere Kapitelbilder f\u00fcr Filme",
"LabelChapterImageExtractionForMoviesHelp": "Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, cpu-intensive and may require several gigabytes of space. It runs as a nightly scheduled task at 4am, although this is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours.",
"LabelEnableAutomaticPortMapping": "Aktiviere automatische Portweiterleitung",
"LabelEnableAutomaticPortMappingHelp": "UPnP allows automated router configuration for easy remote access. This may not work with some router models.",
"ButtonOk": "Ok",
"ButtonCancel": "Abbrechen",
"HeaderSetupLibrary": "Medienbibliothek einrichten",
"ButtonAddMediaFolder": "Medienordner hinzuf\u00fcgen",
"LabelFolderType": "Ordnertyp:",
"MediaFolderHelpPluginRequired": "* Ben\u00f6tigt ein Plugin, wie GameBrowser oder MB Bookshelf.",
"ReferToMediaLibraryWiki": "Refer to the media library wiki.",
"LabelCountry": "Land:",
"LabelLanguage": "Sprache:",
"HeaderPreferredMetadataLanguage": "Bevorzugte Metadata Sprache:",
"LabelSaveLocalMetadata": "Speichere Bildmaterial und Metadaten in den Medienodnern",
"LabelSaveLocalMetadataHelp": "Saving artwork and metadata directly into media folders will put them in a place where they can be easily edited.",
"LabelDownloadInternetMetadata": "Lade Bildmaterial und Metadaten aus dem Internet",
"LabelDownloadInternetMetadataHelp": "Media Browser can download information about your media to enable rich presentations."
}

@ -0,0 +1,50 @@
{
"LabelExit": "Exit",
"LabelVisitCommunity": "Visit Community",
"LabelGithubWiki": "Github Wiki",
"LabelSwagger": "Swagger",
"LabelStandard": "Standard",
"LabelViewApiDocumentation": "View Api Documentation",
"LabelBrowseLibrary": "Browse Library",
"LabelConfigureMediaBrowser": "Configure Media Browser",
"LabelOpenLibraryViewer": "Open Library Viewer",
"LabelRestartServer": "Restart Server",
"LabelShowLogWindow": "Show Log Window",
"LabelPrevious": "Previous",
"LabelFinish": "Finish",
"LabelNext": "Next",
"LabelYoureDone": "You're Done!",
"WelcomeToMediaBrowser": "Welcome to Media Browser!",
"LabelMediaBrowser": "Media Browser",
"ThisWizardWillGuideYou": "This wizard will help guide you through the setup process.",
"TellUsAboutYourself": "Tell us about yourself",
"LabelYourFirstName": "Your first name:",
"MoreUsersCanBeAddedLater": "More users can be added later within the Dashboard.",
"UserProfilesIntro": "Media Browser includes built-in support for user profiles, enabling each user to have their own display settings, playstate and parental controls.",
"LabelWindowsService": "Windows Service",
"AWindowsServiceHasBeenInstalled": "A Windows Service has been installed.",
"WindowsServiceIntro1": "Media Browser Server normally runs as a desktop application with a tray icon, but if you prefer to run it as a background service, it can be started from the windows services control panel instead.",
"WindowsServiceIntro2": "If using the windows service, please note that it cannot be run at the same time as the tray icon, so you'll need to exit the tray in order to run the service. The service will also need to be configured with administrative privileges via the control panel. Please note that at this time the service is unable to self-update, so new versions will require manual interaction.",
"WizardCompleted": "That's all we need for now. Media Browser has begun collecting information about your media library. Check out some of our apps, and then click <b>Finish<\/b> to view the <b>Dashboard<\/b>.",
"LabelConfigureSettings": "Configure settings",
"LabelEnableVideoImageExtraction": "Enable video image extraction",
"VideoImageExtractionHelp": "For videos that don't already have images, and that we're unable to find internet images for. This will add some additional time to the initial library scan but will result in a more pleasing presentation.",
"LabelEnableChapterImageExtractionForMovies": "Extract chapter image extraction for Movies",
"LabelChapterImageExtractionForMoviesHelp": "Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, cpu-intensive and may require several gigabytes of space. It runs as a nightly scheduled task at 4am, although this is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours.",
"LabelEnableAutomaticPortMapping": "Enable automatic port mapping",
"LabelEnableAutomaticPortMappingHelp": "UPnP allows automated router configuration for easy remote access. This may not work with some router models.",
"ButtonOk": "Ok",
"ButtonCancel": "Cancel",
"HeaderSetupLibrary": "Setup your media library",
"ButtonAddMediaFolder": "Add media folder",
"LabelFolderType": "Folder type:",
"MediaFolderHelpPluginRequired": "* Requires the use of a plugin, e.g. GameBrowser or MB Bookshelf.",
"ReferToMediaLibraryWiki": "Refer to the media library wiki.",
"LabelCountry": "Country:",
"LabelLanguage": "Language:",
"HeaderPreferredMetadataLanguage": "Preferred metadata language:",
"LabelSaveLocalMetadata": "Save artwork and metadata into media folders",
"LabelSaveLocalMetadataHelp": "Saving artwork and metadata directly into media folders will put them in a place where they can be easily edited.",
"LabelDownloadInternetMetadata": "Download artwork and metadata from the internet",
"LabelDownloadInternetMetadataHelp": "Media Browser can download information about your media to enable rich presentations."
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save