Merge pull request #1409 from MediaBrowser/beta

merge from beta
This commit is contained in:
Luke 2016-01-19 14:54:07 -05:00
commit 236b5caee2
112 changed files with 2830 additions and 1124 deletions

View File

@ -33,11 +33,11 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath>
<HintPath>..\packages\ImageMagickSharp.1.0.0.17\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

View File

@ -72,11 +72,16 @@ namespace Emby.Drawing.ImageMagick
private void LogVersion()
{
_logger.Info("ImageMagick version: " + Wand.VersionString);
_logger.Info("ImageMagick version: " + GetVersion());
TestWebp();
Wand.SetMagickThreadCount(1);
}
public static string GetVersion()
{
return Wand.VersionString;
}
private bool _webpAvailable = true;
private void TestWebp()
{
@ -148,7 +153,7 @@ namespace Emby.Drawing.ImageMagick
DrawIndicator(originalImage, width, height, options);
originalImage.CurrentImage.CompressionQuality = quality;
//originalImage.CurrentImage.StripImage();
originalImage.CurrentImage.StripImage();
originalImage.SaveImage(outputPath);
}
@ -165,7 +170,7 @@ namespace Emby.Drawing.ImageMagick
DrawIndicator(wand, width, height, options);
wand.CurrentImage.CompressionQuality = quality;
//wand.CurrentImage.StripImage();
wand.CurrentImage.StripImage();
wand.SaveImage(outputPath);
}
@ -176,15 +181,16 @@ namespace Emby.Drawing.ImageMagick
private void ScaleImage(MagickWand wand, int width, int height)
{
wand.CurrentImage.ResizeImage(width, height);
//if (_config.Configuration.EnableHighQualityImageScaling)
//{
// wand.CurrentImage.ResizeImage(width, height);
//}
//else
//{
// wand.CurrentImage.ScaleImage(width, height);
//}
var highQuality = false;
if (highQuality)
{
wand.CurrentImage.ResizeImage(width, height);
}
else
{
wand.CurrentImage.ScaleImage(width, height);
}
}
/// <summary>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.17" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages>

View File

@ -17,6 +17,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Api
{
@ -112,13 +113,15 @@ namespace MediaBrowser.Api
private readonly IServerApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly IJsonSerializer _json;
public ItemLookupService(IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager)
public ItemLookupService(IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager, IJsonSerializer json)
{
_providerManager = providerManager;
_appPaths = appPaths;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_json = json;
}
public object Get(GetExternalIdInfos request)
@ -199,6 +202,7 @@ namespace MediaBrowser.Api
// item.SetProviderId(key.Key, value);
// }
//}
Logger.Info("Setting provider id's to item {0}-{1}: {2}", item.Id, item.Name, _json.SerializeToString(request.ProviderIds));
item.ProviderIds = request.ProviderIds;
var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem)

View File

@ -162,6 +162,14 @@ namespace MediaBrowser.Api.Library
public string Id { get; set; }
}
[Route("/Items", "DELETE", Summary = "Deletes an item from the library and file system")]
[Authenticated]
public class DeleteItems : IReturnVoid
{
[ApiMember(Name = "Ids", Description = "Ids", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Ids { get; set; }
}
[Route("/Items/Counts", "GET")]
[Authenticated]
public class GetItemCounts : IReturn<ItemCounts>
@ -711,31 +719,53 @@ namespace MediaBrowser.Api.Library
}
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Delete(DeleteItems request)
{
var ids = string.IsNullOrWhiteSpace(request.Ids)
? new string[] { }
: request.Ids.Split(',');
var tasks = ids.Select(i =>
{
var item = _libraryManager.GetItemById(i);
var auth = _authContext.GetAuthorizationInfo(Request);
var user = _userManager.GetUserById(auth.UserId);
if (!item.CanDelete(user))
{
if (ids.Length > 1)
{
throw new SecurityException("Unauthorized access");
}
return Task.FromResult(true);
}
if (item is ILiveTvRecording)
{
return _liveTv.DeleteRecording(i);
}
return _libraryManager.DeleteItem(item);
}).ToArray();
Task.WaitAll(tasks);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Delete(DeleteItem request)
{
var item = _libraryManager.GetItemById(request.Id);
var auth = _authContext.GetAuthorizationInfo(Request);
var user = _userManager.GetUserById(auth.UserId);
if (!item.CanDelete(user))
Delete(new DeleteItems
{
throw new SecurityException("Unauthorized access");
}
if (item is ILiveTvRecording)
{
var task = _liveTv.DeleteRecording(request.Id);
Task.WaitAll(task);
}
else
{
var task = _libraryManager.DeleteItem(item);
Task.WaitAll(task);
}
Ids = request.Id
});
}
/// <summary>

View File

@ -47,7 +47,10 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
@ -63,9 +66,6 @@
<Reference Include="ServiceStack.Text">
<HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">

View File

@ -233,7 +233,7 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
}
Task.Run(() => _installationManager.InstallPackage(package, new Progress<double>(), CancellationToken.None));
Task.Run(() => _installationManager.InstallPackage(package, true, new Progress<double>(), CancellationToken.None));
}
/// <summary>

View File

@ -1562,6 +1562,13 @@ namespace MediaBrowser.Api.Playback
RequestedUrl = url
};
//if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
// (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
// (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
//{
// state.SegmentLength = 6;
//}
if (!string.IsNullOrWhiteSpace(request.AudioCodec))
{
state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();

View File

@ -111,7 +111,8 @@ namespace MediaBrowser.Api.Playback.Hls
throw;
}
await WaitForMinimumSegmentCount(playlist, 3, cancellationTokenSource.Token).ConfigureAwait(false);
var waitForSegments = state.SegmentLength >= 10 ? 2 : 3;
await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
}
}
finally

View File

@ -179,6 +179,7 @@ namespace MediaBrowser.Api
if (primaryImageTag != null)
{
result.PrimaryImageTag = primaryImageTag;
result.PrimaryImageAspectRatio = _dtoService.GetPrimaryImageAspectRatio(item);
}
SetThumbImageInfo(result, item);

View File

@ -13,6 +13,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Model.Net;
namespace MediaBrowser.Api.System
{
@ -32,6 +33,12 @@ namespace MediaBrowser.Api.System
}
[Route("/System/Ping", "POST")]
public class PingSystem : IReturnVoid
{
}
/// <summary>
/// Class RestartApplication
/// </summary>
@ -59,7 +66,7 @@ namespace MediaBrowser.Api.System
[Route("/System/Endpoint", "GET", Summary = "Gets information about the request endpoint")]
[Authenticated]
public class GetEndpointInfo : IReturn<EndpointInfo>
public class GetEndpointInfo : IReturn<EndPointInfo>
{
public string Endpoint { get; set; }
}
@ -104,6 +111,11 @@ namespace MediaBrowser.Api.System
_security = security;
}
public object Post(PingSystem request)
{
return _appHost.Name;
}
public object Get(GetServerLogs request)
{
List<FileSystemMetadata> files;
@ -199,17 +211,11 @@ namespace MediaBrowser.Api.System
public object Get(GetEndpointInfo request)
{
return ToOptimizedResult(new EndpointInfo
return ToOptimizedResult(new EndPointInfo
{
IsLocal = Request.IsLocal,
IsInNetwork = _network.IsInLocalNetwork(request.Endpoint ?? Request.RemoteIp)
});
}
}
public class EndpointInfo
{
public bool IsLocal { get; set; }
public bool IsInNetwork { get; set; }
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="morelinq" version="1.1.1" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages>

View File

@ -133,7 +133,7 @@ namespace MediaBrowser.Common.Implementations
/// Gets the HTTP client.
/// </summary>
/// <value>The HTTP client.</value>
protected IHttpClient HttpClient { get; private set; }
public IHttpClient HttpClient { get; private set; }
/// <summary>
/// Gets the network manager.
/// </summary>

View File

@ -150,7 +150,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
request.Method = method;
request.Timeout = options.TimeoutMs;
if (httpWebRequest != null)
{
if (!string.IsNullOrEmpty(options.Host))

View File

@ -49,11 +49,13 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.4.1.1\lib\net45\NLog.dll</HintPath>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="NLog">
<HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
@ -62,13 +64,14 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=2.8.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SimpleInjector.3.0.5\lib\net45\SimpleInjector.dll</HintPath>
<Reference Include="SimpleInjector">
<HintPath>..\packages\SimpleInjector.3.1.2\lib\net45\SimpleInjector.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net" />
<Reference Include="System.Xml" />
<Reference Include="ServiceStack.Text">
@ -105,6 +108,7 @@
<Compile Include="Security\SuppporterInfoResponse.cs" />
<Compile Include="Serialization\JsonSerializer.cs" />
<Compile Include="Serialization\XmlSerializer.cs" />
<Compile Include="Updates\GithubUpdater.cs" />
<Compile Include="Updates\InstallationManager.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -7,6 +7,7 @@ using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using MoreLinq;
namespace MediaBrowser.Common.Implementations.Networking
{
@ -31,14 +32,14 @@ namespace MediaBrowser.Common.Implementations.Networking
}
}
private volatile List<string> _localIpAddresses;
private volatile List<IPAddress> _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
/// <summary>
/// Gets the machine's local ip address
/// </summary>
/// <returns>IPAddress.</returns>
public IEnumerable<string> GetLocalIpAddresses()
public IEnumerable<IPAddress> GetLocalIpAddresses()
{
if (_localIpAddresses == null)
{
@ -58,25 +59,24 @@ namespace MediaBrowser.Common.Implementations.Networking
return _localIpAddresses;
}
private IEnumerable<string> GetLocalIpAddressesInternal()
private IEnumerable<IPAddress> GetLocalIpAddressesInternal()
{
var list = GetIPsDefault()
.Where(i => !IPAddress.IsLoopback(i))
.Select(i => i.ToString())
.Where(FilterIpAddress)
.ToList();
if (list.Count > 0)
if (list.Count == 0)
{
return list;
list.AddRange(GetLocalIpAddressesFallback());
}
return GetLocalIpAddressesFallback().Where(FilterIpAddress);
return list.Where(FilterIpAddress).DistinctBy(i => i.ToString());
}
private bool FilterIpAddress(string address)
private bool FilterIpAddress(IPAddress address)
{
if (address.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
var addressString = address.ToString ();
if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@ -84,8 +84,16 @@ namespace MediaBrowser.Common.Implementations.Networking
return true;
}
private bool IsInPrivateAddressSpace(string endpoint)
public bool IsInPrivateAddressSpace(string endpoint)
{
if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
{
return true;
}
// Handle ipv4 mapped to ipv6
endpoint = endpoint.Replace("::ffff:", string.Empty);
// Private address space:
// http://en.wikipedia.org/wiki/Private_network
@ -96,9 +104,6 @@ namespace MediaBrowser.Common.Implementations.Networking
return
// If url was requested with computer name, we may see this
endpoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 ||
endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
@ -131,26 +136,41 @@ namespace MediaBrowser.Common.Implementations.Networking
throw new ArgumentNullException("endpoint");
}
if (IsInPrivateAddressSpace(endpoint))
{
return true;
}
const int lengthMatch = 4;
if (endpoint.Length >= lengthMatch)
{
var prefix = endpoint.Substring(0, lengthMatch);
if (GetLocalIpAddresses()
.Any(i => i.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
{
return true;
}
}
IPAddress address;
if (resolveHost && !IPAddress.TryParse(endpoint, out address))
if (IPAddress.TryParse(endpoint, out address))
{
var addressString = address.ToString();
int lengthMatch = 100;
if (address.AddressFamily == AddressFamily.InterNetwork)
{
lengthMatch = 4;
if (IsInPrivateAddressSpace(addressString))
{
return true;
}
}
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
lengthMatch = 10;
if (IsInPrivateAddressSpace(endpoint))
{
return true;
}
}
// Should be even be doing this with ipv6?
if (addressString.Length >= lengthMatch)
{
var prefix = addressString.Substring(0, lengthMatch);
if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
{
return true;
}
}
}
else if (resolveHost)
{
Uri uri;
if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out uri))
@ -188,33 +208,45 @@ namespace MediaBrowser.Common.Implementations.Networking
return Dns.GetHostAddresses(hostName);
}
private IEnumerable<IPAddress> GetIPsDefault()
{
foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
{
var props = adapter.GetIPProperties();
var gateways = from ga in props.GatewayAddresses
where !ga.Address.Equals(IPAddress.Any)
select true;
private List<IPAddress> GetIPsDefault()
{
NetworkInterface[] interfaces;
if (!gateways.Any())
{
continue;
}
try
{
interfaces = NetworkInterface.GetAllNetworkInterfaces();
}
catch (Exception ex)
{
Logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
return new List<IPAddress>();
}
foreach (var uni in props.UnicastAddresses)
{
var address = uni.Address;
if (address.AddressFamily != AddressFamily.InterNetwork)
{
continue;
}
yield return address;
}
}
}
return interfaces.SelectMany(network => {
private IEnumerable<string> GetLocalIpAddressesFallback()
try
{
Logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
var properties = network.GetIPProperties();
return properties.UnicastAddresses
.Where(i => i.IsDnsEligible)
.Select(i => i.Address)
.Where(i => i.AddressFamily == AddressFamily.InterNetwork)
.ToList();
}
catch (Exception ex)
{
Logger.ErrorException("Error querying network interface", ex);
return new List<IPAddress>();
}
}).DistinctBy(i => i.ToString())
.ToList();
}
private IEnumerable<IPAddress> GetLocalIpAddressesFallback()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
@ -222,7 +254,6 @@ namespace MediaBrowser.Common.Implementations.Networking
// It's not fool-proof so ultimately the consumer will have to examine them and decide
return host.AddressList
.Where(i => i.AddressFamily == AddressFamily.InterNetwork)
.Select(i => i.ToString())
.Reverse();
}

View File

@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
namespace MediaBrowser.Common.Implementations.Updates
{
public class GithubUpdater
{
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
private TimeSpan _cacheLength;
public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer, TimeSpan cacheLength)
{
_httpClient = httpClient;
_jsonSerializer = jsonSerializer;
_cacheLength = cacheLength;
}
public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, CancellationToken cancellationToken)
{
var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
var options = new HttpRequestOptions
{
Url = url,
EnableKeepAlive = false,
CancellationToken = cancellationToken,
UserAgent = "Emby/3.0"
};
if (_cacheLength.Ticks > 0)
{
options.CacheMode = CacheMode.Unconditional;
options.CacheLength = _cacheLength;
}
using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
{
var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
}
}
private CheckForUpdateResult CheckForUpdateResult(RootObject[] obj, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename)
{
if (updateLevel == PackageVersionClass.Release)
{
obj = obj.Where(i => !i.prerelease).ToArray();
}
else if (updateLevel == PackageVersionClass.Beta)
{
obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase)).ToArray();
}
else if (updateLevel == PackageVersionClass.Dev)
{
obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
}
var availableUpdate = obj
.Select(i => CheckForUpdateResult(i, minVersion, assetFilename, packageName, targetFilename))
.Where(i => i != null)
.OrderByDescending(i => Version.Parse(i.AvailableVersion))
.FirstOrDefault();
return availableUpdate ?? new CheckForUpdateResult
{
IsUpdateAvailable = false
};
}
private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
{
Version version;
if (!Version.TryParse(obj.tag_name, out version))
{
return null;
}
if (version < minVersion)
{
return null;
}
var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename));
if (asset == null)
{
return null;
}
return new CheckForUpdateResult
{
AvailableVersion = version.ToString(),
IsUpdateAvailable = version > minVersion,
Package = new PackageVersionInfo
{
classification = obj.prerelease ?
(obj.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase) ? PackageVersionClass.Dev : PackageVersionClass.Beta) :
PackageVersionClass.Release,
name = packageName,
sourceUrl = asset.browser_download_url,
targetFilename = targetFilename,
versionStr = version.ToString(),
requiredVersionStr = "1.0.0",
description = obj.body
}
};
}
private bool IsAsset(Asset asset, string assetFilename)
{
var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty;
if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1)
{
return true;
}
return string.Equals(assetFilename, downloadFilename, StringComparison.OrdinalIgnoreCase);
}
public class Uploader
{
public string login { get; set; }
public int id { get; set; }
public string avatar_url { get; set; }
public string gravatar_id { get; set; }
public string url { get; set; }
public string html_url { get; set; }
public string followers_url { get; set; }
public string following_url { get; set; }
public string gists_url { get; set; }
public string starred_url { get; set; }
public string subscriptions_url { get; set; }
public string organizations_url { get; set; }
public string repos_url { get; set; }
public string events_url { get; set; }
public string received_events_url { get; set; }
public string type { get; set; }
public bool site_admin { get; set; }
}
public class Asset
{
public string url { get; set; }
public int id { get; set; }
public string name { get; set; }
public object label { get; set; }
public Uploader uploader { get; set; }
public string content_type { get; set; }
public string state { get; set; }
public int size { get; set; }
public int download_count { get; set; }
public string created_at { get; set; }
public string updated_at { get; set; }
public string browser_download_url { get; set; }
}
public class Author
{
public string login { get; set; }
public int id { get; set; }
public string avatar_url { get; set; }
public string gravatar_id { get; set; }
public string url { get; set; }
public string html_url { get; set; }
public string followers_url { get; set; }
public string following_url { get; set; }
public string gists_url { get; set; }
public string starred_url { get; set; }
public string subscriptions_url { get; set; }
public string organizations_url { get; set; }
public string repos_url { get; set; }
public string events_url { get; set; }
public string received_events_url { get; set; }
public string type { get; set; }
public bool site_admin { get; set; }
}
public class RootObject
{
public string url { get; set; }
public string assets_url { get; set; }
public string upload_url { get; set; }
public string html_url { get; set; }
public int id { get; set; }
public string tag_name { get; set; }
public string target_commitish { get; set; }
public string name { get; set; }
public bool draft { get; set; }
public Author author { get; set; }
public bool prerelease { get; set; }
public string created_at { get; set; }
public string published_at { get; set; }
public List<Asset> assets { get; set; }
public string tarball_url { get; set; }
public string zipball_url { get; set; }
public string body { get; set; }
}
}
}

View File

@ -437,11 +437,12 @@ namespace MediaBrowser.Common.Implementations.Updates
/// Installs the package.
/// </summary>
/// <param name="package">The package.</param>
/// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">package</exception>
public async Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
{
if (package == null)
{
@ -494,7 +495,7 @@ namespace MediaBrowser.Common.Implementations.Updates
try
{
await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false);
await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false);
lock (CurrentInstallations)
{
@ -550,18 +551,17 @@ namespace MediaBrowser.Common.Implementations.Updates
/// Installs the package internal.
/// </summary>
/// <param name="package">The package.</param>
/// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
{
// Do the install
await PerformPackageInstallation(progress, package, cancellationToken).ConfigureAwait(false);
var extension = Path.GetExtension(package.targetFilename) ?? "";
// Do plugin-specific processing
if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase))
if (isPlugin)
{
// Set last update time if we were installed before
var plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="NLog" version="4.1.0" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="NLog" version="4.2.3" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
<package id="SimpleInjector" version="3.0.5" targetFramework="net45" />
<package id="SimpleInjector" version="3.1.2" targetFramework="net45" />
</packages>

View File

@ -11,7 +11,7 @@ namespace MediaBrowser.Common.Net
/// Gets the machine's local ip address
/// </summary>
/// <returns>IPAddress.</returns>
IEnumerable<string> GetLocalIpAddresses();
IEnumerable<IPAddress> GetLocalIpAddresses();
/// <summary>
/// Gets a random port number that is currently available
@ -25,6 +25,13 @@ namespace MediaBrowser.Common.Net
/// <returns>[string] MAC Address</returns>
string GetMacAddress();
/// <summary>
/// Determines whether [is in private address space] [the specified endpoint].
/// </summary>
/// <param name="endpoint">The endpoint.</param>
/// <returns><c>true</c> if [is in private address space] [the specified endpoint]; otherwise, <c>false</c>.</returns>
bool IsInPrivateAddressSpace(string endpoint);
/// <summary>
/// Gets the network shares.
/// </summary>

View File

@ -105,11 +105,12 @@ namespace MediaBrowser.Common.Updates
/// Installs the package.
/// </summary>
/// <param name="package">The package.</param>
/// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">package</exception>
Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken);
Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken);
/// <summary>
/// Uninstalls a plugin

View File

@ -22,8 +22,14 @@ namespace MediaBrowser.Controller.Dto
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
/// <param name="fields">The fields.</param>
void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields);
void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item);
/// <summary>
/// Gets the primary image aspect ratio.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.Nullable&lt;System.Double&gt;.</returns>
double? GetPrimaryImageAspectRatio(IHasImages item);
/// <summary>
/// Gets the base item dto.

View File

@ -1,6 +1,8 @@
using MediaBrowser.Common;
using MediaBrowser.Model.System;
using System;
using System.Collections.Generic;
using System.Net;
namespace MediaBrowser.Controller
{
@ -63,7 +65,7 @@ namespace MediaBrowser.Controller
/// Gets the local ip address.
/// </summary>
/// <value>The local ip address.</value>
string LocalIpAddress { get; }
List<IPAddress> LocalIpAddresses { get; }
/// <summary>
/// Gets the local API URL.

View File

@ -46,11 +46,14 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="Interfaces.IO">
<HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
</Reference>
@ -65,9 +68,6 @@
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">

View File

@ -28,12 +28,6 @@ namespace MediaBrowser.Controller.Net
/// the ssl certificate localtion on the file system.</param>
void StartServer(IEnumerable<string> urlPrefixes, string certificatePath);
/// <summary>
/// Gets the local end points.
/// </summary>
/// <value>The local end points.</value>
IEnumerable<string> LocalEndPoints { get; }
/// <summary>
/// Stops this instance.
/// </summary>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
<package id="morelinq" version="1.1.1" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages>

View File

@ -43,19 +43,19 @@ namespace MediaBrowser.Dlna.Main
private readonly List<string> _registeredServerIds = new List<string>();
private bool _dlnaServerStarted;
public DlnaEntryPoint(IServerConfigurationManager config,
ILogManager logManager,
IServerApplicationHost appHost,
INetworkManager network,
ISessionManager sessionManager,
IHttpClient httpClient,
ILibraryManager libraryManager,
IUserManager userManager,
IDlnaManager dlnaManager,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
public DlnaEntryPoint(IServerConfigurationManager config,
ILogManager logManager,
IServerApplicationHost appHost,
INetworkManager network,
ISessionManager sessionManager,
IHttpClient httpClient,
ILibraryManager libraryManager,
IUserManager userManager,
IDlnaManager dlnaManager,
IImageProcessor imageProcessor,
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
ISsdpHandler ssdpHandler, IDeviceDiscovery deviceDiscovery)
{
_config = config;
@ -148,13 +148,20 @@ namespace MediaBrowser.Dlna.Main
private void RegisterServerEndpoints()
{
foreach (var address in _network.GetLocalIpAddresses())
foreach (var address in _appHost.LocalIpAddresses)
{
var guid = address.GetMD5();
//if (IPAddress.IsLoopback(address))
//{
// // Should we allow this?
// continue;
//}
var addressString = address.ToString();
var guid = addressString.GetMD5();
var descriptorURI = "/dlna/" + guid.ToString("N") + "/description.xml";
var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorURI);
var uri = new Uri(_appHost.GetLocalApiUrl(addressString) + descriptorURI);
var services = new List<string>
{
@ -165,8 +172,8 @@ namespace MediaBrowser.Dlna.Main
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
"uuid:" + guid.ToString("N")
};
_ssdpHandler.RegisterNotification(guid, uri, IPAddress.Parse(address), services);
_ssdpHandler.RegisterNotification(guid, uri, address, services);
_registeredServerIds.Add(guid.ToString("N"));
}

View File

@ -42,7 +42,10 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

View File

@ -37,7 +37,6 @@ namespace MediaBrowser.Dlna.Profiles
MusicSyncBitrate = 128000;
EnableAlbumArtInDidl = false;
EnableDlnaProtocol = true;
TranscodingProfiles = new[]
{

View File

@ -14,7 +14,7 @@ namespace MediaBrowser.Dlna.Profiles
Name = "Xbox 360";
// Required according to above
ModelName = "Windows Media Connect";
ModelName = "Windows Media Player Sharing";
ModelNumber = "12.0";
@ -25,13 +25,11 @@ namespace MediaBrowser.Dlna.Profiles
ManufacturerUrl = "http://www.microsoft.com";
XDlnaDoc = "DMS-1.50";
ModelDescription = "Emby : UPnP Media Server";
ModelNumber = "001";
TimelineOffsetSeconds = 40;
RequiresPlainFolders = true;
RequiresPlainVideoItems = true;
EnableMSMediaReceiverRegistrar = true;
EnableDlnaProtocol = false;
Identification = new DeviceIdentification
{

View File

@ -81,7 +81,7 @@ namespace MediaBrowser.Dlna.Profiles
new DirectPlayProfile
{
Container = "mp4,mov,mkv",
VideoCodec = "h264,mpeg4",
VideoCodec = "h264,mpeg4,mpeg2video",
AudioCodec = "aac,ac3",
Type = DlnaProfileType.Video
},

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="avi,mpeg,mkv,ts,mp4,mov,m4v,asf,webm,ogg,ogv,iso" type="Video" />

View File

@ -28,7 +28,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mp3,wma" type="Audio" />

View File

@ -33,7 +33,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>true</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg2video" type="Video" />

View File

@ -35,7 +35,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mp4,mkv,mpeg,ts" audioCodec="mp3,ac3,aac,he-aac,pcm" videoCodec="h264,mpeg2video" type="Video" />

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="" type="Video" />

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="ts" audioCodec="aac,ac3,mp3" videoCodec="h264" type="Video" />

View File

@ -32,7 +32,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />

View File

@ -35,7 +35,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:pv" value="http://www.pv.com/pvns/" />
</XmlRootAttributes>

View File

@ -28,7 +28,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mp4,mov" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" />

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:sec" value="http://www.sec.co.kr/" />
</XmlRootAttributes>

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
</XmlRootAttributes>

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
</XmlRootAttributes>

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
</XmlRootAttributes>

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
</XmlRootAttributes>

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
</XmlRootAttributes>

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
</XmlRootAttributes>

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes>
<XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
</XmlRootAttributes>

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />

View File

@ -36,7 +36,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="avi,mpeg,mkv,ts,mp4,mov,m4v,asf,webm,ogg,ogv,iso" type="Video" />

View File

@ -35,7 +35,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />

View File

@ -11,9 +11,9 @@
<FriendlyName>${HostName}: 1</FriendlyName>
<Manufacturer>Microsoft Corporation</Manufacturer>
<ManufacturerUrl>http://www.microsoft.com</ManufacturerUrl>
<ModelName>Windows Media Connect</ModelName>
<ModelName>Windows Media Player Sharing</ModelName>
<ModelDescription>Emby : UPnP Media Server</ModelDescription>
<ModelNumber>001</ModelNumber>
<ModelNumber>12.0</ModelNumber>
<ModelUrl>http://go.microsoft.com/fwlink/?LinkId=105926</ModelUrl>
<EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
<EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
@ -35,7 +35,6 @@
<RequiresPlainFolders>true</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>true</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>false</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />

View File

@ -35,13 +35,12 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="ts" audioCodec="ac3" videoCodec="h264" type="Video" />
<DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
<DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" />
<DirectPlayProfile container="mp4,mov,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
<DirectPlayProfile container="mp4,mov,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" />
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="wmv2,wmv3,vc1" type="Video" />
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
<DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />

View File

@ -34,7 +34,6 @@
<RequiresPlainFolders>false</RequiresPlainFolders>
<EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<EnableDlnaProtocol>true</EnableDlnaProtocol>
<XmlRootAttributes />
<DirectPlayProfiles>
<DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />

View File

@ -54,14 +54,11 @@ namespace MediaBrowser.Dlna.Server
var attributes = _profile.XmlRootAttributes.ToList();
if (_profile.EnableDlnaProtocol)
attributes.Insert(0, new XmlAttribute
{
attributes.Insert(0, new XmlAttribute
{
Name = "xmlns:dlna",
Value = "urn:schemas-dlna-org:device-1-0"
});
}
Name = "xmlns:dlna",
Value = "urn:schemas-dlna-org:device-1-0"
});
attributes.Insert(0, new XmlAttribute
{
Name = "xmlns",
@ -92,10 +89,7 @@ namespace MediaBrowser.Dlna.Server
builder.Append("<device>");
AppendDeviceProperties(builder);
if (_profile.EnableDlnaProtocol)
{
AppendIconList(builder);
}
AppendIconList(builder);
AppendServiceList(builder);
builder.Append("</device>");
}
@ -104,13 +98,10 @@ namespace MediaBrowser.Dlna.Server
{
builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
if (_profile.EnableDlnaProtocol)
{
builder.Append("<dlna:X_DLNACAP>" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
builder.Append("<dlna:X_DLNACAP>" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>");
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + "</dlna:X_DLNADOC>");
}
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>");
builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + "</dlna:X_DLNADOC>");
builder.Append("<friendlyName>" + SecurityElement.Escape(GetFriendlyName()) + "</friendlyName>");
builder.Append("<manufacturer>" + SecurityElement.Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>");

View File

@ -11,6 +11,8 @@ using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MoreLinq;
namespace MediaBrowser.Dlna.Ssdp
{
@ -26,50 +28,39 @@ namespace MediaBrowser.Dlna.Ssdp
public event EventHandler<SsdpMessageEventArgs> DeviceDiscovered;
public event EventHandler<SsdpMessageEventArgs> DeviceLeft;
private readonly INetworkManager _networkManager;
public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost)
public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost, INetworkManager networkManager)
{
_tokenSource = new CancellationTokenSource();
_logger = logger;
_config = config;
_appHost = appHost;
_networkManager = networkManager;
}
private List<IPAddress> GetLocalIpAddresses()
{
return _networkManager.GetLocalIpAddresses().ToList();
}
public void Start(SsdpHandler ssdpHandler)
{
_ssdpHandler = ssdpHandler;
_ssdpHandler.MessageReceived += _ssdpHandler_MessageReceived;
foreach (var network in GetNetworkInterfaces())
{
_logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
continue;
var properties = network.GetIPProperties();
var ipV4 = properties.GetIPv4Properties();
if (null == ipV4)
continue;
var localIps = properties.UnicastAddresses
.Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork)
.Select(i => i.Address)
.ToList();
foreach (var localIp in localIps)
{
try
{
CreateListener(localIp);
}
catch (Exception e)
{
_logger.ErrorException("Failed to Initilize Socket", e);
}
}
}
foreach (var localIp in GetLocalIpAddresses())
{
try
{
CreateListener(localIp);
}
catch (Exception e)
{
_logger.ErrorException("Failed to Initilize Socket", e);
}
}
}
void _ssdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e)
@ -89,8 +80,11 @@ namespace MediaBrowser.Dlna.Ssdp
{
if (e.LocalEndPoint == null)
{
var ip = _appHost.LocalIpAddress;
e.LocalEndPoint = new IPEndPoint(IPAddress.Parse(ip), 0);
var ip = _appHost.LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i));
if (ip != null)
{
e.LocalEndPoint = new IPEndPoint(ip, 0);
}
}
if (e.LocalEndPoint != null)
@ -107,30 +101,18 @@ namespace MediaBrowser.Dlna.Ssdp
}
}
private IEnumerable<NetworkInterface> GetNetworkInterfaces()
{
try
{
return NetworkInterface.GetAllNetworkInterfaces();
}
catch (Exception ex)
{
_logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
return new List<NetworkInterface>();
}
}
private void CreateListener(IPAddress localIp)
{
Task.Factory.StartNew(async (o) =>
{
try
{
var endPoint = new IPEndPoint(localIp, 1900);
_logger.Info("Creating SSDP listener on {0}", localIp);
var endPoint = new IPEndPoint(localIp, 1900);
var socket = GetMulticastSocket(localIp, endPoint);
_logger.Info("Creating SSDP listener on {0}", localIp);
var receiveBuffer = new byte[64000];
CreateNotifier(localIp);

View File

@ -15,6 +15,7 @@ using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32;
namespace MediaBrowser.Dlna.Ssdp
{
@ -112,7 +113,9 @@ namespace MediaBrowser.Dlna.Ssdp
{
get
{
return _devices.Values.SelectMany(i => i).ToList();
var devices = _devices.Values.ToList();
return devices.SelectMany(i => i).ToList();
}
}
@ -121,6 +124,15 @@ namespace MediaBrowser.Dlna.Ssdp
RestartSocketListener();
ReloadAliveNotifier();
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
}
void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
if (e.Mode == PowerModes.Resume)
{
NotifyAll();
}
}
public void SendSearchMessage(EndPoint localIp)
@ -433,6 +445,7 @@ namespace MediaBrowser.Dlna.Ssdp
public void Dispose()
{
_config.NamedConfigurationUpdated -= _config_ConfigurationUpdated;
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
_isDisposed = true;
while (_messageQueue.Count != 0)

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages>

View File

@ -33,7 +33,7 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages>

View File

@ -41,7 +41,7 @@
</Reference>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="DvdLib">
<HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages>

View File

@ -78,7 +78,6 @@ namespace MediaBrowser.Model.Dlna
public bool EnableMSMediaReceiverRegistrar { get; set; }
public bool IgnoreTranscodeByteRangeRequests { get; set; }
public bool EnableDlnaProtocol { get; set; }
public XmlAttribute[] XmlRootAttributes { get; set; }

View File

@ -144,5 +144,11 @@ namespace MediaBrowser.Model.Search
/// </summary>
/// <value>The name of the channel.</value>
public string ChannelName { get; set; }
/// <summary>
/// Gets or sets the primary image aspect ratio.
/// </summary>
/// <value>The primary image aspect ratio.</value>
public double? PrimaryImageAspectRatio { get; set; }
}
}

View File

@ -133,6 +133,7 @@ namespace MediaBrowser.Providers.Manager
source = memoryStream;
var currentImage = GetCurrentImage(item, type, index);
var savedPaths = new List<string>();
using (source)
{
@ -146,17 +147,17 @@ namespace MediaBrowser.Providers.Manager
{
retryPath = retryPaths[currentPathIndex];
}
await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false);
var savedPath = await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false);
savedPaths.Add(savedPath);
currentPathIndex++;
}
}
// Set the path into the item
SetImagePath(item, type, imageIndex, paths[0]);
SetImagePath(item, type, imageIndex, savedPaths[0]);
// Delete the current path
if (currentImage != null && currentImage.IsLocalFile && !paths.Contains(currentImage.Path, StringComparer.OrdinalIgnoreCase))
if (currentImage != null && currentImage.IsLocalFile && !savedPaths.Contains(currentImage.Path, StringComparer.OrdinalIgnoreCase))
{
var currentPath = currentImage.Path;
@ -184,11 +185,12 @@ namespace MediaBrowser.Providers.Manager
}
}
private async Task SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken)
private async Task<string> SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken)
{
try
{
await SaveImageToLocation(source, path, cancellationToken).ConfigureAwait(false);
return path;
}
catch (UnauthorizedAccessException)
{
@ -207,6 +209,7 @@ namespace MediaBrowser.Providers.Manager
source.Position = 0;
await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false);
return retryPath;
}
/// <summary>

View File

@ -795,22 +795,6 @@ namespace MediaBrowser.Providers.Manager
}
}
// This is a workaround duplicate check for movies, where intersecting provider ids are not always available
if (typeof(TItemType) == typeof(Movie) || typeof(TItemType) == typeof(Series))
{
var titleYearString = string.Format("{0} ({1})", result.Name, result.ProductionYear);
if (foundTitleYearStrings.Contains(titleYearString))
{
bFound = true;
}
else
{
foundTitleYearStrings.Add(titleYearString);
}
}
if (!bFound && resultList.Count < maxResults)
{
resultList.Add(result);

View File

@ -50,15 +50,14 @@
</Reference>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="DvdLib, Version=1.0.5167.21152, Culture=neutral, PublicKeyToken=7a2f3f5ec8d93575, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath>
</Reference>
<Reference Include="MoreLinq, Version=1.1.18418.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

View File

@ -68,7 +68,14 @@ namespace MediaBrowser.Providers.Music
var list = new List<RemoteImageInfo>();
var artistMusicBrainzId = album.MusicArtist.GetProviderId(MetadataProviders.MusicBrainzArtist);
var musicArtist = album.MusicArtist;
if (musicArtist == null)
{
return list;
}
var artistMusicBrainzId = musicArtist.GetProviderId(MetadataProviders.MusicBrainzArtist);
if (!string.IsNullOrEmpty(artistMusicBrainzId))
{

View File

@ -51,25 +51,15 @@ namespace MediaBrowser.Providers.Omdb
return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
}
public async Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
var response = await _httpClient.GetResponse(new HttpRequestOptions
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = OmdbProvider.ResourcePool
}).ConfigureAwait(false);
if (response.ContentLength == 11059)
{
throw new HttpException("File not found")
{
StatusCode = HttpStatusCode.NotFound
};
}
return response;
});
}
public string Name

View File

@ -57,7 +57,12 @@ namespace MediaBrowser.Providers.Omdb
return GetSearchResults(searchInfo, "movie", cancellationToken);
}
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken)
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken)
{
return GetSearchResultsInternal(searchInfo, type, true, cancellationToken);
}
private async Task<IEnumerable<RemoteSearchResult>> GetSearchResultsInternal(ItemLookupInfo searchInfo, string type, bool enableMultipleResults, CancellationToken cancellationToken)
{
bool isSearch = false;
@ -86,7 +91,14 @@ namespace MediaBrowser.Providers.Omdb
}
// &s means search and returns a list of results as opposed to t
url += "&s=" + WebUtility.UrlEncode(name);
if (enableMultipleResults)
{
url += "&s=" + WebUtility.UrlEncode(name);
}
else
{
url += "&t=" + WebUtility.UrlEncode(name);
}
url += "&type=" + type;
isSearch = true;
}
@ -239,14 +251,14 @@ namespace MediaBrowser.Providers.Omdb
private async Task<string> GetMovieImdbId(ItemLookupInfo info, CancellationToken cancellationToken)
{
var results = await GetSearchResults(info, "movie", cancellationToken).ConfigureAwait(false);
var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
}
private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken)
{
var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
}

View File

@ -18,6 +18,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
namespace MediaBrowser.Providers.Subtitles
{
@ -30,15 +31,6 @@ namespace MediaBrowser.Providers.Subtitles
private readonly IServerConfigurationManager _config;
private readonly IEncryptionManager _encryption;
private Timer _dailyTimer;
// This is limited to 200 per day
private int _dailyDownloadCount;
// It's 200 but this will be in-exact so buffer a little
// And the user may restart the server
private const int MaxDownloadsPerDay = 150;
private readonly IJsonSerializer _json;
public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json)
@ -51,9 +43,6 @@ namespace MediaBrowser.Providers.Subtitles
_config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
// Reset the count every 24 hours
_dailyTimer = new Timer(state => _dailyDownloadCount = 0, null, TimeSpan.FromHours(24), TimeSpan.FromHours(24));
Utilities.HttpClient = httpClient;
OpenSubtitles.SetUserAgent("mediabrowser.tv");
}
@ -123,6 +112,7 @@ namespace MediaBrowser.Providers.Subtitles
return GetSubtitlesInternal(id, GetOptions(), cancellationToken);
}
private DateTime _lastRateLimitException;
private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
SubtitleOptions options,
CancellationToken cancellationToken)
@ -132,12 +122,6 @@ namespace MediaBrowser.Providers.Subtitles
throw new ArgumentNullException("id");
}
if (_dailyDownloadCount >= MaxDownloadsPerDay &&
!options.IsOpenSubtitleVipAccount)
{
throw new InvalidOperationException("Open Subtitle's daily download limit has been exceeded. Please try again tomorrow.");
}
var idParts = id.Split(new[] { '-' }, 3);
var format = idParts[0];
@ -148,8 +132,19 @@ namespace MediaBrowser.Providers.Subtitles
await Login(cancellationToken).ConfigureAwait(false);
if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1)
{
throw new ApplicationException("OpenSubtitles rate limit reached");
}
var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false);
if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1)
{
_lastRateLimitException = DateTime.UtcNow;
throw new ApplicationException("OpenSubtitles rate limit reached");
}
if (!(resultDownLoad is MethodResponseSubtitleDownload))
{
throw new ApplicationException("Invalid response type");
@ -157,13 +152,15 @@ namespace MediaBrowser.Providers.Subtitles
var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results;
_lastRateLimitException = DateTime.MinValue;
if (results.Count == 0)
{
var msg = string.Format("Subtitle with Id {0} was not found. Name: {1}. Status: {2}. Message: {3}",
ossId,
resultDownLoad.Name ?? string.Empty,
resultDownLoad.Message ?? string.Empty,
resultDownLoad.Status ?? string.Empty);
resultDownLoad.Status ?? string.Empty,
resultDownLoad.Message ?? string.Empty);
throw new ResourceNotFoundException(msg);
}
@ -339,12 +336,6 @@ namespace MediaBrowser.Providers.Subtitles
public void Dispose()
{
_config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating;
if (_dailyTimer != null)
{
_dailyTimer.Dispose();
_dailyTimer = null;
}
}
}
}

View File

@ -112,6 +112,8 @@ namespace MediaBrowser.Providers.TV
if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
(searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
{
await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds);
var searchNumbers = new EpisodeNumbers();

View File

@ -222,6 +222,11 @@ namespace MediaBrowser.Providers.TV
seriesId = await GetSeriesByRemoteId(seriesId, idType, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
if (string.IsNullOrWhiteSpace(seriesId))
{
throw new ArgumentNullException("seriesId");
}
var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, preferredMetadataLanguage);
using (var zipStream = await _httpClient.Get(new HttpRequestOptions
@ -324,38 +329,48 @@ namespace MediaBrowser.Providers.TV
return false;
}
private SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1,1);
internal async Task<string> EnsureSeriesInfo(Dictionary<string, string> seriesProviderIds, string preferredMetadataLanguage, CancellationToken cancellationToken)
{
string seriesId;
if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId))
{
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
// Only download if not already there
// The post-scan task will take care of updates so we don't need to re-download here
if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
try
{
string seriesId;
if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId))
{
await DownloadSeriesZip(seriesId, MetadataProviders.Tvdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
// Only download if not already there
// The post-scan task will take care of updates so we don't need to re-download here
if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
{
await DownloadSeriesZip(seriesId, MetadataProviders.Tvdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
return seriesDataPath;
}
return seriesDataPath;
}
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId))
{
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
// Only download if not already there
// The post-scan task will take care of updates so we don't need to re-download here
if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId))
{
await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
// Only download if not already there
// The post-scan task will take care of updates so we don't need to re-download here
if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
{
await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
return seriesDataPath;
}
return seriesDataPath;
return null;
}
finally
{
_ensureSemaphore.Release();
}
return null;
}
private bool IsCacheValid(string seriesDataPath, string preferredMetadataLanguage)

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" />
<package id="morelinq" version="1.1.1" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
<package id="taglib" version="2.1.0.0" targetFramework="net45" />
</packages>

View File

@ -310,7 +310,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
try
{
AttachPrimaryImageAspectRatio(dto, item, fields);
AttachPrimaryImageAspectRatio(dto, item);
}
catch (Exception ex)
{
@ -1742,15 +1742,19 @@ namespace MediaBrowser.Server.Implementations.Dto
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
/// <param name="fields">The fields.</param>
/// <returns>Task.</returns>
public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields)
public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item)
{
dto.PrimaryImageAspectRatio = GetPrimaryImageAspectRatio(item);
}
public double? GetPrimaryImageAspectRatio(IHasImages item)
{
var imageInfo = item.GetImageInfo(ImageType.Primary, 0);
if (imageInfo == null || !imageInfo.IsLocalFile)
{
return;
return null;
}
ImageSize size;
@ -1762,7 +1766,7 @@ namespace MediaBrowser.Server.Implementations.Dto
catch (Exception ex)
{
//_logger.ErrorException("Failed to determine primary image aspect ratio for {0}", ex, path);
return;
return null;
}
var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList();
@ -1781,8 +1785,9 @@ namespace MediaBrowser.Server.Implementations.Dto
if (size.Width > 0 && size.Height > 0)
{
dto.PrimaryImageAspectRatio = size.Width / size.Height;
return size.Width / size.Height;
}
return null;
}
}
}

View File

@ -74,42 +74,52 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
if (!string.IsNullOrEmpty(seriesName))
{
var season = episodeInfo.SeasonNumber;
var seasonNumber = episodeInfo.SeasonNumber;
result.ExtractedSeasonNumber = season;
result.ExtractedSeasonNumber = seasonNumber;
if (season.HasValue)
{
// Passing in true will include a few extra regex's
var episode = episodeInfo.EpisodeNumber;
// Passing in true will include a few extra regex's
var episodeNumber = episodeInfo.EpisodeNumber;
result.ExtractedEpisodeNumber = episode;
result.ExtractedEpisodeNumber = episodeNumber;
if (episode.HasValue)
{
_logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, season, episode);
var premiereDate = episodeInfo.IsByDate ?
new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
(DateTime?)null;
var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
{
if (episodeInfo.IsByDate)
{
_logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
}
else
{
_logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
}
result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
await OrganizeEpisode(path, seriesName, season.Value, episode.Value, endingEpisodeNumber, options, overwriteExisting, result, cancellationToken).ConfigureAwait(false);
}
else
{
var msg = string.Format("Unable to determine episode number from {0}", path);
result.Status = FileSortingStatus.Failure;
result.StatusMessage = msg;
_logger.Warn(msg);
}
}
else
{
var msg = string.Format("Unable to determine season number from {0}", path);
result.Status = FileSortingStatus.Failure;
result.StatusMessage = msg;
_logger.Warn(msg);
}
result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
await OrganizeEpisode(path,
seriesName,
seasonNumber,
episodeNumber,
endingEpisodeNumber,
premiereDate,
options,
overwriteExisting,
result,
cancellationToken).ConfigureAwait(false);
}
else
{
var msg = string.Format("Unable to determine episode number from {0}", path);
result.Status = FileSortingStatus.Failure;
result.StatusMessage = msg;
_logger.Warn(msg);
}
}
else
{
@ -141,14 +151,32 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
await OrganizeEpisode(result.OriginalPath, series, request.SeasonNumber, request.EpisodeNumber, request.EndingEpisodeNumber, options, true, result, cancellationToken).ConfigureAwait(false);
await OrganizeEpisode(result.OriginalPath,
series,
request.SeasonNumber,
request.EpisodeNumber,
request.EndingEpisodeNumber,
null,
options,
true,
result,
cancellationToken).ConfigureAwait(false);
await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
return result;
}
private Task OrganizeEpisode(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result, CancellationToken cancellationToken)
private Task OrganizeEpisode(string sourcePath,
string seriesName,
int? seasonNumber,
int? episodeNumber,
int? endingEpiosdeNumber,
DateTime? premiereDate,
TvFileOrganizationOptions options,
bool overwriteExisting,
FileOrganizationResult result,
CancellationToken cancellationToken)
{
var series = GetMatchingSeries(seriesName, result);
@ -161,15 +189,33 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
return Task.FromResult(true);
}
return OrganizeEpisode(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, overwriteExisting, result, cancellationToken);
return OrganizeEpisode(sourcePath,
series,
seasonNumber,
episodeNumber,
endingEpiosdeNumber,
premiereDate,
options,
overwriteExisting,
result,
cancellationToken);
}
private async Task OrganizeEpisode(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result, CancellationToken cancellationToken)
private async Task OrganizeEpisode(string sourcePath,
Series series,
int? seasonNumber,
int? episodeNumber,
int? endingEpiosdeNumber,
DateTime? premiereDate,
TvFileOrganizationOptions options,
bool overwriteExisting,
FileOrganizationResult result,
CancellationToken cancellationToken)
{
_logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
// Proceed to sort the file
var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, cancellationToken).ConfigureAwait(false);
var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(newPath))
{
@ -278,8 +324,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
}
private List<string> GetOtherDuplicatePaths(string targetPath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber)
private List<string> GetOtherDuplicatePaths(string targetPath,
Series series,
int? seasonNumber,
int? episodeNumber,
int? endingEpisodeNumber)
{
// TODO: Support date-naming?
if (!seasonNumber.HasValue || episodeNumber.HasValue)
{
return new List<string> ();
}
var episodePaths = series.GetRecursiveChildren()
.OfType<Episode>()
.Where(i =>
@ -408,7 +464,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
/// <param name="endingEpisodeNumber">The ending episode number.</param>
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
private async Task<string> GetNewPath(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, TvFileOrganizationOptions options, CancellationToken cancellationToken)
private async Task<string> GetNewPath(string sourcePath,
Series series,
int? seasonNumber,
int? episodeNumber,
int? endingEpisodeNumber,
DateTime? premiereDate,
TvFileOrganizationOptions options,
CancellationToken cancellationToken)
{
var episodeInfo = new EpisodeInfo
{
@ -417,7 +480,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
MetadataCountryCode = series.GetPreferredMetadataCountryCode(),
MetadataLanguage = series.GetPreferredMetadataLanguage(),
ParentIndexNumber = seasonNumber,
SeriesProviderIds = series.ProviderIds
SeriesProviderIds = series.ProviderIds,
PremiereDate = premiereDate
};
var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
@ -427,14 +491,24 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}, cancellationToken).ConfigureAwait(false);
var episode = searchResults.FirstOrDefault();
string episodeName = string.Empty;
if (episode == null)
{
_logger.Warn("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
return null;
var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
_logger.Warn(msg);
//throw new Exception(msg);
}
else
{
episodeName = episode.Name;
}
var newPath = GetSeasonFolderPath(series, seasonNumber, options);
seasonNumber = seasonNumber ?? episode.ParentIndexNumber;
episodeNumber = episodeNumber ?? episode.IndexNumber;
var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options);
// MAX_PATH - trailing <NULL> charachter - drive component: 260 - 1 - 3 = 256
// Usually newPath would include the drive component, but use 256 to be sure
@ -449,7 +523,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
// Remove additional 4 chars to prevent PathTooLongException for downloaded subtitles (eg. filename.ext.eng.srt)
maxFilenameLength -= 4;
var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber, episodeNumber, endingEpisodeNumber, episode.Name, options, maxFilenameLength);
var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options, maxFilenameLength);
if (string.IsNullOrEmpty(episodeFileName))
{
@ -505,7 +579,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
if (episodeTitle == null)
if (string.IsNullOrEmpty(episodeTitle))
{
episodeTitle = string.Empty;
}

View File

@ -19,6 +19,7 @@ using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Security;
namespace MediaBrowser.Server.Implementations.HttpServer
@ -39,40 +40,20 @@ namespace MediaBrowser.Server.Implementations.HttpServer
public event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
public event EventHandler<WebSocketConnectingEventArgs> WebSocketConnecting;
private readonly List<string> _localEndpoints = new List<string>();
private readonly ReaderWriterLockSlim _localEndpointLock = new ReaderWriterLockSlim();
public string CertificatePath { get; private set; }
private readonly IServerConfigurationManager _config;
/// <summary>
/// Gets the local end points.
/// </summary>
/// <value>The local end points.</value>
public IEnumerable<string> LocalEndPoints
{
get
{
_localEndpointLock.EnterReadLock();
var list = _localEndpoints.ToList();
_localEndpointLock.ExitReadLock();
return list;
}
}
private readonly INetworkManager _networkManager;
public HttpListenerHost(IApplicationHost applicationHost,
ILogManager logManager,
IServerConfigurationManager config,
string serviceName,
string defaultRedirectPath, params Assembly[] assembliesWithServices)
string defaultRedirectPath, INetworkManager networkManager, params Assembly[] assembliesWithServices)
: base(serviceName, assembliesWithServices)
{
DefaultRedirectPath = defaultRedirectPath;
_networkManager = networkManager;
_config = config;
_logger = logManager.GetLogger("HttpServer");
@ -175,26 +156,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
private void OnRequestReceived(string localEndPoint)
{
var ignore = localEndPoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 ||
localEndPoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
localEndPoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
localEndPoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase);
if (ignore)
{
return;
}
if (_localEndpointLock.TryEnterWriteLock(100))
{
var list = _localEndpoints.ToList();
list.Remove(localEndPoint);
list.Insert(0, localEndPoint);
_localEndpointLock.ExitWriteLock();
}
}
/// <summary>

View File

@ -58,7 +58,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer
if (hasOptions != null)
{
hasOptions.Options["Server"] = "Mono-HTTPAPI/1.1";
if (!hasOptions.Options.ContainsKey("Server"))
{
hasOptions.Options["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50";
}
// Content length has to be explicitly set on on HttpListenerResponse or it won't be happy
string contentLength;

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
@ -17,18 +18,20 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="applicationHost">The application host.</param>
/// <param name="logManager">The log manager.</param>
/// <param name="config">The configuration.</param>
/// <param name="_networkmanager">The _networkmanager.</param>
/// <param name="serverName">Name of the server.</param>
/// <param name="defaultRedirectpath">The default redirectpath.</param>
/// <returns>IHttpServer.</returns>
public static IHttpServer CreateServer(IApplicationHost applicationHost,
ILogManager logManager,
IServerConfigurationManager config,
INetworkManager _networkmanager,
string serverName,
string defaultRedirectpath)
{
LogManager.LogFactory = new ServerLogFactory(logManager);
return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath);
return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, _networkmanager);
}
}
}

View File

@ -0,0 +1,941 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
public static class MyHttpUtility
{
sealed class HttpQSCollection : NameValueCollection
{
public override string ToString()
{
int count = Count;
if (count == 0)
return "";
StringBuilder sb = new StringBuilder();
string[] keys = AllKeys;
for (int i = 0; i < count; i++)
{
sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]);
}
if (sb.Length > 0)
sb.Length--;
return sb.ToString();
}
}
// Must be sorted
static readonly long[] entities = new long[] {
(long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'A' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'A' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'A' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'A' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24,
(long)'A' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24,
(long)'A' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'A' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'B' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'C' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16,
(long)'C' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'D' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16,
(long)'D' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'E' << 56 | (long)'T' << 48 | (long)'H' << 40,
(long)'E' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'E' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'E' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'E' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'E' << 56 | (long)'t' << 48 | (long)'a' << 40,
(long)'E' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'G' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'I' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'I' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'I' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'I' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'I' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'K' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24,
(long)'L' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16,
(long)'M' << 56 | (long)'u' << 48,
(long)'N' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'N' << 56 | (long)'u' << 48,
(long)'O' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'O' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'O' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'O' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'O' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24,
(long)'O' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'O' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16,
(long)'O' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'O' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'P' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'P' << 56 | (long)'i' << 48,
(long)'P' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24,
(long)'P' << 56 | (long)'s' << 48 | (long)'i' << 40,
(long)'R' << 56 | (long)'h' << 48 | (long)'o' << 40,
(long)'S' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16,
(long)'S' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'T' << 56 | (long)'H' << 48 | (long)'O' << 40 | (long)'R' << 32 | (long)'N' << 24,
(long)'T' << 56 | (long)'a' << 48 | (long)'u' << 40,
(long)'T' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'U' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'U' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'U' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'U' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'U' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'X' << 56 | (long)'i' << 48,
(long)'Y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'Y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'Z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'a' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'a' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'a' << 56 | (long)'c' << 48 | (long)'u' << 40 | (long)'t' << 32 | (long)'e' << 24,
(long)'a' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'a' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'a' << 56 | (long)'l' << 48 | (long)'e' << 40 | (long)'f' << 32 | (long)'s' << 24 | (long)'y' << 16 | (long)'m' << 8,
(long)'a' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24,
(long)'a' << 56 | (long)'m' << 48 | (long)'p' << 40,
(long)'a' << 56 | (long)'n' << 48 | (long)'d' << 40,
(long)'a' << 56 | (long)'n' << 48 | (long)'g' << 40,
(long)'a' << 56 | (long)'p' << 48 | (long)'o' << 40 | (long)'s' << 32,
(long)'a' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24,
(long)'a' << 56 | (long)'s' << 48 | (long)'y' << 40 | (long)'m' << 32 | (long)'p' << 24,
(long)'a' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'a' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'b' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'b' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'b' << 56 | (long)'r' << 48 | (long)'v' << 40 | (long)'b' << 32 | (long)'a' << 24 | (long)'r' << 16,
(long)'b' << 56 | (long)'u' << 48 | (long)'l' << 40 | (long)'l' << 32,
(long)'c' << 56 | (long)'a' << 48 | (long)'p' << 40,
(long)'c' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16,
(long)'c' << 56 | (long)'e' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'l' << 24,
(long)'c' << 56 | (long)'e' << 48 | (long)'n' << 40 | (long)'t' << 32,
(long)'c' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'c' << 56 | (long)'i' << 48 | (long)'r' << 40 | (long)'c' << 32,
(long)'c' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'b' << 32 | (long)'s' << 24,
(long)'c' << 56 | (long)'o' << 48 | (long)'n' << 40 | (long)'g' << 32,
(long)'c' << 56 | (long)'o' << 48 | (long)'p' << 40 | (long)'y' << 32,
(long)'c' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'r' << 24,
(long)'c' << 56 | (long)'u' << 48 | (long)'p' << 40,
(long)'c' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'n' << 16,
(long)'d' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'d' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16,
(long)'d' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'d' << 56 | (long)'e' << 48 | (long)'g' << 40,
(long)'d' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'d' << 56 | (long)'i' << 48 | (long)'a' << 40 | (long)'m' << 32 | (long)'s' << 24,
(long)'d' << 56 | (long)'i' << 48 | (long)'v' << 40 | (long)'i' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'e' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'e' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'e' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'e' << 56 | (long)'m' << 48 | (long)'p' << 40 | (long)'t' << 32 | (long)'y' << 24,
(long)'e' << 56 | (long)'m' << 48 | (long)'s' << 40 | (long)'p' << 32,
(long)'e' << 56 | (long)'n' << 48 | (long)'s' << 40 | (long)'p' << 32,
(long)'e' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'e' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'i' << 32 | (long)'v' << 24,
(long)'e' << 56 | (long)'t' << 48 | (long)'a' << 40,
(long)'e' << 56 | (long)'t' << 48 | (long)'h' << 40,
(long)'e' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'e' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'o' << 32,
(long)'e' << 56 | (long)'x' << 48 | (long)'i' << 40 | (long)'s' << 32 | (long)'t' << 24,
(long)'f' << 56 | (long)'n' << 48 | (long)'o' << 40 | (long)'f' << 32,
(long)'f' << 56 | (long)'o' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'l' << 24 | (long)'l' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'2' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'4' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'3' << 24 | (long)'4' << 16,
(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'l' << 24,
(long)'g' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'g' << 56 | (long)'e' << 48,
(long)'g' << 56 | (long)'t' << 48,
(long)'h' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'h' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'h' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'t' << 24 | (long)'s' << 16,
(long)'h' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'l' << 32 | (long)'i' << 24 | (long)'p' << 16,
(long)'i' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'i' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'i' << 56 | (long)'e' << 48 | (long)'x' << 40 | (long)'c' << 32 | (long)'l' << 24,
(long)'i' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'i' << 56 | (long)'m' << 48 | (long)'a' << 40 | (long)'g' << 32 | (long)'e' << 24,
(long)'i' << 56 | (long)'n' << 48 | (long)'f' << 40 | (long)'i' << 32 | (long)'n' << 24,
(long)'i' << 56 | (long)'n' << 48 | (long)'t' << 40,
(long)'i' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'i' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'e' << 32 | (long)'s' << 24 | (long)'t' << 16,
(long)'i' << 56 | (long)'s' << 48 | (long)'i' << 40 | (long)'n' << 32,
(long)'i' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'k' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24,
(long)'l' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'l' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16,
(long)'l' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32,
(long)'l' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'l' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'l' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24,
(long)'l' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'l' << 56 | (long)'e' << 48,
(long)'l' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16,
(long)'l' << 56 | (long)'o' << 48 | (long)'w' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'t' << 16,
(long)'l' << 56 | (long)'o' << 48 | (long)'z' << 40,
(long)'l' << 56 | (long)'r' << 48 | (long)'m' << 40,
(long)'l' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16,
(long)'l' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'l' << 56 | (long)'t' << 48,
(long)'m' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'r' << 32,
(long)'m' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24,
(long)'m' << 56 | (long)'i' << 48 | (long)'c' << 40 | (long)'r' << 32 | (long)'o' << 24,
(long)'m' << 56 | (long)'i' << 48 | (long)'d' << 40 | (long)'d' << 32 | (long)'o' << 24 | (long)'t' << 16,
(long)'m' << 56 | (long)'i' << 48 | (long)'n' << 40 | (long)'u' << 32 | (long)'s' << 24,
(long)'m' << 56 | (long)'u' << 48,
(long)'n' << 56 | (long)'a' << 48 | (long)'b' << 40 | (long)'l' << 32 | (long)'a' << 24,
(long)'n' << 56 | (long)'b' << 48 | (long)'s' << 40 | (long)'p' << 32,
(long)'n' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24,
(long)'n' << 56 | (long)'e' << 48,
(long)'n' << 56 | (long)'i' << 48,
(long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40,
(long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'i' << 32 | (long)'n' << 24,
(long)'n' << 56 | (long)'s' << 48 | (long)'u' << 40 | (long)'b' << 32,
(long)'n' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'n' << 56 | (long)'u' << 48,
(long)'o' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'o' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'o' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'o' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'o' << 56 | (long)'l' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'e' << 24,
(long)'o' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24,
(long)'o' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'o' << 56 | (long)'p' << 48 | (long)'l' << 40 | (long)'u' << 32 | (long)'s' << 24,
(long)'o' << 56 | (long)'r' << 48,
(long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'f' << 32,
(long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'m' << 32,
(long)'o' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16,
(long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16,
(long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24 | (long)'s' << 16,
(long)'o' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'a' << 32,
(long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'t' << 32,
(long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'m' << 32 | (long)'i' << 24 | (long)'l' << 16,
(long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'p' << 32,
(long)'p' << 56 | (long)'h' << 48 | (long)'i' << 40,
(long)'p' << 56 | (long)'i' << 48,
(long)'p' << 56 | (long)'i' << 48 | (long)'v' << 40,
(long)'p' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'s' << 32 | (long)'m' << 24 | (long)'n' << 16,
(long)'p' << 56 | (long)'o' << 48 | (long)'u' << 40 | (long)'n' << 32 | (long)'d' << 24,
(long)'p' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24,
(long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'d' << 32,
(long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'p' << 32,
(long)'p' << 56 | (long)'s' << 48 | (long)'i' << 40,
(long)'q' << 56 | (long)'u' << 48 | (long)'o' << 40 | (long)'t' << 32,
(long)'r' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'r' << 56 | (long)'a' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'c' << 24,
(long)'r' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32,
(long)'r' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'r' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'r' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24,
(long)'r' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'r' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'l' << 32,
(long)'r' << 56 | (long)'e' << 48 | (long)'g' << 40,
(long)'r' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16,
(long)'r' << 56 | (long)'h' << 48 | (long)'o' << 40,
(long)'r' << 56 | (long)'l' << 48 | (long)'m' << 40,
(long)'r' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16,
(long)'r' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'s' << 56 | (long)'b' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24,
(long)'s' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16,
(long)'s' << 56 | (long)'d' << 48 | (long)'o' << 40 | (long)'t' << 32,
(long)'s' << 56 | (long)'e' << 48 | (long)'c' << 40 | (long)'t' << 32,
(long)'s' << 56 | (long)'h' << 48 | (long)'y' << 40,
(long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24,
(long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24 | (long)'f' << 16,
(long)'s' << 56 | (long)'i' << 48 | (long)'m' << 40,
(long)'s' << 56 | (long)'p' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24 | (long)'s' << 16,
(long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40,
(long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40 | (long)'e' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'m' << 40,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'1' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'2' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'3' << 32,
(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'e' << 32,
(long)'s' << 56 | (long)'z' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
(long)'t' << 56 | (long)'a' << 48 | (long)'u' << 40,
(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'4' << 16,
(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24,
(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24 | (long)'s' << 16 | (long)'y' << 8 | (long)'m' << 0,
(long)'t' << 56 | (long)'h' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'s' << 24 | (long)'p' << 16,
(long)'t' << 56 | (long)'h' << 48 | (long)'o' << 40 | (long)'r' << 32 | (long)'n' << 24,
(long)'t' << 56 | (long)'i' << 48 | (long)'l' << 40 | (long)'d' << 32 | (long)'e' << 24,
(long)'t' << 56 | (long)'i' << 48 | (long)'m' << 40 | (long)'e' << 32 | (long)'s' << 24,
(long)'t' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24,
(long)'u' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'u' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'u' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32,
(long)'u' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24,
(long)'u' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16,
(long)'u' << 56 | (long)'m' << 48 | (long)'l' << 40,
(long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'h' << 24,
(long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8,
(long)'u' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'w' << 56 | (long)'e' << 48 | (long)'i' << 40 | (long)'e' << 32 | (long)'r' << 24 | (long)'p' << 16,
(long)'x' << 56 | (long)'i' << 48,
(long)'y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16,
(long)'y' << 56 | (long)'e' << 48 | (long)'n' << 40,
(long)'y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32,
(long)'z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32,
(long)'z' << 56 | (long)'w' << 48 | (long)'j' << 40,
(long)'z' << 56 | (long)'w' << 48 | (long)'n' << 40 | (long)'j' << 32
};
static readonly char[] entities_values = new char[] {
'\u00C6',
'\u00C1',
'\u00C2',
'\u00C0',
'\u0391',
'\u00C5',
'\u00C3',
'\u00C4',
'\u0392',
'\u00C7',
'\u03A7',
'\u2021',
'\u0394',
'\u00D0',
'\u00C9',
'\u00CA',
'\u00C8',
'\u0395',
'\u0397',
'\u00CB',
'\u0393',
'\u00CD',
'\u00CE',
'\u00CC',
'\u0399',
'\u00CF',
'\u039A',
'\u039B',
'\u039C',
'\u00D1',
'\u039D',
'\u0152',
'\u00D3',
'\u00D4',
'\u00D2',
'\u03A9',
'\u039F',
'\u00D8',
'\u00D5',
'\u00D6',
'\u03A6',
'\u03A0',
'\u2033',
'\u03A8',
'\u03A1',
'\u0160',
'\u03A3',
'\u00DE',
'\u03A4',
'\u0398',
'\u00DA',
'\u00DB',
'\u00D9',
'\u03A5',
'\u00DC',
'\u039E',
'\u00DD',
'\u0178',
'\u0396',
'\u00E1',
'\u00E2',
'\u00B4',
'\u00E6',
'\u00E0',
'\u2135',
'\u03B1',
'\u0026',
'\u2227',
'\u2220',
'\u0027',
'\u00E5',
'\u2248',
'\u00E3',
'\u00E4',
'\u201E',
'\u03B2',
'\u00A6',
'\u2022',
'\u2229',
'\u00E7',
'\u00B8',
'\u00A2',
'\u03C7',
'\u02C6',
'\u2663',
'\u2245',
'\u00A9',
'\u21B5',
'\u222A',
'\u00A4',
'\u21D3',
'\u2020',
'\u2193',
'\u00B0',
'\u03B4',
'\u2666',
'\u00F7',
'\u00E9',
'\u00EA',
'\u00E8',
'\u2205',
'\u2003',
'\u2002',
'\u03B5',
'\u2261',
'\u03B7',
'\u00F0',
'\u00EB',
'\u20AC',
'\u2203',
'\u0192',
'\u2200',
'\u00BD',
'\u00BC',
'\u00BE',
'\u2044',
'\u03B3',
'\u2265',
'\u003E',
'\u21D4',
'\u2194',
'\u2665',
'\u2026',
'\u00ED',
'\u00EE',
'\u00A1',
'\u00EC',
'\u2111',
'\u221E',
'\u222B',
'\u03B9',
'\u00BF',
'\u2208',
'\u00EF',
'\u03BA',
'\u21D0',
'\u03BB',
'\u2329',
'\u00AB',
'\u2190',
'\u2308',
'\u201C',
'\u2264',
'\u230A',
'\u2217',
'\u25CA',
'\u200E',
'\u2039',
'\u2018',
'\u003C',
'\u00AF',
'\u2014',
'\u00B5',
'\u00B7',
'\u2212',
'\u03BC',
'\u2207',
'\u00A0',
'\u2013',
'\u2260',
'\u220B',
'\u00AC',
'\u2209',
'\u2284',
'\u00F1',
'\u03BD',
'\u00F3',
'\u00F4',
'\u0153',
'\u00F2',
'\u203E',
'\u03C9',
'\u03BF',
'\u2295',
'\u2228',
'\u00AA',
'\u00BA',
'\u00F8',
'\u00F5',
'\u2297',
'\u00F6',
'\u00B6',
'\u2202',
'\u2030',
'\u22A5',
'\u03C6',
'\u03C0',
'\u03D6',
'\u00B1',
'\u00A3',
'\u2032',
'\u220F',
'\u221D',
'\u03C8',
'\u0022',
'\u21D2',
'\u221A',
'\u232A',
'\u00BB',
'\u2192',
'\u2309',
'\u201D',
'\u211C',
'\u00AE',
'\u230B',
'\u03C1',
'\u200F',
'\u203A',
'\u2019',
'\u201A',
'\u0161',
'\u22C5',
'\u00A7',
'\u00AD',
'\u03C3',
'\u03C2',
'\u223C',
'\u2660',
'\u2282',
'\u2286',
'\u2211',
'\u2283',
'\u00B9',
'\u00B2',
'\u00B3',
'\u2287',
'\u00DF',
'\u03C4',
'\u2234',
'\u03B8',
'\u03D1',
'\u2009',
'\u00FE',
'\u02DC',
'\u00D7',
'\u2122',
'\u21D1',
'\u00FA',
'\u2191',
'\u00FB',
'\u00F9',
'\u00A8',
'\u03D2',
'\u03C5',
'\u00FC',
'\u2118',
'\u03BE',
'\u00FD',
'\u00A5',
'\u00FF',
'\u03B6',
'\u200D',
'\u200C'
};
#region Methods
static void WriteCharBytes(IList buf, char ch, Encoding e)
{
if (ch > 255)
{
foreach (byte b in e.GetBytes(new char[] { ch }))
buf.Add(b);
}
else
buf.Add((byte)ch);
}
public static string UrlDecode(string s, Encoding e)
{
if (null == s)
return null;
if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
return s;
if (e == null)
e = Encoding.UTF8;
long len = s.Length;
var bytes = new List<byte>();
int xchar;
char ch;
for (int i = 0; i < len; i++)
{
ch = s[i];
if (ch == '%' && i + 2 < len && s[i + 1] != '%')
{
if (s[i + 1] == 'u' && i + 5 < len)
{
// unicode hex sequence
xchar = GetChar(s, i + 2, 4);
if (xchar != -1)
{
WriteCharBytes(bytes, (char)xchar, e);
i += 5;
}
else
WriteCharBytes(bytes, '%', e);
}
else if ((xchar = GetChar(s, i + 1, 2)) != -1)
{
WriteCharBytes(bytes, (char)xchar, e);
i += 2;
}
else
{
WriteCharBytes(bytes, '%', e);
}
continue;
}
if (ch == '+')
WriteCharBytes(bytes, ' ', e);
else
WriteCharBytes(bytes, ch, e);
}
byte[] buf = bytes.ToArray();
bytes = null;
return e.GetString(buf);
}
static int GetInt(byte b)
{
char c = (char)b;
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return -1;
}
static int GetChar(string str, int offset, int length)
{
int val = 0;
int end = length + offset;
for (int i = offset; i < end; i++)
{
char c = str[i];
if (c > 127)
return -1;
int current = GetInt((byte)c);
if (current == -1)
return -1;
val = (val << 4) + current;
}
return val;
}
static bool TryConvertKeyToEntity(string key, out char value)
{
var token = CalculateKeyValue(key);
if (token == 0)
{
value = '\0';
return false;
}
var idx = Array.BinarySearch(entities, token);
if (idx < 0)
{
value = '\0';
return false;
}
value = entities_values[idx];
return true;
}
static long CalculateKeyValue(string s)
{
if (s.Length > 8)
return 0;
long key = 0;
for (int i = 0; i < s.Length; ++i)
{
long ch = s[i];
if (ch > 'z' || ch < '0')
return 0;
key |= ch << ((7 - i) * 8);
}
return key;
}
/// <summary>
/// Decodes an HTML-encoded string and returns the decoded string.
/// </summary>
/// <param name="s">The HTML string to decode. </param>
/// <returns>The decoded text.</returns>
public static string HtmlDecode(string s)
{
if (s == null)
throw new ArgumentNullException("s");
if (s.IndexOf('&') == -1)
return s;
StringBuilder entity = new StringBuilder();
StringBuilder output = new StringBuilder();
int len = s.Length;
// 0 -> nothing,
// 1 -> right after '&'
// 2 -> between '&' and ';' but no '#'
// 3 -> '#' found after '&' and getting numbers
int state = 0;
int number = 0;
int digit_start = 0;
bool hex_number = false;
for (int i = 0; i < len; i++)
{
char c = s[i];
if (state == 0)
{
if (c == '&')
{
entity.Append(c);
state = 1;
}
else
{
output.Append(c);
}
continue;
}
if (c == '&')
{
state = 1;
if (digit_start > 0)
{
entity.Append(s, digit_start, i - digit_start);
digit_start = 0;
}
output.Append(entity.ToString());
entity.Length = 0;
entity.Append('&');
continue;
}
switch (state)
{
case 1:
if (c == ';')
{
state = 0;
output.Append(entity.ToString());
output.Append(c);
entity.Length = 0;
break;
}
number = 0;
hex_number = false;
if (c != '#')
{
state = 2;
}
else
{
state = 3;
}
entity.Append(c);
break;
case 2:
entity.Append(c);
if (c == ';')
{
string key = entity.ToString();
state = 0;
entity.Length = 0;
if (key.Length > 1)
{
var skey = key.Substring(1, key.Length - 2);
if (TryConvertKeyToEntity(skey, out c))
{
output.Append(c);
break;
}
}
output.Append(key);
}
break;
case 3:
if (c == ';')
{
if (number < 0x10000)
{
output.Append((char)number);
}
else
{
output.Append((char)(0xd800 + ((number - 0x10000) >> 10)));
output.Append((char)(0xdc00 + ((number - 0x10000) & 0x3ff)));
}
state = 0;
entity.Length = 0;
digit_start = 0;
break;
}
if (c == 'x' || c == 'X' && !hex_number)
{
digit_start = i;
hex_number = true;
break;
}
if (Char.IsDigit(c))
{
if (digit_start == 0)
digit_start = i;
number = number * (hex_number ? 16 : 10) + ((int)c - '0');
break;
}
if (hex_number)
{
if (c >= 'a' && c <= 'f')
{
number = number * 16 + 10 + ((int)c - 'a');
break;
}
if (c >= 'A' && c <= 'F')
{
number = number * 16 + 10 + ((int)c - 'A');
break;
}
}
state = 2;
if (digit_start > 0)
{
entity.Append(s, digit_start, i - digit_start);
digit_start = 0;
}
entity.Append(c);
break;
}
}
if (entity.Length > 0)
{
output.Append(entity);
}
else if (digit_start > 0)
{
output.Append(s, digit_start, s.Length - digit_start);
}
return output.ToString();
}
public static NameValueCollection ParseQueryString(string query)
{
return ParseQueryString(query, Encoding.UTF8);
}
public static NameValueCollection ParseQueryString(string query, Encoding encoding)
{
if (query == null)
throw new ArgumentNullException("query");
if (encoding == null)
throw new ArgumentNullException("encoding");
if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
return new NameValueCollection();
if (query[0] == '?')
query = query.Substring(1);
NameValueCollection result = new HttpQSCollection();
ParseQueryString(query, encoding, result);
return result;
}
internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
{
if (query.Length == 0)
return;
string decoded = HtmlDecode(query);
int decodedLength = decoded.Length;
int namePos = 0;
bool first = true;
while (namePos <= decodedLength)
{
int valuePos = -1, valueEnd = -1;
for (int q = namePos; q < decodedLength; q++)
{
if (valuePos == -1 && decoded[q] == '=')
{
valuePos = q + 1;
}
else if (decoded[q] == '&')
{
valueEnd = q;
break;
}
}
if (first)
{
first = false;
if (decoded[namePos] == '?')
namePos++;
}
string name, value;
if (valuePos == -1)
{
name = null;
valuePos = namePos;
}
else
{
name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
}
if (valueEnd < 0)
{
namePos = -1;
valueEnd = decoded.Length;
}
else
{
namePos = valueEnd + 1;
}
value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
result.Add(name, value);
if (namePos == -1)
break;
}
}
#endregion // Methods
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using Funq;
using MediaBrowser.Model.Logging;
using ServiceStack;
@ -236,7 +237,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
private NameValueCollectionWrapper queryString;
public INameValueCollection QueryString
{
get { return queryString ?? (queryString = new NameValueCollectionWrapper(HttpUtility.ParseQueryString(request.Url.Query))); }
get { return queryString ?? (queryString = new NameValueCollectionWrapper(MyHttpUtility.ParseQueryString(request.Url.Query))); }
}
private NameValueCollectionWrapper formData;

View File

@ -403,10 +403,7 @@ namespace MediaBrowser.Server.Implementations.Library
try
{
_dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user, new List<ItemFields>
{
ItemFields.PrimaryImageAspectRatio
});
_dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
}
catch (Exception ex)
{

View File

@ -1,59 +0,0 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
{
public class EmbyGuide : IListingsProvider
{
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
public EmbyGuide(IHttpClient httpClient, IJsonSerializer jsonSerializer)
{
_httpClient = httpClient;
_jsonSerializer = jsonSerializer;
}
public string Name
{
get { return "Emby Guide"; }
}
public string Type
{
get { return "emby"; }
}
public Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{
return GetListingsProvider(info.Country).GetProgramsAsync(info, channelNumber, startDateUtc, endDateUtc, cancellationToken);
}
public Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken)
{
return GetListingsProvider(info.Country).AddMetadata(info, channels, cancellationToken);
}
public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
{
return GetListingsProvider(info.Country).Validate(info, validateLogin, validateListings);
}
public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
{
return GetListingsProvider(country).GetLineups(country, location);
}
private IEmbyListingProvider GetListingsProvider(string country)
{
return new EmbyListingsNorthAmerica(_httpClient, _jsonSerializer);
}
}
}

View File

@ -1,366 +0,0 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
{
public class EmbyListingsNorthAmerica : IEmbyListingProvider
{
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
public EmbyListingsNorthAmerica(IHttpClient httpClient, IJsonSerializer jsonSerializer)
{
_httpClient = httpClient;
_jsonSerializer = jsonSerializer;
}
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{
channelNumber = NormalizeNumber(channelNumber);
var url = "https://data.emby.media/service/listings?id=" + info.ListingsId;
// Normalize
startDateUtc = startDateUtc.Date;
endDateUtc = startDateUtc.AddDays(7);
url += "&start=" + startDateUtc.ToString("s", CultureInfo.InvariantCulture) + "Z";
url += "&end=" + endDateUtc.ToString("s", CultureInfo.InvariantCulture) + "Z";
var response = await GetResponse<ListingInfo[]>(url).ConfigureAwait(false);
return response.Where(i => IncludeInResults(i, channelNumber)).Select(GetProgramInfo).OrderBy(i => i.StartDate);
}
private ProgramInfo GetProgramInfo(ListingInfo info)
{
var showType = info.showType ?? string.Empty;
var program = new ProgramInfo
{
Id = info.listingID.ToString(CultureInfo.InvariantCulture),
Name = GetStringValue(info.showName),
HomePageUrl = GetStringValue(info.webLink),
Overview = info.description,
IsHD = info.hd,
IsLive = info.live,
IsPremiere = info.seasonPremiere || info.seriesPremiere,
IsMovie = showType.IndexOf("Movie", StringComparison.OrdinalIgnoreCase) != -1,
IsKids = showType.IndexOf("Children", StringComparison.OrdinalIgnoreCase) != -1,
IsNews = showType.IndexOf("News", StringComparison.OrdinalIgnoreCase) != -1,
IsSports = showType.IndexOf("Sports", StringComparison.OrdinalIgnoreCase) != -1
};
if (!string.IsNullOrWhiteSpace(info.listDateTime))
{
program.StartDate = DateTime.ParseExact(info.listDateTime, "yyyy'-'MM'-'dd' 'HH':'mm':'ss", CultureInfo.InvariantCulture);
program.StartDate = DateTime.SpecifyKind(program.StartDate, DateTimeKind.Utc);
program.EndDate = program.StartDate.AddMinutes(info.duration);
}
if (info.starRating > 0)
{
program.CommunityRating = info.starRating*2;
}
if (!string.IsNullOrWhiteSpace(info.rating))
{
// They don't have dashes so try to normalize
program.OfficialRating = info.rating.Replace("TV", "TV-").Replace("--", "-");
var invalid = new[] { "N/A", "Approved", "Not Rated" };
if (invalid.Contains(program.OfficialRating, StringComparer.OrdinalIgnoreCase))
{
program.OfficialRating = null;
}
}
if (!string.IsNullOrWhiteSpace(info.year))
{
program.ProductionYear = int.Parse(info.year, CultureInfo.InvariantCulture);
}
if (info.showID > 0)
{
program.ShowId = info.showID.ToString(CultureInfo.InvariantCulture);
}
if (info.seriesID > 0)
{
program.SeriesId = info.seriesID.ToString(CultureInfo.InvariantCulture);
program.IsSeries = true;
program.IsRepeat = info.repeat;
program.EpisodeTitle = GetStringValue(info.episodeTitle);
if (string.Equals(program.Name, program.EpisodeTitle, StringComparison.OrdinalIgnoreCase))
{
program.EpisodeTitle = null;
}
}
if (info.starRating > 0)
{
program.CommunityRating = info.starRating * 2;
}
if (string.Equals(info.showName, "Movie", StringComparison.OrdinalIgnoreCase))
{
// Sometimes the movie title will be in here
if (!string.IsNullOrWhiteSpace(info.episodeTitle))
{
program.Name = info.episodeTitle;
}
}
return program;
}
private string GetStringValue(string s)
{
return string.IsNullOrWhiteSpace(s) ? null : s;
}
private bool IncludeInResults(ListingInfo info, string itemNumber)
{
if (string.Equals(itemNumber, NormalizeNumber(info.number), StringComparison.OrdinalIgnoreCase))
{
return true;
}
var channelNumber = info.channelNumber.ToString(CultureInfo.InvariantCulture);
if (info.subChannelNumber > 0)
{
channelNumber += "." + info.subChannelNumber.ToString(CultureInfo.InvariantCulture);
}
return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase);
}
public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken)
{
var response = await GetResponse<LineupDetailResponse>("https://data.emby.media/service/lineups?id=" + info.ListingsId).ConfigureAwait(false);
foreach (var channel in channels)
{
var station = response.stations.FirstOrDefault(i =>
{
var itemNumber = NormalizeNumber(channel.Number);
if (string.Equals(itemNumber, NormalizeNumber(i.number), StringComparison.OrdinalIgnoreCase))
{
return true;
}
var channelNumber = i.channelNumber.ToString(CultureInfo.InvariantCulture);
if (i.subChannelNumber > 0)
{
channelNumber += "." + i.subChannelNumber.ToString(CultureInfo.InvariantCulture);
}
return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase);
});
if (station != null)
{
//channel.Name = station.name;
if (!string.IsNullOrWhiteSpace(station.logoFilename))
{
channel.HasImage = true;
channel.ImageUrl = "http://cdn.tvpassport.com/image/station/100x100/" + station.logoFilename;
}
}
}
}
private string NormalizeNumber(string number)
{
return number.Replace('-', '.');
}
public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
{
return Task.FromResult(true);
}
public async Task<List<NameIdPair>> GetLineups(string country, string location)
{
// location = postal code
var response = await GetResponse<LineupInfo[]>("https://data.emby.media/service/lineups?postalCode=" + location).ConfigureAwait(false);
return response.Select(i => new NameIdPair
{
Name = GetName(i),
Id = i.lineupID
}).OrderBy(i => i.Name).ToList();
}
private string GetName(LineupInfo info)
{
var name = info.lineupName;
if (string.Equals(info.lineupType, "cab", StringComparison.OrdinalIgnoreCase))
{
name += " - Cable";
}
else if (string.Equals(info.lineupType, "sat", StringComparison.OrdinalIgnoreCase))
{
name += " - SAT";
}
else if (string.Equals(info.lineupType, "ota", StringComparison.OrdinalIgnoreCase))
{
name += " - OTA";
}
return name;
}
private async Task<T> GetResponse<T>(string url, Func<string, string> filter = null)
where T : class
{
using (var stream = await _httpClient.Get(new HttpRequestOptions
{
Url = url,
CacheLength = TimeSpan.FromDays(1),
CacheMode = CacheMode.Unconditional
}).ConfigureAwait(false))
{
using (var reader = new StreamReader(stream))
{
var path = await reader.ReadToEndAsync().ConfigureAwait(false);
using (var secondStream = await _httpClient.Get(new HttpRequestOptions
{
Url = "https://www.mb3admin.com" + path,
CacheLength = TimeSpan.FromDays(1),
CacheMode = CacheMode.Unconditional
}).ConfigureAwait(false))
{
return ParseResponse<T>(secondStream, filter);
}
}
}
}
private T ParseResponse<T>(Stream response, Func<string,string> filter)
{
using (var reader = new StreamReader(response))
{
var json = reader.ReadToEnd();
if (filter != null)
{
json = filter(json);
}
return _jsonSerializer.DeserializeFromString<T>(json);
}
}
private class LineupInfo
{
public string lineupID { get; set; }
public string lineupName { get; set; }
public string lineupType { get; set; }
public string providerID { get; set; }
public string providerName { get; set; }
public string serviceArea { get; set; }
public string country { get; set; }
}
private class Station
{
public string number { get; set; }
public int channelNumber { get; set; }
public int subChannelNumber { get; set; }
public int stationID { get; set; }
public string name { get; set; }
public string callsign { get; set; }
public string network { get; set; }
public string stationType { get; set; }
public int NTSC_TSID { get; set; }
public int DTV_TSID { get; set; }
public string webLink { get; set; }
public string logoFilename { get; set; }
}
private class LineupDetailResponse
{
public string lineupID { get; set; }
public string lineupName { get; set; }
public string lineupType { get; set; }
public string providerID { get; set; }
public string providerName { get; set; }
public string serviceArea { get; set; }
public string country { get; set; }
public List<Station> stations { get; set; }
}
private class ListingInfo
{
public string number { get; set; }
public int channelNumber { get; set; }
public int subChannelNumber { get; set; }
public int stationID { get; set; }
public string name { get; set; }
public string callsign { get; set; }
public string network { get; set; }
public string stationType { get; set; }
public string webLink { get; set; }
public string logoFilename { get; set; }
public int listingID { get; set; }
public string listDateTime { get; set; }
public int duration { get; set; }
public int showID { get; set; }
public int seriesID { get; set; }
public string showName { get; set; }
public string episodeTitle { get; set; }
public string episodeNumber { get; set; }
public int parts { get; set; }
public int partNum { get; set; }
public bool seriesPremiere { get; set; }
public bool seasonPremiere { get; set; }
public bool seriesFinale { get; set; }
public bool seasonFinale { get; set; }
public bool repeat { get; set; }
public bool @new { get; set; }
public string rating { get; set; }
public bool captioned { get; set; }
public bool educational { get; set; }
public bool blackWhite { get; set; }
public bool subtitled { get; set; }
public bool live { get; set; }
public bool hd { get; set; }
public bool descriptiveVideo { get; set; }
public bool inProgress { get; set; }
public string showTypeID { get; set; }
public int breakoutLevel { get; set; }
public string showType { get; set; }
public string year { get; set; }
public string guest { get; set; }
public string cast { get; set; }
public string director { get; set; }
public int starRating { get; set; }
public string description { get; set; }
public string league { get; set; }
public string team1 { get; set; }
public string team2 { get; set; }
public string @event { get; set; }
public string location { get; set; }
}
}
}

View File

@ -1,18 +0,0 @@
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
{
public interface IEmbyListingProvider
{
Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken);
Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings);
Task<List<NameIdPair>> GetLineups(string country, string location);
}
}

View File

@ -114,7 +114,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
var requestString = _jsonSerializer.SerializeToString(requestList);
_logger.Debug("Request string for schedules is: " + requestString);
httpOptions.RequestContent = requestString;
using (var response = await Post(httpOptions).ConfigureAwait(false))
using (var response = await Post(httpOptions, true, info).ConfigureAwait(false))
{
StreamReader reader = new StreamReader(response.Content);
string responseString = reader.ReadToEnd();
@ -138,7 +138,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
var requestBody = "[\"" + string.Join("\", \"", programsID) + "\"]";
httpOptions.RequestContent = requestBody;
using (var innerResponse = await Post(httpOptions).ConfigureAwait(false))
using (var innerResponse = await Post(httpOptions, true, info).ConfigureAwait(false))
{
StreamReader innerReader = new StreamReader(innerResponse.Content);
responseString = innerReader.ReadToEnd();
@ -148,7 +148,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
responseString);
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
var images = await GetImageForPrograms(programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID).ToList(), cancellationToken);
var images = await GetImageForPrograms(info, programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID).ToList(), cancellationToken);
var schedules = dailySchedules.SelectMany(d => d.programs);
foreach (ScheduleDirect.Program schedule in schedules)
@ -229,7 +229,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
httpOptions.RequestHeaders["token"] = token;
using (var response = await Get(httpOptions).ConfigureAwait(false))
using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
{
var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
_logger.Info("Found " + root.map.Count() + " channels on the lineup on ScheduleDirect");
@ -447,7 +447,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
return url;
}
private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(List<string> programIds,
private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
ListingsProviderInfo info,
List<string> programIds,
CancellationToken cancellationToken)
{
var imageIdString = "[";
@ -472,7 +474,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
TimeoutMs = 60000
};
List<ScheduleDirect.ShowImages> images;
using (var innerResponse2 = await Post(httpOptions).ConfigureAwait(false))
using (var innerResponse2 = await Post(httpOptions, true, info).ConfigureAwait(false))
{
images = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.ShowImages>>(
innerResponse2.Content);
@ -504,7 +506,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
try
{
using (Stream responce = await Get(options).ConfigureAwait(false))
using (Stream responce = await Get(options, false, info).ConfigureAwait(false))
{
var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
@ -606,30 +608,58 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
}
}
private async Task<HttpResponseInfo> Post(HttpRequestOptions options)
private async Task<HttpResponseInfo> Post(HttpRequestOptions options,
bool enableRetry,
ListingsProviderInfo providerInfo)
{
try
{
return await _httpClient.Post(options).ConfigureAwait(false);
}
catch
{
_tokens.Clear();
throw;
}
}
catch (HttpException ex)
{
_tokens.Clear();
if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500)
{
enableRetry = false;
}
if (!enableRetry) {
throw;
}
}
var newToken = await GetToken (providerInfo, options.CancellationToken).ConfigureAwait (false);
options.RequestHeaders ["token"] = newToken;
return await Post (options, false, providerInfo).ConfigureAwait (false);
}
private async Task<Stream> Get(HttpRequestOptions options)
private async Task<Stream> Get(HttpRequestOptions options,
bool enableRetry,
ListingsProviderInfo providerInfo)
{
try
{
return await _httpClient.Get(options).ConfigureAwait(false);
}
catch
{
_tokens.Clear();
throw;
}
}
catch (HttpException ex)
{
_tokens.Clear();
if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500)
{
enableRetry = false;
}
if (!enableRetry) {
throw;
}
}
var newToken = await GetToken (providerInfo, options.CancellationToken).ConfigureAwait (false);
options.RequestHeaders ["token"] = newToken;
return await Get (options, false, providerInfo).ConfigureAwait (false);
}
private async Task<string> GetTokenInternal(string username, string password,
@ -646,7 +676,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
//_logger.Info("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
// httpOptions.RequestContent);
using (var responce = await Post(httpOptions).ConfigureAwait(false))
using (var responce = await Post(httpOptions, false, null).ConfigureAwait(false))
{
var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Token>(responce.Content);
if (root.message == "OK")
@ -728,7 +758,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
try
{
using (var response = await Get(options).ConfigureAwait(false))
using (var response = await Get(options, false, null).ConfigureAwait(false))
{
var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);

View File

@ -231,10 +231,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
dto.ImageTags[ImageType.Primary] = imageTag;
_dtoService.AttachPrimaryImageAspectRatio(dto, info, new List<ItemFields>
{
ItemFields.PrimaryImageAspectRatio
});
_dtoService.AttachPrimaryImageAspectRatio(dto, info);
}
if (currentProgram != null)

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
{
public class SatIp : BaseTunerHost
{
public SatIp(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder)
: base(config, logger, jsonSerializer, mediaEncoder)
{
}
protected override Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public override string Type
{
get { return "SatIp"; }
}
protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
protected override Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
protected override bool IsValidChannelId(string channelId)
{
throw new NotImplementedException();
}
}
}

View File

@ -71,7 +71,7 @@
"ViewTypeTvShowSeries": "S\u00e8ries:",
"ViewTypeTvGenres": "G\u00e8neres",
"ViewTypeTvFavoriteSeries": "S\u00e8ries Preferides",
"ViewTypeTvFavoriteEpisodes": "Episodis preferits",
"ViewTypeTvFavoriteEpisodes": "Episodis Preferits",
"ViewTypeMovieResume": "Resume",
"ViewTypeMovieLatest": "Darrers",
"ViewTypeMovieMovies": "Pel\u00b7l\u00edcules",
@ -173,5 +173,5 @@
"HeaderWriter": "Escriptors",
"HeaderParentalRatings": "Parental Ratings",
"HeaderCommunityRatings": "Qualificacions de la comunitat",
"StartupEmbyServerIsLoading": "El servidor d'Emby s'est\u00e0 carregant. Si us plau, torneu-ho a provar de nou en breu."
"StartupEmbyServerIsLoading": "El servidor d'Emby s'est&agrave; carregant. Si et plau, tornau-ho a provar de nou en breu."
}

View File

@ -43,7 +43,7 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="Interfaces.IO">
<HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
@ -52,15 +52,18 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MediaBrowser.Naming.1.0.0.41\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="Patterns.Logging">
<HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Api.Swagger">
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath>
</Reference>
<Reference Include="SocketHttpListener, Version=1.0.5754.42244, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="SocketHttpListener, Version=1.0.5840.28948, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SocketHttpListener.1.0.0.10\lib\net45\SocketHttpListener.dll</HintPath>
<HintPath>..\packages\SocketHttpListener.1.0.0.25\lib\net45\SocketHttpListener.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -70,6 +73,7 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
@ -94,9 +98,6 @@
<Reference Include="Mono.Nat">
<HintPath>..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
@ -160,6 +161,7 @@
<Compile Include="HttpServer\ServerLogFactory.cs" />
<Compile Include="HttpServer\ServerLogger.cs" />
<Compile Include="HttpServer\Security\SessionContext.cs" />
<Compile Include="HttpServer\SocketSharp\HttpUtility.cs" />
<Compile Include="HttpServer\SocketSharp\SharpWebSocket.cs" />
<Compile Include="HttpServer\StreamWriter.cs" />
<Compile Include="HttpServer\SwaggerService.cs" />
@ -216,9 +218,6 @@
<Compile Include="LiveTv\EmbyTV\RecordingHelper.cs" />
<Compile Include="LiveTv\EmbyTV\SeriesTimerManager.cs" />
<Compile Include="LiveTv\EmbyTV\TimerManager.cs" />
<Compile Include="LiveTv\Listings\Emby\EmbyListings.cs" />
<Compile Include="LiveTv\Listings\Emby\EmbyListingsNorthAmerica.cs" />
<Compile Include="LiveTv\Listings\Emby\IEmbyListingProvider.cs" />
<Compile Include="LiveTv\Listings\SchedulesDirect.cs" />
<Compile Include="LiveTv\Listings\XmlTv.cs" />
<Compile Include="LiveTv\LiveTvConfigurationFactory.cs" />

View File

@ -72,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
try
{
await _installationManager.InstallPackage(i, new Progress<double>(), cancellationToken).ConfigureAwait(false);
await _installationManager.InstallPackage(i, true, new Progress<double>(), cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
<package id="MediaBrowser.Naming" version="1.0.0.41" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
<package id="morelinq" version="1.1.1" targetFramework="net45" />
<package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
<package id="SocketHttpListener" version="1.0.0.10" targetFramework="net45" />
<package id="SocketHttpListener" version="1.0.0.25" targetFramework="net45" />
</packages>

View File

@ -54,7 +54,7 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

View File

@ -82,7 +82,7 @@ namespace MediaBrowser.Server.Mono
var nativeApp = new NativeApp(options);
_appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "MBServer.Mono", nativeApp);
_appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "emby.mono.zip", nativeApp);
if (options.ContainsOption("-v"))
{

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
<package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
<package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages>

View File

@ -91,14 +91,17 @@ using MediaBrowser.Server.Startup.Common.Migrations;
using MediaBrowser.WebDashboard.Api;
using MediaBrowser.XbmcMetadata.Providers;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Common.Implementations.Updates;
namespace MediaBrowser.Server.Startup.Common
{
@ -204,9 +207,10 @@ namespace MediaBrowser.Server.Startup.Common
private IPlaylistManager PlaylistManager { get; set; }
private readonly StartupOptions _startupOptions;
private readonly string _remotePackageName;
private readonly string _releaseAssetFilename;
internal INativeApp NativeApp { get; set; }
private Timer _ipAddressCacheTimer;
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationHost" /> class.
@ -215,21 +219,23 @@ namespace MediaBrowser.Server.Startup.Common
/// <param name="logManager">The log manager.</param>
/// <param name="options">The options.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="remotePackageName">Name of the remote package.</param>
/// <param name="releaseAssetFilename">The release asset filename.</param>
/// <param name="nativeApp">The native application.</param>
public ApplicationHost(ServerApplicationPaths applicationPaths,
ILogManager logManager,
StartupOptions options,
IFileSystem fileSystem,
string remotePackageName,
string releaseAssetFilename,
INativeApp nativeApp)
: base(applicationPaths, logManager, fileSystem)
{
_startupOptions = options;
_remotePackageName = remotePackageName;
_releaseAssetFilename = releaseAssetFilename;
NativeApp = nativeApp;
SetBaseExceptionMessage();
_ipAddressCacheTimer = new Timer(OnCacheClearTimerFired, null, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3));
}
private Version _version;
@ -316,6 +322,7 @@ namespace MediaBrowser.Server.Startup.Common
{
await base.RunStartupTasks().ConfigureAwait(false);
Logger.Info("ServerId: {0}", SystemId);
Logger.Info("Core startup complete");
HttpServer.GlobalResponse = null;
@ -436,7 +443,7 @@ namespace MediaBrowser.Server.Startup.Common
RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager));
HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, "Emby", "web/index.html");
HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, "Emby", "web/index.html");
HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
RegisterSingleInstance(HttpServer, false);
progress.Report(10);
@ -515,7 +522,7 @@ namespace MediaBrowser.Server.Startup.Common
SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager);
RegisterSingleInstance(SubtitleManager);
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, this));
RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, this, NetworkManager));
ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository);
RegisterSingleInstance(ChapterManager);
@ -963,6 +970,10 @@ namespace MediaBrowser.Server.Startup.Common
{
get
{
if (!ServerConfigurationManager.Configuration.EnableAutoUpdate)
{
return false;
}
#if DEBUG
return false;
#endif
@ -1108,14 +1119,14 @@ namespace MediaBrowser.Server.Startup.Common
try
{
// Return the first matched address, if found, or the first known local address
var address = LocalIpAddress;
var address = LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i));
if (!string.IsNullOrWhiteSpace(address))
if (address != null)
{
address = GetLocalApiUrl(address);
return GetLocalApiUrl(address.ToString());
}
return address;
return null;
}
catch (Exception ex)
{
@ -1133,40 +1144,71 @@ namespace MediaBrowser.Server.Startup.Common
HttpPort.ToString(CultureInfo.InvariantCulture));
}
public string LocalIpAddress
{
get
{
return HttpServerIpAddresses.FirstOrDefault();
}
}
private IEnumerable<string> HttpServerIpAddresses
public List<IPAddress> LocalIpAddresses
{
get
{
var localAddresses = NetworkManager.GetLocalIpAddresses()
.Where(IsIpAddressValid)
.ToList();
var httpServerAddresses = HttpServer.LocalEndPoints
.Select(i => i.Split(':').FirstOrDefault())
.Where(i => !string.IsNullOrEmpty(i))
.ToList();
// Cross-check the local ip addresses with addresses that have been received on with the http server
var matchedAddresses = httpServerAddresses
.Where(i => localAddresses.Contains(i, StringComparer.OrdinalIgnoreCase))
.ToList();
if (matchedAddresses.Count == 0)
{
return localAddresses;
}
return matchedAddresses;
return localAddresses;
}
}
private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
private bool IsIpAddressValid(IPAddress address)
{
if (IPAddress.IsLoopback(address))
{
return true;
}
var apiUrl = GetLocalApiUrl(address.ToString());
apiUrl += "/system/ping";
bool cachedResult;
if (_validAddressResults.TryGetValue(apiUrl, out cachedResult))
{
return cachedResult;
}
try
{
using (var response = HttpClient.SendAsync(new HttpRequestOptions
{
Url = apiUrl,
LogErrorResponseBody = false,
LogErrors = false,
LogRequest = false
}, "POST").Result)
{
using (var reader = new StreamReader(response.Content))
{
var result = reader.ReadToEnd();
var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
_validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid);
return valid;
}
}
}
catch
{
Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
_validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
return false;
}
}
private void OnCacheClearTimerFired(object state)
{
_validAddressResults.Clear();
}
public string FriendlyName
{
get
@ -1263,25 +1305,23 @@ namespace MediaBrowser.Server.Startup.Common
/// <returns>Task{CheckForUpdateResult}.</returns>
public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress)
{
var availablePackages = await InstallationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
var cacheLength = TimeSpan.FromHours(3);
var updateLevel = ConfigurationManager.CommonConfiguration.SystemUpdateLevel;
var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, _remotePackageName, null, ApplicationVersion, ConfigurationManager.CommonConfiguration.SystemUpdateLevel);
if (updateLevel == PackageVersionClass.Beta)
{
cacheLength = TimeSpan.FromHours(1);
}
else if (updateLevel == PackageVersionClass.Dev)
{
cacheLength = TimeSpan.FromMinutes(5);
}
var versionObject = version == null || string.IsNullOrWhiteSpace(version.versionStr) ? null : new Version(version.versionStr);
var isUpdateAvailable = versionObject != null && versionObject > ApplicationVersion;
var result = versionObject != null ?
new CheckForUpdateResult { AvailableVersion = versionObject.ToString(), IsUpdateAvailable = isUpdateAvailable, Package = version } :
new CheckForUpdateResult { AvailableVersion = ApplicationVersion.ToString(), IsUpdateAvailable = false };
var result = await new GithubUpdater(HttpClient, JsonSerializer, cacheLength).CheckForUpdateResult("MediaBrowser", "Emby", ApplicationVersion, updateLevel, _releaseAssetFilename,
"MBServer", "Mbserver.zip", cancellationToken).ConfigureAwait(false);
HasUpdateAvailable = result.IsUpdateAvailable;
if (result.IsUpdateAvailable)
{
Logger.Info("New application version is available: {0}", result.AvailableVersion);
}
return result;
}
@ -1294,7 +1334,7 @@ namespace MediaBrowser.Server.Startup.Common
/// <returns>Task.</returns>
public override async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress)
{
await InstallationManager.InstallPackage(package, progress, cancellationToken).ConfigureAwait(false);
await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false);
HasUpdateAvailable = false;

View File

@ -33,7 +33,7 @@
<ItemGroup>
<Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
<HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>

Some files were not shown because too many files have changed in this diff Show More