diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs
index 3ed4f650f..6cbf39758 100644
--- a/Emby.Common.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs
@@ -392,10 +392,27 @@ namespace Emby.Common.Implementations.IO
if (_supportsAsyncFileStreams && isAsync)
{
- return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144, true);
+ return GetFileStream(path, mode, access, share, FileOpenOptions.Asynchronous);
}
- return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144);
+ return GetFileStream(path, mode, access, share, FileOpenOptions.None);
+ }
+
+ public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions)
+ {
+ if (_sharpCifsFileSystem.IsEnabledForPath(path))
+ {
+ return _sharpCifsFileSystem.GetFileStream(path, mode, access, share);
+ }
+
+ var defaultBufferSize = 4096;
+ return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), defaultBufferSize, GetFileOptions(fileOpenOptions));
+ }
+
+ private FileOptions GetFileOptions(FileOpenOptions mode)
+ {
+ var val = (int)mode;
+ return (FileOptions)val;
}
private FileMode GetFileMode(FileOpenMode mode)
diff --git a/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
new file mode 100644
index 000000000..b2a0e9607
--- /dev/null
+++ b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
@@ -0,0 +1,65 @@
+
+
+
+
+ 11.0
+ Debug
+ AnyCPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}
+ Library
+ Properties
+ Emby.Drawing.Skia
+ Emby.Drawing.Skia
+ en-US
+ 512
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ Profile7
+ v4.5
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+ {9142eefa-7570-41e1-bfcc-468bb571af2f}
+ MediaBrowser.Common
+
+
+ {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}
+ MediaBrowser.Controller
+
+
+ {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}
+ MediaBrowser.Model
+
+
+
+
+ Properties\SharedVersion.cs
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Emby.Drawing.Skia/Properties/AssemblyInfo.cs b/Emby.Drawing.Skia/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..c0dc7c5b4
--- /dev/null
+++ b/Emby.Drawing.Skia/Properties/AssemblyInfo.cs
@@ -0,0 +1,25 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Emby.Drawing.Skia")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Emby.Drawing.Skia")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
\ No newline at end of file
diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs
index 78bdc1189..6a3881caf 100644
--- a/Emby.Server.Core/ApplicationHost.cs
+++ b/Emby.Server.Core/ApplicationHost.cs
@@ -1132,7 +1132,8 @@ namespace Emby.Server.Core
// Custom cert
return new CertificateInfo
{
- Path = ServerConfigurationManager.Configuration.CertificatePath
+ Path = ServerConfigurationManager.Configuration.CertificatePath,
+ Password = ServerConfigurationManager.Configuration.CertificatePassword
};
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index ee5245a69..5e96eda94 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -228,7 +228,8 @@ namespace Emby.Server.Implementations.HttpServer
_streamFactory,
_enableDualModeSockets,
GetRequest,
- _fileSystem);
+ _fileSystem,
+ _environment);
}
private IHttpRequest GetRequest(HttpListenerContext httpContext)
@@ -452,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
var date = DateTime.Now;
var httpRes = httpReq.Response;
bool enableLog = false;
+ bool logHeaders = false;
string urlToLog = null;
string remoteIp = null;
@@ -490,13 +492,14 @@ namespace Emby.Server.Implementations.HttpServer
var urlString = url.OriginalString;
enableLog = EnableLogging(urlString, localPath);
urlToLog = urlString;
+ logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
if (enableLog)
{
urlToLog = GetUrlToLog(urlString);
remoteIp = httpReq.RemoteIp;
- LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent);
+ LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent, logHeaders ? httpReq.Headers : null);
}
if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
@@ -611,7 +614,7 @@ namespace Emby.Server.Implementations.HttpServer
var duration = DateTime.Now - date;
- LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration);
+ LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration, logHeaders ? httpRes.Headers : null);
}
}
}
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 687bd62b0..0af88595f 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -353,31 +353,28 @@ namespace Emby.Server.Implementations.HttpServer
///
/// Pres the process optimized result.
///
- /// The request context.
- /// The responseHeaders.
- /// The cache key.
- /// The cache key string.
- /// The last date modified.
- /// Duration of the cache.
- /// Type of the content.
- /// System.Object.
private object GetCachedResult(IRequest requestContext, IDictionary responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
{
responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString);
- if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
+ var noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
+
+ if (!noCache)
{
- AddAgeHeader(responseHeaders, lastDateModified);
- AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
+ if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
+ {
+ AddAgeHeader(responseHeaders, lastDateModified);
+ AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration, noCache);
- var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
+ var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
- AddResponseHeaders(result, responseHeaders);
+ AddResponseHeaders(result, responseHeaders);
- return result;
+ return result;
+ }
}
- AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration);
+ AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration, noCache);
return null;
}
@@ -673,11 +670,7 @@ namespace Emby.Server.Implementations.HttpServer
///
/// Adds the caching responseHeaders.
///
- /// The responseHeaders.
- /// The cache key.
- /// The last date modified.
- /// Duration of the cache.
- private void AddCachingHeaders(IDictionary responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
+ private void AddCachingHeaders(IDictionary responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, bool noCache)
{
// Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
// https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
@@ -687,11 +680,11 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
}
- if (cacheDuration.HasValue)
+ if (!noCache && cacheDuration.HasValue)
{
responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
}
- else if (!string.IsNullOrEmpty(cacheKey))
+ else if (!noCache && !string.IsNullOrEmpty(cacheKey))
{
responseHeaders["Cache-Control"] = "public";
}
@@ -701,18 +694,15 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
}
- AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
+ AddExpiresHeader(responseHeaders, cacheKey, cacheDuration, noCache);
}
///
/// Adds the expires header.
///
- /// The responseHeaders.
- /// The cache key.
- /// Duration of the cache.
- private void AddExpiresHeader(IDictionary responseHeaders, string cacheKey, TimeSpan? cacheDuration)
+ private void AddExpiresHeader(IDictionary responseHeaders, string cacheKey, TimeSpan? cacheDuration, bool noCache)
{
- if (cacheDuration.HasValue)
+ if (!noCache && cacheDuration.HasValue)
{
responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
}
diff --git a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
index 8fc92a09a..f0e75eea4 100644
--- a/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
+++ b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
@@ -1,6 +1,8 @@
using MediaBrowser.Model.Logging;
using System;
using System.Globalization;
+using System.Linq;
+using MediaBrowser.Model.Services;
using SocketHttpListener.Net;
namespace Emby.Server.Implementations.HttpServer
@@ -19,9 +21,18 @@ namespace Emby.Server.Implementations.HttpServer
logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
}
- public static void LogRequest(ILogger logger, string url, string method, string userAgent)
+ public static void LogRequest(ILogger logger, string url, string method, string userAgent, QueryParamCollection headers)
{
- logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty);
+ if (headers == null)
+ {
+ logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty);
+ }
+ else
+ {
+ var headerText = string.Join(", ", headers.Select(i => i.Name + "=" + i.Value).ToArray());
+
+ logger.Info("HTTP {0} {1}. {2}", method, url, headerText);
+ }
}
///
@@ -32,12 +43,13 @@ namespace Emby.Server.Implementations.HttpServer
/// The URL.
/// The end point.
/// The duration.
- public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration)
+ public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration, QueryParamCollection headers)
{
var durationMs = duration.TotalMilliseconds;
var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms";
- logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url);
+ var headerText = headers == null ? string.Empty : "Headers: " + string.Join(", ", headers.Where(i => i.Name.IndexOf("Access-", StringComparison.OrdinalIgnoreCase) == -1).Select(i => i.Name + "=" + i.Value).ToArray());
+ logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4} {5}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url, headerText);
}
}
}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
index b11b2fe88..682fa7a0b 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
@@ -10,6 +10,7 @@ using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
@@ -30,8 +31,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private readonly IFileSystem _fileSystem;
private readonly Func _httpRequestFactory;
private readonly bool _enableDualMode;
+ private readonly IEnvironmentInfo _environment;
- public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func httpRequestFactory, IFileSystem fileSystem)
+ public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func httpRequestFactory, IFileSystem fileSystem, IEnvironmentInfo environment)
{
_logger = logger;
_certificate = certificate;
@@ -44,6 +46,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_enableDualMode = enableDualMode;
_httpRequestFactory = httpRequestFactory;
_fileSystem = fileSystem;
+ _environment = environment;
}
public Action ErrorHandler { get; set; }
@@ -56,7 +59,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
public void Start(IEnumerable urlPrefixes)
{
if (_listener == null)
- _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem);
+ _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem, _environment);
_listener.EnableDualMode = _enableDualMode;
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
index fd30b227f..9e58ee57c 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
@@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
using SocketHttpListener.Net;
using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
@@ -66,6 +67,14 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_response.AddHeader(name, value);
}
+ public QueryParamCollection Headers
+ {
+ get
+ {
+ return _response.Headers;
+ }
+ }
+
public string GetHeader(string name)
{
return _response.Headers[name];
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index 2677f7b2a..b2ec84a82 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Images
{
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
}
- if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre)
+ if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre || item is PhotoAlbum)
{
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index daec00e10..67921ab34 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -671,12 +671,15 @@ namespace MediaBrowser.Api.Playback
request.AudioCodec = EncodingHelper.InferAudioCodec(url);
}
+ var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
+ string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
+
var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
{
Request = request,
RequestedUrl = url,
UserAgent = Request.UserAgent,
- EnableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params)
+ EnableDlnaHeaders = enableDlnaHeaders
};
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 838111a38..60bbf6240 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -55,6 +55,7 @@ namespace MediaBrowser.Model.Configuration
///
/// The value pointing to the file system where the ssl certiifcate is located..
public string CertificatePath { get; set; }
+ public string CertificatePassword { get; set; }
///
/// Gets or sets a value indicating whether this instance is port authorized.
diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs
index 26de9332e..92112f4ae 100644
--- a/MediaBrowser.Model/IO/IFileSystem.cs
+++ b/MediaBrowser.Model/IO/IFileSystem.cs
@@ -108,6 +108,8 @@ namespace MediaBrowser.Model.IO
/// FileStream.
Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false);
+ Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions);
+
///
/// Opens the read.
///
@@ -402,4 +404,46 @@ namespace MediaBrowser.Model.IO
ReadWrite = 3
}
+ //
+ // Summary:
+ // Represents advanced options for creating a System.IO.FileStream object.
+ [Flags]
+ public enum FileOpenOptions
+ {
+ //
+ // Summary:
+ // Indicates that the system should write through any intermediate cache and go
+ // directly to disk.
+ WriteThrough = int.MinValue,
+ //
+ // Summary:
+ // Indicates that no additional options should be used when creating a System.IO.FileStream
+ // object.
+ None = 0,
+ //
+ // Summary:
+ // Indicates that a file is encrypted and can be decrypted only by using the same
+ // user account used for encryption.
+ Encrypted = 16384,
+ //
+ // Summary:
+ // Indicates that a file is automatically deleted when it is no longer in use.
+ DeleteOnClose = 67108864,
+ //
+ // Summary:
+ // Indicates that the file is to be accessed sequentially from beginning to end.
+ // The system can use this as a hint to optimize file caching. If an application
+ // moves the file pointer for random access, optimum caching may not occur; however,
+ // correct operation is still guaranteed.
+ SequentialScan = 134217728,
+ //
+ // Summary:
+ // Indicates that the file is accessed randomly. The system can use this as a hint
+ // to optimize file caching.
+ RandomAccess = 268435456,
+ //
+ // Summary:
+ // Indicates that a file can be used for asynchronous reading and writing.
+ Asynchronous = 1073741824
+ }
}
diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs
index 115ba25ce..f056c7410 100644
--- a/MediaBrowser.Model/Services/IRequest.cs
+++ b/MediaBrowser.Model/Services/IRequest.cs
@@ -155,6 +155,8 @@ namespace MediaBrowser.Model.Services
//Add Metadata to Response
Dictionary Items { get; }
+ QueryParamCollection Headers { get; }
+
Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs
index f544c09dc..d5494c21f 100644
--- a/MediaBrowser.Providers/Manager/ProviderUtils.cs
+++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs
@@ -200,6 +200,7 @@ namespace MediaBrowser.Providers.Manager
MergeCriticRating(source, target, lockedFields, replaceData);
MergeAwards(source, target, lockedFields, replaceData);
MergeTrailers(source, target, lockedFields, replaceData);
+ MergeVideoInfo(source, target, lockedFields, replaceData);
if (mergeMetadataSettings)
{
@@ -307,5 +308,19 @@ namespace MediaBrowser.Providers.Manager
}
}
}
+
+ private static void MergeVideoInfo(BaseItem source, BaseItem target, List lockedFields, bool replaceData)
+ {
+ var sourceCast = source as Video;
+ var targetCast = target as Video;
+
+ if (sourceCast != null && targetCast != null)
+ {
+ if (replaceData || targetCast.Video3DFormat == null)
+ {
+ targetCast.Video3DFormat = sourceCast.Video3DFormat;
+ }
+ }
+ }
}
}
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index b9933969f..219beeab1 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -80,6 +80,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Dr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Skia", "Emby.Drawing.Skia\Emby.Drawing.Skia.csproj", "{2312DA6D-FF86-4597-9777-BCEEC32D96DD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1099,6 +1101,46 @@ Global
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x64.Build.0 = Release|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.ActiveCfg = Release|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|Win32.Build.0 = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|x64.Build.0 = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Debug|x86.Build.0 = Debug|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Win32.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|Win32.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x64.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release Mono|x86.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Win32.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|Win32.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|x64.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|x64.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|x86.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Release|x86.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Win32.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|Win32.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x64.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x64.Build.0 = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x86.ActiveCfg = Release|Any CPU
+ {2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SocketHttpListener.Portable/Net/EndPointListener.cs b/SocketHttpListener.Portable/Net/EndPointListener.cs
index 4f1a17fc0..2106bbec5 100644
--- a/SocketHttpListener.Portable/Net/EndPointListener.cs
+++ b/SocketHttpListener.Portable/Net/EndPointListener.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
@@ -33,8 +34,9 @@ namespace SocketHttpListener.Net
private readonly ITextEncoding _textEncoding;
private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly IFileSystem _fileSystem;
+ private readonly IEnvironmentInfo _environment;
- public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
+ public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
{
this.listener = listener;
_logger = logger;
@@ -44,6 +46,7 @@ namespace SocketHttpListener.Net
_memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding;
_fileSystem = fileSystem;
+ _environment = environment;
this.secure = secure;
this.cert = cert;
@@ -109,7 +112,7 @@ namespace SocketHttpListener.Net
return;
}
- HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding, _fileSystem).ConfigureAwait(false);
+ HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding, _fileSystem, _environment).ConfigureAwait(false);
//_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId);
lock (listener.unregistered)
diff --git a/SocketHttpListener.Portable/Net/EndPointManager.cs b/SocketHttpListener.Portable/Net/EndPointManager.cs
index 11f774915..6a00ed360 100644
--- a/SocketHttpListener.Portable/Net/EndPointManager.cs
+++ b/SocketHttpListener.Portable/Net/EndPointManager.cs
@@ -106,7 +106,7 @@ namespace SocketHttpListener.Net
}
else
{
- epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem);
+ epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem, listener.EnvironmentInfo);
p[port] = epl;
}
diff --git a/SocketHttpListener.Portable/Net/HttpConnection.cs b/SocketHttpListener.Portable/Net/HttpConnection.cs
index ac8ada486..65e7470f7 100644
--- a/SocketHttpListener.Portable/Net/HttpConnection.cs
+++ b/SocketHttpListener.Portable/Net/HttpConnection.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
@@ -41,8 +42,9 @@ namespace SocketHttpListener.Net
private readonly ITextEncoding _textEncoding;
private readonly IStreamFactory _streamFactory;
private readonly IFileSystem _fileSystem;
+ private readonly IEnvironmentInfo _environment;
- private HttpConnection(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
+ private HttpConnection(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
{
_logger = logger;
this.sock = sock;
@@ -53,6 +55,7 @@ namespace SocketHttpListener.Net
_memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding;
_fileSystem = fileSystem;
+ _environment = environment;
_streamFactory = streamFactory;
}
@@ -84,9 +87,9 @@ namespace SocketHttpListener.Net
Init();
}
- public static async Task Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
+ public static async Task Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment)
{
- var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding, fileSystem);
+ var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding, fileSystem, environment);
await connection.InitStream().ConfigureAwait(false);
@@ -217,7 +220,7 @@ namespace SocketHttpListener.Net
{
var supportsDirectSocketAccess = !context.Response.SendChunked && !isExpect100Continue && !secure;
- o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess, _logger);
+ o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess, _logger, _environment);
}
else
{
diff --git a/SocketHttpListener.Portable/Net/HttpListener.cs b/SocketHttpListener.Portable/Net/HttpListener.cs
index c2e7acd8e..b3e01425c 100644
--- a/SocketHttpListener.Portable/Net/HttpListener.cs
+++ b/SocketHttpListener.Portable/Net/HttpListener.cs
@@ -8,6 +8,7 @@ using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
@@ -22,6 +23,7 @@ namespace SocketHttpListener.Net
internal ITextEncoding TextEncoding { get; private set; }
internal IMemoryStreamFactory MemoryStreamFactory { get; private set; }
internal INetworkManager NetworkManager { get; private set; }
+ internal IEnvironmentInfo EnvironmentInfo { get; private set; }
public bool EnableDualMode { get; set; }
@@ -40,7 +42,7 @@ namespace SocketHttpListener.Net
public Action OnContext { get; set; }
- public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem)
+ public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
{
_logger = logger;
CryptoProvider = cryptoProvider;
@@ -50,19 +52,20 @@ namespace SocketHttpListener.Net
TextEncoding = textEncoding;
MemoryStreamFactory = memoryStreamFactory;
FileSystem = fileSystem;
+ EnvironmentInfo = environmentInfo;
prefixes = new HttpListenerPrefixCollection(logger, this);
registry = new Dictionary();
connections = new Dictionary();
auth_schemes = AuthenticationSchemes.Anonymous;
}
- public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem)
- :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem)
+ public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
+ :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo)
{
}
- public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem)
- : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem)
+ public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
+ : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo)
{
_certificate = certificate;
}
diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs
index dea4049d5..9552fe8ca 100644
--- a/SocketHttpListener.Portable/Net/ResponseStream.cs
+++ b/SocketHttpListener.Portable/Net/ResponseStream.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
@@ -28,8 +29,9 @@ namespace SocketHttpListener.Net
private readonly IAcceptSocket _socket;
private readonly bool _supportsDirectSocketAccess;
private readonly ILogger _logger;
+ private readonly IEnvironmentInfo _environment;
- internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IAcceptSocket socket, bool supportsDirectSocketAccess, ILogger logger)
+ internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IAcceptSocket socket, bool supportsDirectSocketAccess, ILogger logger, IEnvironmentInfo environment)
{
this.response = response;
_memoryStreamFactory = memoryStreamFactory;
@@ -38,6 +40,7 @@ namespace SocketHttpListener.Net
_socket = socket;
_supportsDirectSocketAccess = supportsDirectSocketAccess;
_logger = logger;
+ _environment = environment;
this.stream = stream;
}
@@ -344,7 +347,20 @@ namespace SocketHttpListener.Net
private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, true))
+ var allowAsync = _environment.OperatingSystem != OperatingSystem.Windows;
+
+ var fileOpenOptions = offset > 0
+ ? FileOpenOptions.RandomAccess
+ : FileOpenOptions.SequentialScan;
+
+ if (allowAsync)
+ {
+ fileOpenOptions |= FileOpenOptions.Asynchronous;
+ }
+
+ // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+
+ using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions))
{
if (offset > 0)
{
@@ -355,11 +371,53 @@ namespace SocketHttpListener.Net
if (count > 0)
{
- await CopyToInternalAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
+ if (allowAsync)
+ {
+ await CopyToInternalAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ await CopyToInternalAsyncWithSyncRead(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
+ }
}
else
{
- await fs.CopyToAsync(targetStream, 81920, cancellationToken).ConfigureAwait(false);
+ if (allowAsync)
+ {
+ await fs.CopyToAsync(targetStream, 81920, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ fs.CopyTo(targetStream, 81920);
+ }
+ }
+ }
+ }
+
+ private static async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
+ {
+ var array = new byte[81920];
+ int bytesRead;
+
+ while ((bytesRead = source.Read(array, 0, array.Length)) != 0)
+ {
+ if (bytesRead == 0)
+ {
+ break;
+ }
+
+ var bytesToWrite = Math.Min(bytesRead, copyLength);
+
+ if (bytesToWrite > 0)
+ {
+ await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+ }
+
+ copyLength -= bytesToWrite;
+
+ if (copyLength <= 0)
+ {
+ break;
}
}
}
@@ -368,7 +426,7 @@ namespace SocketHttpListener.Net
{
var array = new byte[81920];
int bytesRead;
-
+
while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
{
if (bytesRead == 0)