Merge pull request #2283 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2016-11-12 02:49:33 -05:00 committed by GitHub
commit d5197eabb0
29 changed files with 839 additions and 616 deletions

View File

@ -554,7 +554,7 @@ namespace Emby.Server.Core
ZipClient = new ZipClient(FileSystemManager); ZipClient = new ZipClient(FileSystemManager);
RegisterSingleInstance(ZipClient); RegisterSingleInstance(ZipClient);
RegisterSingleInstance<IHttpResultFactory>(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, XmlSerializer)); RegisterSingleInstance<IHttpResultFactory>(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, MemoryStreamFactory));
RegisterSingleInstance<IServerApplicationHost>(this); RegisterSingleInstance<IServerApplicationHost>(this);
RegisterSingleInstance<IServerApplicationPaths>(ApplicationPaths); RegisterSingleInstance<IServerApplicationPaths>(ApplicationPaths);
@ -614,6 +614,9 @@ namespace Emby.Server.Core
RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager)); RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager));
CertificatePath = GetCertificatePath(true);
Certificate = GetCertificate(CertificatePath);
HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate); HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate);
HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
RegisterSingleInstance(HttpServer, false); RegisterSingleInstance(HttpServer, false);
@ -995,9 +998,6 @@ namespace Emby.Server.Core
/// </summary> /// </summary>
private void StartServer() private void StartServer()
{ {
CertificatePath = GetCertificatePath(true);
Certificate = GetCertificate(CertificatePath);
try try
{ {
ServerManager.Start(GetUrlPrefixes()); ServerManager.Start(GetUrlPrefixes());

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.SocketSharp; using Emby.Server.Implementations.HttpServer.SocketSharp;
@ -86,9 +87,7 @@ namespace Emby.Server.Implementations.HttpServer
public string GlobalResponse { get; set; } public string GlobalResponse { get; set; }
public override void Configure() readonly Dictionary<Type, int> _mapExceptionToStatusCode = new Dictionary<Type, int>
{
var mapExceptionToStatusCode = new Dictionary<Type, int>
{ {
{typeof (InvalidOperationException), 500}, {typeof (InvalidOperationException), 500},
{typeof (NotImplementedException), 500}, {typeof (NotImplementedException), 500},
@ -102,6 +101,8 @@ namespace Emby.Server.Implementations.HttpServer
{typeof (NotSupportedException), 500} {typeof (NotSupportedException), 500}
}; };
public override void Configure()
{
var requestFilters = _appHost.GetExports<IRequestFilter>().ToList(); var requestFilters = _appHost.GetExports<IRequestFilter>().ToList();
foreach (var filter in requestFilters) foreach (var filter in requestFilters)
{ {
@ -240,12 +241,15 @@ namespace Emby.Server.Implementations.HttpServer
return; return;
} }
httpRes.StatusCode = 500; int statusCode;
if (!_mapExceptionToStatusCode.TryGetValue(ex.GetType(), out statusCode))
{
statusCode = 500;
}
httpRes.StatusCode = statusCode;
httpRes.ContentType = "text/html"; httpRes.ContentType = "text/html";
httpRes.Write(ex.Message); Write(httpRes, ex.Message);
httpRes.Close();
} }
catch catch
{ {
@ -399,7 +403,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
httpRes.StatusCode = 400; httpRes.StatusCode = 400;
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
httpRes.Write("Invalid host"); Write(httpRes, "Invalid host");
return; return;
} }
@ -453,7 +457,7 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
{ {
httpRes.Write( Write(httpRes,
"<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
newUrl + "\">" + newUrl + "</a></body></html>"); newUrl + "\">" + newUrl + "</a></body></html>");
return; return;
@ -470,7 +474,7 @@ namespace Emby.Server.Implementations.HttpServer
if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
{ {
httpRes.Write( Write(httpRes,
"<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
newUrl + "\">" + newUrl + "</a></body></html>"); newUrl + "\">" + newUrl + "</a></body></html>");
return; return;
@ -508,7 +512,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
httpRes.StatusCode = 503; httpRes.StatusCode = 503;
httpRes.ContentType = "text/html"; httpRes.ContentType = "text/html";
httpRes.Write(GlobalResponse); Write(httpRes, GlobalResponse);
return; return;
} }
@ -518,6 +522,10 @@ namespace Emby.Server.Implementations.HttpServer
{ {
await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false);
} }
else
{
ErrorHandler(new FileNotFoundException(), httpReq);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -538,6 +546,15 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
private void Write(IResponse response, string text)
{
var bOutput = Encoding.UTF8.GetBytes(text);
response.SetContentLength(bOutput.Length);
var outputStream = response.OutputStream;
outputStream.Write(bOutput, 0, bOutput.Length);
}
public static void RedirectToUrl(IResponse httpRes, string url) public static void RedirectToUrl(IResponse httpRes, string url)
{ {
httpRes.StatusCode = 302; httpRes.StatusCode = 302;

View File

@ -34,19 +34,16 @@ namespace Emby.Server.Implementations.HttpServer
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IXmlSerializer _xmlSerializer; private readonly IMemoryStreamFactory _memoryStreamFactory;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="HttpResultFactory" /> class. /// Initializes a new instance of the <see cref="HttpResultFactory" /> class.
/// </summary> /// </summary>
/// <param name="logManager">The log manager.</param> public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMemoryStreamFactory memoryStreamFactory)
/// <param name="fileSystem">The file system.</param>
/// <param name="jsonSerializer">The json serializer.</param>
public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer)
{ {
_fileSystem = fileSystem; _fileSystem = fileSystem;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_xmlSerializer = xmlSerializer; _memoryStreamFactory = memoryStreamFactory;
_logger = logManager.GetLogger("HttpResultFactory"); _logger = logManager.GetLogger("HttpResultFactory");
} }
@ -59,17 +56,13 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object GetResult(object content, string contentType, IDictionary<string, string> responseHeaders = null) public object GetResult(object content, string contentType, IDictionary<string, string> responseHeaders = null)
{ {
return GetHttpResult(content, contentType, responseHeaders); return GetHttpResult(content, contentType, true, responseHeaders);
} }
/// <summary> /// <summary>
/// Gets the HTTP result. /// Gets the HTTP result.
/// </summary> /// </summary>
/// <param name="content">The content.</param> private IHasHeaders GetHttpResult(object content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
/// <param name="contentType">Type of the content.</param>
/// <param name="responseHeaders">The response headers.</param>
/// <returns>IHasHeaders.</returns>
private IHasHeaders GetHttpResult(object content, string contentType, IDictionary<string, string> responseHeaders = null)
{ {
IHasHeaders result; IHasHeaders result;
@ -98,7 +91,7 @@ namespace Emby.Server.Implementations.HttpServer
} }
else else
{ {
result = new HttpResult(content, contentType); result = new HttpResult(content, contentType, HttpStatusCode.OK);
} }
} }
} }
@ -107,7 +100,11 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>(); responseHeaders = new Dictionary<string, string>();
} }
if (addCachePrevention)
{
responseHeaders["Expires"] = "-1"; responseHeaders["Expires"] = "-1";
}
AddResponseHeaders(result, responseHeaders); AddResponseHeaders(result, responseHeaders);
return result; return result;
@ -184,8 +181,6 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns></returns> /// <returns></returns>
public object ToOptimizedResult<T>(IRequest request, T dto) public object ToOptimizedResult<T>(IRequest request, T dto)
{ {
request.Response.Dto = dto;
var compressionType = GetCompressionType(request); var compressionType = GetCompressionType(request);
if (compressionType == null) if (compressionType == null)
{ {
@ -204,6 +199,7 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
// Do not use the memoryStreamFactory here, they don't place nice with compression
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
using (var compressionStream = GetCompressionStream(ms, compressionType)) using (var compressionStream = GetCompressionStream(ms, compressionType))
@ -213,12 +209,9 @@ namespace Emby.Server.Implementations.HttpServer
var compressedBytes = ms.ToArray(); var compressedBytes = ms.ToArray();
var httpResult = new HttpResult(compressedBytes, request.ResponseContentType) var httpResult = new StreamWriter(compressedBytes, request.ResponseContentType, _logger);
{
Status = request.Response.StatusCode
};
httpResult.Headers["Content-Length"] = compressedBytes.Length.ToString(UsCulture); //httpResult.Headers["Content-Length"] = compressedBytes.Length.ToString(UsCulture);
httpResult.Headers["Content-Encoding"] = compressionType; httpResult.Headers["Content-Encoding"] = compressionType;
return httpResult; return httpResult;
@ -226,6 +219,16 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
private static Stream GetCompressionStream(Stream outputStream, string compressionType)
{
if (compressionType == "deflate")
return new DeflateStream(outputStream, CompressionMode.Compress, true);
if (compressionType == "gzip")
return new GZipStream(outputStream, CompressionMode.Compress, true);
throw new NotSupportedException(compressionType);
}
public static string GetRealContentType(string contentType) public static string GetRealContentType(string contentType)
{ {
return contentType == null return contentType == null
@ -233,7 +236,7 @@ namespace Emby.Server.Implementations.HttpServer
: contentType.Split(';')[0].ToLower().Trim(); : contentType.Split(';')[0].ToLower().Trim();
} }
public static string SerializeToXmlString(object from) private string SerializeToXmlString(object from)
{ {
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
@ -253,16 +256,6 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
private static Stream GetCompressionStream(Stream outputStream, string compressionType)
{
if (compressionType == "deflate")
return new DeflateStream(outputStream, CompressionMode.Compress);
if (compressionType == "gzip")
return new GZipStream(outputStream, CompressionMode.Compress);
throw new NotSupportedException(compressionType);
}
/// <summary> /// <summary>
/// Gets the optimized result using cache. /// Gets the optimized result using cache.
/// </summary> /// </summary>
@ -358,23 +351,7 @@ namespace Emby.Server.Implementations.HttpServer
return hasHeaders; return hasHeaders;
} }
IHasHeaders httpResult; return GetHttpResult(result, contentType, false, responseHeaders);
var stream = result as Stream;
if (stream != null)
{
httpResult = new StreamWriter(stream, contentType, _logger);
}
else
{
// Otherwise wrap into an HttpResult
httpResult = new HttpResult(result, contentType ?? "text/html", HttpStatusCode.NotModified);
}
AddResponseHeaders(httpResult, responseHeaders);
return httpResult;
} }
/// <summary> /// <summary>
@ -603,7 +580,7 @@ namespace Emby.Server.Implementations.HttpServer
{ {
stream.Dispose(); stream.Dispose();
return GetHttpResult(new byte[] { }, contentType); return GetHttpResult(new byte[] { }, contentType, true);
} }
return new StreamWriter(stream, contentType, _logger) return new StreamWriter(stream, contentType, _logger)
@ -630,13 +607,13 @@ namespace Emby.Server.Implementations.HttpServer
if (isHeadRequest) if (isHeadRequest)
{ {
return GetHttpResult(new byte[] { }, contentType); return GetHttpResult(new byte[] { }, contentType, true);
} }
return GetHttpResult(contents, contentType, responseHeaders); return GetHttpResult(contents, contentType, true, responseHeaders);
} }
public static byte[] Compress(string text, string compressionType) private byte[] Compress(string text, string compressionType)
{ {
if (compressionType == "deflate") if (compressionType == "deflate")
return Deflate(text); return Deflate(text);
@ -647,12 +624,12 @@ namespace Emby.Server.Implementations.HttpServer
throw new NotSupportedException(compressionType); throw new NotSupportedException(compressionType);
} }
public static byte[] Deflate(string text) private byte[] Deflate(string text)
{ {
return Deflate(Encoding.UTF8.GetBytes(text)); return Deflate(Encoding.UTF8.GetBytes(text));
} }
public static byte[] Deflate(byte[] bytes) private byte[] Deflate(byte[] bytes)
{ {
// In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream
// Which means we must use MemoryStream since you have to use ToArray() on a closed Stream // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream
@ -666,12 +643,12 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
public static byte[] GZip(string text) private byte[] GZip(string text)
{ {
return GZip(Encoding.UTF8.GetBytes(text)); return GZip(Encoding.UTF8.GetBytes(text));
} }
public static byte[] GZip(byte[] buffer) private byte[] GZip(byte[] buffer)
{ {
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
using (var zipStream = new GZipStream(ms, CompressionMode.Compress)) using (var zipStream = new GZipStream(ms, CompressionMode.Compress))

View File

@ -77,18 +77,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
get { return _response.OutputStream; } get { return _response.OutputStream; }
} }
public object Dto { get; set; }
public void Write(string text)
{
var bOutput = System.Text.Encoding.UTF8.GetBytes(text);
_response.ContentLength64 = bOutput.Length;
var outputStream = _response.OutputStream;
outputStream.Write(bOutput, 0, bOutput.Length);
Close();
}
public void Close() public void Close()
{ {
if (!this.IsClosed) if (!this.IsClosed)
@ -110,8 +98,10 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
{ {
try try
{ {
response.OutputStream.Flush(); var outputStream = response.OutputStream;
response.OutputStream.Dispose();
outputStream.Flush();
outputStream.Dispose();
response.Close(); response.Close();
} }
catch (Exception ex) catch (Exception ex)
@ -120,16 +110,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
} }
} }
public void End()
{
Close();
}
public void Flush()
{
_response.OutputStream.Flush();
}
public bool IsClosed public bool IsClosed
{ {
get; get;

View File

@ -25,6 +25,8 @@ namespace Emby.Server.Implementations.HttpServer
/// <value>The source stream.</value> /// <value>The source stream.</value>
private Stream SourceStream { get; set; } private Stream SourceStream { get; set; }
private byte[] SourceBytes { get; set; }
/// <summary> /// <summary>
/// The _options /// The _options
/// </summary> /// </summary>
@ -40,7 +42,6 @@ namespace Emby.Server.Implementations.HttpServer
public Action OnComplete { get; set; } public Action OnComplete { get; set; }
public Action OnError { get; set; } public Action OnError { get; set; }
private readonly byte[] _bytes;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StreamWriter" /> class. /// Initializes a new instance of the <see cref="StreamWriter" /> class.
@ -73,14 +74,13 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="contentType">Type of the content.</param> /// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param> /// <param name="logger">The logger.</param>
public StreamWriter(byte[] source, string contentType, ILogger logger) public StreamWriter(byte[] source, string contentType, ILogger logger)
: this(new MemoryStream(source), contentType, logger)
{ {
if (string.IsNullOrEmpty(contentType)) if (string.IsNullOrEmpty(contentType))
{ {
throw new ArgumentNullException("contentType"); throw new ArgumentNullException("contentType");
} }
_bytes = source; SourceBytes = source;
Logger = logger; Logger = logger;
Headers["Content-Type"] = contentType; Headers["Content-Type"] = contentType;
@ -92,9 +92,11 @@ namespace Emby.Server.Implementations.HttpServer
{ {
try try
{ {
if (_bytes != null) var bytes = SourceBytes;
if (bytes != null)
{ {
await responseStream.WriteAsync(_bytes, 0, _bytes.Length); await responseStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
} }
else else
{ {

View File

@ -17,6 +17,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.Images namespace Emby.Server.Implementations.Images
{ {
@ -146,7 +147,9 @@ namespace Emby.Server.Implementations.Images
return ItemUpdateType.None; return ItemUpdateType.None;
} }
await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, false, cancellationToken).ConfigureAwait(false); var mimeType = MimeTypes.GetMimeType(outputPath);
await ProviderManager.SaveImage(item, outputPath, mimeType, imageType, null, false, cancellationToken).ConfigureAwait(false);
return ItemUpdateType.ImageUpdate; return ItemUpdateType.ImageUpdate;
} }

View File

@ -941,21 +941,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var result = _liveStreams.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase)); var result = _liveStreams.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
if (result != null && result.EnableStreamSharing) if (result != null && result.EnableStreamSharing)
{ {
var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing); var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing);
result.SharedStreamIds.Add(openedMediaSource.Id); result.SharedStreamIds.Add(openedMediaSource.Id);
_liveStreamsSemaphore.Release();
_logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount); _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost); return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
} }
try
{
foreach (var hostInstance in _liveTvManager.TunerHosts) foreach (var hostInstance in _liveTvManager.TunerHosts)
{ {
try try
@ -1271,6 +1270,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try try
{ {
var recorder = await GetRecorder().ConfigureAwait(false);
var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None) var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
@ -1282,8 +1283,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
// HDHR doesn't seem to release the tuner right away after first probing with ffmpeg // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
//await Task.Delay(3000, cancellationToken).ConfigureAwait(false); //await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
var recorder = await GetRecorder().ConfigureAwait(false);
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath); recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
recordPath = EnsureFileUnique(recordPath, timer.Id); recordPath = EnsureFileUnique(recordPath, timer.Id);

View File

@ -566,6 +566,23 @@ namespace Emby.Server.Implementations.Session
} }
} }
private BaseItem GetNowPlayingItem(SessionInfo session, string itemId)
{
var idGuid = new Guid(itemId);
var item = session.FullNowPlayingItem;
if (item != null && item.Id == idGuid)
{
return item;
}
item = _libraryManager.GetItemById(itemId);
session.FullNowPlayingItem = item;
return item;
}
/// <summary> /// <summary>
/// Used to report that playback has started for an item /// Used to report that playback has started for an item
/// </summary> /// </summary>
@ -583,7 +600,7 @@ namespace Emby.Server.Implementations.Session
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
? null ? null
: _libraryManager.GetItemById(new Guid(info.ItemId)); : GetNowPlayingItem(session, info.ItemId);
await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false); await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
@ -669,7 +686,7 @@ namespace Emby.Server.Implementations.Session
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
? null ? null
: _libraryManager.GetItemById(new Guid(info.ItemId)); : GetNowPlayingItem(session, info.ItemId);
await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false); await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
@ -773,7 +790,7 @@ namespace Emby.Server.Implementations.Session
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId) var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
? null ? null
: _libraryManager.GetItemById(new Guid(info.ItemId)); : GetNowPlayingItem(session, info.ItemId);
// Normalize // Normalize
if (string.IsNullOrWhiteSpace(info.MediaSourceId)) if (string.IsNullOrWhiteSpace(info.MediaSourceId))
@ -1782,18 +1799,18 @@ namespace Emby.Server.Implementations.Session
throw new ArgumentNullException("itemId"); throw new ArgumentNullException("itemId");
} }
var item = _libraryManager.GetItemById(new Guid(itemId)); //var item = _libraryManager.GetItemById(new Guid(itemId));
var info = GetItemInfo(item, null, null); //var info = GetItemInfo(item, null, null);
ReportNowViewingItem(sessionId, info); //ReportNowViewingItem(sessionId, info);
} }
public void ReportNowViewingItem(string sessionId, BaseItemInfo item) public void ReportNowViewingItem(string sessionId, BaseItemInfo item)
{ {
var session = GetSession(sessionId); //var session = GetSession(sessionId);
session.NowViewingItem = item; //session.NowViewingItem = item;
} }
public void ReportTranscodingInfo(string deviceId, TranscodingInfo info) public void ReportTranscodingInfo(string deviceId, TranscodingInfo info)

View File

@ -142,6 +142,7 @@
<Compile Include="System\ActivityLogWebSocketListener.cs" /> <Compile Include="System\ActivityLogWebSocketListener.cs" />
<Compile Include="System\SystemService.cs" /> <Compile Include="System\SystemService.cs" />
<Compile Include="Movies\TrailersService.cs" /> <Compile Include="Movies\TrailersService.cs" />
<Compile Include="TestService.cs" />
<Compile Include="TvShowsService.cs" /> <Compile Include="TvShowsService.cs" />
<Compile Include="UserLibrary\ArtistsService.cs" /> <Compile Include="UserLibrary\ArtistsService.cs" />
<Compile Include="UserLibrary\BaseItemsByNameService.cs" /> <Compile Include="UserLibrary\BaseItemsByNameService.cs" />

View File

@ -1,6 +1,8 @@
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback namespace MediaBrowser.Api.Playback
@ -8,7 +10,7 @@ namespace MediaBrowser.Api.Playback
/// <summary> /// <summary>
/// Class StaticRemoteStreamWriter /// Class StaticRemoteStreamWriter
/// </summary> /// </summary>
public class StaticRemoteStreamWriter : IStreamWriter, IHasHeaders public class StaticRemoteStreamWriter : IAsyncStreamWriter, IHasHeaders
{ {
/// <summary> /// <summary>
/// The _input stream /// The _input stream
@ -34,15 +36,11 @@ namespace MediaBrowser.Api.Playback
get { return _options; } get { return _options; }
} }
/// <summary> public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
/// Writes to.
/// </summary>
/// <param name="responseStream">The response stream.</param>
public void WriteTo(Stream responseStream)
{ {
using (_response) using (_response)
{ {
_response.Content.CopyTo(responseStream, 819200); await _response.Content.CopyToAsync(responseStream, 81920, cancellationToken).ConfigureAwait(false);
} }
} }
} }

View File

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
[Route("/Test/String", "GET")]
public class GetString
{
}
[Route("/Test/OptimizedString", "GET")]
public class GetOptimizedString
{
}
[Route("/Test/Bytes", "GET")]
public class GetBytes
{
}
[Route("/Test/OptimizedBytes", "GET")]
public class GetOptimizedBytes
{
}
[Route("/Test/Stream", "GET")]
public class GetStream
{
}
[Route("/Test/OptimizedStream", "GET")]
public class GetOptimizedStream
{
}
[Route("/Test/BytesWithContentType", "GET")]
public class GetBytesWithContentType
{
}
public class TestService : BaseApiService
{
public object Get(GetString request)
{
return "Welcome to Emby!";
}
public object Get(GetOptimizedString request)
{
return ToOptimizedResult("Welcome to Emby!");
}
public object Get(GetBytes request)
{
return Encoding.UTF8.GetBytes("Welcome to Emby!");
}
public object Get(GetOptimizedBytes request)
{
return ToOptimizedResult(Encoding.UTF8.GetBytes("Welcome to Emby!"));
}
public object Get(GetBytesWithContentType request)
{
return ApiEntryPoint.Instance.ResultFactory.GetResult(Encoding.UTF8.GetBytes("Welcome to Emby!"), "text/html");
}
public object Get(GetStream request)
{
return new MemoryStream(Encoding.UTF8.GetBytes("Welcome to Emby!"));
}
public object Get(GetOptimizedStream request)
{
return ToOptimizedResult(new MemoryStream(Encoding.UTF8.GetBytes("Welcome to Emby!")));
}
}
}

View File

@ -3,6 +3,7 @@ using MediaBrowser.Model.Session;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Session namespace MediaBrowser.Controller.Session
{ {
@ -107,6 +108,8 @@ namespace MediaBrowser.Controller.Session
/// <value>The now playing item.</value> /// <value>The now playing item.</value>
public BaseItemInfo NowPlayingItem { get; set; } public BaseItemInfo NowPlayingItem { get; set; }
public BaseItem FullNowPlayingItem { get; set; }
/// <summary> /// <summary>
/// Gets or sets the device id. /// Gets or sets the device id.
/// </summary> /// </summary>

View File

@ -19,11 +19,6 @@ namespace MediaBrowser.Model.Services
/// </summary> /// </summary>
HttpStatusCode StatusCode { get; set; } HttpStatusCode StatusCode { get; set; }
/// <summary>
/// The HTTP Status Description
/// </summary>
string StatusDescription { get; set; }
/// <summary> /// <summary>
/// The HTTP Response ContentType /// The HTTP Response ContentType
/// </summary> /// </summary>

View File

@ -136,39 +136,12 @@ namespace MediaBrowser.Model.Services
Stream OutputStream { get; } Stream OutputStream { get; }
/// <summary>
/// The Response DTO
/// </summary>
object Dto { get; set; }
/// <summary>
/// Write once to the Response Stream then close it.
/// </summary>
/// <param name="text"></param>
void Write(string text);
/// <summary>
/// Buffer the Response OutputStream so it can be written in 1 batch
/// </summary>
bool UseBufferedStream { get; set; }
/// <summary> /// <summary>
/// Signal that this response has been handled and no more processing should be done. /// Signal that this response has been handled and no more processing should be done.
/// When used in a request or response filter, no more filters or processing is done on this request. /// When used in a request or response filter, no more filters or processing is done on this request.
/// </summary> /// </summary>
void Close(); void Close();
/// <summary>
/// Calls Response.End() on ASP.NET HttpResponse otherwise is an alias for Close().
/// Useful when you want to prevent ASP.NET to provide it's own custom error page.
/// </summary>
void End();
/// <summary>
/// Response.Flush() and OutputStream.Flush() seem to have different behaviour in ASP.NET
/// </summary>
void Flush();
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is closed. /// Gets a value indicating whether this instance is closed.
/// </summary> /// </summary>
@ -176,8 +149,6 @@ namespace MediaBrowser.Model.Services
void SetContentLength(long contentLength); void SetContentLength(long contentLength);
bool KeepAlive { get; set; }
//Add Metadata to Response //Add Metadata to Response
Dictionary<string, object> Items { get; } Dictionary<string, object> Items { get; }
} }

View File

@ -13,37 +13,23 @@ namespace ServiceStack.Host
public void SerializeToStream(IRequest req, object response, Stream responseStream) public void SerializeToStream(IRequest req, object response, Stream responseStream)
{ {
var contentType = req.ResponseContentType; var contentType = req.ResponseContentType;
var serializer = GetResponseSerializer(contentType);
if (serializer == null)
throw new NotSupportedException("ContentType not supported: " + contentType);
var httpRes = new HttpResponseStreamWrapper(responseStream, req)
{
Dto = req.Response.Dto
};
serializer(req, response, httpRes);
}
public Action<IRequest, object, IResponse> GetResponseSerializer(string contentType)
{
var serializer = GetStreamSerializer(contentType); var serializer = GetStreamSerializer(contentType);
if (serializer == null) return null;
return (httpReq, dto, httpRes) => serializer(httpReq, dto, httpRes.OutputStream); serializer(response, responseStream);
} }
public Action<IRequest, object, Stream> GetStreamSerializer(string contentType) public static Action<object, Stream> GetStreamSerializer(string contentType)
{ {
switch (GetRealContentType(contentType)) switch (GetRealContentType(contentType))
{ {
case "application/xml": case "application/xml":
case "text/xml": case "text/xml":
case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
return (r, o, s) => ServiceStackHost.Instance.SerializeToXml(o, s); return (o, s) => ServiceStackHost.Instance.SerializeToXml(o, s);
case "application/json": case "application/json":
case "text/json": case "text/json":
return (r, o, s) => ServiceStackHost.Instance.SerializeToJson(o, s); return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s);
} }
return null; return null;

View File

@ -1,95 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using MediaBrowser.Model.Services;
namespace ServiceStack.Host
{
public class HttpResponseStreamWrapper : IHttpResponse
{
private static readonly UTF8Encoding UTF8EncodingWithoutBom = new UTF8Encoding(false);
public HttpResponseStreamWrapper(Stream stream, IRequest request)
{
this.OutputStream = stream;
this.Request = request;
this.Headers = new Dictionary<string, string>();
this.Items = new Dictionary<string, object>();
}
public Dictionary<string, string> Headers { get; set; }
public object OriginalResponse
{
get { return null; }
}
public IRequest Request { get; private set; }
public int StatusCode { set; get; }
public string StatusDescription { set; get; }
public string ContentType { get; set; }
public void AddHeader(string name, string value)
{
this.Headers[name] = value;
}
public string GetHeader(string name)
{
return this.Headers[name];
}
public void Redirect(string url)
{
this.Headers["Location"] = url;
}
public Stream OutputStream { get; private set; }
public object Dto { get; set; }
public void Write(string text)
{
var bytes = UTF8EncodingWithoutBom.GetBytes(text);
OutputStream.Write(bytes, 0, bytes.Length);
}
public bool UseBufferedStream { get; set; }
public void Close()
{
if (IsClosed) return;
OutputStream.Dispose();
IsClosed = true;
}
public void End()
{
Close();
}
public void Flush()
{
OutputStream.Flush();
}
public bool IsClosed { get; private set; }
public void SetContentLength(long contentLength) {}
public bool KeepAlive { get; set; }
public Dictionary<string, object> Items { get; private set; }
public void SetCookie(Cookie cookie)
{
}
public void ClearCookies()
{
}
}
}

View File

@ -210,9 +210,6 @@ namespace ServiceStack.Host
//Executes the service and returns the result //Executes the service and returns the result
var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetOperationName()).ConfigureAwait(false); var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetOperationName()).ConfigureAwait(false);
if (req.Response.Dto == null)
req.Response.Dto = response;
return response; return response;
} }
} }

View File

@ -6,6 +6,7 @@ using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Threading; using System.Threading;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using ServiceStack.Host; using ServiceStack.Host;
@ -33,9 +34,12 @@ namespace ServiceStack
var stream = result as Stream; var stream = result as Stream;
if (stream != null) if (stream != null)
{ {
WriteTo(stream, response.OutputStream); using (stream)
{
await stream.CopyToAsync(response.OutputStream).ConfigureAwait(false);
return true; return true;
} }
}
var bytes = result as byte[]; var bytes = result as byte[];
if (bytes != null) if (bytes != null)
@ -43,35 +47,13 @@ namespace ServiceStack
response.ContentType = "application/octet-stream"; response.ContentType = "application/octet-stream";
response.SetContentLength(bytes.Length); response.SetContentLength(bytes.Length);
response.OutputStream.Write(bytes, 0, bytes.Length); await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
return true; return true;
} }
return false; return false;
} }
public static long WriteTo(Stream inStream, Stream outStream)
{
var memoryStream = inStream as MemoryStream;
if (memoryStream != null)
{
memoryStream.WriteTo(outStream);
return memoryStream.Position;
}
var data = new byte[4096];
long total = 0;
int bytesRead;
while ((bytesRead = inStream.Read(data, 0, data.Length)) > 0)
{
outStream.Write(data, 0, bytesRead);
total += bytesRead;
}
return total;
}
/// <summary> /// <summary>
/// End a ServiceStack Request with no content /// End a ServiceStack Request with no content
/// </summary> /// </summary>
@ -85,7 +67,7 @@ namespace ServiceStack
httpRes.SetContentLength(0); httpRes.SetContentLength(0);
} }
public static Task WriteToResponse(this IResponse httpRes, MediaBrowser.Model.Services.IRequest httpReq, object result) public static Task WriteToResponse(this IResponse httpRes, IRequest httpReq, object result)
{ {
if (result == null) if (result == null)
{ {
@ -98,19 +80,10 @@ namespace ServiceStack
{ {
httpResult.RequestContext = httpReq; httpResult.RequestContext = httpReq;
httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType; httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
var httpResSerializer = ContentTypes.Instance.GetResponseSerializer(httpReq.ResponseContentType); return httpRes.WriteToResponseInternal(httpResult, httpReq);
return httpRes.WriteToResponse(httpResult, httpResSerializer, httpReq);
} }
var serializer = ContentTypes.Instance.GetResponseSerializer(httpReq.ResponseContentType); return httpRes.WriteToResponseInternal(result, httpReq);
return httpRes.WriteToResponse(result, serializer, httpReq);
}
private static object GetDto(object response)
{
if (response == null) return null;
var httpResult = response as IHttpResult;
return httpResult != null ? httpResult.Response : response;
} }
/// <summary> /// <summary>
@ -119,17 +92,11 @@ namespace ServiceStack
/// </summary> /// </summary>
/// <param name="response">The response.</param> /// <param name="response">The response.</param>
/// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param> /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
/// <param name="defaultAction">The default action.</param>
/// <param name="request">The serialization context.</param> /// <param name="request">The serialization context.</param>
/// <returns></returns> /// <returns></returns>
public static async Task WriteToResponse(this IResponse response, object result, Action<IRequest, object, IResponse> defaultAction, MediaBrowser.Model.Services.IRequest request) private static async Task WriteToResponseInternal(this IResponse response, object result, IRequest request)
{ {
var defaultContentType = request.ResponseContentType; var defaultContentType = request.ResponseContentType;
if (result == null)
{
response.EndRequestWithNoContent();
return;
}
var httpResult = result as IHttpResult; var httpResult = result as IHttpResult;
if (httpResult != null) if (httpResult != null)
@ -137,10 +104,8 @@ namespace ServiceStack
if (httpResult.RequestContext == null) if (httpResult.RequestContext == null)
httpResult.RequestContext = request; httpResult.RequestContext = request;
response.Dto = response.Dto ?? GetDto(httpResult);
response.StatusCode = httpResult.Status; response.StatusCode = httpResult.Status;
response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString(); response.StatusDescription = httpResult.StatusCode.ToString();
if (string.IsNullOrEmpty(httpResult.ContentType)) if (string.IsNullOrEmpty(httpResult.ContentType))
{ {
httpResult.ContentType = defaultContentType; httpResult.ContentType = defaultContentType;
@ -159,21 +124,12 @@ namespace ServiceStack
} }
} }
} }
else
{
response.Dto = result;
}
/* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
var responseOptions = result as IHasHeaders; var responseOptions = result as IHasHeaders;
if (responseOptions != null) if (responseOptions != null)
{ {
//Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
const string reservedOptions = ".";
foreach (var responseHeaders in responseOptions.Headers) foreach (var responseHeaders in responseOptions.Headers)
{ {
if (responseHeaders.Key.Contains(reservedOptions)) continue;
if (responseHeaders.Key == "Content-Length") if (responseHeaders.Key == "Content-Length")
{ {
response.SetContentLength(long.Parse(responseHeaders.Value)); response.SetContentLength(long.Parse(responseHeaders.Value));
@ -196,42 +152,41 @@ namespace ServiceStack
response.ContentType += "; charset=utf-8"; response.ContentType += "; charset=utf-8";
} }
var disposableResult = result as IDisposable;
var writeToOutputStreamResult = await WriteToOutputStream(response, result).ConfigureAwait(false); var writeToOutputStreamResult = await WriteToOutputStream(response, result).ConfigureAwait(false);
if (writeToOutputStreamResult) if (writeToOutputStreamResult)
{ {
response.Flush(); //required for Compression
if (disposableResult != null) disposableResult.Dispose();
return; return;
} }
if (httpResult != null)
result = httpResult.Response;
var responseText = result as string; var responseText = result as string;
if (responseText != null) if (responseText != null)
{ {
if (response.ContentType == null || response.ContentType == "text/html") if (response.ContentType == null || response.ContentType == "text/html")
response.ContentType = defaultContentType; response.ContentType = defaultContentType;
response.Write(responseText);
var bytes = Encoding.UTF8.GetBytes(responseText);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
return; return;
} }
if (defaultAction == null) await WriteObject(request, result, response).ConfigureAwait(false);
}
public static async Task WriteObject(IRequest request, object result, IResponse response)
{ {
throw new ArgumentNullException("defaultAction", String.Format( var contentType = request.ResponseContentType;
"As result '{0}' is not a supported responseType, a defaultAction must be supplied", var serializer = ContentTypes.GetStreamSerializer(contentType);
(result != null ? result.GetType().GetOperationName() : "")));
} using (var ms = new MemoryStream())
{
serializer(result, ms);
if (result != null)
defaultAction(request, result, response); ms.Position = 0;
response.SetContentLength(ms.Length);
if (disposableResult != null) await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
disposableResult.Dispose();
} }
//serializer(result, outputStream);
}
} }
} }

View File

@ -13,31 +13,7 @@ namespace ServiceStack
public class HttpResult public class HttpResult
: IHttpResult, IAsyncStreamWriter : IHttpResult, IAsyncStreamWriter
{ {
public HttpResult() public object Response { get; set; }
: this((object)null, null)
{
}
public HttpResult(object response)
: this(response, null)
{
}
public HttpResult(object response, string contentType)
: this(response, contentType, HttpStatusCode.OK)
{
}
public HttpResult(HttpStatusCode statusCode, string statusDescription)
: this()
{
StatusCode = statusCode;
StatusDescription = statusDescription;
}
public HttpResult(object response, HttpStatusCode statusCode)
: this(response, null, statusCode)
{ }
public HttpResult(object response, string contentType, HttpStatusCode statusCode) public HttpResult(object response, string contentType, HttpStatusCode statusCode)
{ {
@ -49,102 +25,12 @@ namespace ServiceStack
this.StatusCode = statusCode; this.StatusCode = statusCode;
} }
public HttpResult(Stream responseStream, string contentType)
: this(null, contentType, HttpStatusCode.OK)
{
this.ResponseStream = responseStream;
}
public HttpResult(string responseText, string contentType)
: this(null, contentType, HttpStatusCode.OK)
{
this.ResponseText = responseText;
}
public HttpResult(byte[] responseBytes, string contentType)
: this(null, contentType, HttpStatusCode.OK)
{
this.ResponseStream = new MemoryStream(responseBytes);
}
public string ResponseText { get; private set; }
public Stream ResponseStream { get; private set; }
public string ContentType { get; set; } public string ContentType { get; set; }
public IDictionary<string, string> Headers { get; private set; } public IDictionary<string, string> Headers { get; private set; }
public List<Cookie> Cookies { get; private set; } public List<Cookie> Cookies { get; private set; }
public string ETag { get; set; }
public TimeSpan? Age { get; set; }
public TimeSpan? MaxAge { get; set; }
public DateTime? Expires { get; set; }
public DateTime? LastModified { get; set; }
public Func<IDisposable> ResultScope { get; set; }
public string Location
{
set
{
if (StatusCode == HttpStatusCode.OK)
StatusCode = HttpStatusCode.Redirect;
this.Headers["Location"] = value;
}
}
public void SetPermanentCookie(string name, string value)
{
SetCookie(name, value, DateTime.UtcNow.AddYears(20), null);
}
public void SetPermanentCookie(string name, string value, string path)
{
SetCookie(name, value, DateTime.UtcNow.AddYears(20), path);
}
public void SetSessionCookie(string name, string value)
{
SetSessionCookie(name, value, null);
}
public void SetSessionCookie(string name, string value, string path)
{
path = path ?? "/";
this.Headers["Set-Cookie"] = string.Format("{0}={1};path=" + path, name, value);
}
public void SetCookie(string name, string value, TimeSpan expiresIn, string path)
{
var expiresAt = DateTime.UtcNow.Add(expiresIn);
SetCookie(name, value, expiresAt, path);
}
public void SetCookie(string name, string value, DateTime expiresAt, string path, bool secure = false, bool httpOnly = false)
{
path = path ?? "/";
var cookie = string.Format("{0}={1};expires={2};path={3}", name, value, expiresAt.ToString("R"), path);
if (secure)
cookie += ";Secure";
if (httpOnly)
cookie += ";HttpOnly";
this.Headers["Set-Cookie"] = cookie;
}
public void DeleteCookie(string name)
{
var cookie = string.Format("{0}=;expires={1};path=/", name, DateTime.UtcNow.AddDays(-1).ToString("R"));
this.Headers["Set-Cookie"] = cookie;
}
public int Status { get; set; } public int Status { get; set; }
public HttpStatusCode StatusCode public HttpStatusCode StatusCode
@ -153,75 +39,12 @@ namespace ServiceStack
set { Status = (int)value; } set { Status = (int)value; }
} }
public string StatusDescription { get; set; } public IRequest RequestContext { get; set; }
public object Response { get; set; }
public MediaBrowser.Model.Services.IRequest RequestContext { get; set; }
public string View { get; set; }
public string Template { get; set; }
public int PaddingLength { get; set; }
public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
try
{
await WriteToInternalAsync(responseStream, cancellationToken).ConfigureAwait(false);
responseStream.Flush();
}
finally
{
DisposeStream();
}
}
public static Task WriteTo(Stream inStream, Stream outStream, CancellationToken cancellationToken)
{
var memoryStream = inStream as MemoryStream;
if (memoryStream != null)
{
memoryStream.WriteTo(outStream);
return Task.FromResult(true);
}
return inStream.CopyToAsync(outStream, 81920, cancellationToken);
}
public async Task WriteToInternalAsync(Stream responseStream, CancellationToken cancellationToken)
{ {
var response = RequestContext != null ? RequestContext.Response : null; var response = RequestContext != null ? RequestContext.Response : null;
if (this.ResponseStream != null)
{
if (response != null)
{
var ms = ResponseStream as MemoryStream;
if (ms != null)
{
response.SetContentLength(ms.Length);
await ms.CopyToAsync(responseStream, 81920, cancellationToken).ConfigureAwait(false);
return;
}
}
await WriteTo(this.ResponseStream, responseStream, cancellationToken).ConfigureAwait(false);
return;
}
if (this.ResponseText != null)
{
var bytes = Encoding.UTF8.GetBytes(this.ResponseText);
if (response != null)
response.SetContentLength(bytes.Length);
await responseStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
return;
}
var bytesResponse = this.Response as byte[]; var bytesResponse = this.Response as byte[];
if (bytesResponse != null) if (bytesResponse != null)
{ {
@ -232,19 +55,7 @@ namespace ServiceStack
return; return;
} }
ContentTypes.Instance.SerializeToStream(this.RequestContext, this.Response, responseStream); await HttpResponseExtensionsInternal.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
}
private void DisposeStream()
{
try
{
if (ResponseStream != null)
{
this.ResponseStream.Dispose();
}
}
catch { /*ignore*/ }
} }
} }
} }

View File

@ -73,7 +73,6 @@
<Compile Include="Host\ContentTypes.cs" /> <Compile Include="Host\ContentTypes.cs" />
<Compile Include="ReflectionExtensions.cs" /> <Compile Include="ReflectionExtensions.cs" />
<Compile Include="StringMapTypeDeserializer.cs" /> <Compile Include="StringMapTypeDeserializer.cs" />
<Compile Include="Host\HttpResponseStreamWrapper.cs" />
<Compile Include="HttpResult.cs" /> <Compile Include="HttpResult.cs" />
<Compile Include="ServiceStackHost.cs" /> <Compile Include="ServiceStackHost.cs" />
<Compile Include="ServiceStackHost.Runtime.cs" /> <Compile Include="ServiceStackHost.Runtime.cs" />

View File

@ -168,12 +168,6 @@ namespace SocketHttpListener
: null; : null;
} }
internal static void Close(this HttpListenerResponse response, HttpStatusCode code)
{
response.StatusCode = (int)code;
response.OutputStream.Dispose();
}
internal static Stream Compress(this Stream stream, CompressionMethod method) internal static Stream Compress(this Stream stream, CompressionMethod method)
{ {
return method == CompressionMethod.Deflate return method == CompressionMethod.Deflate

View File

@ -119,7 +119,6 @@ namespace SocketHttpListener.Net
if (listener == null) if (listener == null)
return false; return false;
context.Listener = listener;
context.Connection.Prefix = prefix; context.Connection.Prefix = prefix;
return true; return true;
} }
@ -129,7 +128,7 @@ namespace SocketHttpListener.Net
if (context == null || context.Request == null) if (context == null || context.Request == null)
return; return;
context.Listener.UnregisterContext(context); listener.UnregisterContext(context);
} }
HttpListener SearchListener(Uri uri, out ListenerPrefix prefix) HttpListener SearchListener(Uri uri, out ListenerPrefix prefix)

View File

@ -1,7 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -210,12 +209,7 @@ namespace SocketHttpListener.Net
// TODO: can we get this stream before reading the input? // TODO: can we get this stream before reading the input?
if (o_stream == null) if (o_stream == null)
{ {
HttpListener listener = context.Listener; o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding);
if (listener == null)
return new ResponseStream(stream, context.Response, true, _memoryStreamFactory, _textEncoding);
o_stream = new ResponseStream(stream, context.Response, listener.IgnoreWriteExceptions, _memoryStreamFactory, _textEncoding);
} }
return o_stream; return o_stream;
} }
@ -257,7 +251,7 @@ namespace SocketHttpListener.Net
Close(true); Close(true);
return; return;
} }
HttpListener listener = context.Listener; HttpListener listener = epl.Listener;
if (last_listener != listener) if (last_listener != listener)
{ {
RemoveConnection(); RemoveConnection();

View File

@ -28,7 +28,6 @@ namespace SocketHttpListener.Net
HttpListenerPrefixCollection prefixes; HttpListenerPrefixCollection prefixes;
AuthenticationSchemeSelector auth_selector; AuthenticationSchemeSelector auth_selector;
string realm; string realm;
bool ignore_write_exceptions;
bool unsafe_ntlm_auth; bool unsafe_ntlm_auth;
bool listening; bool listening;
bool disposed; bool disposed;
@ -92,16 +91,6 @@ namespace SocketHttpListener.Net
} }
} }
public bool IgnoreWriteExceptions
{
get { return ignore_write_exceptions; }
set
{
CheckDisposed();
ignore_write_exceptions = value;
}
}
public bool IsListening public bool IsListening
{ {
get { return listening; } get { return listening; }

View File

@ -18,7 +18,6 @@ namespace SocketHttpListener.Net
HttpConnection cnc; HttpConnection cnc;
string error; string error;
int err_status = 400; int err_status = 400;
internal HttpListener Listener;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ICryptoProvider _cryptoProvider; private readonly ICryptoProvider _cryptoProvider;
private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly IMemoryStreamFactory _memoryStreamFactory;

View File

@ -17,17 +17,15 @@ namespace SocketHttpListener.Net
class ResponseStream : Stream class ResponseStream : Stream
{ {
HttpListenerResponse response; HttpListenerResponse response;
bool ignore_errors;
bool disposed; bool disposed;
bool trailer_sent; bool trailer_sent;
Stream stream; Stream stream;
private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly ITextEncoding _textEncoding; private readonly ITextEncoding _textEncoding;
internal ResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding) internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
{ {
this.response = response; this.response = response;
this.ignore_errors = ignore_errors;
_memoryStreamFactory = memoryStreamFactory; _memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding; _textEncoding = textEncoding;
this.stream = stream; this.stream = stream;
@ -129,20 +127,9 @@ namespace SocketHttpListener.Net
} }
internal void InternalWrite(byte[] buffer, int offset, int count) internal void InternalWrite(byte[] buffer, int offset, int count)
{
if (ignore_errors)
{
try
{ {
stream.Write(buffer, offset, count); stream.Write(buffer, offset, count);
} }
catch { }
}
else
{
stream.Write(buffer, offset, count);
}
}
public override void Write(byte[] buffer, int offset, int count) public override void Write(byte[] buffer, int offset, int count)
{ {
@ -214,8 +201,6 @@ namespace SocketHttpListener.Net
InternalWrite(bytes, 0, bytes.Length); InternalWrite(bytes, 0, bytes.Length);
} }
try
{
if (count > 0) if (count > 0)
{ {
await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
@ -224,14 +209,6 @@ namespace SocketHttpListener.Net
if (response.SendChunked) if (response.SendChunked)
stream.Write(crlf, 0, 2); stream.Write(crlf, 0, 2);
} }
catch
{
if (!ignore_errors)
{
throw;
}
}
}
//public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, //public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count,
// AsyncCallback cback, object state) // AsyncCallback cback, object state)

View File

@ -322,7 +322,8 @@ namespace SocketHttpListener.Net.WebSockets
internal void Close(HttpStatusCode code) internal void Close(HttpStatusCode code)
{ {
_context.Response.Close(code); _context.Response.StatusCode = (int)code;
_context.Response.OutputStream.Dispose();
} }
#endregion #endregion

View File

@ -95,9 +95,8 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>if $(ConfigurationName) == Release ( <PostBuildEvent>
xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i </PostBuildEvent>
)</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -1,14 +1,592 @@
using System; using MediaBrowser.Model.Logging;
using System.Collections.Generic; using MediaBrowser.Server.Implementations;
using MediaBrowser.Server.Startup.Common;
using MediaBrowser.ServerApplication.Native;
using MediaBrowser.ServerApplication.Splash;
using MediaBrowser.ServerApplication.Updates;
using Microsoft.Win32;
using System;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms;
using Emby.Common.Implementations.EnvironmentInfo;
using Emby.Common.Implementations.IO;
using Emby.Common.Implementations.Logging;
using Emby.Common.Implementations.Networking;
using Emby.Common.Implementations.Security;
using Emby.Server.Core;
using Emby.Server.Core.Browser;
using Emby.Server.Implementations.IO;
using ImageMagickSharp;
using MediaBrowser.Common.Net;
using MediaBrowser.Server.Startup.Common.IO;
namespace Emby.Server namespace Emby.Server
{ {
public class Program public class Program
{ {
private static ApplicationHost _appHost;
private static ILogger _logger;
private static bool _isRunningAsService = false;
private static bool _canRestartService = false;
private static bool _appHostDisposed;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
/// <summary>
/// Defines the entry point of the application.
/// </summary>
public static void Main(string[] args) public static void Main(string[] args)
{ {
var options = new StartupOptions();
_isRunningAsService = options.ContainsOption("-service");
if (_isRunningAsService)
{
//_canRestartService = CanRestartWindowsService();
}
var currentProcess = Process.GetCurrentProcess();
var applicationPath = currentProcess.MainModule.FileName;
var architecturePath = Path.Combine(Path.GetDirectoryName(applicationPath), Environment.Is64BitProcess ? "x64" : "x86");
Wand.SetMagickCoderModulePath(architecturePath);
var success = SetDllDirectory(architecturePath);
var appPaths = CreateApplicationPaths(applicationPath, _isRunningAsService);
var logManager = new NlogManager(appPaths.LogDirectoryPath, "server");
logManager.ReloadLogger(LogSeverity.Debug);
logManager.AddConsoleOutput();
var logger = _logger = logManager.GetLogger("Main");
ApplicationHost.LogEnvironmentInfo(logger, appPaths, true);
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
if (IsAlreadyRunning(applicationPath, currentProcess))
{
logger.Info("Shutting down because another instance of Emby Server is already running.");
return;
}
if (PerformUpdateIfNeeded(appPaths, logger))
{
logger.Info("Exiting to perform application update.");
return;
}
try
{
RunApplication(appPaths, logManager, _isRunningAsService, options);
}
finally
{
OnServiceShutdown();
}
}
/// <summary>
/// Determines whether [is already running] [the specified current process].
/// </summary>
/// <param name="applicationPath">The application path.</param>
/// <param name="currentProcess">The current process.</param>
/// <returns><c>true</c> if [is already running] [the specified current process]; otherwise, <c>false</c>.</returns>
private static bool IsAlreadyRunning(string applicationPath, Process currentProcess)
{
var duplicate = Process.GetProcesses().FirstOrDefault(i =>
{
try
{
if (currentProcess.Id == i.Id)
{
return false;
}
}
catch (Exception)
{
return false;
}
try
{
//_logger.Info("Module: {0}", i.MainModule.FileName);
if (string.Equals(applicationPath, i.MainModule.FileName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
catch (Exception)
{
return false;
}
});
if (duplicate != null)
{
_logger.Info("Found a duplicate process. Giving it time to exit.");
if (!duplicate.WaitForExit(30000))
{
_logger.Info("The duplicate process did not exit.");
return true;
}
}
if (!_isRunningAsService)
{
return false;
}
return false;
}
/// <summary>
/// Creates the application paths.
/// </summary>
/// <param name="applicationPath">The application path.</param>
/// <param name="runAsService">if set to <c>true</c> [run as service].</param>
/// <returns>ServerApplicationPaths.</returns>
private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, bool runAsService)
{
var resourcesPath = Path.GetDirectoryName(applicationPath);
if (runAsService)
{
var systemPath = Path.GetDirectoryName(applicationPath);
var programDataPath = Path.GetDirectoryName(systemPath);
return new ServerApplicationPaths(programDataPath, applicationPath, resourcesPath);
}
return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), applicationPath, resourcesPath);
}
/// <summary>
/// Gets a value indicating whether this instance can self restart.
/// </summary>
/// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
public static bool CanSelfRestart
{
get
{
if (_isRunningAsService)
{
return _canRestartService;
}
else
{
return true;
}
}
}
/// <summary>
/// Gets a value indicating whether this instance can self update.
/// </summary>
/// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
public static bool CanSelfUpdate
{
get
{
if (_isRunningAsService)
{
return _canRestartService;
}
else
{
return true;
}
}
}
private static readonly TaskCompletionSource<bool> ApplicationTaskCompletionSource = new TaskCompletionSource<bool>();
/// <summary>
/// Runs the application.
/// </summary>
/// <param name="appPaths">The app paths.</param>
/// <param name="logManager">The log manager.</param>
/// <param name="runService">if set to <c>true</c> [run service].</param>
/// <param name="options">The options.</param>
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options)
{
var fileSystem = new WindowsFileSystem(logManager.GetLogger("FileSystem"));
fileSystem.AddShortcutHandler(new LnkShortcutHandler());
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
var nativeApp = new WindowsApp(fileSystem, _logger)
{
IsRunningAsService = runService
};
var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths);
_appHost = new ApplicationHost(appPaths,
logManager,
options,
fileSystem,
nativeApp,
new PowerManagement(),
"emby.windows.zip",
new EnvironmentInfo(),
imageEncoder,
new Server.Startup.Common.SystemEvents(logManager.GetLogger("SystemEvents")),
new RecyclableMemoryStreamProvider(),
new NetworkManager(logManager.GetLogger("NetworkManager")),
GenerateCertificate,
() => Environment.UserDomainName);
var initProgress = new Progress<double>();
if (!runService)
{
// Not crazy about this but it's the only way to suppress ffmpeg crash dialog boxes
SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT |
ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX);
}
var task = _appHost.Init(initProgress);
Task.WaitAll(task);
task = task.ContinueWith(new Action<Task>(a => _appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
if (runService)
{
StartService(logManager);
}
else
{
Task.WaitAll(task);
task = InstallVcredist2013IfNeeded(_appHost, _logger);
Task.WaitAll(task);
Microsoft.Win32.SystemEvents.SessionEnding += SystemEvents_SessionEnding;
Microsoft.Win32.SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
task = ApplicationTaskCompletionSource.Task;
Task.WaitAll(task);
}
}
private static void GenerateCertificate(string certPath, string certHost)
{
CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger);
}
static void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLogon)
{
BrowserLauncher.OpenDashboard(_appHost);
}
}
/// <summary>
/// Starts the service.
/// </summary>
private static void StartService(ILogManager logManager)
{
var service = new BackgroundService(logManager.GetLogger("Service"));
service.Disposed += service_Disposed;
ServiceBase.Run(service);
}
/// <summary>
/// Handles the Disposed event of the service control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
static void service_Disposed(object sender, EventArgs e)
{
ApplicationTaskCompletionSource.SetResult(true);
OnServiceShutdown();
}
private static void OnServiceShutdown()
{
_logger.Info("Shutting down");
DisposeAppHost();
}
/// <summary>
/// Handles the SessionEnding event of the SystemEvents control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="SessionEndingEventArgs"/> instance containing the event data.</param>
static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
if (e.Reason == SessionEndReasons.SystemShutdown || !_isRunningAsService)
{
Shutdown();
}
}
/// <summary>
/// Handles the UnhandledException event of the CurrentDomain control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="UnhandledExceptionEventArgs"/> instance containing the event data.</param>
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = (Exception)e.ExceptionObject;
new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager).Log(exception);
if (!_isRunningAsService)
{
MessageBox.Show("Unhandled exception: " + exception.Message);
}
if (!Debugger.IsAttached)
{
Environment.Exit(Marshal.GetHRForException(exception));
}
}
/// <summary>
/// Performs the update if needed.
/// </summary>
/// <param name="appPaths">The app paths.</param>
/// <param name="logger">The logger.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
private static bool PerformUpdateIfNeeded(ServerApplicationPaths appPaths, ILogger logger)
{
// Look for the existence of an update archive
var updateArchive = Path.Combine(appPaths.TempUpdatePath, "MBServer" + ".zip");
if (File.Exists(updateArchive))
{
logger.Info("An update is available from {0}", updateArchive);
// Update is there - execute update
try
{
var serviceName = _isRunningAsService ? BackgroundService.GetExistingServiceName() : string.Empty;
new ApplicationUpdater().UpdateApplication(appPaths, updateArchive, logger, serviceName);
// And just let the app exit so it can update
return true;
}
catch (Exception e)
{
logger.ErrorException("Error starting updater.", e);
MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
}
}
return false;
}
public static void Shutdown()
{
if (_isRunningAsService)
{
ShutdownWindowsService();
}
else
{
DisposeAppHost();
ShutdownWindowsApplication();
}
}
public static void Restart()
{
DisposeAppHost();
if (_isRunningAsService)
{
RestartWindowsService();
}
else
{
//_logger.Info("Hiding server notify icon");
//_serverNotifyIcon.Visible = false;
_logger.Info("Starting new instance");
//Application.Restart();
Process.Start(_appHost.ServerConfigurationManager.ApplicationPaths.ApplicationPath);
ShutdownWindowsApplication();
}
}
private static void DisposeAppHost()
{
if (!_appHostDisposed)
{
_logger.Info("Disposing app host");
_appHostDisposed = true;
_appHost.Dispose();
}
}
private static void ShutdownWindowsApplication()
{
//_logger.Info("Calling Application.Exit");
//Application.Exit();
_logger.Info("Calling Environment.Exit");
Environment.Exit(0);
_logger.Info("Calling ApplicationTaskCompletionSource.SetResult");
ApplicationTaskCompletionSource.SetResult(true);
}
private static void ShutdownWindowsService()
{
}
private static void RestartWindowsService()
{
}
private static bool CanRestartWindowsService()
{
return false;
}
private static async Task InstallVcredist2013IfNeeded(ApplicationHost appHost, ILogger logger)
{
// Reference
// http://stackoverflow.com/questions/12206314/detect-if-visual-c-redistributable-for-visual-studio-2012-is-installed
try
{
var subkey = Environment.Is64BitProcess
? "SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\12.0\\VC\\Runtimes\\x64"
: "SOFTWARE\\Microsoft\\VisualStudio\\12.0\\VC\\Runtimes\\x86";
using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default)
.OpenSubKey(subkey))
{
if (ndpKey != null && ndpKey.GetValue("Version") != null)
{
var installedVersion = ((string)ndpKey.GetValue("Version")).TrimStart('v');
if (installedVersion.StartsWith("12", StringComparison.OrdinalIgnoreCase))
{
return;
}
}
}
}
catch (Exception ex)
{
logger.ErrorException("Error getting .NET Framework version", ex);
return;
}
try
{
await InstallVcredist2013().ConfigureAwait(false);
}
catch (Exception ex)
{
logger.ErrorException("Error installing Visual Studio C++ runtime", ex);
}
}
private async static Task InstallVcredist2013()
{
var httpClient = _appHost.HttpClient;
var tmp = await httpClient.GetTempFile(new HttpRequestOptions
{
Url = GetVcredist2013Url(),
Progress = new Progress<double>()
}).ConfigureAwait(false);
var exePath = Path.ChangeExtension(tmp, ".exe");
File.Copy(tmp, exePath);
var startInfo = new ProcessStartInfo
{
FileName = exePath,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
Verb = "runas",
ErrorDialog = false
};
_logger.Info("Running {0}", startInfo.FileName);
using (var process = Process.Start(startInfo))
{
process.WaitForExit();
}
}
private static string GetVcredist2013Url()
{
if (Environment.Is64BitProcess)
{
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2013/vcredist_x64.exe";
}
// TODO: ARM url - https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2013/vcredist_arm.exe
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2013/vcredist_x86.exe";
}
/// <summary>
/// Sets the error mode.
/// </summary>
/// <param name="uMode">The u mode.</param>
/// <returns>ErrorModes.</returns>
[DllImport("kernel32.dll")]
static extern ErrorModes SetErrorMode(ErrorModes uMode);
/// <summary>
/// Enum ErrorModes
/// </summary>
[Flags]
public enum ErrorModes : uint
{
/// <summary>
/// The SYSTE m_ DEFAULT
/// </summary>
SYSTEM_DEFAULT = 0x0,
/// <summary>
/// The SE m_ FAILCRITICALERRORS
/// </summary>
SEM_FAILCRITICALERRORS = 0x0001,
/// <summary>
/// The SE m_ NOALIGNMENTFAULTEXCEPT
/// </summary>
SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
/// <summary>
/// The SE m_ NOGPFAULTERRORBOX
/// </summary>
SEM_NOGPFAULTERRORBOX = 0x0002,
/// <summary>
/// The SE m_ NOOPENFILEERRORBOX
/// </summary>
SEM_NOOPENFILEERRORBOX = 0x8000
} }
} }
} }