2
0

DlnaChannelFactory.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. using MediaBrowser.Common.Extensions;
  2. using MediaBrowser.Common.Net;
  3. using MediaBrowser.Controller.Channels;
  4. using MediaBrowser.Controller.Configuration;
  5. using MediaBrowser.Controller.Dlna;
  6. using MediaBrowser.Controller.Providers;
  7. using MediaBrowser.Dlna.ContentDirectory;
  8. using MediaBrowser.Dlna.PlayTo;
  9. using MediaBrowser.Dlna.Ssdp;
  10. using MediaBrowser.Model.Channels;
  11. using MediaBrowser.Model.Entities;
  12. using MediaBrowser.Model.Logging;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using System.Threading;
  17. using System.Threading.Tasks;
  18. namespace MediaBrowser.Dlna.Channels
  19. {
  20. public class DlnaChannelFactory : IChannelFactory, IDisposable
  21. {
  22. private readonly IServerConfigurationManager _config;
  23. private readonly ILogger _logger;
  24. private readonly IHttpClient _httpClient;
  25. private readonly IDeviceDiscovery _deviceDiscovery;
  26. private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1);
  27. private List<Device> _servers = new List<Device>();
  28. public static DlnaChannelFactory Instance;
  29. private Func<List<string>> _localServersLookup;
  30. public DlnaChannelFactory(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IDeviceDiscovery deviceDiscovery)
  31. {
  32. _config = config;
  33. _httpClient = httpClient;
  34. _logger = logger;
  35. _deviceDiscovery = deviceDiscovery;
  36. Instance = this;
  37. }
  38. internal void Start(Func<List<string>> localServersLookup)
  39. {
  40. _localServersLookup = localServersLookup;
  41. //deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered;
  42. _deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft;
  43. }
  44. async void deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
  45. {
  46. string usn;
  47. if (!e.Headers.TryGetValue("USN", out usn)) usn = string.Empty;
  48. string nt;
  49. if (!e.Headers.TryGetValue("NT", out nt)) nt = string.Empty;
  50. string location;
  51. if (!e.Headers.TryGetValue("Location", out location)) location = string.Empty;
  52. if (!IsValid(nt, usn))
  53. {
  54. return;
  55. }
  56. if (_localServersLookup != null)
  57. {
  58. if (_localServersLookup().Any(i => usn.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1))
  59. {
  60. // Don't add the local Dlna server to this
  61. return;
  62. }
  63. }
  64. if (GetExistingServers(usn).Any())
  65. {
  66. return;
  67. }
  68. await _syncLock.WaitAsync().ConfigureAwait(false);
  69. try
  70. {
  71. if (GetExistingServers(usn).Any())
  72. {
  73. return;
  74. }
  75. var device = await Device.CreateuPnpDeviceAsync(new Uri(location), _httpClient, _config, _logger)
  76. .ConfigureAwait(false);
  77. if (!_servers.Any(i => string.Equals(i.Properties.UUID, device.Properties.UUID, StringComparison.OrdinalIgnoreCase)))
  78. {
  79. _servers.Add(device);
  80. }
  81. }
  82. catch (Exception ex)
  83. {
  84. }
  85. finally
  86. {
  87. _syncLock.Release();
  88. }
  89. }
  90. async void deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e)
  91. {
  92. string usn;
  93. if (!e.Headers.TryGetValue("USN", out usn)) usn = String.Empty;
  94. string nt;
  95. if (!e.Headers.TryGetValue("NT", out nt)) nt = String.Empty;
  96. if (!IsValid(nt, usn))
  97. {
  98. return;
  99. }
  100. if (!GetExistingServers(usn).Any())
  101. {
  102. return;
  103. }
  104. await _syncLock.WaitAsync().ConfigureAwait(false);
  105. try
  106. {
  107. var list = _servers.ToList();
  108. foreach (var device in GetExistingServers(usn).ToList())
  109. {
  110. list.Remove(device);
  111. }
  112. _servers = list;
  113. }
  114. finally
  115. {
  116. _syncLock.Release();
  117. }
  118. }
  119. private bool IsValid(string nt, string usn)
  120. {
  121. // It has to report that it's a media renderer
  122. if (usn.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 &&
  123. nt.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 &&
  124. usn.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1 &&
  125. nt.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1)
  126. {
  127. return false;
  128. }
  129. return true;
  130. }
  131. private IEnumerable<Device> GetExistingServers(string usn)
  132. {
  133. return _servers
  134. .Where(i => usn.IndexOf(i.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1);
  135. }
  136. public IEnumerable<IChannel> GetChannels()
  137. {
  138. //if (_servers.Count > 0)
  139. //{
  140. // var service = _servers[0].Properties.Services
  141. // .FirstOrDefault(i => string.Equals(i.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase));
  142. // var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/'));
  143. // if (!string.IsNullOrEmpty(controlUrl))
  144. // {
  145. // return new List<IChannel>
  146. // {
  147. // new ServerChannel(_servers.ToList(), _httpClient, _logger, controlUrl)
  148. // };
  149. // }
  150. //}
  151. return new List<IChannel>();
  152. }
  153. public void Dispose()
  154. {
  155. if (_deviceDiscovery != null)
  156. {
  157. _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered;
  158. _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft;
  159. }
  160. }
  161. }
  162. public class ServerChannel : IChannel, IFactoryChannel
  163. {
  164. private readonly IHttpClient _httpClient;
  165. private readonly ILogger _logger;
  166. public string ControlUrl { get; set; }
  167. public List<Device> Servers { get; set; }
  168. public ServerChannel(IHttpClient httpClient, ILogger logger)
  169. {
  170. _httpClient = httpClient;
  171. _logger = logger;
  172. Servers = new List<Device>();
  173. }
  174. public string Name
  175. {
  176. get { return "Devices"; }
  177. }
  178. public string Description
  179. {
  180. get { return string.Empty; }
  181. }
  182. public string DataVersion
  183. {
  184. get { return DateTime.UtcNow.Ticks.ToString(); }
  185. }
  186. public string HomePageUrl
  187. {
  188. get { return string.Empty; }
  189. }
  190. public ChannelParentalRating ParentalRating
  191. {
  192. get { return ChannelParentalRating.GeneralAudience; }
  193. }
  194. public InternalChannelFeatures GetChannelFeatures()
  195. {
  196. return new InternalChannelFeatures
  197. {
  198. ContentTypes = new List<ChannelMediaContentType>
  199. {
  200. ChannelMediaContentType.Song,
  201. ChannelMediaContentType.Clip
  202. },
  203. MediaTypes = new List<ChannelMediaType>
  204. {
  205. ChannelMediaType.Audio,
  206. ChannelMediaType.Video,
  207. ChannelMediaType.Photo
  208. }
  209. };
  210. }
  211. public bool IsEnabledFor(string userId)
  212. {
  213. return true;
  214. }
  215. public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
  216. {
  217. IEnumerable<ChannelItemInfo> items;
  218. if (string.IsNullOrWhiteSpace(query.FolderId))
  219. {
  220. items = Servers.Select(i => new ChannelItemInfo
  221. {
  222. FolderType = ChannelFolderType.Container,
  223. Id = GetServerId(i),
  224. Name = i.Properties.Name,
  225. Overview = i.Properties.ModelDescription,
  226. Type = ChannelItemType.Folder
  227. });
  228. }
  229. else
  230. {
  231. var idParts = query.FolderId.Split('|');
  232. var folderId = idParts.Length == 2 ? idParts[1] : null;
  233. var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest
  234. {
  235. Limit = query.Limit,
  236. StartIndex = query.StartIndex,
  237. ParentId = folderId,
  238. ContentDirectoryUrl = ControlUrl
  239. }, cancellationToken).ConfigureAwait(false);
  240. items = result.Items.ToList();
  241. }
  242. var list = items.ToList();
  243. var count = list.Count;
  244. list = ApplyPaging(list, query).ToList();
  245. return new ChannelItemResult
  246. {
  247. Items = list,
  248. TotalRecordCount = count
  249. };
  250. }
  251. private string GetServerId(Device device)
  252. {
  253. return device.Properties.UUID.GetMD5().ToString("N");
  254. }
  255. private IEnumerable<T> ApplyPaging<T>(IEnumerable<T> items, InternalChannelItemQuery query)
  256. {
  257. if (query.StartIndex.HasValue)
  258. {
  259. items = items.Skip(query.StartIndex.Value);
  260. }
  261. if (query.Limit.HasValue)
  262. {
  263. items = items.Take(query.Limit.Value);
  264. }
  265. return items;
  266. }
  267. public Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken)
  268. {
  269. // TODO: Implement
  270. return Task.FromResult(new DynamicImageResponse
  271. {
  272. HasImage = false
  273. });
  274. }
  275. public IEnumerable<ImageType> GetSupportedChannelImages()
  276. {
  277. return new List<ImageType>
  278. {
  279. ImageType.Primary
  280. };
  281. }
  282. }
  283. }