tikuf 11 years ago
commit 520b77a098

@ -211,12 +211,7 @@ namespace MediaBrowser.Api
private void UpdateItem(BaseItemDto request, BaseItem item) private void UpdateItem(BaseItemDto request, BaseItem item)
{ {
item.Name = request.Name; item.Name = request.Name;
item.ForcedSortName = request.ForcedSortName;
// Only set the forced value if they changed it, or there's already one
if (!string.Equals(item.SortName, request.SortName) || !string.IsNullOrEmpty(item.ForcedSortName))
{
item.ForcedSortName = request.SortName;
}
var hasBudget = item as IHasBudget; var hasBudget = item as IHasBudget;
if (hasBudget != null) if (hasBudget != null)

@ -10,8 +10,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetCultures /// Class GetCultures
/// </summary> /// </summary>
[Route("/Localization/Cultures", "GET")] [Route("/Localization/Cultures", "GET", Summary = "Gets known cultures")]
[Api(Description = "Gets known cultures")]
public class GetCultures : IReturn<List<CultureDto>> public class GetCultures : IReturn<List<CultureDto>>
{ {
} }
@ -19,8 +18,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetCountries /// Class GetCountries
/// </summary> /// </summary>
[Route("/Localization/Countries", "GET")] [Route("/Localization/Countries", "GET", Summary = "Gets known countries")]
[Api(Description = "Gets known countries")]
public class GetCountries : IReturn<List<CountryInfo>> public class GetCountries : IReturn<List<CountryInfo>>
{ {
} }
@ -28,8 +26,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class ParentalRatings /// Class ParentalRatings
/// </summary> /// </summary>
[Route("/Localization/ParentalRatings", "GET")] [Route("/Localization/ParentalRatings", "GET", Summary = "Gets known parental ratings")]
[Api(Description = "Gets known parental ratings")]
public class GetParentalRatings : IReturn<List<ParentalRating>> public class GetParentalRatings : IReturn<List<ParentalRating>>
{ {
} }

@ -117,10 +117,9 @@ namespace MediaBrowser.Api.Movies
public object Get(GetMovieRecommendations request) public object Get(GetMovieRecommendations request)
{ {
var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null; var user = _userManager.GetUserById(request.UserId.Value);
var folder = user.RootFolder; var movies = user.RootFolder.GetRecursiveChildren(user).OfType<Movie>().ToList();
var movies = folder.RecursiveChildren.OfType<Movie>().ToList();
var result = GetRecommendationCategories(user, movies, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList()); var result = GetRecommendationCategories(user, movies, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList());

@ -5,8 +5,7 @@ using ServiceStack;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
[Route("/News/Product", "GET")] [Route("/News/Product", "GET", Summary = "Gets the latest product news.")]
[Api(Description = "Gets the latest product news.")]
public class GetProductNews : IReturn<QueryResult<NewsItem>> public class GetProductNews : IReturn<QueryResult<NewsItem>>
{ {
/// <summary> /// <summary>

@ -1,15 +1,14 @@
using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Notifications;
using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Notifications;
using ServiceStack;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ServiceStack;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
[Route("/Notifications/{UserId}", "GET")] [Route("/Notifications/{UserId}", "GET", Summary = "Gets notifications")]
[Api(Description = "Gets notifications")]
public class GetNotifications : IReturn<NotificationResult> public class GetNotifications : IReturn<NotificationResult>
{ {
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
@ -25,16 +24,14 @@ namespace MediaBrowser.Api
public int? Limit { get; set; } public int? Limit { get; set; }
} }
[Route("/Notifications/{UserId}/Summary", "GET")] [Route("/Notifications/{UserId}/Summary", "GET", Summary = "Gets a notification summary for a user")]
[Api(Description = "Gets a notification summary for a user")]
public class GetNotificationsSummary : IReturn<NotificationsSummary> public class GetNotificationsSummary : IReturn<NotificationsSummary>
{ {
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public Guid UserId { get; set; } public Guid UserId { get; set; }
} }
[Route("/Notifications/{UserId}", "POST")] [Route("/Notifications/{UserId}", "POST", Summary = "Adds a notifications")]
[Api(Description = "Adds a notifications")]
public class AddUserNotification : IReturn<Notification> public class AddUserNotification : IReturn<Notification>
{ {
[ApiMember(Name = "Id", Description = "The Id of the new notification. If unspecified one will be provided.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] [ApiMember(Name = "Id", Description = "The Id of the new notification. If unspecified one will be provided.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
@ -62,8 +59,7 @@ namespace MediaBrowser.Api
public NotificationLevel Level { get; set; } public NotificationLevel Level { get; set; }
} }
[Route("/Notifications/{UserId}/Read", "POST")] [Route("/Notifications/{UserId}/Read", "POST", Summary = "Marks notifications as read")]
[Api(Description = "Marks notifications as read")]
public class MarkRead : IReturnVoid public class MarkRead : IReturnVoid
{ {
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@ -73,8 +69,7 @@ namespace MediaBrowser.Api
public string Ids { get; set; } public string Ids { get; set; }
} }
[Route("/Notifications/{UserId}/Unread", "POST")] [Route("/Notifications/{UserId}/Unread", "POST", Summary = "Marks notifications as unread")]
[Api(Description = "Marks notifications as unread")]
public class MarkUnread : IReturnVoid public class MarkUnread : IReturnVoid
{ {
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]

@ -1,21 +1,20 @@
using System.Collections.Generic; using MediaBrowser.Common.Constants;
using System.Globalization;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Constants;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using ServiceStack; using ServiceStack;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Api namespace MediaBrowser.Api
{ {
/// <summary> /// <summary>
/// Class InstallPackage /// Class InstallPackage
/// </summary> /// </summary>
[Route("/PackageReviews/{Id}", "POST")] [Route("/Packages/Reviews/{Id}", "POST", Summary = "Creates or updates a package review")]
[Api(("Creates or updates a package review"))]
public class CreateReviewRequest : IReturnVoid public class CreateReviewRequest : IReturnVoid
{ {
/// <summary> /// <summary>
@ -57,8 +56,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class InstallPackage /// Class InstallPackage
/// </summary> /// </summary>
[Route("/PackageReviews/{Id}", "GET")] [Route("/Packages/{Id}/Reviews", "GET", Summary = "Gets reviews for a package")]
[Api(("Retrieve reviews for a package"))]
public class ReviewRequest : IReturn<List<PackageReviewInfo>> public class ReviewRequest : IReturn<List<PackageReviewInfo>>
{ {
/// <summary> /// <summary>
@ -132,7 +130,7 @@ namespace MediaBrowser.Api
parms += "&title=true"; parms += "&title=true";
} }
var result = _httpClient.Get(Constants.MbAdminUrl + "/service/packageReview/retrieve"+parms, CancellationToken.None).Result; var result = _httpClient.Get(Constants.MbAdminUrl + "/service/packageReview/retrieve" + parms, CancellationToken.None).Result;
var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result); var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result);

@ -14,8 +14,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetPackage /// Class GetPackage
/// </summary> /// </summary>
[Route("/Packages/{Name}", "GET")] [Route("/Packages/{Name}", "GET", Summary = "Gets a package, by name or assembly guid")]
[Api(("Gets a package, by name or assembly guid"))]
public class GetPackage : IReturn<PackageInfo> public class GetPackage : IReturn<PackageInfo>
{ {
/// <summary> /// <summary>
@ -36,8 +35,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetPackages /// Class GetPackages
/// </summary> /// </summary>
[Route("/Packages", "GET")] [Route("/Packages", "GET", Summary = "Gets available packages")]
[Api(("Gets available packages"))]
public class GetPackages : IReturn<List<PackageInfo>> public class GetPackages : IReturn<List<PackageInfo>>
{ {
/// <summary> /// <summary>
@ -57,8 +55,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetPackageVersionUpdates /// Class GetPackageVersionUpdates
/// </summary> /// </summary>
[Route("/Packages/Updates", "GET")] [Route("/Packages/Updates", "GET", Summary = "Gets available package updates for currently installed packages")]
[Api(("Gets available package updates for currently installed packages"))]
public class GetPackageVersionUpdates : IReturn<List<PackageVersionInfo>> public class GetPackageVersionUpdates : IReturn<List<PackageVersionInfo>>
{ {
/// <summary> /// <summary>
@ -72,8 +69,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class InstallPackage /// Class InstallPackage
/// </summary> /// </summary>
[Route("/Packages/Installed/{Name}", "POST")] [Route("/Packages/Installed/{Name}", "POST", Summary = "Installs a package")]
[Api(("Installs a package"))]
public class InstallPackage : IReturnVoid public class InstallPackage : IReturnVoid
{ {
/// <summary> /// <summary>
@ -108,8 +104,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class CancelPackageInstallation /// Class CancelPackageInstallation
/// </summary> /// </summary>
[Route("/Packages/Installing/{Id}", "DELETE")] [Route("/Packages/Installing/{Id}", "DELETE", Summary = "Cancels a package installation")]
[Api(("Cancels a package installation"))]
public class CancelPackageInstallation : IReturnVoid public class CancelPackageInstallation : IReturnVoid
{ {
/// <summary> /// <summary>

@ -9,7 +9,6 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library; using MediaBrowser.Model.Library;
@ -735,14 +734,21 @@ namespace MediaBrowser.Api.Playback
{ {
if (audioStream != null) if (audioStream != null)
{ {
if (audioStream.Channels > 2 && request.AudioCodec.HasValue) if (audioStream.Channels > 2 && string.Equals(request.AudioCodec, "wma", StringComparison.OrdinalIgnoreCase))
{
if (request.AudioCodec.Value == AudioCodecs.Wma)
{ {
// wmav2 currently only supports two channel output // wmav2 currently only supports two channel output
return 2; 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; return request.AudioChannels;
@ -768,26 +774,26 @@ namespace MediaBrowser.Api.Playback
{ {
var codec = request.AudioCodec; var codec = request.AudioCodec;
if (codec.HasValue) if (!string.IsNullOrEmpty(codec))
{ {
if (codec == AudioCodecs.Aac) if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
{ {
return "aac -strict experimental"; return "aac -strict experimental";
} }
if (codec == AudioCodecs.Mp3) if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
{ {
return "libmp3lame"; return "libmp3lame";
} }
if (codec == AudioCodecs.Vorbis) if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase))
{ {
return "libvorbis"; return "libvorbis";
} }
if (codec == AudioCodecs.Wma) if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase))
{ {
return "wmav2"; return "wmav2";
} }
return codec.ToString().ToLower(); return codec.ToLower();
} }
return "copy"; return "copy";
@ -802,26 +808,26 @@ namespace MediaBrowser.Api.Playback
{ {
var codec = request.VideoCodec; var codec = request.VideoCodec;
if (codec.HasValue) if (!string.IsNullOrEmpty(codec))
{ {
if (codec == VideoCodecs.H264) if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
{ {
return "libx264"; return "libx264";
} }
if (codec == VideoCodecs.Vpx) if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
{ {
return "libvpx"; return "libvpx";
} }
if (codec == VideoCodecs.Wmv) if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase))
{ {
return "msmpeg4"; return "msmpeg4";
} }
if (codec == VideoCodecs.Theora) if (string.Equals(codec, "theora", StringComparison.OrdinalIgnoreCase))
{ {
return "libtheora"; return "libtheora";
} }
return codec.ToString().ToLower(); return codec.ToLower();
} }
return "copy"; return "copy";
@ -1162,18 +1168,23 @@ namespace MediaBrowser.Api.Playback
protected double? GetFramerateParam(StreamState state) protected double? GetFramerateParam(StreamState state)
{ {
if (state.VideoRequest != null && state.VideoRequest.Framerate.HasValue) if (state.VideoRequest != null)
{
if (state.VideoRequest.Framerate.HasValue)
{ {
return state.VideoRequest.Framerate.Value; return state.VideoRequest.Framerate.Value;
} }
var maxrate = state.VideoRequest.MaxFramerate ?? 23.976;
if (state.VideoStream != null) if (state.VideoStream != null)
{ {
var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate; var contentRate = state.VideoStream.AverageFrameRate ?? state.VideoStream.RealFrameRate;
if (contentRate.HasValue && contentRate.Value > 23.976) if (contentRate.HasValue && contentRate.Value > maxrate)
{ {
return 23.976; return maxrate;
}
} }
} }
@ -1201,77 +1212,102 @@ namespace MediaBrowser.Api.Playback
if (i == 0) if (i == 0)
{ {
request.DeviceId = val; // Device profile name
} }
else if (i == 1) else if (i == 1)
{ {
request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); request.DeviceId = val;
} }
else if (i == 2) else if (i == 2)
{
request.MediaSourceId = val;
}
else if (i == 3)
{
request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
else if (i == 4)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
videoRequest.VideoCodec = (VideoCodecs)Enum.Parse(typeof(VideoCodecs), val, true); videoRequest.VideoCodec = val;
} }
} }
else if (i == 3) else if (i == 5)
{ {
request.AudioCodec = (AudioCodecs)Enum.Parse(typeof(AudioCodecs), val, true); request.AudioCodec = val;
} }
else if (i == 4) else if (i == 6)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
videoRequest.AudioStreamIndex = int.Parse(val, UsCulture); videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
} }
} }
else if (i == 5) else if (i == 7)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture); videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
} }
} }
else if (i == 6) else if (i == 8)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
videoRequest.VideoBitRate = int.Parse(val, UsCulture); videoRequest.VideoBitRate = int.Parse(val, UsCulture);
} }
} }
else if (i == 7) else if (i == 9)
{ {
request.AudioBitRate = int.Parse(val, UsCulture); request.AudioBitRate = int.Parse(val, UsCulture);
} }
else if (i == 8) else if (i == 10)
{ {
request.AudioChannels = int.Parse(val, UsCulture); request.MaxAudioChannels = int.Parse(val, UsCulture);
} }
else if (i == 9) else if (i == 11)
{
if (videoRequest != null)
{
videoRequest.MaxWidth = int.Parse(val, UsCulture);
}
}
else if (i == 12)
{
if (videoRequest != null)
{
videoRequest.MaxHeight = int.Parse(val, UsCulture);
}
}
else if (i == 13)
{
if (videoRequest != null)
{
videoRequest.Framerate = int.Parse(val, UsCulture);
}
}
else if (i == 14)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
request.StartTimeTicks = long.Parse(val, UsCulture); request.StartTimeTicks = long.Parse(val, UsCulture);
} }
} }
else if (i == 10) else if (i == 15)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
videoRequest.Profile = val; videoRequest.Profile = val;
} }
} }
else if (i == 11) else if (i == 16)
{ {
if (videoRequest != null) if (videoRequest != null)
{ {
videoRequest.Level = val; videoRequest.Level = val;
} }
} }
else if (i == 12)
{
request.ForcedMimeType = val;
}
} }
} }
@ -1297,7 +1333,7 @@ namespace MediaBrowser.Api.Playback
var url = Request.PathInfo; var url = Request.PathInfo;
if (!request.AudioCodec.HasValue) if (string.IsNullOrEmpty(request.AudioCodec))
{ {
request.AudioCodec = InferAudioCodec(url); request.AudioCodec = InferAudioCodec(url);
} }
@ -1425,7 +1461,7 @@ namespace MediaBrowser.Api.Playback
if (videoRequest != null) if (videoRequest != null)
{ {
if (!videoRequest.VideoCodec.HasValue) if (string.IsNullOrEmpty(videoRequest.VideoCodec))
{ {
videoRequest.VideoCodec = InferVideoCodec(url); videoRequest.VideoCodec = InferVideoCodec(url);
} }
@ -1532,41 +1568,41 @@ namespace MediaBrowser.Api.Playback
/// </summary> /// </summary>
/// <param name="url">The URL.</param> /// <param name="url">The URL.</param>
/// <returns>System.Nullable{AudioCodecs}.</returns> /// <returns>System.Nullable{AudioCodecs}.</returns>
private AudioCodecs? InferAudioCodec(string url) private string InferAudioCodec(string url)
{ {
var ext = Path.GetExtension(url); var ext = Path.GetExtension(url);
if (string.Equals(ext, ".mp3", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".mp3", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Mp3; return "mp3";
} }
if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Aac; return "aac";
} }
if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Wma; return "wma";
} }
if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Vorbis; return "vorbis";
} }
if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Vorbis; return "vorbis";
} }
if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Vorbis; return "vorbis";
} }
if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Vorbis; return "vorbis";
} }
if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase))
{ {
return AudioCodecs.Vorbis; return "vorbis";
} }
return null; return null;
@ -1577,28 +1613,28 @@ namespace MediaBrowser.Api.Playback
/// </summary> /// </summary>
/// <param name="url">The URL.</param> /// <param name="url">The URL.</param>
/// <returns>System.Nullable{VideoCodecs}.</returns> /// <returns>System.Nullable{VideoCodecs}.</returns>
private VideoCodecs? InferVideoCodec(string url) private string InferVideoCodec(string url)
{ {
var ext = Path.GetExtension(url); var ext = Path.GetExtension(url);
if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
{ {
return VideoCodecs.Wmv; return "wmv";
} }
if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
{ {
return VideoCodecs.Vpx; return "vpx";
} }
if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
{ {
return VideoCodecs.Theora; return "theora";
} }
if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase)) if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
{ {
return VideoCodecs.H264; return "h264";
} }
return VideoCodecs.Copy; return "copy";
} }
} }
} }

@ -91,11 +91,11 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var state = GetState(request, CancellationToken.None).Result; var state = GetState(request, CancellationToken.None).Result;
if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy)) if (!state.VideoRequest.VideoBitRate.HasValue && (string.IsNullOrEmpty(state.VideoRequest.VideoCodec) || !string.Equals(state.VideoRequest.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase)))
{ {
throw new ArgumentException("A video bitrate is required"); throw new ArgumentException("A video bitrate is required");
} }
if (!state.Request.AudioBitRate.HasValue && (!state.Request.AudioCodec.HasValue || state.Request.AudioCodec.Value != AudioCodecs.Copy)) if (!state.Request.AudioBitRate.HasValue && (string.IsNullOrEmpty(state.Request.AudioCodec) || !string.Equals(state.Request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase)))
{ {
throw new ArgumentException("An audio bitrate is required"); throw new ArgumentException("An audio bitrate is required");
} }

@ -136,11 +136,11 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy)) if (!state.VideoRequest.VideoBitRate.HasValue && (string.IsNullOrEmpty(state.VideoRequest.VideoCodec) || !string.Equals(state.VideoRequest.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase)))
{ {
throw new ArgumentException("A video bitrate is required"); throw new ArgumentException("A video bitrate is required");
} }
if (!state.Request.AudioBitRate.HasValue && (!state.Request.AudioCodec.HasValue || state.Request.AudioCodec.Value != AudioCodecs.Copy)) if (!state.Request.AudioBitRate.HasValue && (string.IsNullOrEmpty(state.Request.AudioCodec) || !string.Equals(state.Request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase)))
{ {
throw new ArgumentException("An audio bitrate is required"); throw new ArgumentException("An audio bitrate is required");
} }

@ -50,38 +50,48 @@ namespace MediaBrowser.Api.Playback.Progressive
var videoRequest = state.Request as VideoStreamRequest; var videoRequest = state.Request as VideoStreamRequest;
// Try to infer based on the desired video codec // Try to infer based on the desired video codec
if (videoRequest != null && videoRequest.VideoCodec.HasValue) if (videoRequest != null && !string.IsNullOrEmpty(videoRequest.VideoCodec))
{ {
if (state.IsInputVideo) if (state.IsInputVideo)
{ {
switch (videoRequest.VideoCodec.Value) if (string.Equals(videoRequest.VideoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{ {
case VideoCodecs.H264:
return ".ts"; return ".ts";
case VideoCodecs.Theora: }
if (string.Equals(videoRequest.VideoCodec, "theora", StringComparison.OrdinalIgnoreCase))
{
return ".ogv"; return ".ogv";
case VideoCodecs.Vpx: }
if (string.Equals(videoRequest.VideoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
{
return ".webm"; return ".webm";
case VideoCodecs.Wmv: }
if (string.Equals(videoRequest.VideoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
{
return ".asf"; return ".asf";
} }
} }
} }
// Try to infer based on the desired audio codec // Try to infer based on the desired audio codec
if (state.Request.AudioCodec.HasValue) if (!string.IsNullOrEmpty(state.Request.AudioCodec))
{ {
if (!state.IsInputVideo) if (!state.IsInputVideo)
{ {
switch (state.Request.AudioCodec.Value) if (string.Equals("aac", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{ {
case AudioCodecs.Aac:
return ".aac"; return ".aac";
case AudioCodecs.Mp3: }
if (string.Equals("mp3", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".mp3"; return ".mp3";
case AudioCodecs.Vorbis: }
if (string.Equals("vorbis", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".ogg"; return ".ogg";
case AudioCodecs.Wma: }
if (string.Equals("wma", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".wma"; return ".wma";
} }
} }

@ -1,5 +1,4 @@
using MediaBrowser.Model.Dto; using ServiceStack;
using ServiceStack;
namespace MediaBrowser.Api.Playback namespace MediaBrowser.Api.Playback
{ {
@ -26,7 +25,7 @@ namespace MediaBrowser.Api.Playback
/// </summary> /// </summary>
/// <value>The audio codec.</value> /// <value>The audio codec.</value>
[ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public AudioCodecs? AudioCodec { get; set; } public string AudioCodec { get; set; }
/// <summary> /// <summary>
/// Gets or sets the start time ticks. /// Gets or sets the start time ticks.
@ -49,6 +48,9 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "AudioChannels", Description = "Optional. Specify a specific number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "AudioChannels", Description = "Optional. Specify a specific number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? AudioChannels { get; set; } public int? AudioChannels { get; set; }
[ApiMember(Name = "MaxAudioChannels", Description = "Optional. Specify a maximum number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? MaxAudioChannels { get; set; }
/// <summary> /// <summary>
/// Gets or sets the audio sample rate. /// Gets or sets the audio sample rate.
/// </summary> /// </summary>
@ -69,8 +71,6 @@ namespace MediaBrowser.Api.Playback
public bool ThrowDebugError { get; set; } public bool ThrowDebugError { get; set; }
public string Params { get; set; } public string Params { get; set; }
public string ForcedMimeType { get; set; }
} }
public class VideoStreamRequest : StreamRequest public class VideoStreamRequest : StreamRequest
@ -80,7 +80,7 @@ namespace MediaBrowser.Api.Playback
/// </summary> /// </summary>
/// <value>The video codec.</value> /// <value>The video codec.</value>
[ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public VideoCodecs? VideoCodec { get; set; } public string VideoCodec { get; set; }
/// <summary> /// <summary>
/// Gets or sets the video bit rate. /// Gets or sets the video bit rate.
@ -145,6 +145,9 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "Framerate", Description = "Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "Framerate", Description = "Optional. A specific video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
public double? Framerate { get; set; } public double? Framerate { get; set; }
[ApiMember(Name = "MaxFramerate", Description = "Optional. A specific maximum video framerate to encode to, e.g. 23.976. Generally this should be omitted unless the device has specific requirements.", IsRequired = false, DataType = "double", ParameterType = "query", Verb = "GET")]
public double? MaxFramerate { get; set; }
/// <summary> /// <summary>
/// Gets or sets the profile. /// Gets or sets the profile.
/// </summary> /// </summary>

@ -79,16 +79,6 @@ namespace MediaBrowser.Api.Playback
public string GetMimeType(string outputPath) public string GetMimeType(string outputPath)
{ {
if (!string.IsNullOrWhiteSpace(Request.ForcedMimeType))
{
if (VideoRequest == null)
{
return "audio/" + Request.ForcedMimeType;
}
return "video/" + Request.ForcedMimeType;
}
return MimeTypes.GetMimeType(outputPath); return MimeTypes.GetMimeType(outputPath);
} }
} }

@ -18,8 +18,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class Plugins /// Class Plugins
/// </summary> /// </summary>
[Route("/Plugins", "GET")] [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")]
[Api(("Gets a list of currently installed plugins"))]
public class GetPlugins : IReturn<List<PluginInfo>> public class GetPlugins : IReturn<List<PluginInfo>>
{ {
} }
@ -27,8 +26,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class UninstallPlugin /// Class UninstallPlugin
/// </summary> /// </summary>
[Route("/Plugins/{Id}", "DELETE")] [Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")]
[Api(("Uninstalls a plugin"))]
public class UninstallPlugin : IReturnVoid public class UninstallPlugin : IReturnVoid
{ {
/// <summary> /// <summary>
@ -42,8 +40,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetPluginConfiguration /// Class GetPluginConfiguration
/// </summary> /// </summary>
[Route("/Plugins/{Id}/Configuration", "GET")] [Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")]
[Api(("Gets a plugin's configuration"))]
public class GetPluginConfiguration public class GetPluginConfiguration
{ {
/// <summary> /// <summary>
@ -57,8 +54,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class UpdatePluginConfiguration /// Class UpdatePluginConfiguration
/// </summary> /// </summary>
[Route("/Plugins/{Id}/Configuration", "POST")] [Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")]
[Api(("Updates a plugin's configuration"))]
public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid
{ {
/// <summary> /// <summary>
@ -78,8 +74,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetPluginSecurityInfo /// Class GetPluginSecurityInfo
/// </summary> /// </summary>
[Route("/Plugins/SecurityInfo", "GET")] [Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information")]
[Api(("Gets plugin registration information"))]
public class GetPluginSecurityInfo : IReturn<PluginSecurityInfo> public class GetPluginSecurityInfo : IReturn<PluginSecurityInfo>
{ {
} }
@ -87,14 +82,12 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class UpdatePluginSecurityInfo /// Class UpdatePluginSecurityInfo
/// </summary> /// </summary>
[Route("/Plugins/SecurityInfo", "POST")] [Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information")]
[Api("Updates plugin registration information")]
public class UpdatePluginSecurityInfo : PluginSecurityInfo, IReturnVoid public class UpdatePluginSecurityInfo : PluginSecurityInfo, IReturnVoid
{ {
} }
[Route("/Plugins/RegistrationRecords/{Name}", "GET")] [Route("/Plugins/RegistrationRecords/{Name}", "GET", Summary = "Gets registration status for a feature")]
[Api("Gets registration status for a feature")]
public class GetRegistrationStatus public class GetRegistrationStatus
{ {
[ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]

@ -39,6 +39,8 @@ namespace MediaBrowser.Api.ScheduledTasks
TaskManager = taskManager; TaskManager = taskManager;
} }
private bool _lastResponseHadTasksRunning = true;
/// <summary> /// <summary>
/// Gets the data to send. /// Gets the data to send.
/// </summary> /// </summary>
@ -46,7 +48,25 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <returns>Task{IEnumerable{TaskInfo}}.</returns> /// <returns>Task{IEnumerable{TaskInfo}}.</returns>
protected override Task<IEnumerable<TaskInfo>> GetDataToSend(object state) protected override Task<IEnumerable<TaskInfo>> GetDataToSend(object state)
{ {
return Task.FromResult(TaskManager.ScheduledTasks var tasks = TaskManager.ScheduledTasks.ToList();
var anyRunning = tasks.Any(i => i.State != TaskState.Idle);
if (anyRunning)
{
_lastResponseHadTasksRunning = true;
}
else
{
if (!_lastResponseHadTasksRunning)
{
return Task.FromResult<IEnumerable<TaskInfo>>(null);
}
_lastResponseHadTasksRunning = false;
}
return Task.FromResult(tasks
.OrderBy(i => i.Name) .OrderBy(i => i.Name)
.Select(ScheduledTaskHelpers.GetTaskInfo) .Select(ScheduledTaskHelpers.GetTaskInfo)
.Where(i => !i.IsHidden)); .Where(i => !i.IsHidden));

@ -1,5 +1,4 @@
using System; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -8,6 +7,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Search; using MediaBrowser.Model.Search;
using ServiceStack; using ServiceStack;
using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -16,8 +16,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetSearchHints /// Class GetSearchHints
/// </summary> /// </summary>
[Route("/Search/Hints", "GET")] [Route("/Search/Hints", "GET", Summary = "Gets search hints based on a search term")]
[Api(Description = "Gets search hints based on a search term")]
public class GetSearchHints : IReturn<SearchHintResult> public class GetSearchHints : IReturn<SearchHintResult>
{ {
/// <summary> /// <summary>

@ -14,8 +14,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetSessions /// Class GetSessions
/// </summary> /// </summary>
[Route("/Sessions", "GET")] [Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
[Api(("Gets a list of sessions"))]
public class GetSessions : IReturn<List<SessionInfoDto>> public class GetSessions : IReturn<List<SessionInfoDto>>
{ {
/// <summary> /// <summary>
@ -28,15 +27,14 @@ namespace MediaBrowser.Api
[ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid? ControllableByUserId { get; set; } public Guid? ControllableByUserId { get; set; }
[ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceId { get; set; } public string DeviceId { get; set; }
} }
/// <summary> /// <summary>
/// Class BrowseTo /// Class BrowseTo
/// </summary> /// </summary>
[Route("/Sessions/{Id}/Viewing", "POST")] [Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")]
[Api(("Instructs a session to browse to an item or view"))]
public class BrowseTo : IReturnVoid public class BrowseTo : IReturnVoid
{ {
/// <summary> /// <summary>
@ -77,8 +75,7 @@ namespace MediaBrowser.Api
public string Context { get; set; } public string Context { get; set; }
} }
[Route("/Sessions/{Id}/Playing", "POST")] [Route("/Sessions/{Id}/Playing", "POST", Summary = "Instructs a session to play an item")]
[Api(("Instructs a session to play an item"))]
public class Play : IReturnVoid public class Play : IReturnVoid
{ {
/// <summary> /// <summary>
@ -110,8 +107,7 @@ namespace MediaBrowser.Api
public PlayCommand PlayCommand { get; set; } public PlayCommand PlayCommand { get; set; }
} }
[Route("/Sessions/{Id}/Playing/{Command}", "POST")] [Route("/Sessions/{Id}/Playing/{Command}", "POST", Summary = "Issues a playstate command to a client")]
[Api(("Issues a playstate command to a client"))]
public class SendPlaystateCommand : IReturnVoid public class SendPlaystateCommand : IReturnVoid
{ {
/// <summary> /// <summary>
@ -135,8 +131,7 @@ namespace MediaBrowser.Api
public PlaystateCommand Command { get; set; } public PlaystateCommand Command { get; set; }
} }
[Route("/Sessions/{Id}/System/{Command}", "POST")] [Route("/Sessions/{Id}/System/{Command}", "POST", Summary = "Issues a system command to a client")]
[Api(("Issues a system command to a client"))]
public class SendSystemCommand : IReturnVoid public class SendSystemCommand : IReturnVoid
{ {
/// <summary> /// <summary>
@ -154,8 +149,7 @@ namespace MediaBrowser.Api
public SystemCommand Command { get; set; } public SystemCommand Command { get; set; }
} }
[Route("/Sessions/{Id}/Message", "POST")] [Route("/Sessions/{Id}/Message", "POST", Summary = "Issues a command to a client to display a message to the user")]
[Api(("Issues a command to a client to display a message to the user"))]
public class SendMessageCommand : IReturnVoid public class SendMessageCommand : IReturnVoid
{ {
/// <summary> /// <summary>
@ -175,8 +169,7 @@ namespace MediaBrowser.Api
public long? TimeoutMs { get; set; } public long? TimeoutMs { get; set; }
} }
[Route("/Sessions/{Id}/Users/{UserId}", "POST")] [Route("/Sessions/{Id}/Users/{UserId}", "POST", Summary = "Adds an additional user to a session")]
[Api(("Adds an additional user to a session"))]
public class AddUserToSession : IReturnVoid public class AddUserToSession : IReturnVoid
{ {
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@ -186,8 +179,7 @@ namespace MediaBrowser.Api
public Guid UserId { get; set; } public Guid UserId { get; set; }
} }
[Route("/Sessions/{Id}/Users/{UserId}", "DELETE")] [Route("/Sessions/{Id}/Users/{UserId}", "DELETE", Summary = "Removes an additional user from a session")]
[Api(("Removes an additional user from a session"))]
public class RemoveUserFromSession : IReturnVoid public class RemoveUserFromSession : IReturnVoid
{ {
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@ -197,8 +189,7 @@ namespace MediaBrowser.Api
public Guid UserId { get; set; } public Guid UserId { get; set; }
} }
[Route("/Sessions/{Id}/Capabilities", "POST")] [Route("/Sessions/{Id}/Capabilities", "POST", Summary = "Updates capabilities for a device")]
[Api(("Updates capabilities for a device"))]
public class PostCapabilities : IReturnVoid public class PostCapabilities : IReturnVoid
{ {
/// <summary> /// <summary>

@ -8,8 +8,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetSystemInfo /// Class GetSystemInfo
/// </summary> /// </summary>
[Route("/System/Info", "GET")] [Route("/System/Info", "GET", Summary = "Gets information about the server")]
[Api(Description = "Gets information about the server")]
public class GetSystemInfo : IReturn<SystemInfo> public class GetSystemInfo : IReturn<SystemInfo>
{ {
@ -18,14 +17,12 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class RestartApplication /// Class RestartApplication
/// </summary> /// </summary>
[Route("/System/Restart", "POST")] [Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")]
[Api(("Restarts the application, if needed"))]
public class RestartApplication public class RestartApplication
{ {
} }
[Route("/System/Shutdown", "POST")] [Route("/System/Shutdown", "POST", Summary = "Shuts down the application")]
[Api(("Shuts down the application"))]
public class ShutdownApplication public class ShutdownApplication
{ {
} }

@ -17,8 +17,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetNextUpEpisodes /// Class GetNextUpEpisodes
/// </summary> /// </summary>
[Route("/Shows/NextUp", "GET")] [Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")]
[Api(("Gets a list of next up episodes"))]
public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
{ {
/// <summary> /// <summary>
@ -53,8 +52,7 @@ namespace MediaBrowser.Api
public string SeriesId { get; set; } public string SeriesId { get; set; }
} }
[Route("/Shows/Upcoming", "GET")] [Route("/Shows/Upcoming", "GET", Summary = "Gets a list of upcoming episodes")]
[Api(("Gets a list of upcoming episodes"))]
public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields
{ {
/// <summary> /// <summary>
@ -86,14 +84,12 @@ namespace MediaBrowser.Api
public string Fields { get; set; } public string Fields { get; set; }
} }
[Route("/Shows/{Id}/Similar", "GET")] [Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")]
[Api(Description = "Finds tv shows similar to a given one.")]
public class GetSimilarShows : BaseGetSimilarItemsFromItem public class GetSimilarShows : BaseGetSimilarItemsFromItem
{ {
} }
[Route("/Shows/{Id}/Episodes", "GET")] [Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")]
[Api(Description = "Gets episodes for a tv season")]
public class GetEpisodes : IReturn<ItemsResult>, IHasItemFields public class GetEpisodes : IReturn<ItemsResult>, IHasItemFields
{ {
/// <summary> /// <summary>
@ -129,8 +125,7 @@ namespace MediaBrowser.Api
public string AdjacentTo { get; set; } public string AdjacentTo { get; set; }
} }
[Route("/Shows/{Id}/Seasons", "GET")] [Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
[Api(Description = "Gets seasons for a tv series")]
public class GetSeasons : IReturn<ItemsResult>, IHasItemFields public class GetSeasons : IReturn<ItemsResult>, IHasItemFields
{ {
/// <summary> /// <summary>
@ -320,7 +315,7 @@ namespace MediaBrowser.Api
return 0; return 0;
}) })
.ThenByDescending(i =>i.Item2) .ThenByDescending(i => i.Item2)
.ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue) .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
.Select(i => i.Item1); .Select(i => i.Item1);
} }

@ -17,8 +17,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetUsers /// Class GetUsers
/// </summary> /// </summary>
[Route("/Users", "GET")] [Route("/Users", "GET", Summary = "Gets a list of users")]
[Api(Description = "Gets a list of users")]
public class GetUsers : IReturn<List<UserDto>> public class GetUsers : IReturn<List<UserDto>>
{ {
[ApiMember(Name = "IsHidden", Description = "Optional filter by IsHidden=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "IsHidden", Description = "Optional filter by IsHidden=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
@ -28,8 +27,7 @@ namespace MediaBrowser.Api
public bool? IsDisabled { get; set; } public bool? IsDisabled { get; set; }
} }
[Route("/Users/Public", "GET")] [Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")]
[Api(Description = "Gets a list of publicly visible users for display on a login screen.")]
public class GetPublicUsers : IReturn<List<UserDto>> public class GetPublicUsers : IReturn<List<UserDto>>
{ {
} }
@ -37,8 +35,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class GetUser /// Class GetUser
/// </summary> /// </summary>
[Route("/Users/{Id}", "GET")] [Route("/Users/{Id}", "GET", Summary = "Gets a user by Id")]
[Api(Description = "Gets a user by Id")]
public class GetUser : IReturn<UserDto> public class GetUser : IReturn<UserDto>
{ {
/// <summary> /// <summary>
@ -52,8 +49,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class DeleteUser /// Class DeleteUser
/// </summary> /// </summary>
[Route("/Users/{Id}", "DELETE")] [Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")]
[Api(Description = "Deletes a user")]
public class DeleteUser : IReturnVoid public class DeleteUser : IReturnVoid
{ {
/// <summary> /// <summary>
@ -67,8 +63,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class AuthenticateUser /// Class AuthenticateUser
/// </summary> /// </summary>
[Route("/Users/{Id}/Authenticate", "POST")] [Route("/Users/{Id}/Authenticate", "POST", Summary = "Authenticates a user")]
[Api(Description = "Authenticates a user")]
public class AuthenticateUser : IReturn<AuthenticationResult> public class AuthenticateUser : IReturn<AuthenticationResult>
{ {
/// <summary> /// <summary>
@ -89,8 +84,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class AuthenticateUser /// Class AuthenticateUser
/// </summary> /// </summary>
[Route("/Users/AuthenticateByName", "POST")] [Route("/Users/AuthenticateByName", "POST", Summary = "Authenticates a user")]
[Api(Description = "Authenticates a user")]
public class AuthenticateUserByName : IReturn<AuthenticationResult> public class AuthenticateUserByName : IReturn<AuthenticationResult>
{ {
/// <summary> /// <summary>
@ -111,8 +105,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class UpdateUserPassword /// Class UpdateUserPassword
/// </summary> /// </summary>
[Route("/Users/{Id}/Password", "POST")] [Route("/Users/{Id}/Password", "POST", Summary = "Updates a user's password")]
[Api(Description = "Updates a user's password")]
public class UpdateUserPassword : IReturnVoid public class UpdateUserPassword : IReturnVoid
{ {
/// <summary> /// <summary>
@ -143,8 +136,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class UpdateUser /// Class UpdateUser
/// </summary> /// </summary>
[Route("/Users/{Id}", "POST")] [Route("/Users/{Id}", "POST", Summary = "Updates a user")]
[Api(Description = "Updates a user")]
public class UpdateUser : UserDto, IReturnVoid public class UpdateUser : UserDto, IReturnVoid
{ {
} }
@ -152,8 +144,7 @@ namespace MediaBrowser.Api
/// <summary> /// <summary>
/// Class CreateUser /// Class CreateUser
/// </summary> /// </summary>
[Route("/Users", "POST")] [Route("/Users", "POST", Summary = "Creates a user")]
[Api(Description = "Creates a user")]
public class CreateUser : UserDto, IReturn<UserDto> public class CreateUser : UserDto, IReturn<UserDto>
{ {
} }

@ -1,11 +1,11 @@
using System.Globalization; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace MediaBrowser.Common.Net namespace MediaBrowser.Common.Net
{ {
@ -16,6 +16,7 @@ namespace MediaBrowser.Common.Net
/// <typeparam name="TStateType">The type of the T state type.</typeparam> /// <typeparam name="TStateType">The type of the T state type.</typeparam>
public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IDisposable public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IDisposable
where TStateType : class, new() where TStateType : class, new()
where TReturnDataType : class
{ {
/// <summary> /// <summary>
/// The _active connections /// The _active connections
@ -144,12 +145,15 @@ namespace MediaBrowser.Common.Net
var data = await GetDataToSend(tuple.Item4).ConfigureAwait(false); var data = await GetDataToSend(tuple.Item4).ConfigureAwait(false);
if (data != null)
{
await connection.SendAsync(new WebSocketMessage<TReturnDataType> await connection.SendAsync(new WebSocketMessage<TReturnDataType>
{ {
MessageType = Name, MessageType = Name,
Data = data Data = data
}, tuple.Item2.Token).ConfigureAwait(false); }, tuple.Item2.Token).ConfigureAwait(false);
}
tuple.Item5.Release(); tuple.Item5.Release();
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace MediaBrowser.Controller.Dlna namespace MediaBrowser.Controller.Dlna
@ -6,18 +7,25 @@ namespace MediaBrowser.Controller.Dlna
public class CodecProfile public class CodecProfile
{ {
public CodecType Type { get; set; } public CodecType Type { get; set; }
public List<ProfileCondition> Conditions { get; set; } public ProfileCondition[] Conditions { get; set; }
public string Codec { get; set; } public string Codec { get; set; }
public CodecProfile() public CodecProfile()
{ {
Conditions = new List<ProfileCondition>(); Conditions = new ProfileCondition[] {};
} }
public List<string> GetCodecs() public List<string> GetCodecs()
{ {
return (Codec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); return (Codec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
} }
public bool ContainsCodec(string codec)
{
var codecs = GetCodecs();
return codecs.Count == 0 || codecs.Contains(codec, StringComparer.OrdinalIgnoreCase);
}
} }
public enum CodecType public enum CodecType
@ -57,9 +65,12 @@ namespace MediaBrowser.Controller.Dlna
Width, Width,
Height, Height,
Has64BitOffsets, Has64BitOffsets,
VideoBitDepth,
VideoBitrate, VideoBitrate,
VideoFramerate, VideoFramerate,
VideoLevel, VideoLevel,
VideoProfile VideoPacketLength,
VideoProfile,
VideoTimestamp
} }
} }

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Dlna
{
public class ContainerProfile
{
public DlnaProfileType Type { get; set; }
public ProfileCondition[] Conditions { get; set; }
public string Container { get; set; }
public ContainerProfile()
{
Conditions = new ProfileCondition[] { };
}
public List<string> GetContainers()
{
return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
}
}
}

@ -1,5 +1,4 @@
using System.Collections.Generic; 
namespace MediaBrowser.Controller.Dlna namespace MediaBrowser.Controller.Dlna
{ {
public class DeviceIdentification public class DeviceIdentification
@ -55,11 +54,11 @@ namespace MediaBrowser.Controller.Dlna
/// Gets or sets the headers. /// Gets or sets the headers.
/// </summary> /// </summary>
/// <value>The headers.</value> /// <value>The headers.</value>
public List<HttpHeaderInfo> Headers { get; set; } public HttpHeaderInfo[] Headers { get; set; }
public DeviceIdentification() public DeviceIdentification()
{ {
Headers = new List<HttpHeaderInfo>(); Headers = new HttpHeaderInfo[] {};
} }
} }
@ -73,6 +72,7 @@ namespace MediaBrowser.Controller.Dlna
public enum HeaderMatchType public enum HeaderMatchType
{ {
Equals = 0, Equals = 0,
Substring = 1 Regex = 1,
Substring = 2
} }
} }

@ -27,6 +27,8 @@ namespace MediaBrowser.Controller.Dlna
/// <value>The direct play profiles.</value> /// <value>The direct play profiles.</value>
public DirectPlayProfile[] DirectPlayProfiles { get; set; } public DirectPlayProfile[] DirectPlayProfiles { get; set; }
public ContainerProfile[] ContainerProfiles { get; set; }
/// <summary> /// <summary>
/// Gets or sets the identification. /// Gets or sets the identification.
/// </summary> /// </summary>
@ -40,6 +42,9 @@ namespace MediaBrowser.Controller.Dlna
public string ModelDescription { get; set; } public string ModelDescription { get; set; }
public string ModelNumber { get; set; } public string ModelNumber { get; set; }
public string ModelUrl { get; set; } public string ModelUrl { get; set; }
public bool IgnoreTranscodeByteRangeRequests { get; set; }
public bool SupportsAlbumArtInDidl { get; set; }
/// <summary> /// <summary>
/// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace. /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
/// </summary> /// </summary>
@ -69,6 +74,7 @@ namespace MediaBrowser.Controller.Dlna
TranscodingProfiles = new TranscodingProfile[] { }; TranscodingProfiles = new TranscodingProfile[] { };
MediaProfiles = new MediaProfile[] { }; MediaProfiles = new MediaProfile[] { };
CodecProfiles = new CodecProfile[] { }; CodecProfiles = new CodecProfile[] { };
ContainerProfiles = new ContainerProfile[] { };
} }
} }
} }

@ -5,19 +5,15 @@ namespace MediaBrowser.Controller.Dlna
{ {
public class DirectPlayProfile public class DirectPlayProfile
{ {
public string[] Containers { get; set; } public string Container { get; set; }
public string AudioCodec { get; set; } public string AudioCodec { get; set; }
public string VideoCodec { get; set; } public string VideoCodec { get; set; }
public DlnaProfileType Type { get; set; } public DlnaProfileType Type { get; set; }
public List<ProfileCondition> Conditions { get; set; } public List<string> GetContainers()
public DirectPlayProfile()
{ {
Conditions = new List<ProfileCondition>(); return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
Containers = new string[] { };
} }
public List<string> GetAudioCodecs() public List<string> GetAudioCodecs()

@ -13,6 +13,13 @@ namespace MediaBrowser.Controller.Dlna
public string OrgPn { get; set; } public string OrgPn { get; set; }
public string MimeType { get; set; } public string MimeType { get; set; }
public ProfileCondition[] Conditions { get; set; }
public MediaProfile()
{
Conditions = new ProfileCondition[] {};
}
public List<string> GetAudioCodecs() public List<string> GetAudioCodecs()
{ {
return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();

@ -1,5 +1,4 @@
using System.Collections.Generic; 
namespace MediaBrowser.Controller.Dlna namespace MediaBrowser.Controller.Dlna
{ {
public class TranscodingProfile public class TranscodingProfile
@ -15,12 +14,14 @@ namespace MediaBrowser.Controller.Dlna
public TranscodeSeekInfo TranscodeSeekInfo { get; set; } public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
public List<TranscodingSetting> Settings { get; set; } public TranscodingSetting[] Settings { get; set; }
public TranscodingProfile() public TranscodingProfile()
{ {
Settings = new List<TranscodingSetting>(); Settings = new TranscodingSetting[] { };
} }
public bool EnableMpegtsM2TsMode { get; set; }
} }
public class TranscodingSetting public class TranscodingSetting
@ -31,8 +32,7 @@ namespace MediaBrowser.Controller.Dlna
public enum TranscodingSettingType public enum TranscodingSettingType
{ {
Profile = 0, VideoProfile = 0
MaxAudioChannels = 1
} }
public enum TranscodeSeekInfo public enum TranscodeSeekInfo

@ -79,6 +79,7 @@
<Compile Include="Collections\CollectionCreationOptions.cs" /> <Compile Include="Collections\CollectionCreationOptions.cs" />
<Compile Include="Collections\ICollectionManager.cs" /> <Compile Include="Collections\ICollectionManager.cs" />
<Compile Include="Dlna\CodecProfile.cs" /> <Compile Include="Dlna\CodecProfile.cs" />
<Compile Include="Dlna\ContainerProfile.cs" />
<Compile Include="Dlna\DeviceIdentification.cs" /> <Compile Include="Dlna\DeviceIdentification.cs" />
<Compile Include="Dlna\DirectPlayProfile.cs" /> <Compile Include="Dlna\DirectPlayProfile.cs" />
<Compile Include="Dlna\IDlnaManager.cs" /> <Compile Include="Dlna\IDlnaManager.cs" />

File diff suppressed because it is too large Load Diff

@ -81,7 +81,28 @@
<Compile Include="PlayTo\uIcon.cs" /> <Compile Include="PlayTo\uIcon.cs" />
<Compile Include="PlayTo\uParser.cs" /> <Compile Include="PlayTo\uParser.cs" />
<Compile Include="PlayTo\uPnpNamespaces.cs" /> <Compile Include="PlayTo\uPnpNamespaces.cs" />
<Compile Include="Profiles\DefaultProfile.cs" />
<Compile Include="Profiles\DenonAvrProfile.cs" />
<Compile Include="Profiles\LgTvProfile.cs" />
<Compile Include="Profiles\LinksysDMA2100Profile.cs" />
<Compile Include="Profiles\PanasonicVieraProfile.cs" />
<Compile Include="Profiles\SamsungSmartTvProfile.cs" />
<Compile Include="Profiles\SonyBlurayPlayer2013Profile.cs" />
<Compile Include="Profiles\SonyBlurayPlayerProfile.cs" />
<Compile Include="Profiles\SonyBravia2010Profile.cs" />
<Compile Include="Profiles\SonyBravia2011Profile.cs" />
<Compile Include="Profiles\SonyBravia2012Profile.cs" />
<Compile Include="Profiles\SonyBravia2013Profile.cs" />
<Compile Include="Profiles\SonyPs3Profile.cs" />
<Compile Include="Profiles\WdtvLiveProfile.cs" />
<Compile Include="Profiles\Xbox360Profile.cs" />
<Compile Include="Profiles\XboxOneProfile.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Server\DlnaServerEntryPoint.cs" />
<Compile Include="Server\Headers.cs" />
<Compile Include="Server\RawHeaders.cs" />
<Compile Include="Server\SsdpHandler.cs" />
<Compile Include="Server\UpnpDevice.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@ -97,9 +118,7 @@
<Name>MediaBrowser.Model</Name> <Name>MediaBrowser.Model</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup />
<Folder Include="Server\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

@ -681,6 +681,9 @@ namespace MediaBrowser.Dlna.PlayTo
var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault(); var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
if (presentationUrl != null) if (presentationUrl != null)
deviceProperties.PresentationUrl = presentationUrl.Value; deviceProperties.PresentationUrl = presentationUrl.Value;
var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
if (modelUrl != null)
deviceProperties.ModelUrl = modelUrl.Value;
deviceProperties.BaseUrl = String.Format("http://{0}:{1}", url.Host, url.Port); deviceProperties.BaseUrl = String.Format("http://{0}:{1}", url.Host, url.Port);

@ -34,6 +34,8 @@ namespace MediaBrowser.Dlna.PlayTo
public string ModelNumber { get; set; } public string ModelNumber { get; set; }
public string ModelUrl { get; set; }
public string Manufacturer { get; set; } public string Manufacturer { get; set; }
public string ManufacturerUrl { get; set; } public string ManufacturerUrl { get; set; }
@ -72,7 +74,8 @@ namespace MediaBrowser.Dlna.PlayTo
ModelName = ModelName, ModelName = ModelName,
ModelNumber = ModelNumber, ModelNumber = ModelNumber,
FriendlyName = Name, FriendlyName = Name,
ManufacturerUrl = ManufacturerUrl ManufacturerUrl = ManufacturerUrl,
ModelUrl = ModelUrl
}; };
} }
} }

@ -270,7 +270,7 @@ namespace MediaBrowser.Dlna.PlayTo
playlistItem.StartPositionTicks = newItem.StartPositionTicks; playlistItem.StartPositionTicks = newItem.StartPositionTicks;
playlistItem.StreamUrl = newItem.StreamUrl; playlistItem.StreamUrl = newItem.StreamUrl;
playlistItem.Didl = newItem.Didl; playlistItem.Didl = newItem.Didl;
return _device.SetAvTransport(playlistItem.StreamUrl, playlistItem.DlnaHeaders, playlistItem.Didl); return _device.SetAvTransport(playlistItem.StreamUrl, GetDlnaHeaders(playlistItem), playlistItem.Didl);
} }
return _device.Seek(TimeSpan.FromTicks(command.SeekPositionTicks ?? 0)); return _device.Seek(TimeSpan.FromTicks(command.SeekPositionTicks ?? 0));
@ -391,16 +391,23 @@ namespace MediaBrowser.Dlna.PlayTo
private PlaylistItem CreatePlaylistItem(BaseItem item, long startPostionTicks, string serverAddress) private PlaylistItem CreatePlaylistItem(BaseItem item, long startPostionTicks, string serverAddress)
{ {
var streams = _itemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList(); var streams = _itemRepository.GetMediaStreams(new MediaStreamQuery
{
ItemId = item.Id
}).ToList();
var deviceInfo = _device.Properties; var deviceInfo = _device.Properties;
var playlistItem = GetPlaylistItem(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification())); var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification());
var playlistItem = GetPlaylistItem(item, streams, profile);
playlistItem.StartPositionTicks = startPostionTicks; playlistItem.StartPositionTicks = startPostionTicks;
playlistItem.DeviceProfileName = profile.Name;
if (playlistItem.MediaType == DlnaProfileType.Audio) if (playlistItem.MediaType == DlnaProfileType.Audio)
{ {
playlistItem.StreamUrl = StreamHelper.GetAudioUrl(playlistItem, serverAddress); playlistItem.StreamUrl = StreamHelper.GetAudioUrl(deviceInfo, playlistItem, streams, serverAddress);
} }
else else
{ {
@ -410,32 +417,92 @@ namespace MediaBrowser.Dlna.PlayTo
var didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams); var didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams);
playlistItem.Didl = didl; playlistItem.Didl = didl;
var header = StreamHelper.GetDlnaHeaders(playlistItem);
playlistItem.DlnaHeaders = header;
return playlistItem; return playlistItem;
} }
private PlaylistItem GetPlaylistItem(BaseItem item, DeviceProfile profile) private string GetDlnaHeaders(PlaylistItem item)
{
var orgOp = item.Transcode ? ";DLNA.ORG_OP=00" : ";DLNA.ORG_OP=01";
var orgCi = item.Transcode ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
string contentFeatures;
var container = item.Container.TrimStart('.');
if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MP3";
}
else if (string.Equals(container, "wma", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMABASE";
}
else if (string.Equals(container, "wmw", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
else if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
else if (string.Equals(container, "avi", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVI";
}
else if (string.Equals(container, "mkv", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MATROSKA";
}
else if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC";
}
else if (string.Equals(container, "mpeg", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (item.MediaType == DlnaProfileType.Video)
{
// Default to AVI for video
contentFeatures = "DLNA.ORG_PN=AVI";
}
else
{
// Default to MP3 for audio
contentFeatures = "DLNA.ORG_PN=MP3";
}
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
}
private PlaylistItem GetPlaylistItem(BaseItem item, List<MediaStream> mediaStreams, DeviceProfile profile)
{ {
var video = item as Video; var video = item as Video;
if (video != null) if (video != null)
{ {
return new PlaylistItemFactory(_itemRepository).Create(video, profile); return new PlaylistItemFactory().Create(video, mediaStreams, profile);
} }
var audio = item as Audio; var audio = item as Audio;
if (audio != null) if (audio != null)
{ {
return new PlaylistItemFactory(_itemRepository).Create(audio, profile); return new PlaylistItemFactory().Create(audio, mediaStreams, profile);
} }
var photo = item as Photo; var photo = item as Photo;
if (photo != null) if (photo != null)
{ {
return new PlaylistItemFactory(_itemRepository).Create(photo, profile); return new PlaylistItemFactory().Create(photo, profile);
} }
throw new ArgumentException("Unrecognized item type."); throw new ArgumentException("Unrecognized item type.");
@ -482,11 +549,18 @@ namespace MediaBrowser.Dlna.PlayTo
await _device.SetStop(); await _device.SetStop();
return true; return true;
} }
nextTrack.PlayState = 1; nextTrack.PlayState = 1;
_logger.Debug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", _device.Properties.Name, nextTrack.StreamUrl, nextTrack.DlnaHeaders);
await _device.SetAvTransport(nextTrack.StreamUrl, nextTrack.DlnaHeaders, nextTrack.Didl); var dlnaheaders = GetDlnaHeaders(nextTrack);
_logger.Debug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", _device.Properties.Name, nextTrack.StreamUrl, dlnaheaders);
await _device.SetAvTransport(nextTrack.StreamUrl, dlnaheaders, nextTrack.Didl);
if (nextTrack.StartPositionTicks > 0 && !nextTrack.Transcode) if (nextTrack.StartPositionTicks > 0 && !nextTrack.Transcode)
await _device.Seek(TimeSpan.FromTicks(nextTrack.StartPositionTicks)); await _device.Seek(TimeSpan.FromTicks(nextTrack.StartPositionTicks));
return true; return true;
} }
@ -508,7 +582,7 @@ namespace MediaBrowser.Dlna.PlayTo
return Task.FromResult(false); return Task.FromResult(false);
prevTrack.PlayState = 1; prevTrack.PlayState = 1;
return _device.SetAvTransport(prevTrack.StreamUrl, prevTrack.DlnaHeaders, prevTrack.Didl); return _device.SetAvTransport(prevTrack.StreamUrl, GetDlnaHeaders(prevTrack), prevTrack.Didl);
} }
#endregion #endregion

@ -7,6 +7,7 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
@ -16,7 +17,6 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Session;
namespace MediaBrowser.Dlna.PlayTo namespace MediaBrowser.Dlna.PlayTo
{ {
@ -54,10 +54,8 @@ namespace MediaBrowser.Dlna.PlayTo
_config = config; _config = config;
} }
public async void Start() public void Start()
{ {
_logger.Log(LogSeverity.Info, "PlayTo-Manager starting");
_locations = new ConcurrentDictionary<string, DateTime>(); _locations = new ConcurrentDictionary<string, DateTime>();
foreach (var network in NetworkInterface.GetAllNetworkInterfaces()) foreach (var network in NetworkInterface.GetAllNetworkInterfaces())
@ -73,7 +71,7 @@ namespace MediaBrowser.Dlna.PlayTo
IPAddress localIp = null; IPAddress localIp = null;
foreach (UnicastIPAddressInformation ipInfo in network.GetIPProperties().UnicastAddresses) foreach (var ipInfo in network.GetIPProperties().UnicastAddresses)
{ {
if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork) if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork)
{ {
@ -95,8 +93,6 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
_logger.ErrorException("Failed to Initilize Socket", e); _logger.ErrorException("Failed to Initilize Socket", e);
} }
await Task.Delay(100).ConfigureAwait(false);
} }
} }
@ -139,7 +135,7 @@ namespace MediaBrowser.Dlna.PlayTo
_logger.Info("SSDP listener - Task completed"); _logger.Info("SSDP listener - Task completed");
} }
catch (OperationCanceledException c) catch (OperationCanceledException)
{ {
} }
catch (Exception e) catch (Exception e)
@ -158,7 +154,7 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
await CreateController(uri).ConfigureAwait(false); await CreateController(uri).ConfigureAwait(false);
} }
catch (OperationCanceledException c) catch (OperationCanceledException)
{ {
} }
catch (Exception ex) catch (Exception ex)
@ -180,10 +176,12 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
socket.SendTo(request, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900)); socket.SendTo(request, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900));
await Task.Delay(10000).ConfigureAwait(false); var delay = _config.Configuration.DlnaOptions.ClientDiscoveryIntervalSeconds * 1000;
await Task.Delay(delay).ConfigureAwait(false);
} }
} }
catch (OperationCanceledException c) catch (OperationCanceledException)
{ {
} }
catch (Exception ex) catch (Exception ex)

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using System.Collections.Generic;
namespace MediaBrowser.Dlna.PlayTo namespace MediaBrowser.Dlna.PlayTo
{ {
@ -14,16 +15,42 @@ namespace MediaBrowser.Dlna.PlayTo
public string Container { get; set; } public string Container { get; set; }
public string MimeType { get; set; }
public int PlayState { get; set; } public int PlayState { get; set; }
public string StreamUrl { get; set; } public string StreamUrl { get; set; }
public string DlnaHeaders { get; set; }
public string Didl { get; set; } public string Didl { get; set; }
public long StartPositionTicks { get; set; } public long StartPositionTicks { get; set; }
public string VideoCodec { get; set; }
public string AudioCodec { get; set; }
public List<TranscodingSetting> TranscodingSettings { get; set; }
public int? AudioStreamIndex { get; set; }
public int? SubtitleStreamIndex { get; set; }
public string DeviceProfileName { get; set; }
public int? MaxAudioChannels { get; set; }
public int? AudioBitrate { get; set; }
public int? VideoBitrate { get; set; }
public int? VideoLevel { get; set; }
public int? MaxWidth { get; set; }
public int? MaxHeight { get; set; }
public int? MaxFramerate { get; set; }
public PlaylistItem()
{
TranscodingSettings = new List<TranscodingSetting>();
}
} }
} }

@ -1,9 +1,9 @@
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -12,15 +12,9 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
public class PlaylistItemFactory public class PlaylistItemFactory
{ {
private readonly IItemRepository _itemRepo;
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public PlaylistItemFactory(IItemRepository itemRepo) public PlaylistItem Create(Audio item, List<MediaStream> mediaStreams, DeviceProfile profile)
{
_itemRepo = itemRepo;
}
public PlaylistItem Create(Audio item, DeviceProfile profile)
{ {
var playlistItem = new PlaylistItem var playlistItem = new PlaylistItem
{ {
@ -28,21 +22,18 @@ namespace MediaBrowser.Dlna.PlayTo
MediaType = DlnaProfileType.Audio MediaType = DlnaProfileType.Audio
}; };
var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
{
ItemId = item.Id,
Type = MediaStreamType.Audio
});
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
if (profile.CodecProfiles.Where(i => i.Type == CodecType.AudioCodec)
.All(i => IsCodecProfileSupported(i, item.Path, null, audioStream)))
{
var directPlay = profile.DirectPlayProfiles var directPlay = profile.DirectPlayProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream)); .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
if (directPlay != null) if (directPlay != null)
{
var audioCodec = audioStream == null ? null : audioStream.Codec;
// Make sure audio codec profiles are satisfied
if (!string.IsNullOrEmpty(audioCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.AudioCodec && i.ContainsCodec(audioCodec))
.All(i => AreConditionsSatisfied(i.Conditions, item.Path, null, audioStream)))
{ {
playlistItem.Transcode = false; playlistItem.Transcode = false;
playlistItem.Container = Path.GetExtension(item.Path); playlistItem.Container = Path.GetExtension(item.Path);
@ -57,11 +48,17 @@ 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;
var audioTranscodingConditions = profile.CodecProfiles
.Where(i => i.Type == CodecType.AudioCodec && i.ContainsCodec(transcodingProfile.AudioCodec))
.Take(1)
.SelectMany(i => i.Conditions);
AttachMediaProfile(playlistItem, profile); ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
}
return playlistItem; return playlistItem;
} }
@ -91,16 +88,14 @@ 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('.');
} }
AttachMediaProfile(playlistItem, profile);
return playlistItem; return playlistItem;
} }
public PlaylistItem Create(Video item, DeviceProfile profile) public PlaylistItem Create(Video item, List<MediaStream> mediaStreams, DeviceProfile profile)
{ {
var playlistItem = new PlaylistItem var playlistItem = new PlaylistItem
{ {
@ -108,22 +103,25 @@ namespace MediaBrowser.Dlna.PlayTo
MediaType = DlnaProfileType.Video MediaType = DlnaProfileType.Video
}; };
var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
{
ItemId = item.Id
}).ToList();
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
if (profile.CodecProfiles.Where(i => i.Type == CodecType.VideoCodec || i.Type == CodecType.VideoAudioCodec)
.All(i => IsCodecProfileSupported(i, item.Path, videoStream, audioStream)))
{
var directPlay = profile.DirectPlayProfiles var directPlay = profile.DirectPlayProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream)); .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
if (directPlay != null) if (directPlay != null)
{
var videoCodec = videoStream == null ? null : videoStream.Codec;
// Make sure video codec profiles are satisfied
if (!string.IsNullOrEmpty(videoCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.VideoCodec && i.ContainsCodec(videoCodec))
.All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream)))
{
var audioCodec = audioStream == null ? null : audioStream.Codec;
// Make sure audio codec profiles are satisfied
if (string.IsNullOrEmpty(audioCodec) || profile.CodecProfiles.Where(i => i.Type == CodecType.VideoAudioCodec && i.ContainsCodec(audioCodec))
.All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream)))
{ {
playlistItem.Transcode = false; playlistItem.Transcode = false;
playlistItem.Container = Path.GetExtension(item.Path); playlistItem.Container = Path.GetExtension(item.Path);
@ -131,6 +129,7 @@ namespace MediaBrowser.Dlna.PlayTo
return playlistItem; return playlistItem;
} }
} }
}
var transcodingProfile = profile.TranscodingProfiles var transcodingProfile = profile.TranscodingProfiles
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item)); .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
@ -138,63 +137,131 @@ 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.VideoCodec = transcodingProfile.VideoCodec;
var videoTranscodingConditions = profile.CodecProfiles
.Where(i => i.Type == CodecType.VideoCodec && i.ContainsCodec(transcodingProfile.VideoCodec))
.Take(1)
.SelectMany(i => i.Conditions);
ApplyTranscodingConditions(playlistItem, videoTranscodingConditions);
var audioTranscodingConditions = profile.CodecProfiles
.Where(i => i.Type == CodecType.VideoAudioCodec && i.ContainsCodec(transcodingProfile.AudioCodec))
.Take(1)
.SelectMany(i => i.Conditions);
AttachMediaProfile(playlistItem, profile); ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
}
return playlistItem; return playlistItem;
} }
private void AttachMediaProfile(PlaylistItem item, DeviceProfile profile) private void ApplyTranscodingConditions(PlaylistItem item, IEnumerable<ProfileCondition> conditions)
{ {
var mediaProfile = GetMediaProfile(item, profile); foreach (var condition in conditions.Where(i => !string.IsNullOrEmpty(i.Value)))
if (mediaProfile != null)
{ {
item.MimeType = (mediaProfile.MimeType ?? string.Empty).Split('/').LastOrDefault(); var value = condition.Value;
// TODO: Org_pn? switch (condition.Property)
{
case ProfileConditionValue.AudioBitrate:
{
var num = 0;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.AudioBitrate = num;
} }
break;
} }
case ProfileConditionValue.AudioChannels:
private MediaProfile GetMediaProfile(PlaylistItem item, DeviceProfile profile)
{ {
return profile.MediaProfiles.FirstOrDefault(i => var num = 0;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{ {
if (i.Type == item.MediaType) item.MaxAudioChannels = num;
}
break;
}
case ProfileConditionValue.Filesize:
case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.VideoBitDepth:
case ProfileConditionValue.VideoPacketLength:
case ProfileConditionValue.VideoProfile:
case ProfileConditionValue.VideoTimestamp:
{ {
if (string.Equals(item.Container.TrimStart('.'), i.Container.TrimStart('.'), StringComparison.OrdinalIgnoreCase)) // Not supported yet
break;
}
case ProfileConditionValue.Height:
{ {
// TODO: Enforce codecs var num = 0;
return true; if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.MaxHeight = num;
}
break;
}
case ProfileConditionValue.VideoBitrate:
{
var num = 0;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.VideoBitrate = num;
}
break;
}
case ProfileConditionValue.VideoFramerate:
{
var num = 0;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.MaxFramerate = num;
}
break;
}
case ProfileConditionValue.VideoLevel:
{
var num = 0;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.VideoLevel = num;
}
break;
}
case ProfileConditionValue.Width:
{
var num = 0;
if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
{
item.MaxWidth = num;
}
break;
}
default:
throw new ArgumentException("Unrecognized ProfileConditionValue");
} }
} }
return false;
});
} }
private bool IsSupported(DirectPlayProfile profile, Photo item) private bool IsSupported(DirectPlayProfile profile, Photo item)
{ {
var mediaPath = item.Path; var mediaPath = item.Path;
if (profile.Containers.Length > 0) if (profile.Container.Length > 0)
{ {
// Check container type // Check container type
var mediaContainer = Path.GetExtension(mediaPath); var mediaContainer = Path.GetExtension(mediaPath);
if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase))) if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
{ {
return false; return false;
} }
} }
// Check additional conditions
if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, null)))
{
return false;
}
return true; return true;
} }
@ -202,22 +269,16 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
var mediaPath = item.Path; var mediaPath = item.Path;
if (profile.Containers.Length > 0) if (profile.Container.Length > 0)
{ {
// Check container type // Check container type
var mediaContainer = Path.GetExtension(mediaPath); var mediaContainer = Path.GetExtension(mediaPath);
if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase))) if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
{ {
return false; return false;
} }
} }
// Check additional conditions
if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, audioStream)))
{
return false;
}
return true; return true;
} }
@ -230,11 +291,11 @@ namespace MediaBrowser.Dlna.PlayTo
var mediaPath = item.Path; var mediaPath = item.Path;
if (profile.Containers.Length > 0) if (profile.Container.Length > 0)
{ {
// Check container type // Check container type
var mediaContainer = Path.GetExtension(mediaPath); var mediaContainer = Path.GetExtension(mediaPath);
if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase))) if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
{ {
return false; return false;
} }
@ -262,12 +323,6 @@ namespace MediaBrowser.Dlna.PlayTo
} }
} }
// Check additional conditions
if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)))
{
return false;
}
return true; return true;
} }
@ -289,22 +344,9 @@ namespace MediaBrowser.Dlna.PlayTo
return true; return true;
} }
private bool IsCodecProfileSupported(CodecProfile profile, string mediaPath, MediaStream videoStream, MediaStream audioStream) private bool AreConditionsSatisfied(IEnumerable<ProfileCondition> conditions, string mediaPath, MediaStream videoStream, MediaStream audioStream)
{ {
var codecs = profile.GetCodecs(); return conditions.All(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream));
var stream = profile.Type == CodecType.VideoCodec ? videoStream : audioStream;
var existingCodec = (stream == null ? null : stream.Codec) ?? string.Empty;
if (codecs.Count == 0 || codecs.Contains(existingCodec, StringComparer.OrdinalIgnoreCase))
{
// Check additional conditions
if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)))
{
return false;
}
}
return true;
} }
/// <summary> /// <summary>
@ -318,6 +360,11 @@ namespace MediaBrowser.Dlna.PlayTo
/// <exception cref="System.InvalidOperationException">Unexpected ProfileConditionType</exception> /// <exception cref="System.InvalidOperationException">Unexpected ProfileConditionType</exception>
private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream) private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
{ {
if (condition.Property == ProfileConditionValue.Has64BitOffsets)
{
// TODO: Determine how to evaluate this
}
if (condition.Property == ProfileConditionValue.VideoProfile) if (condition.Property == ProfileConditionValue.VideoProfile)
{ {
var profile = videoStream == null ? null : videoStream.Profile; var profile = videoStream == null ? null : videoStream.Profile;
@ -413,6 +460,12 @@ namespace MediaBrowser.Dlna.PlayTo
return videoStream == null ? null : videoStream.Width; return videoStream == null ? null : videoStream.Width;
case ProfileConditionValue.VideoLevel: case ProfileConditionValue.VideoLevel:
return videoStream == null ? null : ConvertToLong(videoStream.Level); return videoStream == null ? null : ConvertToLong(videoStream.Level);
case ProfileConditionValue.VideoPacketLength:
// TODO: Determine how to get this
return null;
case ProfileConditionValue.VideoTimestamp:
// TODO: Determine how to get this
return null;
default: default:
throw new InvalidOperationException("Unexpected Property"); throw new InvalidOperationException("Unexpected Property");
} }

@ -49,7 +49,7 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
Url = url.ToString(), Url = url.ToString(),
UserAgent = USERAGENT, UserAgent = USERAGENT,
LogRequest = _config.Configuration.DlnaOptions.EnablePlayToDebugLogging LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging
}; };
options.RequestHeaders["HOST"] = ip + ":" + port; options.RequestHeaders["HOST"] = ip + ":" + port;
@ -88,7 +88,7 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
Url = url.ToString(), Url = url.ToString(),
UserAgent = USERAGENT, UserAgent = USERAGENT,
LogRequest = _config.Configuration.DlnaOptions.EnablePlayToDebugLogging LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging
}; };
options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName; options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
@ -112,7 +112,7 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
Url = url.ToString(), Url = url.ToString(),
UserAgent = USERAGENT, UserAgent = USERAGENT,
LogRequest = _config.Configuration.DlnaOptions.EnablePlayToDebugLogging LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging
}; };
options.RequestHeaders["SOAPAction"] = soapAction; options.RequestHeaders["SOAPAction"] = soapAction;

@ -1,6 +1,5 @@
using MediaBrowser.Model.Dto; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
@ -9,91 +8,21 @@ namespace MediaBrowser.Dlna.PlayTo
{ {
class StreamHelper class StreamHelper
{ {
/// <summary>
/// Gets the dlna headers.
/// </summary>
/// <param name="item">The item.</param>
/// <returns></returns>
internal static string GetDlnaHeaders(PlaylistItem item)
{
var orgOp = item.Transcode ? ";DLNA.ORG_OP=00" : ";DLNA.ORG_OP=01";
var orgCi = item.Transcode ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
var contentFeatures = string.Empty;
if (string.Equals(item.Container, "mp3", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MP3";
}
else if (string.Equals(item.Container, "wma", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMABASE";
}
else if (string.Equals(item.Container, "wmw", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
else if (string.Equals(item.Container, "asf", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
}
else if (string.Equals(item.Container, "avi", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVI";
}
else if (string.Equals(item.Container, "mkv", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MATROSKA";
}
else if (string.Equals(item.Container, "mp4", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC";
}
else if (string.Equals(item.Container, "mpeg", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (string.Equals(item.Container, "ts", StringComparison.OrdinalIgnoreCase))
{
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
}
else if (item.MediaType == Controller.Dlna.DlnaProfileType.Video)
{
//Default to AVI for video
contentFeatures = "DLNA.ORG_PN=AVI";
}
else
{
//Default to MP3 for audio
contentFeatures = "DLNA.ORG_PN=MP3";
}
return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
}
#region Audio
/// <summary> /// <summary>
/// Gets the audio URL. /// Gets the audio URL.
/// </summary> /// </summary>
/// <param name="deviceProperties">The device properties.</param>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="streams">The streams.</param>
/// <param name="serverAddress">The server address.</param> /// <param name="serverAddress">The server address.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
internal static string GetAudioUrl(PlaylistItem item, string serverAddress) internal static string GetAudioUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
{ {
if (!item.Transcode) var dlnaCommand = BuildDlnaUrl(deviceProperties, item);
return string.Format("{0}/audio/{1}/stream{2}?Static=True", serverAddress, item.ItemId, item.Container);
return string.Format("{0}/audio/{1}/stream.mp3?AudioCodec=Mp3", serverAddress, item.ItemId); return string.Format("{0}/audio/{1}/stream{2}?{3}", serverAddress, item.ItemId, "." + item.Container.TrimStart('.'), dlnaCommand);
} }
#endregion
#region Video
/// <summary> /// <summary>
/// Gets the video URL. /// Gets the video URL.
/// </summary> /// </summary>
@ -104,97 +33,44 @@ namespace MediaBrowser.Dlna.PlayTo
/// <returns>The url to send to the device</returns> /// <returns>The url to send to the device</returns>
internal static string GetVideoUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress) internal static string GetVideoUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
{ {
string dlnaCommand = string.Empty; var dlnaCommand = BuildDlnaUrl(deviceProperties, item);
if (!item.Transcode)
{
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, null, null, null, null, null, null, null, null, null, null, item.MimeType);
return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand);
}
var videostream = streams.Where(m => m.Type == MediaStreamType.Video).OrderBy(m => m.IsDefault).FirstOrDefault();
var audiostream = streams.Where(m => m.Type == MediaStreamType.Audio).OrderBy(m => m.IsDefault).FirstOrDefault();
var videoCodec = GetVideoCodec(videostream);
var audioCodec = GetAudioCodec(audiostream);
int? videoBitrate = null;
int? audioBitrate = null;
int? audioChannels = null;
if (videoCodec != VideoCodecs.Copy)
videoBitrate = 2000000;
if (audioCodec != AudioCodecs.Copy)
{
audioBitrate = 128000;
audioChannels = 2;
}
dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, videoCodec, audioCodec, null, null, videoBitrate, audioChannels, audioBitrate, item.StartPositionTicks, "baseline", "3", item.MimeType);
return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand); return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand);
} }
/// <summary>
/// Gets the video codec.
/// </summary>
/// <param name="videoStream">The video stream.</param>
/// <returns></returns>
private static VideoCodecs GetVideoCodec(MediaStream videoStream)
{
switch (videoStream.Codec.ToLower())
{
case "h264":
case "mpeg4":
return VideoCodecs.Copy;
}
return VideoCodecs.H264;
}
/// <summary>
/// Gets the audio codec.
/// </summary>
/// <param name="audioStream">The audio stream.</param>
/// <returns></returns>
private static AudioCodecs GetAudioCodec(MediaStream audioStream)
{
if (audioStream != null)
{
switch (audioStream.Codec.ToLower())
{
case "aac":
case "mp3":
case "wma":
return AudioCodecs.Copy;
}
}
return AudioCodecs.Aac;
}
/// <summary> /// <summary>
/// Builds the dlna URL. /// Builds the dlna URL.
/// </summary> /// </summary>
private static string BuildDlnaUrl(string deviceID, bool isStatic, VideoCodecs? videoCodec, AudioCodecs? audioCodec, int? subtitleIndex, int? audiostreamIndex, int? videoBitrate, int? audiochannels, int? audioBitrate, long? startPositionTicks, string profile, string videoLevel, string mimeType) private static string BuildDlnaUrl(DeviceInfo deviceProperties, PlaylistItem item)
{ {
var usCulture = new CultureInfo("en-US"); var profile = item.TranscodingSettings.Where(i => i.Name == TranscodingSettingType.VideoProfile)
.Select(i => i.Value)
.FirstOrDefault();
var dlnaparam = string.Format("Params={0};", deviceID); var usCulture = new CultureInfo("en-US");
dlnaparam += isStatic ? "true;" : "false;";
dlnaparam += videoCodec.HasValue ? videoCodec.Value + ";" : ";";
dlnaparam += audioCodec.HasValue ? audioCodec.Value + ";" : ";";
dlnaparam += audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) + ";" : ";";
dlnaparam += subtitleIndex.HasValue ? subtitleIndex.Value.ToString(usCulture) + ";" : ";";
dlnaparam += videoBitrate.HasValue ? videoBitrate.Value.ToString(usCulture) + ";" : ";";
dlnaparam += audioBitrate.HasValue ? audioBitrate.Value.ToString(usCulture) + ";" : ";";
dlnaparam += audiochannels.HasValue ? audiochannels.Value.ToString(usCulture) + ";" : ";";
dlnaparam += startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) + ";" : ";";
dlnaparam += profile + ";";
dlnaparam += videoLevel + ";";
dlnaparam += mimeType + ";";
return dlnaparam; var list = new List<string>
{
item.DeviceProfileName ?? string.Empty,
deviceProperties.UUID ?? string.Empty,
item.MediaSourceId ?? string.Empty,
(!item.Transcode).ToString().ToLower(),
item.VideoCodec ?? string.Empty,
item.AudioCodec ?? string.Empty,
item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(usCulture) : string.Empty,
item.SubtitleStreamIndex.HasValue ? item.SubtitleStreamIndex.Value.ToString(usCulture) : string.Empty,
item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(usCulture) : string.Empty,
item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(usCulture) : string.Empty,
item.MaxAudioChannels.HasValue ? item.MaxAudioChannels.Value.ToString(usCulture) : string.Empty,
item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(usCulture) : string.Empty,
item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(usCulture) : string.Empty,
item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(usCulture) : string.Empty,
item.StartPositionTicks.ToString(usCulture),
profile ?? string.Empty,
item.VideoLevel.HasValue ? item.VideoLevel.Value.ToString(usCulture) : string.Empty
};
return string.Format("Params={0}", string.Join(";", list.ToArray()));
} }
#endregion
} }
} }

@ -0,0 +1,76 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class DefaultProfile : DeviceProfile
{
public DefaultProfile()
{
ProtocolInfo = "DLNA";
ClientType = "DLNA";
Manufacturer = "Media Browser";
ModelDescription = "Media Browser";
ModelName = "Media Browser";
ModelNumber = "Media Browser";
ModelUrl = "http://mediabrowser3.com/";
ManufacturerUrl = "http://mediabrowser3.com/";
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
Type = DlnaProfileType.Video,
AudioCodec = "aac",
VideoCodec = "h264",
Settings = new []
{
new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
}
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "mp3,wma",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "avi,mp4",
Type = DlnaProfileType.Video
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "3",
IsRequired = false
}
}
}
};
}
}
}

@ -0,0 +1,27 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class DenonAvrProfile : DefaultProfile
{
public DenonAvrProfile()
{
Name = "Denon AVR";
Identification = new DeviceIdentification
{
FriendlyName = @"Denon:\[AVR:.*",
Manufacturer = "Denon"
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "mp3,flac,m4a,wma",
Type = DlnaProfileType.Audio
},
};
}
}
}

@ -0,0 +1,192 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class LgTvProfile : DefaultProfile
{
public LgTvProfile()
{
Name = "LG Smart TV";
TimelineOffsetSeconds = 10;
Identification = new DeviceIdentification
{
FriendlyName = @"LG.*",
Headers = new[]
{
new HttpHeaderInfo
{
Name = "User-Agent",
Value = "LG",
Match = HeaderMatchType.Substring
}
}
};
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
AudioCodec = "ac3",
VideoCodec = "h264",
Type = DlnaProfileType.Video
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "aac,ac3,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mkv",
VideoCodec = "h264",
AudioCodec = "aac,ac3,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp4",
VideoCodec = "h264,mpeg4",
AudioCodec = "aac,ac3,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "mpeg4",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "h264",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "ac3,aac,mp3",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "6"
}
}
}
};
}
}
}

@ -0,0 +1,33 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class LinksysDMA2100Profile : DefaultProfile
{
public LinksysDMA2100Profile()
{
// Linksys DMA2100us does not need any transcoding of the formats we support statically
Name = "Linksys DMA2100";
Identification = new DeviceIdentification
{
ModelName = "DMA2100us"
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "mp3,flac,m4a,wma",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "avi,mp4,mkv,ts",
Type = DlnaProfileType.Video
}
};
}
}
}

@ -0,0 +1,186 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class PanasonicVieraProfile : DefaultProfile
{
public PanasonicVieraProfile()
{
Name = "Panasonic Viera";
Identification = new DeviceIdentification
{
FriendlyName = @"VIERA",
Manufacturer = "Panasonic",
Headers = new[]
{
new HttpHeaderInfo
{
Name = "User-Agent",
Value = "Panasonic MIL DLNA",
Match = HeaderMatchType.Substring
}
}
};
TimelineOffsetSeconds = 10;
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
AudioCodec = "ac3",
VideoCodec = "h264",
Type = DlnaProfileType.Video
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "mpeg",
VideoCodec = "mpeg2video,mpeg4",
AudioCodec = "ac3,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mkv",
VideoCodec = "h264",
AudioCodec = "aac,ac3,mp3,pcm",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "aac,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp4",
VideoCodec = "h264",
AudioCodec = "aac,ac3,mp3,pcm",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mov",
VideoCodec = "h264",
AudioCodec = "aac,pcm",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "avi",
VideoCodec = "mpeg4",
AudioCodec = "pcm",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "flv",
VideoCodec = "h264",
AudioCodec = "aac",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "mp4",
AudioCodec = "aac",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitDepth,
Value = "8",
IsRequired = false
}
}
}
};
}
}
}

@ -0,0 +1,315 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class SamsungSmartTvProfile : DefaultProfile
{
public SamsungSmartTvProfile()
{
Name = "Samsung Smart TV";
SupportsAlbumArtInDidl = true;
Identification = new DeviceIdentification
{
ModelUrl = "samsung.com"
};
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
AudioCodec = "ac3",
VideoCodec = "h264",
Type = DlnaProfileType.Video
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "asf",
VideoCodec = "h264,mpeg4,mjpeg",
AudioCodec = "mp3,ac3,wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "avi",
VideoCodec = "h264,mpeg4,mjpeg",
AudioCodec = "mp3,ac3,dca",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mkv",
VideoCodec = "h264,mpeg4,mjpeg4",
AudioCodec = "mp3,ac3,dca,aac",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp4",
VideoCodec = "h264,mpeg4",
AudioCodec = "mp3,aac",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "3gpp",
VideoCodec = "h264,mpeg4",
AudioCodec = "aac,he-aac",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mpg,mpeg",
VideoCodec = "mpeg1video,mpeg2video,h264",
AudioCodec = "ac3,mp2,mp3,aac",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "vro,vob",
VideoCodec = "mpeg1video,mpeg2video",
AudioCodec = "ac3,mp2,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "mpeg2video,h264,vc1",
AudioCodec = "ac3,aac,mp3,eac3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "asf",
VideoCodec = "wmv2,wmv3",
AudioCodec = "wmav2,wmavoice",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "mpeg2video",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "30720000"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "mpeg4",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "8192000"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "h264",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "37500000"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "wmv2,wmv3,vc1",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "25600000"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "ac3,wmav2,dca,aac,mp3",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "6"
}
}
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "mkv",
MimeType = "video/x-mkv",
Type = DlnaProfileType.Video
}
};
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,286 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class SonyBravia2010Profile : DefaultProfile
{
public SonyBravia2010Profile()
{
Name = "Sony Bravia (2010)";
Identification = new DeviceIdentification
{
FriendlyName = @"KDL-\d{2}[EHLNPB]X\d[01]\d.*",
Manufacturer = "Sony",
Headers = new[]
{
new HttpHeaderInfo
{
Name = "X-AV-Client-Info",
Value = @".*KDL-\d{2}[EHLNPB]X\d[01]\d.*",
Match = HeaderMatchType.Regex
}
}
};
ModelName = "Windows Media Player Sharing";
ModelNumber = "3.0";
ModelUrl = "http://www.microsoft.com/";
Manufacturer = "Microsoft Corporation";
ManufacturerUrl = "http://www.microsoft.com/";
SonyAggregationFlags = "10";
ProtocolInfo =
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,aac",
Type = DlnaProfileType.Video,
EnableMpegtsM2TsMode = true
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,aac,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "mpeg1video,mpeg2video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mpeg",
VideoCodec = "mpeg2video,mpeg1video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/mpeg",
OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "ts",
VideoCodec="mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video",
MimeType = "video/mpeg",
OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "h264",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "20000000"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "mpeg2video",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "20000000"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "ac3",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "6"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "aac",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "2"
},
new ProfileCondition
{
Condition = ProfileConditionType.NotEquals,
Property = ProfileConditionValue.AudioProfile,
Value = "he-aac"
}
}
}
};
}
}
}

@ -0,0 +1,304 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class SonyBravia2011Profile : DefaultProfile
{
public SonyBravia2011Profile()
{
Name = "Sony Bravia (2011)";
Identification = new DeviceIdentification
{
FriendlyName = @"KDL-\d{2}([A-Z]X\d2\d|CX400).*",
Manufacturer = "Sony",
Headers = new[]
{
new HttpHeaderInfo
{
Name = "X-AV-Client-Info",
Value = @".*KDL-\d{2}([A-Z]X\d2\d|CX400).*",
Match = HeaderMatchType.Regex
}
}
};
ModelName = "Windows Media Player Sharing";
ModelNumber = "3.0";
ModelUrl = "http://www.microsoft.com/";
Manufacturer = "Microsoft Corporation";
ManufacturerUrl = "http://www.microsoft.com/";
SonyAggregationFlags = "10";
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,aac",
Type = DlnaProfileType.Video,
EnableMpegtsM2TsMode = true
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,aac,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "mpeg2video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp4",
VideoCodec = "h264,mpeg4",
AudioCodec = "ac3,aac,mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mpeg",
VideoCodec = "mpeg2video,mpeg1video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "asf",
VideoCodec = "wmv2,wmv3,vc1",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "asf",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Audio
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/mpeg",
OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "ts",
VideoCodec="mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video",
MimeType = "video/mpeg",
OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "h264",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "20000000"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41"
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "mpeg2video",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "20000000"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "ac3",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "6"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "aac",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "2"
},
new ProfileCondition
{
Condition = ProfileConditionType.NotEquals,
Property = ProfileConditionValue.AudioProfile,
Value = "he-aac"
}
}
}
};
}
}
}

@ -0,0 +1,246 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class SonyBravia2012Profile : DefaultProfile
{
public SonyBravia2012Profile()
{
Name = "Sony Bravia (2012)";
Identification = new DeviceIdentification
{
FriendlyName = @"KDL-\d{2}[A-Z]X\d5(\d|G).*",
Manufacturer = "Sony",
Headers = new[]
{
new HttpHeaderInfo
{
Name = "X-AV-Client-Info",
Value = @".*KDL-\d{2}[A-Z]X\d5(\d|G).*",
Match = HeaderMatchType.Regex
}
}
};
ModelName = "Windows Media Player Sharing";
ModelNumber = "3.0";
ModelUrl = "http://www.microsoft.com/";
Manufacturer = "Microsoft Corporation";
ManufacturerUrl = "http://www.microsoft.com/";
SonyAggregationFlags = "10";
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,aac",
Type = DlnaProfileType.Video,
EnableMpegtsM2TsMode = true
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,aac,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "mpeg2video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp4",
VideoCodec = "h264,mpeg4",
AudioCodec = "ac3,aac,mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "avi",
VideoCodec = "mpeg4",
AudioCodec = "ac3,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mpeg",
VideoCodec = "mpeg2video,mpeg1video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "asf",
VideoCodec = "wmv2,wmv3,vc1",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "asf",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/mpeg",
OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "ts",
VideoCodec="mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video",
MimeType = "video/mpeg",
OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "ac3",
Conditions = new[]
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "6"
}
}
}
};
}
}
}

@ -0,0 +1,264 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class SonyBravia2013Profile : DefaultProfile
{
public SonyBravia2013Profile()
{
Name = "Sony Bravia (2013)";
Identification = new DeviceIdentification
{
FriendlyName = @"KDL-\d{2}[WR][5689]\d{2}A.*",
Manufacturer = "Sony",
Headers = new[]
{
new HttpHeaderInfo
{
Name = "X-AV-Client-Info",
Value = @".*KDL-\d{2}[WR][5689]\d{2}A.*",
Match = HeaderMatchType.Regex
}
}
};
ModelName = "Windows Media Player Sharing";
ModelNumber = "3.0";
ModelUrl = "http://www.microsoft.com/";
Manufacturer = "Microsoft Corporation";
ManufacturerUrl = "http://www.microsoft.com/";
SonyAggregationFlags = "10";
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,aac",
Type = DlnaProfileType.Video,
EnableMpegtsM2TsMode = true
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "ac3,eac3,aac,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "ts",
VideoCodec = "mpeg2video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp4",
VideoCodec = "h264,mpeg4",
AudioCodec = "ac3,eac3,aac,mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mov",
VideoCodec = "h264,mpeg4,mjpeg",
AudioCodec = "ac3,eac3,aac,mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mkv",
VideoCodec = "h264,mpeg4,vp8",
AudioCodec = "ac3,eac3,aac,mp3,mp2,pcm,vorbis",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "avi",
VideoCodec = "mpeg4",
AudioCodec = "ac3,eac3,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "avi",
VideoCodec = "mjpeg",
AudioCodec = "pcm",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mpeg",
VideoCodec = "mpeg2video,mpeg1video",
AudioCodec = "mp3,mp2",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "asf",
VideoCodec = "wmv2,wmv3,vc1",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "mp4",
AudioCodec = "aac",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "wav",
AudioCodec = "pcm",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "asf",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/mpeg",
OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
Type = DlnaProfileType.Video,
Conditions = new []
{
new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
}
},
new MediaProfile
{
Container = "ts",
VideoCodec="h264",
AudioCodec="ac3,aac,mp3",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "ts",
VideoCodec="mpeg2video",
MimeType = "video/vnd.dlna.mpeg-tts",
OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "mpeg",
VideoCodec="mpeg1video,mpeg2video",
MimeType = "video/mpeg",
OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
Type = DlnaProfileType.Video
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
}
}
}

@ -0,0 +1,235 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class SonyPs3Profile : DefaultProfile
{
public SonyPs3Profile()
{
Name = "Sony PlayStation 3";
Identification = new DeviceIdentification
{
FriendlyName = "PLAYSTATION 3",
Headers = new[]
{
new HttpHeaderInfo
{
Name = "User-Agent",
Value = @"PLAYSTATION 3",
Match = HeaderMatchType.Substring
},
new HttpHeaderInfo
{
Name = "X-AV-Client-Info",
Value = @"PLAYSTATION 3",
Match = HeaderMatchType.Substring
}
}
};
SonyAggregationFlags = "10";
XDlnaDoc = "DMS-1.50";
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "mp3",
Type = DlnaProfileType.Video
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "h264",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "15360000",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41",
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "ac3",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "6",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioBitrate,
Value = "640000",
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "wmapro",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "2"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "aac",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.NotEquals,
Property = ProfileConditionValue.AudioProfile,
Value = "he-aac",
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "aac",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "2"
},
new ProfileCondition
{
Condition = ProfileConditionType.NotEquals,
Property = ProfileConditionValue.AudioProfile,
Value = "he-aac"
}
}
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "mp4,mov",
AudioCodec="aac",
MimeType = "video/mp4",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "avi",
MimeType = "video/divx",
OrgPn="AVI",
Type = DlnaProfileType.Video
},
new MediaProfile
{
Container = "wav",
MimeType = "audio/wav",
Type = DlnaProfileType.Audio
}
};
}
}
}

@ -0,0 +1,240 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class WdtvLiveProfile : DefaultProfile
{
public WdtvLiveProfile()
{
Name = "WDTV Live";
TimelineOffsetSeconds = 5;
IgnoreTranscodeByteRangeRequests = true;
Identification = new DeviceIdentification
{
ModelName = "WD TV HD Live",
Headers = new []
{
new HttpHeaderInfo {Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring},
new HttpHeaderInfo
{
Name = "User-Agent",
Value = "ALPHA Networks",
Match = HeaderMatchType.Substring
}
}
};
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
Type = DlnaProfileType.Audio,
AudioCodec = "mp3"
},
new TranscodingProfile
{
Container = "ts",
Type = DlnaProfileType.Video,
VideoCodec = "h264",
AudioCodec = "aac",
Settings = new []
{
new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
}
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "avi",
Type = DlnaProfileType.Video,
VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
AudioCodec = "ac3,dca,mp2,mp3,pcm"
},
new DirectPlayProfile
{
Container = "mpeg",
Type = DlnaProfileType.Video,
VideoCodec = "mpeg1video,mpeg2video",
AudioCodec = "ac3,dca,mp2,mp3,pcm"
},
new DirectPlayProfile
{
Container = "mkv",
Type = DlnaProfileType.Video,
VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
AudioCodec = "ac3,dca,aac,mp2,mp3,pcm"
},
new DirectPlayProfile
{
Container = "ts",
Type = DlnaProfileType.Video,
VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
AudioCodec = "ac3,dca,mp2,mp3"
},
new DirectPlayProfile
{
Container = "mp4,mov",
Type = DlnaProfileType.Video,
VideoCodec = "h264,mpeg4",
AudioCodec = "ac3,aac,mp2,mp3"
},
new DirectPlayProfile
{
Container = "asf",
Type = DlnaProfileType.Video,
VideoCodec = "vc1",
AudioCodec = "wmav2,wmapro"
},
new DirectPlayProfile
{
Container = "asf",
Type = DlnaProfileType.Video,
VideoCodec = "mpeg2video",
AudioCodec = "mp2,ac3"
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp2,mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "mp4",
AudioCodec = "mp4",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "flac",
AudioCodec = "flac",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "asf",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "ogg",
AudioCodec = "vorbis",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Type = DlnaProfileType.Photo,
Container = "jpeg,png,gif,bmp,tiff"
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "ts",
OrgPn = "MPEG_TS_SD_NA",
Type = DlnaProfileType.Video
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "h264",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41"
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "aac",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "2"
}
}
}
};
}
}
}

@ -0,0 +1,324 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class Xbox360Profile : DefaultProfile
{
public Xbox360Profile()
{
Name = "Xbox 360";
ModelName = "Windows Media Player Sharing";
ModelNumber = "12.0";
ModelUrl = "http://www.microsoft.com/";
Manufacturer = "Microsoft Corporation";
ManufacturerUrl = "http://www.microsoft.com/";
XDlnaDoc = "DMS-1.50";
TimelineOffsetSeconds = 40;
RequiresPlainFolders = true;
RequiresPlainVideoItems = true;
Identification = new DeviceIdentification
{
ModelName = "Xbox 360",
Headers = new []
{
new HttpHeaderInfo {Name = "User-Agent", Value = "Xbox", Match = HeaderMatchType.Substring},
new HttpHeaderInfo {Name = "User-Agent", Value = "Xenon", Match = HeaderMatchType.Substring}
}
};
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "asf",
VideoCodec = "wmv2",
AudioCodec = "wmav2",
Type = DlnaProfileType.Video,
TranscodeSeekInfo = TranscodeSeekInfo.Bytes,
EstimateContentLength = true,
Settings = new []
{
new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
}
},
new TranscodingProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "avi",
VideoCodec = "mpeg4",
AudioCodec = "ac3,mp3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "avi",
VideoCodec = "h264",
AudioCodec = "aac",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "mp4,mov",
VideoCodec = "h264,mpeg4",
AudioCodec = "aac,ac3",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "asf",
VideoCodec = "wmv2,wmv3,vc1",
AudioCodec = "wmav2,wmapro",
Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
Container = "asf",
AudioCodec = "wmav2,wmapro,wmavoice",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new DirectPlayProfile
{
Container = "jpeg",
Type = DlnaProfileType.Photo
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "avi",
MimeType = "video/avi",
Type = DlnaProfileType.Video
}
};
ContainerProfiles = new[]
{
new ContainerProfile
{
Type = DlnaProfileType.Video,
Container = "mp4,mov",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Has64BitOffsets,
Value = "false",
IsRequired = false
}
}
},
new ContainerProfile
{
Type = DlnaProfileType.Photo,
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
}
}
}
};
CodecProfiles = new[]
{
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "mpeg4",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1280"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "720"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "5120000",
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "h264",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "41",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "10240000",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "3",
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.VideoCodec,
Codec = "wmv2,wmv3,vc1",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Width,
Value = "1920"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.Height,
Value = "1080"
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoFramerate,
Value = "30",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoBitrate,
Value = "15360000",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.VideoLevel,
Value = "3",
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "ac3,wmav2,wmapro",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "6",
IsRequired = false
}
}
},
new CodecProfile
{
Type = CodecType.VideoAudioCodec,
Codec = "aac",
Conditions = new []
{
new ProfileCondition
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "2",
IsRequired = false
},
new ProfileCondition
{
Condition = ProfileConditionType.Equals,
Property = ProfileConditionValue.AudioProfile,
Value = "lc",
IsRequired = false
}
}
}
};
}
}
}

@ -0,0 +1,54 @@
using MediaBrowser.Controller.Dlna;
namespace MediaBrowser.Dlna.Profiles
{
public class XboxOneProfile : DefaultProfile
{
public XboxOneProfile()
{
Name = "Xbox One";
Identification = new DeviceIdentification
{
ModelName = "Xbox One",
FriendlyName = "Xbox-SystemOS"
};
TranscodingProfiles = new[]
{
new TranscodingProfile
{
Container = "mp3",
AudioCodec = "mp3",
Type = DlnaProfileType.Audio
},
new TranscodingProfile
{
Container = "ts",
VideoCodec = "h264",
AudioCodec = "aac",
Type = DlnaProfileType.Video
}
};
DirectPlayProfiles = new[]
{
new DirectPlayProfile
{
Container = "mp3,wma",
Type = DlnaProfileType.Audio
}
};
MediaProfiles = new[]
{
new MediaProfile
{
Container = "avi",
MimeType = "video/x-msvideo",
Type = DlnaProfileType.Video
}
};
}
}
}

@ -0,0 +1,115 @@
using MediaBrowser.Common;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using System;
namespace MediaBrowser.Dlna.Server
{
public class DlnaServerEntryPoint : IServerEntryPoint
{
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private SsdpHandler _ssdpHandler;
private readonly IApplicationHost _appHost;
public DlnaServerEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost)
{
_config = config;
_appHost = appHost;
_logger = logManager.GetLogger("DlnaServer");
}
public void Run()
{
_config.ConfigurationUpdated += ConfigurationUpdated;
//ReloadServer();
}
void ConfigurationUpdated(object sender, EventArgs e)
{
//ReloadServer();
}
private void ReloadServer()
{
var isStarted = _ssdpHandler != null;
if (_config.Configuration.DlnaOptions.EnableServer && !isStarted)
{
StartServer();
}
else if (!_config.Configuration.DlnaOptions.EnableServer && isStarted)
{
DisposeServer();
}
}
private readonly object _syncLock = new object();
private void StartServer()
{
var signature = GenerateServerSignature();
lock (_syncLock)
{
try
{
_ssdpHandler = new SsdpHandler(_logger, _config, signature);
}
catch (Exception ex)
{
_logger.ErrorException("Error starting Dlna server", ex);
}
}
}
private void DisposeServer()
{
lock (_syncLock)
{
if (_ssdpHandler != null)
{
try
{
_ssdpHandler.Dispose();
}
catch (Exception ex)
{
_logger.ErrorException("Error disposing Dlna server", ex);
}
_ssdpHandler = null;
}
}
}
private string GenerateServerSignature()
{
var os = Environment.OSVersion;
var pstring = os.Platform.ToString();
switch (os.Platform)
{
case PlatformID.Win32NT:
case PlatformID.Win32S:
case PlatformID.Win32Windows:
pstring = "WIN";
break;
}
return String.Format(
"{0}{1}/{2}.{3} UPnP/1.0 DLNADOC/1.5 MediaBrowser/{4}",
pstring,
IntPtr.Size * 8,
os.Version.Major,
os.Version.Minor,
_appHost.ApplicationVersion
);
}
public void Dispose()
{
DisposeServer();
}
}
}

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace MediaBrowser.Dlna.Server
{
public class Headers : IDictionary<string, string>
{
private readonly bool _asIs = false;
private readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
protected Headers(bool asIs)
{
_asIs = asIs;
}
public Headers()
: this(asIs: false)
{
}
public int Count
{
get
{
return _dict.Count;
}
}
public string HeaderBlock
{
get
{
var hb = new StringBuilder();
foreach (var h in this)
{
hb.AppendFormat("{0}: {1}\r\n", h.Key, h.Value);
}
return hb.ToString();
}
}
public Stream HeaderStream
{
get
{
return new MemoryStream(Encoding.ASCII.GetBytes(HeaderBlock));
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public ICollection<string> Keys
{
get
{
return _dict.Keys;
}
}
public ICollection<string> Values
{
get
{
return _dict.Values;
}
}
public string this[string key]
{
get
{
return _dict[Normalize(key)];
}
set
{
_dict[Normalize(key)] = value;
}
}
private string Normalize(string header)
{
if (!_asIs)
{
header = header.ToLower();
}
header = header.Trim();
if (!Validator.IsMatch(header))
{
throw new ArgumentException("Invalid header: " + header);
}
return header;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _dict.GetEnumerator();
}
public void Add(KeyValuePair<string, string> item)
{
Add(item.Key, item.Value);
}
public void Add(string key, string value)
{
_dict.Add(Normalize(key), value);
}
public void Clear()
{
_dict.Clear();
}
public bool Contains(KeyValuePair<string, string> item)
{
var p = new KeyValuePair<string, string>(Normalize(item.Key), item.Value);
return _dict.Contains(p);
}
public bool ContainsKey(string key)
{
return _dict.ContainsKey(Normalize(key));
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _dict.GetEnumerator();
}
public bool Remove(string key)
{
return _dict.Remove(Normalize(key));
}
public bool Remove(KeyValuePair<string, string> item)
{
return Remove(item.Key);
}
public override string ToString()
{
return string.Format("({0})", string.Join(", ", (from x in _dict
select string.Format("{0}={1}", x.Key, x.Value))));
}
public bool TryGetValue(string key, out string value)
{
return _dict.TryGetValue(Normalize(key), out value);
}
}
}

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MediaBrowser.Dlna.Server
{
public class RawHeaders : Headers
{
public RawHeaders()
: base(true)
{
}
}
}

@ -0,0 +1,260 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace MediaBrowser.Dlna.Server
{
public class SsdpHandler : IDisposable
{
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
private readonly string _serverSignature;
private bool _isDisposed = false;
const string SSDPAddr = "239.255.255.250";
const int SSDPPort = 1900;
private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
private UdpClient _udpClient;
private readonly Dictionary<Guid, List<UpnpDevice>> _devices = new Dictionary<Guid, List<UpnpDevice>>();
public SsdpHandler(ILogger logger, IServerConfigurationManager config, string serverSignature)
{
_logger = logger;
_config = config;
_serverSignature = serverSignature;
Start();
}
private IEnumerable<UpnpDevice> Devices
{
get
{
UpnpDevice[] devs;
lock (_devices)
{
devs = _devices.Values.SelectMany(i => i).ToArray();
}
return devs;
}
}
private void Start()
{
_udpClient = new UdpClient();
_udpClient.Client.UseOnlyOverlappedIO = true;
_udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_udpClient.ExclusiveAddressUse = false;
_udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
_udpClient.JoinMulticastGroup(_ssdpIp, 2);
_logger.Info("SSDP service started");
Receive();
}
private void Receive()
{
try
{
_udpClient.BeginReceive(ReceiveCallback, null);
}
catch (ObjectDisposedException)
{
}
}
private void ReceiveCallback(IAsyncResult result)
{
try
{
var endpoint = new IPEndPoint(IPAddress.None, SSDPPort);
var received = _udpClient.EndReceive(result, ref endpoint);
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
{
_logger.Debug("{0} - SSDP Received a datagram", endpoint);
}
using (var reader = new StreamReader(new MemoryStream(received), Encoding.ASCII))
{
var proto = (reader.ReadLine() ?? string.Empty).Trim();
var method = proto.Split(new[] { ' ' }, 2)[0];
var headers = new Headers();
for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
{
line = line.Trim();
if (string.IsNullOrEmpty(line))
{
break;
}
var parts = line.Split(new char[] { ':' }, 2);
headers[parts[0]] = parts[1].Trim();
}
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
{
_logger.Debug("{0} - Datagram method: {1}", endpoint, method);
//_logger.Debug(headers);
}
if (string.Equals(method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
{
RespondToSearch(endpoint, headers["st"]);
}
}
}
catch (Exception ex)
{
_logger.ErrorException("Failed to read SSDP message", ex);
}
if (!_isDisposed)
{
Receive();
}
}
private void RespondToSearch(IPEndPoint endpoint, string req)
{
if (req == "ssdp:all")
{
req = null;
}
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
{
_logger.Debug("RespondToSearch");
}
foreach (var d in Devices)
{
if (!string.IsNullOrEmpty(req) && req != d.Type)
{
continue;
}
SendSearchResponse(endpoint, d);
}
}
private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev)
{
var headers = new RawHeaders();
headers.Add("CACHE-CONTROL", "max-age = 600");
headers.Add("DATE", DateTime.Now.ToString("R"));
headers.Add("EXT", "");
headers.Add("LOCATION", dev.Descriptor.ToString());
headers.Add("SERVER", _serverSignature);
headers.Add("ST", dev.Type);
headers.Add("USN", dev.USN);
SendDatagram(endpoint, String.Format("HTTP/1.1 200 OK\r\n{0}\r\n", headers.HeaderBlock), false);
_logger.Info("{1} - Responded to a {0} request", dev.Type, endpoint);
}
private void SendDatagram(IPEndPoint endpoint, string msg, bool sticky)
{
if (_isDisposed)
{
return;
}
//var dgram = new Datagram(endpoint, msg, sticky);
//if (messageQueue.Count == 0)
//{
// dgram.Send();
//}
//messageQueue.Enqueue(dgram);
//queueTimer.Enabled = true;
}
private void NotifyAll()
{
_logger.Debug("NotifyAll");
foreach (var d in Devices)
{
NotifyDevice(d, "alive", false);
}
}
private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
{
_logger.Debug("NotifyDevice");
var headers = new RawHeaders();
headers.Add("HOST", "239.255.255.250:1900");
headers.Add("CACHE-CONTROL", "max-age = 600");
headers.Add("LOCATION", dev.Descriptor.ToString());
headers.Add("SERVER", _serverSignature);
headers.Add("NTS", "ssdp:" + type);
headers.Add("NT", dev.Type);
headers.Add("USN", dev.USN);
SendDatagram(_ssdpEndp, String.Format("NOTIFY * HTTP/1.1\r\n{0}\r\n", headers.HeaderBlock), sticky);
_logger.Debug("{0} said {1}", dev.USN, type);
}
private void RegisterNotification(Guid UUID, Uri Descriptor)
{
List<UpnpDevice> list;
lock (_devices)
{
if (!_devices.TryGetValue(UUID, out list))
{
_devices.Add(UUID, list = new List<UpnpDevice>());
}
}
foreach (var t in new[] { "upnp:rootdevice", "urn:schemas-upnp-org:device:MediaServer:1", "urn:schemas-upnp-org:service:ContentDirectory:1", "uuid:" + UUID })
{
list.Add(new UpnpDevice(UUID, t, Descriptor));
}
NotifyAll();
_logger.Debug("Registered mount {0}", UUID);
}
internal void UnregisterNotification(Guid UUID)
{
List<UpnpDevice> dl;
lock (_devices)
{
if (!_devices.TryGetValue(UUID, out dl))
{
return;
}
_devices.Remove(UUID);
}
foreach (var d in dl)
{
NotifyDevice(d, "byebye", true);
}
_logger.Debug("Unregistered mount {0}", UUID);
}
public void Dispose()
{
_isDisposed = true;
//while (messageQueue.Count != 0)
//{
// datagramPosted.WaitOne();
//}
_udpClient.DropMulticastGroup(_ssdpIp);
_udpClient.Close();
//notificationTimer.Enabled = false;
//queueTimer.Enabled = false;
//notificationTimer.Dispose();
//queueTimer.Dispose();
//datagramPosted.Dispose();
}
}
}

@ -0,0 +1,28 @@
using System;
namespace MediaBrowser.Dlna.Server
{
public sealed class UpnpDevice
{
public readonly Uri Descriptor;
public readonly string Type;
public readonly string USN;
public readonly Guid Uuid;
public UpnpDevice(Guid aUuid, string aType, Uri aDescriptor)
{
Uuid = aUuid;
Type = aType;
Descriptor = aDescriptor;
if (Type.StartsWith("uuid:"))
{
USN = Type;
}
else
{
USN = String.Format("uuid:{0}::{1}", Uuid.ToString(), Type);
}
}
}
}

@ -1030,7 +1030,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="timer">The timer.</param> /// <param name="timer">The timer.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task CreateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken); Task CreateLiveTvTimerAsync(BaseTimerInfoDto timer, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Updates the live tv timer asynchronous. /// Updates the live tv timer asynchronous.

@ -4,11 +4,15 @@ namespace MediaBrowser.Model.Configuration
public class DlnaOptions public class DlnaOptions
{ {
public bool EnablePlayTo { get; set; } public bool EnablePlayTo { get; set; }
public bool EnablePlayToDebugLogging { get; set; } public bool EnableServer { get; set; }
public bool EnableDebugLogging { get; set; }
public int ClientDiscoveryIntervalSeconds { get; set; }
public DlnaOptions() public DlnaOptions()
{ {
EnablePlayTo = true; EnablePlayTo = true;
EnableServer = true;
ClientDiscoveryIntervalSeconds = 60;
} }
} }
} }

@ -69,6 +69,7 @@ namespace MediaBrowser.Model.Dto
/// </summary> /// </summary>
/// <value>The name of the sort.</value> /// <value>The name of the sort.</value>
public string SortName { get; set; } public string SortName { get; set; }
public string ForcedSortName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the video3 D format. /// Gets or sets the video3 D format.

@ -10,7 +10,7 @@
/// Omit to copy /// Omit to copy
/// </summary> /// </summary>
/// <value>The video codec.</value> /// <value>The video codec.</value>
public VideoCodecs? VideoCodec { get; set; } public string VideoCodec { get; set; }
/// <summary> /// <summary>
/// Gets or sets the video bit rate. /// Gets or sets the video bit rate.
@ -113,7 +113,7 @@
/// Omit to copy the original stream /// Omit to copy the original stream
/// </summary> /// </summary>
/// <value>The audio encoding format.</value> /// <value>The audio encoding format.</value>
public AudioCodecs? AudioCodec { get; set; } public string AudioCodec { get; set; }
/// <summary> /// <summary>
/// Gets or sets the item id. /// Gets or sets the item id.
@ -158,68 +158,4 @@
/// <value>The device id.</value> /// <value>The device id.</value>
public string DeviceId { get; set; } public string DeviceId { get; set; }
} }
/// <summary>
/// These are the codecs the api is capable of encoding to
/// </summary>
public enum AudioCodecs
{
/// <summary>
/// The aac
/// </summary>
Aac,
/// <summary>
/// The MP3
/// </summary>
Mp3,
/// <summary>
/// The vorbis
/// </summary>
Vorbis,
/// <summary>
/// The wma
/// </summary>
Wma,
/// <summary>
/// The copy
/// </summary>
Copy
}
/// <summary>
/// Enum VideoCodecs
/// </summary>
public enum VideoCodecs
{
H263,
/// <summary>
/// The H264
/// </summary>
H264,
/// <summary>
/// The mpeg4
/// </summary>
Mpeg4,
/// <summary>
/// The theora
/// </summary>
Theora,
/// <summary>
/// The VPX
/// </summary>
Vpx,
/// <summary>
/// The WMV
/// </summary>
Wmv,
/// <summary>
/// The copy
/// </summary>
Copy
}
} }

@ -1,80 +1,14 @@
using System; using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.LiveTv namespace MediaBrowser.Model.LiveTv
{ {
[DebuggerDisplay("Name = {Name}")] [DebuggerDisplay("Name = {Name}")]
public class SeriesTimerInfoDto : INotifyPropertyChanged public class SeriesTimerInfoDto : BaseTimerInfoDto
{ {
/// <summary>
/// Id of the recording.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Gets or sets the external identifier.
/// </summary>
/// <value>The external identifier.</value>
public string ExternalId { get; set; }
/// <summary>
/// ChannelId of the recording.
/// </summary>
public string ChannelId { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the external channel identifier.
/// </summary>
/// <value>The external channel identifier.</value>
public string ExternalChannelId { get; set; }
/// <summary>
/// ChannelName of the recording.
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
/// <summary>
/// Gets or sets the external program identifier.
/// </summary>
/// <value>The external program identifier.</value>
public string ExternalProgramId { get; set; }
/// <summary>
/// Name of the recording.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Description of the recording.
/// </summary>
public string Overview { get; set; }
/// <summary>
/// The start date of the recording, in UTC.
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// The end date of the recording, in UTC.
/// </summary>
public DateTime EndDate { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [record any time]. /// Gets or sets a value indicating whether [record any time].
/// </summary> /// </summary>
@ -105,36 +39,6 @@ namespace MediaBrowser.Model.LiveTv
/// <value>The day pattern.</value> /// <value>The day pattern.</value>
public DayPattern? DayPattern { get; set; } public DayPattern? DayPattern { get; set; }
/// <summary>
/// Gets or sets the priority.
/// </summary>
/// <value>The priority.</value>
public int Priority { get; set; }
/// <summary>
/// Gets or sets the pre padding seconds.
/// </summary>
/// <value>The pre padding seconds.</value>
public int PrePaddingSeconds { get; set; }
/// <summary>
/// Gets or sets the post padding seconds.
/// </summary>
/// <value>The post padding seconds.</value>
public int PostPaddingSeconds { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is pre padding required.
/// </summary>
/// <value><c>true</c> if this instance is pre padding required; otherwise, <c>false</c>.</value>
public bool IsPrePaddingRequired { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is post padding required.
/// </summary>
/// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
public bool IsPostPaddingRequired { get; set; }
/// <summary> /// <summary>
/// Gets or sets the image tags. /// Gets or sets the image tags.
/// </summary> /// </summary>
@ -156,7 +60,5 @@ namespace MediaBrowser.Model.LiveTv
ImageTags = new Dictionary<ImageType, Guid>(); ImageTags = new Dictionary<ImageType, Guid>();
Days = new List<DayOfWeek>(); Days = new List<DayOfWeek>();
} }
public event PropertyChangedEventHandler PropertyChanged;
} }
} }

@ -3,8 +3,47 @@ using System.ComponentModel;
namespace MediaBrowser.Model.LiveTv namespace MediaBrowser.Model.LiveTv
{ {
public class TimerInfoDto : INotifyPropertyChanged public class TimerInfoDto : BaseTimerInfoDto
{ {
/// <summary>
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
public RecordingStatus Status { get; set; }
/// <summary>
/// Gets or sets the series timer identifier.
/// </summary>
/// <value>The series timer identifier.</value>
public string SeriesTimerId { get; set; }
/// <summary>
/// Gets or sets the external series timer identifier.
/// </summary>
/// <value>The external series timer identifier.</value>
public string ExternalSeriesTimerId { get; set; }
/// <summary>
/// Gets or sets the run time ticks.
/// </summary>
/// <value>The run time ticks.</value>
public long? RunTimeTicks { get; set; }
/// <summary>
/// Gets or sets the program information.
/// </summary>
/// <value>The program information.</value>
public ProgramInfoDto ProgramInfo { get; set; }
}
public class BaseTimerInfoDto : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary> /// <summary>
/// Id of the recording. /// Id of the recording.
/// </summary> /// </summary>
@ -32,12 +71,6 @@ namespace MediaBrowser.Model.LiveTv
/// </summary> /// </summary>
public string ChannelName { get; set; } public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the program identifier. /// Gets or sets the program identifier.
/// </summary> /// </summary>
@ -71,22 +104,16 @@ namespace MediaBrowser.Model.LiveTv
public DateTime EndDate { get; set; } public DateTime EndDate { get; set; }
/// <summary> /// <summary>
/// Gets or sets the status. /// Gets or sets the name of the service.
/// </summary>
/// <value>The status.</value>
public RecordingStatus Status { get; set; }
/// <summary>
/// Gets or sets the series timer identifier.
/// </summary> /// </summary>
/// <value>The series timer identifier.</value> /// <value>The name of the service.</value>
public string SeriesTimerId { get; set; } public string ServiceName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the external series timer identifier. /// Gets or sets the priority.
/// </summary> /// </summary>
/// <value>The external series timer identifier.</value> /// <value>The priority.</value>
public string ExternalSeriesTimerId { get; set; } public int Priority { get; set; }
/// <summary> /// <summary>
/// Gets or sets the pre padding seconds. /// Gets or sets the pre padding seconds.
@ -111,28 +138,5 @@ namespace MediaBrowser.Model.LiveTv
/// </summary> /// </summary>
/// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value> /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
public bool IsPostPaddingRequired { get; set; } public bool IsPostPaddingRequired { get; set; }
/// <summary>
/// Gets or sets the run time ticks.
/// </summary>
/// <value>The run time ticks.</value>
public long? RunTimeTicks { get; set; }
/// <summary>
/// Gets or sets the priority.
/// </summary>
/// <value>The priority.</value>
public int Priority { get; set; }
/// <summary>
/// Gets or sets the program information.
/// </summary>
/// <value>The program information.</value>
public ProgramInfoDto ProgramInfo { get; set; }
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
} }
} }

@ -107,7 +107,8 @@ namespace MediaBrowser.Providers.Manager
// Next run metadata providers // Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{ {
var providers = GetProviders(item, refreshResult.DateLastMetadataRefresh.HasValue, refreshOptions).ToList(); var providers = GetProviders(item, refreshResult.DateLastMetadataRefresh.HasValue, refreshOptions)
.ToList();
if (providers.Count > 0 || !refreshResult.DateLastMetadataRefresh.HasValue) if (providers.Count > 0 || !refreshResult.DateLastMetadataRefresh.HasValue)
{ {

@ -741,6 +741,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
dto.LockedFields = item.LockedFields; dto.LockedFields = item.LockedFields;
dto.LockData = item.IsLocked; dto.LockData = item.IsLocked;
dto.ForcedSortName = item.ForcedSortName;
} }
var hasBudget = item as IHasBudget; var hasBudget = item as IHasBudget;

@ -88,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
CreateRules(device); CreateRules(device);
} }
catch (Exception ex) catch (Exception)
{ {
//_logger.ErrorException("Error creating port forwarding rules", ex); //_logger.ErrorException("Error creating port forwarding rules", ex);
} }

@ -266,7 +266,7 @@ namespace MediaBrowser.ServerApplication
{ {
MigrateUserFolders(); MigrateUserFolders();
} }
catch (IOException ex) catch (IOException)
{ {
} }
@ -498,7 +498,7 @@ namespace MediaBrowser.ServerApplication
var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger); var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
RegisterSingleInstance<IAppThemeManager>(appThemeManager); RegisterSingleInstance<IAppThemeManager>(appThemeManager);
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager); var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, JsonSerializer);
RegisterSingleInstance<IDlnaManager>(dlnaManager); RegisterSingleInstance<IDlnaManager>(dlnaManager);
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor); var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);

@ -4029,7 +4029,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
self.createPackageReview = function (review) { self.createPackageReview = function (review) {
var url = self.getUrl("PackageReviews/" + review.id, review); var url = self.getUrl("Packages/Reviews/" + review.id, review);
return self.ajax({ return self.ajax({
type: "POST", type: "POST",
@ -4058,7 +4058,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
options.ForceTitle = true; options.ForceTitle = true;
} }
var url = self.getUrl("PackageReviews/" + packageId, options); var url = self.getUrl("Packages/" + packageId + "Reviews", options);
return self.ajax({ return self.ajax({
type: "GET", type: "GET",

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.247" targetFramework="net45" /> <package id="MediaBrowser.ApiClient.Javascript" version="3.0.248" targetFramework="net45" />
</packages> </packages>

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

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

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

Loading…
Cancel
Save