You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jellyfin/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs

343 lines
10 KiB

using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Dlna.ContentDirectory;
using MediaBrowser.Dlna.PlayTo;
using MediaBrowser.Dlna.Ssdp;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Dlna.Channels
{
public class DlnaChannelFactory : IChannelFactory, IDisposable
{
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
private readonly IDeviceDiscovery _deviceDiscovery;
private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1);
private List<Device> _servers = new List<Device>();
public static DlnaChannelFactory Instance;
private Func<List<string>> _localServersLookup;
public DlnaChannelFactory(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IDeviceDiscovery deviceDiscovery)
{
_config = config;
_httpClient = httpClient;
_logger = logger;
_deviceDiscovery = deviceDiscovery;
Instance = this;
}
internal void Start(Func<List<string>> localServersLookup)
{
_localServersLookup = localServersLookup;
//deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered;
_deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft;
}
async void deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
{
string usn;
if (!e.Headers.TryGetValue("USN", out usn)) usn = string.Empty;
string nt;
if (!e.Headers.TryGetValue("NT", out nt)) nt = string.Empty;
string location;
if (!e.Headers.TryGetValue("Location", out location)) location = string.Empty;
if (!IsValid(nt, usn))
{
return;
}
if (_localServersLookup != null)
{
if (_localServersLookup().Any(i => usn.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1))
{
// Don't add the local Dlna server to this
return;
}
}
if (GetExistingServers(usn).Any())
{
return;
}
await _syncLock.WaitAsync().ConfigureAwait(false);
try
{
if (GetExistingServers(usn).Any())
{
return;
}
var device = await Device.CreateuPnpDeviceAsync(new Uri(location), _httpClient, _config, _logger)
.ConfigureAwait(false);
if (!_servers.Any(i => string.Equals(i.Properties.UUID, device.Properties.UUID, StringComparison.OrdinalIgnoreCase)))
{
_servers.Add(device);
}
}
catch (Exception ex)
{
}
finally
{
_syncLock.Release();
}
}
async void deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e)
{
string usn;
if (!e.Headers.TryGetValue("USN", out usn)) usn = String.Empty;
string nt;
if (!e.Headers.TryGetValue("NT", out nt)) nt = String.Empty;
if (!IsValid(nt, usn))
{
return;
}
if (!GetExistingServers(usn).Any())
{
return;
}
await _syncLock.WaitAsync().ConfigureAwait(false);
try
{
var list = _servers.ToList();
foreach (var device in GetExistingServers(usn).ToList())
{
list.Remove(device);
}
_servers = list;
}
finally
{
_syncLock.Release();
}
}
private bool IsValid(string nt, string usn)
{
// It has to report that it's a media renderer
if (usn.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 &&
nt.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 &&
usn.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1 &&
nt.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1)
{
return false;
}
return true;
}
private IEnumerable<Device> GetExistingServers(string usn)
{
return _servers
.Where(i => usn.IndexOf(i.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1);
}
public IEnumerable<IChannel> GetChannels()
{
//if (_servers.Count > 0)
//{
// var service = _servers[0].Properties.Services
// .FirstOrDefault(i => string.Equals(i.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase));
// var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/'));
// if (!string.IsNullOrEmpty(controlUrl))
// {
// return new List<IChannel>
// {
// new ServerChannel(_servers.ToList(), _httpClient, _logger, controlUrl)
// };
// }
//}
return new List<IChannel>();
}
public void Dispose()
{
if (_deviceDiscovery != null)
{
_deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered;
_deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft;
}
}
}
public class ServerChannel : IChannel, IFactoryChannel
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
public string ControlUrl { get; set; }
public List<Device> Servers { get; set; }
public ServerChannel(IHttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
Servers = new List<Device>();
}
public string Name
{
get { return "Devices"; }
}
public string Description
{
get { return string.Empty; }
}
public string DataVersion
{
get { return DateTime.UtcNow.Ticks.ToString(); }
}
public string HomePageUrl
{
get { return string.Empty; }
}
public ChannelParentalRating ParentalRating
{
get { return ChannelParentalRating.GeneralAudience; }
}
public InternalChannelFeatures GetChannelFeatures()
{
return new InternalChannelFeatures
{
ContentTypes = new List<ChannelMediaContentType>
{
ChannelMediaContentType.Song,
ChannelMediaContentType.Clip
},
MediaTypes = new List<ChannelMediaType>
{
ChannelMediaType.Audio,
ChannelMediaType.Video,
ChannelMediaType.Photo
}
};
}
public bool IsEnabledFor(string userId)
{
return true;
}
public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
{
IEnumerable<ChannelItemInfo> items;
if (string.IsNullOrWhiteSpace(query.FolderId))
{
items = Servers.Select(i => new ChannelItemInfo
{
FolderType = ChannelFolderType.Container,
Id = GetServerId(i),
Name = i.Properties.Name,
Overview = i.Properties.ModelDescription,
Type = ChannelItemType.Folder
});
}
else
{
var idParts = query.FolderId.Split('|');
var folderId = idParts.Length == 2 ? idParts[1] : null;
var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest
{
Limit = query.Limit,
StartIndex = query.StartIndex,
ParentId = folderId,
ContentDirectoryUrl = ControlUrl
}, cancellationToken).ConfigureAwait(false);
items = result.Items.ToList();
}
var list = items.ToList();
var count = list.Count;
list = ApplyPaging(list, query).ToList();
return new ChannelItemResult
{
Items = list,
TotalRecordCount = count
};
}
private string GetServerId(Device device)
{
return device.Properties.UUID.GetMD5().ToString("N");
}
private IEnumerable<T> ApplyPaging<T>(IEnumerable<T> items, InternalChannelItemQuery query)
{
if (query.StartIndex.HasValue)
{
items = items.Skip(query.StartIndex.Value);
}
if (query.Limit.HasValue)
{
items = items.Take(query.Limit.Value);
}
return items;
}
public Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken)
{
// TODO: Implement
return Task.FromResult(new DynamicImageResponse
{
HasImage = false
});
}
public IEnumerable<ImageType> GetSupportedChannelImages()
{
return new List<ImageType>
{
ImageType.Primary
};
}
}
}