mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-15 18:08:53 -07:00
dlna server fixes
This commit is contained in:
parent
a5b3ab9fc5
commit
247400717e
@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using ServiceStack;
|
||||
using ServiceStack.Text.Controller;
|
||||
using ServiceStack.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Dlna
|
||||
@ -17,21 +19,28 @@ namespace MediaBrowser.Api.Dlna
|
||||
public string UuId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Dlna/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")]
|
||||
[Route("/Dlna/contentdirectory", "GET", Summary = "Gets dlna content directory xml")]
|
||||
[Route("/Dlna/contentdirectory/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")]
|
||||
[Route("/Dlna/contentdirectory/contentdirectory", "GET", Summary = "Gets dlna content directory xml")]
|
||||
public class GetContentDirectory
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Dlna/{UuId}/control", "POST", Summary = "Processes a control request")]
|
||||
[Route("/Dlna/contentdirectory/{UuId}/control", "POST", Summary = "Processes a control request")]
|
||||
public class ProcessControlRequest : IRequiresRequestStream
|
||||
{
|
||||
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string UuId { get; set; }
|
||||
|
||||
|
||||
public Stream RequestStream { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Dlna/contentdirectory/{UuId}/events", Summary = "Processes an event subscription request")]
|
||||
public class ProcessEventRequest
|
||||
{
|
||||
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string UuId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")]
|
||||
public class GetIcon
|
||||
{
|
||||
@ -72,8 +81,8 @@ namespace MediaBrowser.Api.Dlna
|
||||
private async Task<ControlResponse> PostAsync(ProcessControlRequest request)
|
||||
{
|
||||
var pathInfo = PathInfo.Parse(Request.PathInfo);
|
||||
var id = pathInfo.GetArgumentValue<string>(1);
|
||||
|
||||
var id = pathInfo.GetArgumentValue<string>(2);
|
||||
|
||||
using (var reader = new StreamReader(request.RequestStream))
|
||||
{
|
||||
return _dlnaManager.ProcessControlRequest(new ControlRequest
|
||||
@ -111,5 +120,76 @@ namespace MediaBrowser.Api.Dlna
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object Any(ProcessEventRequest request)
|
||||
{
|
||||
var subscriptionId = GetHeader("SID");
|
||||
var notificationType = GetHeader("NT");
|
||||
var callback = GetHeader("CALLBACK");
|
||||
var timeoutString = GetHeader("TIMEOUT");
|
||||
|
||||
var timeout = ParseTimeout(timeoutString) ?? 300;
|
||||
|
||||
if (string.Equals(Request.Verb, "SUBSCRIBE", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(notificationType))
|
||||
{
|
||||
RenewEvent(subscriptionId, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
SubscribeToEvent(notificationType, timeout, callback);
|
||||
}
|
||||
|
||||
return GetSubscriptionResponse(request.UuId, timeout);
|
||||
}
|
||||
|
||||
UnsubscribeFromEvent(subscriptionId);
|
||||
return ResultFactory.GetResult("", "text/plain");
|
||||
}
|
||||
|
||||
private void UnsubscribeFromEvent(string subscriptionId)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void SubscribeToEvent(string notificationType, int? timeout, string callback)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void RenewEvent(string subscriptionId, int? timeout)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private object GetSubscriptionResponse(string uuid, int timeout)
|
||||
{
|
||||
var headers = new Dictionary<string, string>();
|
||||
|
||||
headers["SID"] = "uuid:" + uuid;
|
||||
headers["TIMEOUT"] = "SECOND-" + timeout.ToString(_usCulture);
|
||||
|
||||
return ResultFactory.GetResult("\r\n", "text/plain", headers);
|
||||
}
|
||||
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private int? ParseTimeout(string header)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(header))
|
||||
{
|
||||
// Starts with SECOND-
|
||||
header = header.Split('-').Last();
|
||||
|
||||
int val;
|
||||
|
||||
if (int.TryParse(header, NumberStyles.Any, _usCulture, out val))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Dlna.Profiles;
|
||||
using MediaBrowser.Dlna.Server;
|
||||
@ -31,8 +33,9 @@ namespace MediaBrowser.Dlna
|
||||
private readonly IDtoService _dtoService;
|
||||
private readonly IImageProcessor _imageProcessor;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager)
|
||||
public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IApplicationPaths appPaths, ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, IServerConfigurationManager config)
|
||||
{
|
||||
_xmlSerializer = xmlSerializer;
|
||||
_fileSystem = fileSystem;
|
||||
@ -44,6 +47,7 @@ namespace MediaBrowser.Dlna
|
||||
_dtoService = dtoService;
|
||||
_imageProcessor = imageProcessor;
|
||||
_userDataManager = userDataManager;
|
||||
_config = config;
|
||||
|
||||
//DumpProfiles();
|
||||
}
|
||||
@ -451,15 +455,11 @@ namespace MediaBrowser.Dlna
|
||||
|
||||
var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profile.Id, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (current.Info.Type == DeviceProfileType.System)
|
||||
{
|
||||
throw new ArgumentException("System profiles are readonly");
|
||||
}
|
||||
|
||||
var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
|
||||
var path = Path.Combine(UserProfilesPath, newFilename);
|
||||
|
||||
if (!string.Equals(path, current.Path, StringComparison.Ordinal))
|
||||
if (!string.Equals(path, current.Path, StringComparison.Ordinal) &&
|
||||
current.Info.Type != DeviceProfileType.System)
|
||||
{
|
||||
File.Delete(current.Path);
|
||||
}
|
||||
@ -516,15 +516,17 @@ namespace MediaBrowser.Dlna
|
||||
|
||||
var serverAddress = device.Descriptor.ToString().Substring(0, device.Descriptor.ToString().IndexOf("/dlna", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var user = GetUser(profile);
|
||||
|
||||
return new ControlHandler(
|
||||
_logger,
|
||||
_userManager,
|
||||
_libraryManager,
|
||||
profile,
|
||||
serverAddress,
|
||||
_dtoService,
|
||||
_imageProcessor,
|
||||
_userDataManager)
|
||||
_userDataManager,
|
||||
user)
|
||||
.ProcessControlRequest(request);
|
||||
}
|
||||
|
||||
@ -540,5 +542,33 @@ namespace MediaBrowser.Dlna
|
||||
Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
private User GetUser(DeviceProfile profile)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(profile.UserId))
|
||||
{
|
||||
var user = _userManager.GetUserById(new Guid(profile.UserId));
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_config.Configuration.DlnaOptions.DefaultUserId))
|
||||
{
|
||||
var user = _userManager.GetUserById(new Guid(_config.Configuration.DlnaOptions.DefaultUserId));
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
// No configuration so it's going to be pretty arbitrary
|
||||
return _userManager.Users.First();
|
||||
}
|
||||
}
|
||||
}
|
@ -25,12 +25,12 @@ namespace MediaBrowser.Dlna.Server
|
||||
public class ControlHandler
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly DeviceProfile _profile;
|
||||
private readonly IDtoService _dtoService;
|
||||
private readonly IImageProcessor _imageProcessor;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private readonly User _user;
|
||||
|
||||
private readonly string _serverAddress;
|
||||
|
||||
@ -44,16 +44,16 @@ namespace MediaBrowser.Dlna.Server
|
||||
private int systemID = 0;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
public ControlHandler(ILogger logger, IUserManager userManager, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager)
|
||||
public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user)
|
||||
{
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_libraryManager = libraryManager;
|
||||
_profile = profile;
|
||||
_serverAddress = serverAddress;
|
||||
_dtoService = dtoService;
|
||||
_imageProcessor = imageProcessor;
|
||||
_userDataManager = userDataManager;
|
||||
_user = user;
|
||||
}
|
||||
|
||||
public ControlResponse ProcessControlRequest(ControlRequest request)
|
||||
@ -104,31 +104,24 @@ namespace MediaBrowser.Dlna.Server
|
||||
|
||||
_logger.Debug("Received control request {0}", method.Name);
|
||||
|
||||
var user = _userManager.Users.First();
|
||||
var user = _user;
|
||||
|
||||
switch (method.LocalName)
|
||||
{
|
||||
case "GetSearchCapabilities":
|
||||
result = HandleGetSearchCapabilities();
|
||||
break;
|
||||
case "GetSortCapabilities":
|
||||
result = HandleGetSortCapabilities();
|
||||
break;
|
||||
case "GetSystemUpdateID":
|
||||
result = HandleGetSystemUpdateID();
|
||||
break;
|
||||
case "Browse":
|
||||
result = HandleBrowse(sparams, user, deviceId);
|
||||
break;
|
||||
case "X_GetFeatureList":
|
||||
result = HandleXGetFeatureList();
|
||||
break;
|
||||
case "X_SetBookmark":
|
||||
result = HandleXSetBookmark(sparams, user);
|
||||
break;
|
||||
default:
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
if (string.Equals(method.LocalName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
|
||||
result = HandleGetSearchCapabilities();
|
||||
else if (string.Equals(method.LocalName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
|
||||
result = HandleGetSortCapabilities();
|
||||
else if (string.Equals(method.LocalName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
|
||||
result = HandleGetSystemUpdateID();
|
||||
else if (string.Equals(method.LocalName, "Browse", StringComparison.OrdinalIgnoreCase))
|
||||
result = HandleBrowse(sparams, user, deviceId);
|
||||
else if (string.Equals(method.LocalName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
|
||||
result = HandleXGetFeatureList();
|
||||
else if (string.Equals(method.LocalName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
|
||||
result = HandleXSetBookmark(sparams, user);
|
||||
else if (string.Equals(method.LocalName, "Search", StringComparison.OrdinalIgnoreCase))
|
||||
result = HandleSearch(sparams, user, deviceId);
|
||||
else
|
||||
throw new ResourceNotFoundException("Unexpected control request name: " + method.LocalName);
|
||||
|
||||
var response = env.CreateElement(String.Format("u:{0}Response", method.LocalName), method.NamespaceURI);
|
||||
rbody.AppendChild(response);
|
||||
@ -241,10 +234,12 @@ namespace MediaBrowser.Dlna.Server
|
||||
{
|
||||
var id = sparams["ObjectID"];
|
||||
var flag = sparams["BrowseFlag"];
|
||||
var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
|
||||
var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
|
||||
|
||||
var provided = 0;
|
||||
int requested = 0;
|
||||
int start = 0;
|
||||
var requested = 0;
|
||||
var start = 0;
|
||||
|
||||
if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
|
||||
{
|
||||
@ -267,11 +262,13 @@ namespace MediaBrowser.Dlna.Server
|
||||
|
||||
var folder = (Folder)GetItemFromObjectId(id, user);
|
||||
|
||||
var children = GetChildrenSorted(folder, user).ToList();
|
||||
var children = GetChildrenSorted(folder, user, sortCriteria).ToList();
|
||||
|
||||
var totalCount = children.Count;
|
||||
|
||||
if (string.Equals(flag, "BrowseMetadata"))
|
||||
{
|
||||
Browse_AddFolder(result, folder, children.Count);
|
||||
Browse_AddFolder(result, folder, children.Count, filter);
|
||||
provided++;
|
||||
}
|
||||
else
|
||||
@ -292,13 +289,13 @@ namespace MediaBrowser.Dlna.Server
|
||||
if (i.IsFolder)
|
||||
{
|
||||
var f = (Folder)i;
|
||||
var childCount = GetChildrenSorted(f, user).Count();
|
||||
var childCount = GetChildrenSorted(f, user, sortCriteria).Count();
|
||||
|
||||
Browse_AddFolder(result, f, childCount);
|
||||
Browse_AddFolder(result, f, childCount, filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Browse_AddItem(result, i, user, deviceId);
|
||||
Browse_AddItem(result, i, user, deviceId, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -309,35 +306,175 @@ namespace MediaBrowser.Dlna.Server
|
||||
{
|
||||
new KeyValuePair<string,string>("Result", resXML),
|
||||
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
|
||||
new KeyValuePair<string,string>("TotalMatches", children.Count.ToString(_usCulture)),
|
||||
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
|
||||
new KeyValuePair<string,string>("UpdateID", systemID.ToString(_usCulture))
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user)
|
||||
private IEnumerable<KeyValuePair<string, string>> HandleSearch(Headers sparams, User user, string deviceId)
|
||||
{
|
||||
var children = folder.GetChildren(user, true).Where(i => i.LocationType != LocationType.Virtual);
|
||||
var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
|
||||
var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
|
||||
var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
|
||||
|
||||
// sort example: dc:title, dc:date
|
||||
|
||||
var provided = 0;
|
||||
var requested = 0;
|
||||
var start = 0;
|
||||
|
||||
if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
|
||||
{
|
||||
requested = 0;
|
||||
}
|
||||
if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0)
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
|
||||
//var root = GetItem(id) as IMediaFolder;
|
||||
var result = new XmlDocument();
|
||||
|
||||
var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
|
||||
didl.SetAttribute("xmlns:dc", NS_DC);
|
||||
didl.SetAttribute("xmlns:dlna", NS_DLNA);
|
||||
didl.SetAttribute("xmlns:upnp", NS_UPNP);
|
||||
didl.SetAttribute("xmlns:sec", NS_SEC);
|
||||
result.AppendChild(didl);
|
||||
|
||||
var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user);
|
||||
|
||||
var children = GetChildrenSorted(folder, user, searchCriteria, sortCriteria).ToList();
|
||||
|
||||
var totalCount = children.Count;
|
||||
|
||||
if (start > 0)
|
||||
{
|
||||
children = children.Skip(start).ToList();
|
||||
}
|
||||
if (requested > 0)
|
||||
{
|
||||
children = children.Take(requested).ToList();
|
||||
}
|
||||
|
||||
provided = children.Count;
|
||||
|
||||
foreach (var i in children)
|
||||
{
|
||||
if (i.IsFolder)
|
||||
{
|
||||
var f = (Folder)i;
|
||||
var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count();
|
||||
|
||||
Browse_AddFolder(result, f, childCount, filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Browse_AddItem(result, i, user, deviceId, filter);
|
||||
}
|
||||
}
|
||||
|
||||
var resXML = result.OuterXml;
|
||||
|
||||
return new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string,string>("Result", resXML),
|
||||
new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
|
||||
new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
|
||||
new KeyValuePair<string,string>("UpdateID", systemID.ToString(_usCulture))
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort)
|
||||
{
|
||||
if (search.SearchType == SearchType.Unknown)
|
||||
{
|
||||
return GetChildrenSorted(folder, user, sort);
|
||||
}
|
||||
|
||||
var items = folder.GetRecursiveChildren(user);
|
||||
items = FilterUnsupportedContent(items);
|
||||
|
||||
if (search.SearchType == SearchType.Audio)
|
||||
{
|
||||
items = items.OfType<Audio>();
|
||||
}
|
||||
else if (search.SearchType == SearchType.Video)
|
||||
{
|
||||
items = items.OfType<Video>();
|
||||
}
|
||||
else if (search.SearchType == SearchType.Image)
|
||||
{
|
||||
items = items.OfType<Photo>();
|
||||
}
|
||||
else if (search.SearchType == SearchType.Playlist)
|
||||
{
|
||||
}
|
||||
|
||||
return SortItems(items, user, sort);
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SortCriteria sort)
|
||||
{
|
||||
var items = folder.GetChildren(user, true);
|
||||
|
||||
items = FilterUnsupportedContent(items);
|
||||
|
||||
if (folder is Series || folder is Season || folder is BoxSet)
|
||||
{
|
||||
return children;
|
||||
return items;
|
||||
}
|
||||
|
||||
return _libraryManager.Sort(children, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
|
||||
return SortItems(items, user, sort);
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> SortItems(IEnumerable<BaseItem> items, User user, SortCriteria sort)
|
||||
{
|
||||
return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> FilterUnsupportedContent(IEnumerable<BaseItem> items)
|
||||
{
|
||||
return items.Where(i =>
|
||||
{
|
||||
// Unplayable
|
||||
// TODO: Display and prevent playback with restricted flag?
|
||||
if (i.LocationType == LocationType.Virtual)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unplayable
|
||||
// TODO: Display and prevent playback with restricted flag?
|
||||
var supportsPlaceHolder = i as ISupportsPlaceHolders;
|
||||
if (supportsPlaceHolder != null && supportsPlaceHolder.IsPlaceHolder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upnp renderers won't understand these
|
||||
// TODO: Display and prevent playback with restricted flag?
|
||||
if (i is Game || i is Book)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private BaseItem GetItemFromObjectId(string id, User user)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(id) || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
|
||||
return string.IsNullOrWhiteSpace(id) || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
|
||||
|
||||
// Samsung sometimes uses 1 as root
|
||||
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase)
|
||||
// Samsung sometimes uses 1 as root
|
||||
|| string.Equals(id, "1", StringComparison.OrdinalIgnoreCase)
|
||||
|
||||
? user.RootFolder
|
||||
: _libraryManager.GetItemById(new Guid(id));
|
||||
? user.RootFolder
|
||||
: _libraryManager.GetItemById(new Guid(id));
|
||||
}
|
||||
|
||||
private void Browse_AddFolder(XmlDocument result, Folder f, int childCount)
|
||||
private void Browse_AddFolder(XmlDocument result, Folder f, int childCount, Filter filter)
|
||||
{
|
||||
var container = result.CreateElement(string.Empty, "container", NS_DIDL);
|
||||
container.SetAttribute("restricted", "0");
|
||||
@ -355,7 +492,7 @@ namespace MediaBrowser.Dlna.Server
|
||||
container.SetAttribute("parentID", parent.Id.ToString("N"));
|
||||
}
|
||||
|
||||
AddCommonFields(f, container);
|
||||
AddCommonFields(f, container, filter);
|
||||
|
||||
AddCover(f, container);
|
||||
|
||||
@ -377,7 +514,7 @@ namespace MediaBrowser.Dlna.Server
|
||||
}
|
||||
}
|
||||
|
||||
private void Browse_AddItem(XmlDocument result, BaseItem item, User user, string deviceId)
|
||||
private void Browse_AddItem(XmlDocument result, BaseItem item, User user, string deviceId, Filter filter)
|
||||
{
|
||||
var element = result.CreateElement(string.Empty, "item", NS_DIDL);
|
||||
element.SetAttribute("restricted", "1");
|
||||
@ -392,7 +529,7 @@ namespace MediaBrowser.Dlna.Server
|
||||
|
||||
AddBookmarkInfo(item, user, element);
|
||||
|
||||
AddGeneralProperties(item, element);
|
||||
AddGeneralProperties(item, element, filter);
|
||||
|
||||
// refID?
|
||||
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
|
||||
@ -400,13 +537,13 @@ namespace MediaBrowser.Dlna.Server
|
||||
var audio = item as Audio;
|
||||
if (audio != null)
|
||||
{
|
||||
AddAudioResource(element, audio, deviceId);
|
||||
AddAudioResource(element, audio, deviceId, filter);
|
||||
}
|
||||
|
||||
var video = item as Video;
|
||||
if (video != null)
|
||||
{
|
||||
AddVideoResource(element, video, deviceId);
|
||||
AddVideoResource(element, video, deviceId, filter);
|
||||
}
|
||||
|
||||
AddCover(item, element);
|
||||
@ -414,7 +551,7 @@ namespace MediaBrowser.Dlna.Server
|
||||
result.DocumentElement.AppendChild(element);
|
||||
}
|
||||
|
||||
private void AddVideoResource(XmlElement container, Video video, string deviceId)
|
||||
private void AddVideoResource(XmlElement container, Video video, string deviceId, Filter filter)
|
||||
{
|
||||
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
|
||||
|
||||
@ -441,13 +578,16 @@ namespace MediaBrowser.Dlna.Server
|
||||
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
|
||||
}
|
||||
|
||||
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
|
||||
if (filter.Contains("res@size"))
|
||||
{
|
||||
var size = streamInfo.TargetSize;
|
||||
|
||||
if (size.HasValue)
|
||||
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
|
||||
{
|
||||
res.SetAttribute("size", size.Value.ToString(_usCulture));
|
||||
var size = streamInfo.TargetSize;
|
||||
|
||||
if (size.HasValue)
|
||||
{
|
||||
res.SetAttribute("size", size.Value.ToString(_usCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,11 +613,14 @@ namespace MediaBrowser.Dlna.Server
|
||||
res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
|
||||
}
|
||||
|
||||
if (targetWidth.HasValue && targetHeight.HasValue)
|
||||
if (filter.Contains("res@resolution"))
|
||||
{
|
||||
res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
|
||||
if (targetWidth.HasValue && targetHeight.HasValue)
|
||||
{
|
||||
res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (targetSampleRate.HasValue)
|
||||
{
|
||||
res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
|
||||
@ -514,7 +657,7 @@ namespace MediaBrowser.Dlna.Server
|
||||
container.AppendChild(res);
|
||||
}
|
||||
|
||||
private void AddAudioResource(XmlElement container, Audio audio, string deviceId)
|
||||
private void AddAudioResource(XmlElement container, Audio audio, string deviceId, Filter filter)
|
||||
{
|
||||
var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
|
||||
|
||||
@ -538,13 +681,16 @@ namespace MediaBrowser.Dlna.Server
|
||||
res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
|
||||
}
|
||||
|
||||
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
|
||||
if (filter.Contains("res@size"))
|
||||
{
|
||||
var size = streamInfo.TargetSize;
|
||||
|
||||
if (size.HasValue)
|
||||
if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
|
||||
{
|
||||
res.SetAttribute("size", size.Value.ToString(_usCulture));
|
||||
var size = streamInfo.TargetSize;
|
||||
|
||||
if (size.HasValue)
|
||||
{
|
||||
res.SetAttribute("size", size.Value.ToString(_usCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,13 +805,17 @@ namespace MediaBrowser.Dlna.Server
|
||||
/// <summary>
|
||||
/// Adds fields used by both items and folders
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="element"></param>
|
||||
private void AddCommonFields(BaseItem item, XmlElement element)
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="element">The element.</param>
|
||||
/// <param name="filter">The filter.</param>
|
||||
private void AddCommonFields(BaseItem item, XmlElement element, Filter filter)
|
||||
{
|
||||
if (item.PremiereDate.HasValue)
|
||||
if (filter.Contains("dc:date"))
|
||||
{
|
||||
AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
|
||||
if (item.PremiereDate.HasValue)
|
||||
{
|
||||
AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Genres.Count > 0)
|
||||
@ -678,24 +828,44 @@ namespace MediaBrowser.Dlna.Server
|
||||
AddValue(element, "upnp", "publisher", item.Studios[0], NS_UPNP);
|
||||
}
|
||||
|
||||
AddValue(element, "dc", "title", item.Name, NS_DC);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(item.Overview))
|
||||
if (filter.Contains("dc:title"))
|
||||
{
|
||||
AddValue(element, "dc", "description", item.Overview, NS_DC);
|
||||
AddValue(element, "dc", "title", item.Name, NS_DC);
|
||||
}
|
||||
|
||||
if (filter.Contains("dc:description"))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item.Overview))
|
||||
{
|
||||
AddValue(element, "dc", "description", item.Overview, NS_DC);
|
||||
}
|
||||
}
|
||||
if (filter.Contains("upnp:longDescription"))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item.Overview))
|
||||
{
|
||||
AddValue(element, "upnp", "longDescription", item.Overview, NS_UPNP);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(item.OfficialRating))
|
||||
{
|
||||
AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
|
||||
if (filter.Contains("dc:rating"))
|
||||
{
|
||||
AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
|
||||
}
|
||||
if (filter.Contains("upnp:rating"))
|
||||
{
|
||||
AddValue(element, "upnp", "rating", item.OfficialRating, NS_UPNP);
|
||||
}
|
||||
}
|
||||
|
||||
AddPeople(item, element);
|
||||
}
|
||||
|
||||
private void AddGeneralProperties(BaseItem item, XmlElement element)
|
||||
private void AddGeneralProperties(BaseItem item, XmlElement element, Filter filter)
|
||||
{
|
||||
AddCommonFields(item, element);
|
||||
AddCommonFields(item, element, filter);
|
||||
|
||||
var audio = item as Audio;
|
||||
|
||||
@ -861,7 +1031,7 @@ namespace MediaBrowser.Dlna.Server
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
return new ImageDownloadInfo
|
||||
{
|
||||
ItemId = item.Id.ToString("N"),
|
||||
|
@ -163,7 +163,7 @@ namespace MediaBrowser.Dlna.Server
|
||||
Height = 48,
|
||||
Url = "/mediabrowser/dlna/icons/logo48.jpg"
|
||||
});
|
||||
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@ -175,8 +175,9 @@ namespace MediaBrowser.Dlna.Server
|
||||
{
|
||||
ServiceType = "urn:schemas-upnp-org:service:ContentDirectory:1",
|
||||
ServiceId = "urn:upnp-org:serviceId:ContentDirectory",
|
||||
ScpdUrl = "/mediabrowser/dlna/contentdirectory.xml",
|
||||
ControlUrl = "/mediabrowser/dlna/" + _serverUdn + "/control"
|
||||
ScpdUrl = "/mediabrowser/dlna/contentdirectory/contentdirectory.xml",
|
||||
ControlUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/control",
|
||||
EventSubUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/events"
|
||||
});
|
||||
|
||||
return list;
|
||||
|
@ -160,5 +160,17 @@ namespace MediaBrowser.Dlna.Server
|
||||
{
|
||||
return _dict.TryGetValue(Normalize(key), out value);
|
||||
}
|
||||
|
||||
public string GetValueOrDefault(string key, string defaultValue)
|
||||
{
|
||||
string val;
|
||||
|
||||
if (TryGetValue(key, out val))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,11 @@ namespace MediaBrowser.Dlna.Server
|
||||
break;
|
||||
}
|
||||
var parts = line.Split(new[] { ':' }, 2);
|
||||
headers[parts[0]] = parts[1].Trim();
|
||||
|
||||
if (parts.Length >= 2)
|
||||
{
|
||||
headers[parts[0]] = parts[1].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (_config.Configuration.DlnaOptions.EnableDebugLogging)
|
||||
@ -158,18 +162,21 @@ namespace MediaBrowser.Dlna.Server
|
||||
|
||||
private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev)
|
||||
{
|
||||
var headers = new Headers(true);
|
||||
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);
|
||||
var builder = new StringBuilder();
|
||||
|
||||
var msg = String.Format("HTTP/1.1 200 OK\r\n{0}\r\n", headers.HeaderBlock);
|
||||
const string argFormat = "{0}: {1}\r\n";
|
||||
|
||||
SendDatagram(endpoint, dev.Address, msg, false);
|
||||
builder.Append("HTTP/1.1 200 OK\r\n");
|
||||
builder.AppendFormat(argFormat, "CACHE-CONTROL", "max-age = 600");
|
||||
builder.AppendFormat(argFormat, "DATE", DateTime.Now.ToString("R"));
|
||||
builder.AppendFormat(argFormat, "EXT", "");
|
||||
builder.AppendFormat(argFormat, "LOCATION", dev.Descriptor);
|
||||
builder.AppendFormat(argFormat, "SERVER", _serverSignature);
|
||||
builder.AppendFormat(argFormat, "ST", dev.Type);
|
||||
builder.AppendFormat(argFormat, "USN", dev.USN);
|
||||
builder.Append("\r\n");
|
||||
|
||||
SendDatagram(endpoint, dev.Address, builder.ToString(), false);
|
||||
|
||||
_logger.Info("{1} - Responded to a {0} request to {2}", dev.Type, endpoint, dev.Address.ToString());
|
||||
}
|
||||
@ -237,19 +244,22 @@ namespace MediaBrowser.Dlna.Server
|
||||
private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
|
||||
{
|
||||
_logger.Debug("NotifyDevice");
|
||||
var headers = new Headers(true);
|
||||
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);
|
||||
var builder = new StringBuilder();
|
||||
|
||||
var msg = String.Format("NOTIFY * HTTP/1.1\r\n{0}\r\n", headers.HeaderBlock);
|
||||
const string argFormat = "{0}: {1}\r\n";
|
||||
|
||||
builder.Append("NOTIFY * HTTP/1.1\r\n{0}\r\n");
|
||||
builder.AppendFormat(argFormat, "HOST", "239.255.255.250:1900");
|
||||
builder.AppendFormat(argFormat, "CACHE-CONTROL", "max-age = 600");
|
||||
builder.AppendFormat(argFormat, "LOCATION", dev.Descriptor);
|
||||
builder.AppendFormat(argFormat, "SERVER", _serverSignature);
|
||||
builder.AppendFormat(argFormat, "NTS", "ssdp:" + type);
|
||||
builder.AppendFormat(argFormat, "NT", dev.Type);
|
||||
builder.AppendFormat(argFormat, "USN", dev.USN);
|
||||
builder.Append("\r\n");
|
||||
|
||||
_logger.Debug("{0} said {1}", dev.USN, type);
|
||||
SendDatagram(_ssdpEndp, dev.Address, msg, sticky);
|
||||
SendDatagram(_ssdpEndp, dev.Address, builder.ToString(), sticky);
|
||||
}
|
||||
|
||||
public void RegisterNotification(Guid uuid, Uri descriptor, IPAddress address)
|
||||
@ -343,7 +353,12 @@ namespace MediaBrowser.Dlna.Server
|
||||
private readonly object _notificationTimerSyncLock = new object();
|
||||
private void StartNotificationTimer()
|
||||
{
|
||||
const int intervalMs = 60000;
|
||||
if (!_config.Configuration.DlnaOptions.BlastAliveMessages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var intervalMs = _config.Configuration.DlnaOptions.BlastAliveMessageIntervalSeconds * 1000;
|
||||
|
||||
lock (_notificationTimerSyncLock)
|
||||
{
|
||||
|
@ -19,13 +19,13 @@ namespace MediaBrowser.Dlna.Server
|
||||
|
||||
Address = address;
|
||||
|
||||
if (Type.StartsWith("uuid:"))
|
||||
if (Type.StartsWith("uuid:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
USN = Type;
|
||||
}
|
||||
else
|
||||
{
|
||||
USN = String.Format("uuid:{0}::{1}", Uuid.ToString(), Type);
|
||||
USN = String.Format("uuid:{0}::{1}", Uuid.ToString("N"), Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
/// </summary>
|
||||
/// <param name="inputFiles">The input files.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private static string GetConcatInputArgument(List<string> inputFiles)
|
||||
private static string GetConcatInputArgument(IReadOnlyList<string> inputFiles)
|
||||
{
|
||||
// Get all streams
|
||||
// If there's more than one we'll need to use the concat command
|
||||
|
@ -122,6 +122,9 @@
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
|
||||
<Link>Dlna\DlnaMaps.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
|
||||
<Link>Dlna\Filter.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
|
||||
<Link>Dlna\MediaFormatProfile.cs</Link>
|
||||
</Compile>
|
||||
@ -131,6 +134,12 @@
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
|
||||
<Link>Dlna\ResponseProfile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\SearchCriteria.cs">
|
||||
<Link>Dlna\SearchCriteria.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\SortCriteria.cs">
|
||||
<Link>Dlna\SortCriteria.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
|
||||
<Link>Dlna\StreamBuilder.cs</Link>
|
||||
</Compile>
|
||||
|
@ -109,6 +109,9 @@
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
|
||||
<Link>Dlna\DlnaMaps.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\Filter.cs">
|
||||
<Link>Dlna\Filter.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
|
||||
<Link>Dlna\MediaFormatProfile.cs</Link>
|
||||
</Compile>
|
||||
@ -118,6 +121,12 @@
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
|
||||
<Link>Dlna\ResponseProfile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\SearchCriteria.cs">
|
||||
<Link>Dlna\SearchCriteria.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\SortCriteria.cs">
|
||||
<Link>Dlna\SortCriteria.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
|
||||
<Link>Dlna\StreamBuilder.cs</Link>
|
||||
</Compile>
|
||||
|
@ -6,13 +6,18 @@ namespace MediaBrowser.Model.Configuration
|
||||
public bool EnablePlayTo { get; set; }
|
||||
public bool EnableServer { get; set; }
|
||||
public bool EnableDebugLogging { get; set; }
|
||||
public bool BlastAliveMessages { get; set; }
|
||||
public int ClientDiscoveryIntervalSeconds { get; set; }
|
||||
public int BlastAliveMessageIntervalSeconds { get; set; }
|
||||
public string DefaultUserId { get; set; }
|
||||
|
||||
public DlnaOptions()
|
||||
{
|
||||
EnablePlayTo = true;
|
||||
EnableServer = true;
|
||||
BlastAliveMessages = true;
|
||||
ClientDiscoveryIntervalSeconds = 60;
|
||||
BlastAliveMessageIntervalSeconds = 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ namespace MediaBrowser.Model.Dlna
|
||||
|
||||
public string SupportedMediaTypes { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
|
||||
/// </summary>
|
||||
|
26
MediaBrowser.Model/Dlna/Filter.cs
Normal file
26
MediaBrowser.Model/Dlna/Filter.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class Filter
|
||||
{
|
||||
private readonly List<string> _fields;
|
||||
private readonly bool _all;
|
||||
|
||||
public Filter(string filter)
|
||||
{
|
||||
_all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
_fields = (filter ?? string.Empty)
|
||||
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public bool Contains(string field)
|
||||
{
|
||||
return _all || _fields.Contains(field, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
49
MediaBrowser.Model/Dlna/SearchCriteria.cs
Normal file
49
MediaBrowser.Model/Dlna/SearchCriteria.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class SearchCriteria
|
||||
{
|
||||
public SearchType SearchType { get; set; }
|
||||
|
||||
public SearchCriteria(string search)
|
||||
{
|
||||
if (string.IsNullOrEmpty(search))
|
||||
{
|
||||
throw new ArgumentNullException("search");
|
||||
}
|
||||
|
||||
SearchType = SearchType.Unknown;
|
||||
|
||||
if (search.IndexOf("upnp:class", StringComparison.OrdinalIgnoreCase) != -1 &&
|
||||
search.IndexOf("derivedfrom", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
if (search.IndexOf("object.item.audioItem", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
SearchType = SearchType.Audio;
|
||||
}
|
||||
else if (search.IndexOf("object.item.imageItem", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
SearchType = SearchType.Image;
|
||||
}
|
||||
else if (search.IndexOf("object.item.videoItem", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
SearchType = SearchType.Video;
|
||||
}
|
||||
else if (search.IndexOf("object.container.playlistContainer", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
SearchType = SearchType.Playlist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum SearchType
|
||||
{
|
||||
Unknown = 0,
|
||||
Audio = 1,
|
||||
Image = 2,
|
||||
Video = 3,
|
||||
Playlist = 4
|
||||
}
|
||||
}
|
11
MediaBrowser.Model/Dlna/SortCriteria.cs
Normal file
11
MediaBrowser.Model/Dlna/SortCriteria.cs
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
namespace MediaBrowser.Model.Dlna
|
||||
{
|
||||
public class SortCriteria
|
||||
{
|
||||
public SortCriteria(string value)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -73,9 +73,12 @@
|
||||
<Compile Include="Dlna\DeviceProfileInfo.cs" />
|
||||
<Compile Include="Dlna\DirectPlayProfile.cs" />
|
||||
<Compile Include="Dlna\DlnaMaps.cs" />
|
||||
<Compile Include="Dlna\Filter.cs" />
|
||||
<Compile Include="Dlna\MediaFormatProfile.cs" />
|
||||
<Compile Include="Dlna\MediaFormatProfileResolver.cs" />
|
||||
<Compile Include="Dlna\ResponseProfile.cs" />
|
||||
<Compile Include="Dlna\SearchCriteria.cs" />
|
||||
<Compile Include="Dlna\SortCriteria.cs" />
|
||||
<Compile Include="Dlna\StreamBuilder.cs" />
|
||||
<Compile Include="Dlna\StreamInfo.cs" />
|
||||
<Compile Include="Dlna\TranscodingProfile.cs" />
|
||||
|
@ -44,7 +44,16 @@ namespace MediaBrowser.Providers.Movies
|
||||
|
||||
var file = new FileInfo(specificFile);
|
||||
|
||||
return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "movie.xml"));
|
||||
// In a mixed folder, only {moviename}.xml is supported
|
||||
if (info.IsInMixedFolder)
|
||||
{
|
||||
return file;
|
||||
}
|
||||
|
||||
// If in it's own folder, prefer movie.xml, but allow the specific file as well
|
||||
var movieFile = new FileInfo(Path.Combine(directoryPath, "movie.xml"));
|
||||
|
||||
return movieFile.Exists ? movieFile : file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -414,7 +414,7 @@
|
||||
"LabelEnableDlnaDebugLogging": "Enable DLNA debug logging",
|
||||
"LabelEnableDlnaDebugLoggingHelp": "This will create large log files and should only be used as needed for troubleshooting purposes.",
|
||||
"LabelEnableDlnaClientDiscoveryInterval": "Client discovery interval (seconds)",
|
||||
"LabelEnableDlnaClientDiscoveryIntervalHelp": "Determines the duration in seconds of the interval between SSDP searches performed by Media Browser.",
|
||||
"LabelEnableDlnaClientDiscoveryIntervalHelp": "Determines the duration in seconds between SSDP searches performed by Media Browser.",
|
||||
"HeaderCustomDlnaProfiles": "Custom Profiles",
|
||||
"HeaderSystemDlnaProfiles": "System Profiles",
|
||||
"CustomDlnaProfilesHelp": "Create a custom profile to target a new device or override a system profile.",
|
||||
@ -532,5 +532,15 @@
|
||||
"LabelSupporterKey": "Supporter Key (paste from email)",
|
||||
"LabelSupporterKeyHelp": "Enter your supporter key to start enjoying additional benefits the community has developed for Media Browser.",
|
||||
"MessageInvalidKey": "MB3 Key Missing or Invalid",
|
||||
"ErrorMessageInvalidKey": "In order for any premium content to be registered, you must also be an MB3 Supporter. Please donate and support the continued development of the core product. Thank you."
|
||||
"ErrorMessageInvalidKey": "In order for any premium content to be registered, you must also be an MB3 Supporter. Please donate and support the continued development of the core product. Thank you.",
|
||||
"HeaderDisplaySettings": "Display Settings",
|
||||
"TabPlayTo": "Play To",
|
||||
"LabelEnableDlnaServer": "Enable Dlna server",
|
||||
"LabelEnableDlnaServerHelp": "Allows UPnP devices on your network to browse and play Media Browser content.",
|
||||
"LabelEnableBlastAliveMessages": "Blast alive messages",
|
||||
"LabelEnableBlastAliveMessagesHelp": "Enable this if the server is not detected reliably by other UPnP devices on your network.",
|
||||
"LabelBlastMessageInterval": "Alive message interval (seconds)",
|
||||
"LabelBlastMessageIntervalHelp": "Determines the duration in seconds between server alive messages.",
|
||||
"LabelDefaultUser": "Default user:",
|
||||
"LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden using a device profile."
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -506,7 +506,7 @@ namespace MediaBrowser.ServerApplication
|
||||
var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
|
||||
RegisterSingleInstance<IAppThemeManager>(appThemeManager);
|
||||
|
||||
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"), JsonSerializer, UserManager, LibraryManager, DtoService, ImageProcessor, UserDataManager);
|
||||
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("DLNA"), JsonSerializer, UserManager, LibraryManager, DtoService, ImageProcessor, UserDataManager, ServerConfigurationManager);
|
||||
RegisterSingleInstance<IDlnaManager>(dlnaManager);
|
||||
|
||||
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);
|
||||
|
@ -543,6 +543,7 @@ namespace MediaBrowser.WebDashboard.Api
|
||||
"dlnaprofile.js",
|
||||
"dlnaprofiles.js",
|
||||
"dlnasettings.js",
|
||||
"dlnaserversettings.js",
|
||||
"editcollectionitems.js",
|
||||
"edititemmetadata.js",
|
||||
"edititempeople.js",
|
||||
|
@ -241,6 +241,9 @@
|
||||
<Content Include="dashboard-ui\dlnaprofiles.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\dlnaserversettings.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\dlnasettings.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@ -541,6 +544,9 @@
|
||||
<Content Include="dashboard-ui\scripts\dlnaprofiles.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\dlnaserversettings.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\dlnasettings.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
Loading…
Reference in New Issue
Block a user