ControlHandler.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. using MediaBrowser.Common.Extensions;
  2. using MediaBrowser.Controller.Configuration;
  3. using MediaBrowser.Controller.Drawing;
  4. using MediaBrowser.Controller.Entities;
  5. using MediaBrowser.Controller.Entities.Movies;
  6. using MediaBrowser.Controller.Library;
  7. using MediaBrowser.Controller.Localization;
  8. using MediaBrowser.Dlna.Didl;
  9. using MediaBrowser.Dlna.Server;
  10. using MediaBrowser.Dlna.Service;
  11. using MediaBrowser.Model.Dlna;
  12. using MediaBrowser.Model.Entities;
  13. using MediaBrowser.Model.Logging;
  14. using MediaBrowser.Model.Querying;
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Globalization;
  18. using System.Linq;
  19. using System.Text;
  20. using System.Threading;
  21. using System.Threading.Tasks;
  22. using System.Xml;
  23. namespace MediaBrowser.Dlna.ContentDirectory
  24. {
  25. public class ControlHandler : BaseControlHandler
  26. {
  27. private readonly ILibraryManager _libraryManager;
  28. private readonly IUserDataManager _userDataManager;
  29. private readonly User _user;
  30. private const string NS_DC = "http://purl.org/dc/elements/1.1/";
  31. private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
  32. private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/";
  33. private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
  34. private readonly int _systemUpdateId;
  35. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  36. private readonly DidlBuilder _didlBuilder;
  37. private readonly DeviceProfile _profile;
  38. public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization)
  39. : base(config, logger)
  40. {
  41. _libraryManager = libraryManager;
  42. _userDataManager = userDataManager;
  43. _user = user;
  44. _systemUpdateId = systemUpdateId;
  45. _profile = profile;
  46. _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, userDataManager, localization);
  47. }
  48. protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
  49. {
  50. var deviceId = "test";
  51. var user = _user;
  52. if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
  53. return HandleGetSearchCapabilities();
  54. if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
  55. return HandleGetSortCapabilities();
  56. if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
  57. return HandleGetSortExtensionCapabilities();
  58. if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
  59. return HandleGetSystemUpdateID();
  60. if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
  61. return HandleBrowse(methodParams, user, deviceId).Result;
  62. if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
  63. return HandleXGetFeatureList();
  64. if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
  65. return HandleGetFeatureList();
  66. if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
  67. return HandleXSetBookmark(methodParams, user);
  68. if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
  69. return HandleSearch(methodParams, user, deviceId).Result;
  70. throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
  71. }
  72. private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user)
  73. {
  74. var id = sparams["ObjectID"];
  75. var serverItem = GetItemFromObjectId(id, user);
  76. var item = serverItem.Item;
  77. var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
  78. var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey());
  79. userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
  80. _userDataManager.SaveUserData(user.Id, item, userdata, UserDataSaveReason.TogglePlayed,
  81. CancellationToken.None);
  82. return new Headers();
  83. }
  84. private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
  85. {
  86. return new Headers(true) { { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" } };
  87. }
  88. private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
  89. {
  90. return new Headers(true)
  91. {
  92. { "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
  93. };
  94. }
  95. private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
  96. {
  97. return new Headers(true)
  98. {
  99. { "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
  100. };
  101. }
  102. private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
  103. {
  104. var headers = new Headers(true);
  105. headers.Add("Id", _systemUpdateId.ToString(_usCulture));
  106. return headers;
  107. }
  108. private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
  109. {
  110. return new Headers(true)
  111. {
  112. { "FeatureList", GetFeatureListXml() }
  113. };
  114. }
  115. private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
  116. {
  117. return new Headers(true)
  118. {
  119. { "FeatureList", GetFeatureListXml() }
  120. };
  121. }
  122. private string GetFeatureListXml()
  123. {
  124. var builder = new StringBuilder();
  125. builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  126. builder.Append("<Features xmlns=\"urn:schemas-upnp-org:av:avs\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd\">");
  127. builder.Append("<Feature name=\"samsung.com_BASICVIEW\" version=\"1\">");
  128. builder.Append("<container id=\"I\" type=\"object.item.imageItem\"/>");
  129. builder.Append("<container id=\"A\" type=\"object.item.audioItem\"/>");
  130. builder.Append("<container id=\"V\" type=\"object.item.videoItem\"/>");
  131. builder.Append("</Feature>");
  132. builder.Append("</Features>");
  133. return builder.ToString();
  134. }
  135. private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(Headers sparams, User user, string deviceId)
  136. {
  137. var id = sparams["ObjectID"];
  138. var flag = sparams["BrowseFlag"];
  139. var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
  140. var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
  141. var provided = 0;
  142. int? requested = 0;
  143. int? start = 0;
  144. int requestedVal;
  145. if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
  146. {
  147. requested = requestedVal;
  148. }
  149. int startVal;
  150. if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
  151. {
  152. start = startVal;
  153. }
  154. //var root = GetItem(id) as IMediaFolder;
  155. var result = new XmlDocument();
  156. var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
  157. didl.SetAttribute("xmlns:dc", NS_DC);
  158. didl.SetAttribute("xmlns:dlna", NS_DLNA);
  159. didl.SetAttribute("xmlns:upnp", NS_UPNP);
  160. //didl.SetAttribute("xmlns:sec", NS_SEC);
  161. result.AppendChild(didl);
  162. var serverItem = GetItemFromObjectId(id, user);
  163. var item = serverItem.Item;
  164. var totalCount = 0;
  165. if (string.Equals(flag, "BrowseMetadata"))
  166. {
  167. if (item.IsFolder || serverItem.StubType.HasValue)
  168. {
  169. var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false));
  170. totalCount = childrenResult.TotalRecordCount;
  171. result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, totalCount, filter, id));
  172. }
  173. else
  174. {
  175. result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, item, null, null, deviceId, filter));
  176. }
  177. provided++;
  178. }
  179. else
  180. {
  181. var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false));
  182. totalCount = childrenResult.TotalRecordCount;
  183. provided = childrenResult.Items.Length;
  184. foreach (var i in childrenResult.Items)
  185. {
  186. var displayStubType = GetDisplayStubType(i, serverItem.Item);
  187. if (i.IsFolder || displayStubType.HasValue)
  188. {
  189. var childCount = (await GetUserItems(i, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
  190. .TotalRecordCount;
  191. result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, displayStubType, item, childCount, filter));
  192. }
  193. else
  194. {
  195. result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter));
  196. }
  197. }
  198. }
  199. var resXML = result.OuterXml;
  200. return new List<KeyValuePair<string, string>>
  201. {
  202. new KeyValuePair<string,string>("Result", resXML),
  203. new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
  204. new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
  205. new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
  206. };
  207. }
  208. private StubType? GetDisplayStubType(BaseItem item, BaseItem context)
  209. {
  210. if (context == null || context.IsFolder)
  211. {
  212. var movie = item as Movie;
  213. if (movie != null)
  214. {
  215. if (movie.LocalTrailerIds.Count > 0 ||
  216. movie.SpecialFeatureIds.Count > 0)
  217. {
  218. return StubType.Folder;
  219. }
  220. }
  221. }
  222. return null;
  223. }
  224. private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
  225. {
  226. var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
  227. var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
  228. var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
  229. // sort example: dc:title, dc:date
  230. int? requested = 0;
  231. int? start = 0;
  232. int requestedVal;
  233. if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
  234. {
  235. requested = requestedVal;
  236. }
  237. int startVal;
  238. if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
  239. {
  240. start = startVal;
  241. }
  242. //var root = GetItem(id) as IMediaFolder;
  243. var result = new XmlDocument();
  244. var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
  245. didl.SetAttribute("xmlns:dc", NS_DC);
  246. didl.SetAttribute("xmlns:dlna", NS_DLNA);
  247. didl.SetAttribute("xmlns:upnp", NS_UPNP);
  248. foreach (var att in _profile.XmlRootAttributes)
  249. {
  250. didl.SetAttribute(att.Name, att.Value);
  251. }
  252. result.AppendChild(didl);
  253. var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
  254. var item = serverItem.Item;
  255. var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false));
  256. var totalCount = childrenResult.TotalRecordCount;
  257. var provided = childrenResult.Items.Length;
  258. foreach (var i in childrenResult.Items)
  259. {
  260. if (i.IsFolder)
  261. {
  262. var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
  263. .TotalRecordCount;
  264. result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, null, item, childCount, filter));
  265. }
  266. else
  267. {
  268. result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter));
  269. }
  270. }
  271. var resXML = result.OuterXml;
  272. return new List<KeyValuePair<string, string>>
  273. {
  274. new KeyValuePair<string,string>("Result", resXML),
  275. new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
  276. new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
  277. new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
  278. };
  279. }
  280. private async Task<QueryResult<BaseItem>> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
  281. {
  282. var folder = (Folder)item;
  283. var sortOrders = new List<string>();
  284. if (!folder.IsPreSorted)
  285. {
  286. sortOrders.Add(ItemSortBy.SortName);
  287. }
  288. var mediaTypes = new List<string>();
  289. bool? isFolder = null;
  290. if (search.SearchType == SearchType.Audio)
  291. {
  292. mediaTypes.Add(MediaType.Audio);
  293. isFolder = false;
  294. }
  295. else if (search.SearchType == SearchType.Video)
  296. {
  297. mediaTypes.Add(MediaType.Video);
  298. isFolder = false;
  299. }
  300. else if (search.SearchType == SearchType.Image)
  301. {
  302. mediaTypes.Add(MediaType.Photo);
  303. isFolder = false;
  304. }
  305. else if (search.SearchType == SearchType.Playlist)
  306. {
  307. //items = items.OfType<Playlist>();
  308. isFolder = true;
  309. }
  310. else if (search.SearchType == SearchType.MusicAlbum)
  311. {
  312. //items = items.OfType<MusicAlbum>();
  313. isFolder = true;
  314. }
  315. return await folder.GetItems(new InternalItemsQuery
  316. {
  317. Limit = limit,
  318. StartIndex = startIndex,
  319. SortBy = sortOrders.ToArray(),
  320. SortOrder = sort.SortOrder,
  321. User = user,
  322. Recursive = true,
  323. Filter = FilterUnsupportedContent,
  324. IsFolder = isFolder,
  325. MediaTypes = mediaTypes.ToArray()
  326. }).ConfigureAwait(false);
  327. }
  328. private async Task<QueryResult<BaseItem>> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
  329. {
  330. if (stubType.HasValue)
  331. {
  332. var movie = item as Movie;
  333. if (movie != null)
  334. {
  335. return await GetMovieItems(movie).ConfigureAwait(false);
  336. }
  337. }
  338. var folder = (Folder)item;
  339. var sortOrders = new List<string>();
  340. if (!folder.IsPreSorted)
  341. {
  342. sortOrders.Add(ItemSortBy.SortName);
  343. }
  344. return await folder.GetItems(new InternalItemsQuery
  345. {
  346. Limit = limit,
  347. StartIndex = startIndex,
  348. SortBy = sortOrders.ToArray(),
  349. SortOrder = sort.SortOrder,
  350. User = user,
  351. Filter = FilterUnsupportedContent
  352. }).ConfigureAwait(false);
  353. }
  354. private Task<QueryResult<BaseItem>> GetMovieItems(Movie item)
  355. {
  356. var list = new List<BaseItem>();
  357. list.Add(item);
  358. list.AddRange(item.LocalTrailerIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
  359. list.AddRange(item.SpecialFeatureIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
  360. list.AddRange(item.ThemeVideoIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
  361. return Task.FromResult(new QueryResult<BaseItem>
  362. {
  363. Items = list.ToArray(),
  364. TotalRecordCount = list.Count
  365. });
  366. }
  367. private bool FilterUnsupportedContent(BaseItem i, User user)
  368. {
  369. // Unplayable
  370. if (i.LocationType == LocationType.Virtual && !i.IsFolder)
  371. {
  372. return false;
  373. }
  374. // Unplayable
  375. var supportsPlaceHolder = i as ISupportsPlaceHolders;
  376. if (supportsPlaceHolder != null && supportsPlaceHolder.IsPlaceHolder)
  377. {
  378. return false;
  379. }
  380. if (i is Game || i is Book)
  381. {
  382. //return false;
  383. }
  384. return true;
  385. }
  386. private ServerItem GetItemFromObjectId(string id, User user)
  387. {
  388. return DidlBuilder.IsIdRoot(id)
  389. ? new ServerItem { Item = user.RootFolder }
  390. : ParseItemId(id, user);
  391. }
  392. private ServerItem ParseItemId(string id, User user)
  393. {
  394. Guid itemId;
  395. StubType? stubType = null;
  396. if (id.StartsWith("folder_", StringComparison.OrdinalIgnoreCase))
  397. {
  398. stubType = StubType.Folder;
  399. id = id.Split(new[] { '_' }, 2)[1];
  400. }
  401. if (Guid.TryParse(id, out itemId))
  402. {
  403. var item = _libraryManager.GetItemById(itemId);
  404. return new ServerItem
  405. {
  406. Item = item,
  407. StubType = stubType
  408. };
  409. }
  410. Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id);
  411. return new ServerItem { Item = user.RootFolder };
  412. }
  413. }
  414. internal class ServerItem
  415. {
  416. public BaseItem Item { get; set; }
  417. public StubType? StubType { get; set; }
  418. }
  419. public enum StubType
  420. {
  421. Folder = 0
  422. }
  423. }