added first play to classes

This commit is contained in:
Luke Pulverenti 2014-02-26 16:31:47 -05:00
parent 96d3c35ba0
commit ec131ba0dc
22 changed files with 1797 additions and 4 deletions

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common.Configuration;
using System.Collections.Specialized;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging;
@ -367,7 +368,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
ContentType = httpResponse.ContentType,
Headers = httpResponse.Headers,
Headers = new NameValueCollection(httpResponse.Headers),
ContentLength = contentLength
};

View File

@ -55,7 +55,9 @@ namespace MediaBrowser.Common.Implementations.IO
if (string.Equals(Path.GetExtension(filename), ".mblink", StringComparison.OrdinalIgnoreCase))
{
return File.ReadAllText(filename);
var path = File.ReadAllText(filename);
return NormalizePath(path);
}
return null;

View File

@ -51,8 +51,37 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="PlayTo\Argument.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
<Compile Include="PlayTo\DeviceProperties.cs" />
<Compile Include="PlayTo\Extensions.cs" />
<Compile Include="PlayTo\PlaylistItem.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="PlayTo\ServiceAction.cs" />
<Compile Include="PlayTo\SsdpHelper.cs" />
<Compile Include="PlayTo\SsdpHttpClient.cs" />
<Compile Include="PlayTo\StateVariable.cs" />
<Compile Include="PlayTo\TransportCommands.cs" />
<Compile Include="PlayTo\TransportStateEventArgs.cs" />
<Compile Include="PlayTo\uBaseObject.cs" />
<Compile Include="PlayTo\uContainer.cs" />
<Compile Include="PlayTo\uIcon.cs" />
<Compile Include="PlayTo\uParser.cs" />
<Compile Include="PlayTo\uPnpNamespaces.cs" />
<Compile Include="PlayTo\uService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.

View File

@ -0,0 +1,29 @@
using System;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class Argument
{
public string Name { get; set; }
public string Direction { get; set; }
public string RelatedStateVariable { get; set; }
public static Argument FromXml(XElement container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
return new Argument
{
Name = container.GetValue(uPnpNamespaces.svc + "name"),
Direction = container.GetValue(uPnpNamespaces.svc + "direction"),
RelatedStateVariable = container.GetValue(uPnpNamespaces.svc + "relatedStateVariable")
};
}
}
}

View File

@ -0,0 +1,21 @@
using System;
namespace MediaBrowser.Dlna.PlayTo
{
public class CurrentIdEventArgs : EventArgs
{
public Guid Id { get; set; }
public CurrentIdEventArgs(string id)
{
if (string.IsNullOrWhiteSpace(id) || id == "0")
{
Id = Guid.Empty;
}
else
{
Id = new Guid(id);
}
}
}
}

View File

@ -0,0 +1,682 @@
using MediaBrowser.Common.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public sealed class Device : IDisposable
{
const string ServiceAvtransportId = "urn:upnp-org:serviceId:AVTransport";
const string ServiceRenderingId = "urn:upnp-org:serviceId:RenderingControl";
#region Fields & Properties
private Timer _dt;
public DeviceProperties Properties { get; set; }
private int _muteVol;
public bool IsMuted
{
get
{
return _muteVol > 0;
}
}
string _currentId = String.Empty;
public string CurrentId
{
get
{
return _currentId;
}
set
{
if (_currentId == value)
return;
_currentId = value;
NotifyCurrentIdChanged(value);
}
}
public int Volume { get; set; }
public TimeSpan Duration { get; set; }
private TimeSpan _position = TimeSpan.FromSeconds(0);
public TimeSpan Position
{
get
{
return _position;
}
set
{
_position = value;
}
}
private string _transportState = String.Empty;
public string TransportState
{
get
{
return _transportState;
}
set
{
if (_transportState == value)
return;
_transportState = value;
if (value == "PLAYING" || value == "STOPPED")
NotifyPlaybackChanged(value == "STOPPED");
}
}
public bool IsPlaying
{
get
{
return TransportState == "PLAYING";
}
}
public bool IsTransitioning
{
get
{
return (TransportState == "TRANSITIONING");
}
}
public bool IsPaused
{
get
{
if (TransportState == "PAUSED" || TransportState == "PAUSED_PLAYBACK")
return true;
return false;
}
}
public bool IsStopped
{
get
{
return (TransportState == "STOPPED");
}
}
public DateTime UpdateTime
{ get; private set; }
#endregion
private readonly IHttpClient _httpClient;
#region Constructor & Initializer
public Device(DeviceProperties deviceProperties)
{
Properties = deviceProperties;
}
internal void Start()
{
UpdateTime = DateTime.UtcNow;
_dt = new Timer(1000);
_dt.Elapsed += dt_Elapsed;
_dt.Start();
}
#endregion
#region Commanding
public Task<bool> VolumeDown(bool mute = false)
{
var sendVolume = (Volume - 5) > 0 ? Volume - 5 : 0;
if (mute && _muteVol == 0)
{
sendVolume = 0;
_muteVol = Volume;
}
return SetVolume(sendVolume);
}
public Task<bool> VolumeUp(bool unmute = false)
{
var sendVolume = (Volume + 5) < 100 ? Volume + 5 : 100;
if (unmute && _muteVol > 0)
sendVolume = _muteVol;
_muteVol = 0;
return SetVolume(sendVolume);
}
public Task ToggleMute()
{
if (_muteVol == 0)
{
_muteVol = Volume;
return SetVolume(0);
}
int tmp = _muteVol;
_muteVol = 0;
return SetVolume(tmp);
}
public async Task<bool> SetVolume(int value)
{
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
if (command == null)
return true;
var service = this.Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceRenderingId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value));
Volume = value;
return true;
}
public async Task<TimeSpan> Seek(TimeSpan value)
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
if (command == null)
return value;
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, String.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"));
return value;
}
public async Task<bool> SetAvTransport(string url, string header, string metaData)
{
_dt.Stop();
TransportState = "STOPPED";
CurrentId = "0";
await Task.Delay(50);
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
if (command == null)
return false;
var dictionary = new Dictionary<string, string>
{
{"CurrentURI", url},
{"CurrentURIMetaData", CreateDidlMeta(metaData)}
};
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, url, dictionary), header);
if (!IsPlaying)
{
await Task.Delay(50);
await SetPlay();
}
_count = 5;
_dt.Start();
return true;
}
private string CreateDidlMeta(string value)
{
if (value == null)
return String.Empty;
var escapedData = value.Replace("<", "&lt;").Replace(">", "&gt;");
return String.Format(BaseDidl, escapedData.Replace("\r\n", ""));
}
private const string BaseDidl = "&lt;DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"&gt;{0}&lt;/DIDL-Lite&gt;";
public async Task<bool> SetNextAvTransport(string value, string header, string metaData)
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetNextAVTransportURI");
if (command == null)
return false;
var dictionary = new Dictionary<string, string>();
dictionary.Add("NextURI", value);
dictionary.Add("NextURIMetaData", CreateDidlMeta(metaData));
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, value, dictionary), header);
await Task.Delay(100);
return true;
}
public async Task<bool> SetPlay()
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
if (command == null)
return false;
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, 1));
_count = 5;
return true;
}
public async Task<bool> SetStop()
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
if (command == null)
return false;
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, 1));
await Task.Delay(50);
_count = 4;
return true;
}
public async Task<bool> SetPause()
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
if (command == null)
return false;
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, 0));
await Task.Delay(50);
TransportState = "PAUSED_PLAYBACK";
return true;
}
#endregion
#region Get data
int _count = 5;
async void dt_Elapsed(object sender, ElapsedEventArgs e)
{
if (_disposed)
return;
((Timer)sender).Stop();
var hasTrack = await GetPositionInfo();
if (_count > 4)
{
await GetTransportInfo();
if (!hasTrack)
{
await GetMediaInfo();
}
await GetVolume();
_count = 0;
}
_count++;
if (_disposed)
return;
((Timer)sender).Start();
}
private async Task GetVolume()
{
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
if (command == null)
return;
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceRenderingId);
try
{
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType));
if (result == null)
return;
var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").FirstOrDefault().Element("CurrentVolume").Value;
if (volume == null)
return;
Volume = Int32.Parse(volume);
//Reset the Mute value if Volume is bigger than zero
if (Volume > 0 && _muteVol > 0)
{
_muteVol = 0;
}
}
catch { }
}
private async Task GetTransportInfo()
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
if (command == null)
return;
var service = this.Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
if (service == null)
return;
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType));
try
{
var transportState = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").FirstOrDefault().Element("CurrentTransportState").Value;
if (transportState != null)
TransportState = transportState;
}
catch { }
if (result != null)
UpdateTime = DateTime.UtcNow;
}
private async Task GetMediaInfo()
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
if (command == null)
return;
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType));
try
{
var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault().Value;
if (String.IsNullOrEmpty(track))
{
CurrentId = "0";
return;
}
XElement uPnpResponse = XElement.Parse((String)track);
var e = uPnpResponse.Element(uPnpNamespaces.items);
if (e == null)
e = uPnpResponse;
var uTrack = uParser.CreateObjectFromXML(new uParserObject { Type = e.Element(uPnpNamespaces.uClass).Value, Element = e });
if (uTrack != null)
CurrentId = uTrack.Id;
}
catch { }
}
private async Task<bool> GetPositionInfo()
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
if (command == null)
return true;
var service = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
var result = await SsdpHttpClient.SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType));
try
{
var duration = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").FirstOrDefault().Element("TrackDuration").Value;
if (duration != null)
{
Duration = TimeSpan.Parse(duration);
}
var position = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").FirstOrDefault().Element("RelTime").Value;
if (position != null)
{
Position = TimeSpan.Parse(position);
}
var track = result.Document.Descendants("TrackMetaData").Select(i => i.Value)
.FirstOrDefault();
if (String.IsNullOrEmpty(track))
{
//If track is null, some vendors do this, use GetMediaInfo instead
return false;
}
var uPnpResponse = XElement.Parse(track);
var e = uPnpResponse.Element(uPnpNamespaces.items) ?? uPnpResponse;
var uTrack = uBaseObject.Create(e);
if (uTrack == null)
return true;
CurrentId = uTrack.Id;
return true;
}
catch { return false; }
}
#endregion
#region From XML
internal async Task GetAVProtocolAsync()
{
var avService = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceAvtransportId);
if (avService == null)
return;
string url = avService.SCPDURL;
if (!url.Contains("/"))
url = "/dmr/" + url;
if (!url.StartsWith("/"))
url = "/" + url;
var httpClient = new SsdpHttpClient();
var stream = await httpClient.GetDataAsync(new Uri(Properties.BaseUrl + url));
if (stream == null)
return;
XDocument document = httpClient.ParseStream(stream);
stream.Dispose();
AvCommands = TransportCommands.Create(document);
}
internal async Task GetRenderingProtocolAsync()
{
var avService = Properties.Services.FirstOrDefault(s => s.ServiceId == ServiceRenderingId);
if (avService == null)
return;
string url = avService.SCPDURL;
if (!url.Contains("/"))
url = "/dmr/" + url;
if (!url.StartsWith("/"))
url = "/" + url;
var httpClient = new SsdpHttpClient();
var stream = await httpClient.GetDataAsync(new Uri(Properties.BaseUrl + url));
if (stream == null)
return;
XDocument document = httpClient.ParseStream(stream);
stream.Dispose();
RendererCommands = TransportCommands.Create(document);
}
internal TransportCommands AvCommands
{
get;
set;
}
internal TransportCommands RendererCommands
{
get;
set;
}
public static async Task<Device> CreateuPnpDeviceAsync(Uri url)
{
var httpClient = new SsdpHttpClient();
var stream = await httpClient.GetDataAsync(url);
if (stream == null)
return null;
var document = httpClient.ParseStream(stream);
stream.Dispose();
var deviceProperties = new DeviceProperties();
var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault();
if (name != null)
deviceProperties.Name = name.Value;
var name2 = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault();
if (name2 != null)
deviceProperties.Name = name2.Value;
var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault();
if (model != null)
deviceProperties.ModelName = model.Value;
var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault();
if (modelNumber != null)
deviceProperties.ModelNumber = modelNumber.Value;
var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault();
if (uuid != null)
deviceProperties.UUID = uuid.Value;
var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault();
if (manufacturer != null)
deviceProperties.Manufacturer = manufacturer.Value;
var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault();
if (manufacturerUrl != null)
deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
if (presentationUrl != null)
deviceProperties.PresentationUrl = presentationUrl.Value;
deviceProperties.BaseUrl = String.Format("http://{0}:{1}", url.Host, url.Port);
var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault();
if (icon != null)
{
deviceProperties.Icon = uIcon.Create(icon);
}
var isRenderer = false;
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
{
if (services == null)
return null;
var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service"));
if (servicesList == null)
return null;
foreach (var element in servicesList)
{
var service = uService.Create(element);
if (service != null)
{
deviceProperties.Services.Add(service);
if (service.ServiceId == ServiceAvtransportId)
{
isRenderer = true;
}
}
}
}
if (isRenderer)
{
var device = new Device(deviceProperties);
await device.GetRenderingProtocolAsync();
await device.GetAVProtocolAsync();
return device;
}
return null;
}
#endregion
#region Events
public event EventHandler<TransportStateEventArgs> PlaybackChanged;
public event EventHandler<CurrentIdEventArgs> CurrentIdChanged;
private void NotifyPlaybackChanged(bool value)
{
if (PlaybackChanged != null)
{
PlaybackChanged.Invoke(this, new TransportStateEventArgs
{
Stopped = IsStopped
});
}
}
private void NotifyCurrentIdChanged(string value)
{
if (CurrentIdChanged != null)
CurrentIdChanged.Invoke(this, new CurrentIdEventArgs(value));
}
#endregion
#region IDisposable
bool _disposed;
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
_dt.Stop();
}
}
#endregion
public override string ToString()
{
return String.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
}
private XDocument ParseStream(Stream stream)
{
var reader = new StreamReader(stream, Encoding.UTF8);
try
{
var doc = XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
stream.Dispose();
return doc;
}
catch
{
}
return null;
}
}
}

View File

@ -0,0 +1,176 @@
using System.Collections.Generic;
namespace MediaBrowser.Dlna.PlayTo
{
public class DeviceProperties
{
private string _uuid = string.Empty;
public string UUID
{
get
{
return _uuid;
}
set
{
_uuid = value;
}
}
private string _name = "PlayTo 1.0.0.0";
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
private string _clientType = "DLNA";
public string ClientType
{
get
{
return _clientType;
}
set
{
_clientType = value;
}
}
private string _displayName = string.Empty;
public string DisplayName
{
get
{
return string.IsNullOrEmpty(_displayName) ? _name : _displayName;
}
set
{
_displayName = value;
}
}
private string _modelName = string.Empty;
public string ModelName
{
get
{
return _modelName;
}
set
{
_modelName = value;
}
}
private string _modelNumber = string.Empty;
public string ModelNumber
{
get
{
return _modelNumber;
}
set
{
_modelNumber = value;
}
}
private string _manufacturer = string.Empty;
public string Manufacturer
{
get
{
return _manufacturer;
}
set
{
_manufacturer = value;
}
}
private string _manufacturerUrl = string.Empty;
public string ManufacturerUrl
{
get
{
return _manufacturerUrl;
}
set
{
_manufacturerUrl = value;
}
}
private string _presentationUrl = string.Empty;
public string PresentationUrl
{
get
{
return _presentationUrl;
}
set
{
_presentationUrl = value;
}
}
private string _baseUrl = string.Empty;
public string BaseUrl
{
get
{
return _baseUrl;
}
set
{
_baseUrl = value;
}
}
private uIcon _icon;
public uIcon Icon
{
get
{
return _icon;
}
set
{
_icon = value;
}
}
private string _iconUrl;
public string IconUrl
{
get
{
if (string.IsNullOrWhiteSpace(_iconUrl) && _icon != null)
{
if (!_icon.Url.StartsWith("/"))
_iconUrl = _baseUrl + "/" + _icon.Url;
else
_iconUrl = _baseUrl + _icon.Url;
}
return _iconUrl;
}
}
private readonly List<uService> _services = new List<uService>();
public List<uService> Services
{
get
{
return _services;
}
}
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public static class Extensions
{
public static Task<int> ReceiveAsync(this Socket socket, byte[] buffer, int offset, int size)
{
var tcs = new TaskCompletionSource<int>(socket);
var remoteip = new IPEndPoint(IPAddress.Any, 0);
var endpoint = (EndPoint)remoteip;
socket.BeginReceiveFrom(buffer, offset, size, SocketFlags.None, ref endpoint, iar =>
{
var result = (TaskCompletionSource<int>)iar.AsyncState;
var iarSocket = (Socket)result.Task.AsyncState;
try
{
result.TrySetResult(iarSocket.EndReceive(iar));
}
catch (Exception exc)
{
result.TrySetException(exc);
}
}, tcs);
return tcs.Task;
}
public static string GetValue(this XElement container, XName name)
{
var node = container.Element(name);
return node == null ? null : node.Value;
}
public static string GetDescendantValue(this XElement container, XName name)
{
var node = container.Descendants(name)
.FirstOrDefault();
return node == null ? null : node.Value;
}
}
}

View File

@ -0,0 +1,95 @@

namespace MediaBrowser.Dlna.PlayTo
{
public class PlaylistItem
{
public string ItemId { get; set; }
public bool Transcode { get; set; }
public bool IsVideo { get; set; }
public bool IsAudio { get; set; }
public string FileFormat { get; set; }
public int PlayState { get; set; }
public string StreamUrl { get; set; }
public string DlnaHeaders { get; set; }
public string Didl { get; set; }
public long StartPositionTicks { get; set; }
//internal static PlaylistItem GetBasicConfig(BaseItem item, TranscodeSettings[] profileTranscodings)
//{
// var playlistItem = new PlaylistItem();
// playlistItem.ItemId = item.Id.ToString();
// if (string.Equals(item.MediaType, MediaBrowser.Model.Entities.MediaType.Video, StringComparison.OrdinalIgnoreCase))
// {
// playlistItem.IsVideo = true;
// }
// else
// {
// playlistItem.IsAudio = true;
// }
// var path = item.Path.ToLower();
// //Check the DlnaProfile associated with the renderer
// if (profileTranscodings != null)
// {
// foreach (TranscodeSettings transcodeSetting in profileTranscodings)
// {
// if (string.IsNullOrWhiteSpace(transcodeSetting.Container))
// continue;
// if (path.EndsWith(transcodeSetting.Container))
// {
// playlistItem.Transcode = true;
// playlistItem.FileFormat = transcodeSetting.TargetContainer;
// return playlistItem;
// }
// }
// }
// if (playlistItem.IsVideo)
// {
// //Check to see if we support serving the format statically
// foreach (string supported in PlayToConfiguration.SupportedStaticFormats)
// {
// if (path.EndsWith(supported))
// {
// playlistItem.Transcode = false;
// playlistItem.FileFormat = supported;
// return playlistItem;
// }
// }
// playlistItem.Transcode = true;
// playlistItem.FileFormat = "ts";
// }
// else
// {
// foreach (string supported in PlayToConfiguration.SupportedStaticFormats)
// {
// if (path.EndsWith(supported))
// {
// playlistItem.Transcode = false;
// playlistItem.FileFormat = supported;
// return playlistItem;
// }
// }
// playlistItem.Transcode = true;
// playlistItem.FileFormat = "mp3";
// }
// return playlistItem;
//}
}
}

View File

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class ServiceAction
{
public string Name { get; set; }
public List<Argument> ArgumentList { get; set; }
public override string ToString()
{
return Name;
}
public static ServiceAction FromXml(XElement container)
{
var argumentList = new List<Argument>();
foreach (var arg in container.Descendants(uPnpNamespaces.svc + "argument"))
{
argumentList.Add(Argument.FromXml(arg));
}
return new ServiceAction
{
Name = container.GetValue(uPnpNamespaces.svc + "name"),
ArgumentList = argumentList
};
}
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.Linq;
using System.Text;
namespace MediaBrowser.Dlna.PlayTo
{
public class SsdpHelper
{
private const string SsdpRenderer = "M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"User-Agent: UPnP/1.0 DLNADOC/1.50 Platinum/0.6.9.1\r\n" +
"ST: urn:schemas-upnp-org:device:MediaRenderer:1\r\n" +
"MAN: \"ssdp:discover\"\r\n" +
"MX: {0}\r\n" +
"\r\n";
/// <summary>
/// Creates a SSDP MSearch packet for DlnaRenderers.
/// </summary>
/// <param name="mx">The mx. (Delaytime for device before responding)</param>
/// <returns></returns>
public static byte[] CreateRendererSSDP(int mx)
{
return Encoding.UTF8.GetBytes(string.Format(SsdpRenderer, mx));
}
/// <summary>
/// Parses the socket response into a location Uri for the DeviceDescription.xml.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public static Uri ParseSsdpResponse(string data)
{
var res = (from line in data.Split(new[] { '\r', '\n' })
where line.ToLowerInvariant().StartsWith("location:")
select line).FirstOrDefault();
return !string.IsNullOrEmpty(res) ? new Uri(res.Substring(9).Trim()) : null;
}
/// <summary>
/// Parses data into SSDP event.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
[Obsolete("Not yet used", true)]
public static string ParseSsdpEvent(string data)
{
var sid = (from line in data.Split(new[] { '\r', '\n' })
where line.ToLowerInvariant().StartsWith("sid:")
select line).FirstOrDefault();
return data;
}
}
}

View File

@ -0,0 +1,127 @@
using MediaBrowser.Common.Net;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class SsdpHttpClient
{
private const string USERAGENT = "Microsoft-Windows/6.2 UPnP/1.0 Microsoft-DLNA DLNADOC/1.50";
private const string FriendlyName = "MediaBrowser";
private static readonly CookieContainer Container = new CookieContainer();
private readonly IHttpClient _httpClient;
public SsdpHttpClient(IHttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<XDocument> SendCommandAsync(string baseUrl, uService service, string command, string postData, string header = null)
{
var serviceUrl = service.ControlURL;
if (!serviceUrl.StartsWith("/"))
serviceUrl = "/" + serviceUrl;
var response = await PostSoapDataAsync(new Uri(baseUrl + serviceUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header)
.ConfigureAwait(false);
using (var stream = response.Content)
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
}
}
}
public async Task SubscribeAsync(Uri url, string ip, int port, string localIp, int eventport, int timeOut = 3600)
{
var options = new HttpRequestOptions
{
Url = url.ToString()
};
options.RequestHeaders["UserAgent"] = USERAGENT;
options.RequestHeaders["HOST"] = ip + ":" + port;
options.RequestHeaders["CALLBACK"] = "<" + localIp + ":" + eventport + ">";
options.RequestHeaders["NT"] = "upnp:event";
options.RequestHeaders["TIMEOUT"] = "Second - " + timeOut;
//request.CookieContainer = Container;
using (await _httpClient.Get(options).ConfigureAwait(false))
{
}
}
public async Task RespondAsync(Uri url, string ip, int port, string localIp, int eventport, int timeOut = 20000)
{
var options = new HttpRequestOptions
{
Url = url.ToString()
};
options.RequestHeaders["UserAgent"] = USERAGENT;
options.RequestHeaders["HOST"] = ip + ":" + port;
options.RequestHeaders["CALLBACK"] = "<" + localIp + ":" + eventport + ">";
options.RequestHeaders["NT"] = "upnp:event";
options.RequestHeaders["TIMEOUT"] = "Second - 3600";
//request.CookieContainer = Container;
using (await _httpClient.Get(options).ConfigureAwait(false))
{
}
}
public async Task<XDocument> GetDataAsync(Uri url)
{
var options = new HttpRequestOptions
{
Url = url.ToString()
};
options.RequestHeaders["UserAgent"] = USERAGENT;
options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
//request.CookieContainer = Container;
using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
return XDocument.Parse(reader.ReadToEnd(), LoadOptions.PreserveWhitespace);
}
}
}
public Task<HttpResponseInfo> PostSoapDataAsync(Uri url, string soapAction, string postData, string header = null, int timeOut = 20000)
{
if (!soapAction.StartsWith("\""))
soapAction = "\"" + soapAction + "\"";
var options = new HttpRequestOptions
{
Url = url.ToString()
};
options.RequestHeaders["SOAPAction"] = soapAction;
options.RequestHeaders["Pragma"] = "no-cache";
options.RequestHeaders["UserAgent"] = USERAGENT;
options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
if (!string.IsNullOrWhiteSpace(header))
{
options.RequestHeaders["contentFeatures.dlna.org"] = header;
}
options.RequestContentType = "text/xml; charset=\"utf-8\"";
options.RequestContent = postData;
return _httpClient.Post(options);
}
}
}

View File

@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class StateVariable
{
public string Name { get; set; }
public string DataType { get; set; }
private List<string> _allowedValues = new List<string>();
public List<string> AllowedValues
{
get
{
return _allowedValues;
}
set
{
_allowedValues = value;
}
}
public override string ToString()
{
return Name;
}
public static StateVariable FromXml(XElement container)
{
var allowedValues = new List<string>();
var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList")
.FirstOrDefault();
if (element != null)
{
var values = element.Descendants(uPnpNamespaces.svc + "allowedValue");
allowedValues.AddRange(values.Select(child => child.Value));
}
return new StateVariable
{
Name = container.GetValue(uPnpNamespaces.svc + "name"),
DataType = container.GetValue(uPnpNamespaces.svc + "dataType"),
AllowedValues = allowedValues
};
}
}
}

View File

@ -0,0 +1,157 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class TransportCommands
{
List<StateVariable> _stateVariables = new List<StateVariable>();
public List<StateVariable> StateVariables
{
get
{
return _stateVariables;
}
set
{
_stateVariables = value;
}
}
List<ServiceAction> _serviceActions = new List<ServiceAction>();
public List<ServiceAction> ServiceActions
{
get
{
return _serviceActions;
}
set
{
_serviceActions = value;
}
}
public static TransportCommands Create(XDocument document)
{
var command = new TransportCommands();
var actionList = document.Descendants(uPnpNamespaces.svc + "actionList");
foreach (var container in actionList.Descendants(uPnpNamespaces.svc + "action"))
{
command.ServiceActions.Add(ServiceAction.FromXml(container));
}
var stateValues = document.Descendants(uPnpNamespaces.ServiceStateTable).FirstOrDefault();
if (stateValues != null)
{
foreach (var container in stateValues.Elements(uPnpNamespaces.svc + "stateVariable"))
{
command.StateVariables.Add(StateVariable.FromXml(container));
}
}
return command;
}
public string BuildPost(ServiceAction action, string xmlNamespace)
{
var stateString = string.Empty;
foreach (var arg in action.ArgumentList)
{
if (arg.Direction == "out")
continue;
if (arg.Name == "InstanceID")
stateString += BuildArgumentXml(arg, "0");
else
stateString += BuildArgumentXml(arg, null);
}
return string.Format(CommandBase, action.Name, xmlNamespace, stateString);
}
public string BuildPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "")
{
var stateString = string.Empty;
foreach (var arg in action.ArgumentList)
{
if (arg.Direction == "out")
continue;
if (arg.Name == "InstanceID")
stateString += BuildArgumentXml(arg, "0");
else
stateString += BuildArgumentXml(arg, value.ToString(), commandParameter);
}
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
}
public string BuildSearchPost(ServiceAction action, string xmlNamesapce, object value, string commandParameter = "")
{
var stateString = string.Empty;
foreach (var arg in action.ArgumentList)
{
if (arg.Direction == "out")
continue;
if (arg.Name == "ObjectID")
stateString += BuildArgumentXml(arg, value.ToString());
else if (arg.Name == "Filter")
stateString += BuildArgumentXml(arg, "*");
else if (arg.Name == "StartingIndex")
stateString += BuildArgumentXml(arg, "0");
else if (arg.Name == "RequestedCount")
stateString += BuildArgumentXml(arg, "200");
else if (arg.Name == "BrowseFlag")
stateString += BuildArgumentXml(arg, null, "BrowseDirectChildren");
else if (arg.Name == "SortCriteria")
stateString += BuildArgumentXml(arg, "");
else
stateString += BuildArgumentXml(arg, value.ToString(), commandParameter);
}
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
}
public string BuildPost(ServiceAction action, string xmlNamesapce, object value, Dictionary<string, string> dictionary)
{
var stateString = string.Empty;
foreach (var arg in action.ArgumentList)
{
if (arg.Name == "InstanceID")
stateString += BuildArgumentXml(arg, "0");
else if (dictionary.ContainsKey(arg.Name))
stateString += BuildArgumentXml(arg, dictionary[arg.Name]);
else
stateString += BuildArgumentXml(arg, value.ToString());
}
return string.Format(CommandBase, action.Name, xmlNamesapce, stateString);
}
private string BuildArgumentXml(Argument argument, string value, string commandParameter = "")
{
var state = StateVariables.FirstOrDefault(a => a.Name == argument.RelatedStateVariable);
if (state != null)
{
var sendValue = (state.AllowedValues.FirstOrDefault(a => a == commandParameter) ??
state.AllowedValues.FirstOrDefault()) ??
value;
return string.Format("<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}</{0}>", argument.Name, state.DataType ?? "string", sendValue);
}
return string.Format("<{0}>{1}</{0}>", argument.Name, value);
}
private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace MediaBrowser.Dlna.PlayTo
{
public class TransportStateEventArgs : EventArgs
{
public bool Stopped { get; set; }
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class uBaseObject
{
public string Id { get; set; }
public string ParentId { get; set; }
public string Title { get; set; }
public string SecondText { get; set; }
public string IconUrl { get; set; }
public string MetaData { get; set; }
public string Url { get; set; }
public string[] ProtocolInfo { get; set; }
public static uBaseObject Create(XElement container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
return new uBaseObject
{
Id = container.Attribute(uPnpNamespaces.Id).Value,
ParentId = container.Attribute(uPnpNamespaces.ParentId).Value,
Title = container.GetValue(uPnpNamespaces.title),
IconUrl = container.GetValue(uPnpNamespaces.Artwork),
SecondText = "",
Url = container.GetValue(uPnpNamespaces.Res),
ProtocolInfo = GetProtocolInfo(container),
MetaData = container.ToString()
};
}
private static string[] GetProtocolInfo(XElement container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
var resElement = container.Element(uPnpNamespaces.Res);
if (resElement != null)
{
var info = resElement.Attribute(uPnpNamespaces.ProtocolInfo);
if (info != null && !string.IsNullOrWhiteSpace(info.Value))
{
return info.Value.Split(':');
}
}
return new string[4];
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class uContainer : uBaseObject
{
new public static uBaseObject Create(XElement container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
return new uBaseObject
{
Id = (string)container.Attribute(uPnpNamespaces.Id),
ParentId = (string)container.Attribute(uPnpNamespaces.ParentId),
Title = (string)container.Element(uPnpNamespaces.title),
IconUrl = container.GetValue(uPnpNamespaces.Artwork)
};
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class uIcon
{
public string Url { get; private set; }
public string MimeType { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public string Depth { get; private set; }
public uIcon(string mimeType, string width, string height, string depth, string url)
{
MimeType = mimeType;
Width = (!string.IsNullOrEmpty(width)) ? int.Parse(width) : 0;
Height = (!string.IsNullOrEmpty(height)) ? int.Parse(height) : 0;
Depth = depth;
Url = url;
}
public static uIcon Create(XElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
var mimeType = element.GetDescendantValue(uPnpNamespaces.ud.GetName("mimetype"));
var width = element.GetDescendantValue(uPnpNamespaces.ud.GetName("width"));
var height = element.GetDescendantValue(uPnpNamespaces.ud.GetName("height"));
var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
return new uIcon(mimeType, width, height, depth, url);
}
public override string ToString()
{
return string.Format("{0}x{1}", Height, Width);
}
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class uParser
{
public static IList<uBaseObject> ParseBrowseXml(XDocument doc)
{
if (doc == null)
{
throw new ArgumentException("doc");
}
var list = new List<uBaseObject>();
var document = doc.Document;
if (document == null)
return list;
var item = (from result in document.Descendants("Result") select result).FirstOrDefault();
if (item == null)
return list;
var uPnpResponse = XElement.Parse((String)item);
var uObjects = from container in uPnpResponse.Elements(uPnpNamespaces.containers)
select new uParserObject { Type = (string)container.Element(uPnpNamespaces.uClass), Element = container };
var uObjects2 = from container in uPnpResponse.Elements(uPnpNamespaces.items)
select new uParserObject { Type = (string)container.Element(uPnpNamespaces.uClass), Element = container };
list.AddRange(uObjects.Concat(uObjects2).Select(CreateObjectFromXML).Where(uObject => uObject != null));
return list;
}
public static uBaseObject CreateObjectFromXML(uParserObject uItem)
{
return uContainer.Create(uItem.Element);
}
}
public class uParserObject
{
public string Type { get; set; }
public XElement Element { get; set; }
}
}

View File

@ -0,0 +1,39 @@
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class uPnpNamespaces
{
public static XNamespace dc = "http://purl.org/dc/elements/1.1/";
public static XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
public static XNamespace svc = "urn:schemas-upnp-org:service-1-0";
public static XNamespace ud = "urn:schemas-upnp-org:device-1-0";
public static XNamespace upnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
public static XNamespace RenderingControl = "urn:schemas-upnp-org:service:RenderingControl:1";
public static XNamespace AvTransport = "urn:schemas-upnp-org:service:AVTransport:1";
public static XNamespace ContentDirectory = "urn:schemas-upnp-org:service:ContentDirectory:1";
public static XName containers = ns + "container";
public static XName items = ns + "item";
public static XName title = dc + "title";
public static XName creator = dc + "creator";
public static XName artist = upnp + "artist";
public static XName Id = "id";
public static XName ParentId = "parentID";
public static XName uClass = upnp + "class";
public static XName Artwork = upnp + "albumArtURI";
public static XName Description = dc + "description";
public static XName LongDescription = upnp + "longDescription";
public static XName Album = upnp + "album";
public static XName Author = upnp + "author";
public static XName Director = upnp + "director";
public static XName PlayCount = upnp + "playbackCount";
public static XName Tracknumber = upnp + "originalTrackNumber";
public static XName Res = ns + "res";
public static XName Duration = "duration";
public static XName ProtocolInfo = "protocolInfo";
public static XName ServiceStateTable = svc + "serviceStateTable";
public static XName StateVariable = svc + "stateVariable";
}
}

View File

@ -0,0 +1,42 @@
using System.Xml.Linq;
namespace MediaBrowser.Dlna.PlayTo
{
public class uService
{
public string ServiceType { get; set; }
public string ServiceId { get; set; }
public string SCPDURL { get; set; }
public string ControlURL { get; set; }
public string EventSubURL { get; set; }
public uService(string serviceType, string serviceId, string scpdUrl, string controlUrl, string eventSubUrl)
{
ServiceType = serviceType;
ServiceId = serviceId;
SCPDURL = scpdUrl;
ControlURL = controlUrl;
EventSubURL = eventSubUrl;
}
public static uService Create(XElement element)
{
var type = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceType"));
var id = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceId"));
var scpdUrl = element.GetDescendantValue(uPnpNamespaces.ud.GetName("SCPDURL"));
var controlURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("controlURL"));
var eventSubURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("eventSubURL"));
return new uService(type, id, scpdUrl, controlURL, eventSubURL);
}
public override string ToString()
{
return string.Format("{0}", ServiceId);
}
}
}

View File

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following