diff --git a/Emby.Drawing.Skia/SkiaEncoder.cs b/Emby.Drawing.Skia/SkiaEncoder.cs index 0eaa966097..eb0602afef 100644 --- a/Emby.Drawing.Skia/SkiaEncoder.cs +++ b/Emby.Drawing.Skia/SkiaEncoder.cs @@ -48,7 +48,10 @@ namespace Emby.Drawing.Skia "astc", "ktx", "pkm", - "wbmp" + "wbmp", + + // working on windows at least + "cr2" }; } } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 673798294c..02308f79f5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1,5 +1,4 @@ -using Emby.Common.Implementations; -using Emby.Common.Implementations.Serialization; +using Emby.Common.Implementations.Serialization; using Emby.Dlna; using Emby.Dlna.ConnectionManager; using Emby.Dlna.ContentDirectory; @@ -8,12 +7,16 @@ using Emby.Dlna.MediaReceiverRegistrar; using Emby.Dlna.Ssdp; using Emby.Drawing; using Emby.Photos; +using Emby.Server.Core.Cryptography; using Emby.Server.Implementations.Activity; +using Emby.Server.Implementations.Archiving; using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Configuration; +using Emby.Server.Implementations.Cryptography; using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Devices; +using Emby.Server.Implementations.Diagnostics; using Emby.Server.Implementations.Dto; using Emby.Server.Implementations.FFMpeg; using Emby.Server.Implementations.HttpServer; @@ -23,14 +26,20 @@ using Emby.Server.Implementations.Library; using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; using Emby.Server.Implementations.MediaEncoder; -using Emby.Server.Implementations.Migrations; +using Emby.Server.Implementations.Net; using Emby.Server.Implementations.Notifications; using Emby.Server.Implementations.Playlists; +using Emby.Server.Implementations.Reflection; +using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.Security; +using Emby.Server.Implementations.Serialization; using Emby.Server.Implementations.Session; using Emby.Server.Implementations.Social; +using Emby.Server.Implementations.Threading; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; +using Emby.Server.Implementations.Xml; +using Emby.Server.MediaEncoding.Subtitles; using MediaBrowser.Api; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -52,9 +61,6 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; @@ -72,10 +78,13 @@ using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Sync; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; +using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -87,7 +96,9 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.Social; using MediaBrowser.Model.System; +using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Text; +using MediaBrowser.Model.Threading; using MediaBrowser.Model.Updates; using MediaBrowser.Model.Xml; using MediaBrowser.Providers.Chapters; @@ -97,7 +108,6 @@ using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using OpenSubtitlesHandler; using ServiceStack; -using SocketHttpListener.Primitives; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -111,22 +121,6 @@ using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; -using Emby.Server.Core.Cryptography; -using Emby.Server.Implementations.Archiving; -using Emby.Server.Implementations.Cryptography; -using Emby.Server.Implementations.Diagnostics; -using Emby.Server.Implementations.Net; -using Emby.Server.Implementations.Reflection; -using Emby.Server.Implementations.ScheduledTasks; -using Emby.Server.Implementations.Serialization; -using Emby.Server.Implementations.Threading; -using Emby.Server.Implementations.Xml; -using Emby.Server.MediaEncoding.Subtitles; -using MediaBrowser.MediaEncoding.BdInfo; -using MediaBrowser.Model.Cryptography; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Tasks; -using MediaBrowser.Model.Threading; using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions; using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate; @@ -135,7 +129,7 @@ namespace Emby.Server.Implementations /// /// Class CompositionRoot /// - public abstract class ApplicationHost : IServerApplicationHost, IDependencyContainer + public abstract class ApplicationHost : IServerApplicationHost, IDependencyContainer, IDisposable { /// /// Gets a value indicating whether this instance can self restart. @@ -171,6 +165,8 @@ namespace Emby.Server.Implementations /// true if this instance has pending application restart; otherwise, false. public bool HasPendingRestart { get; private set; } + public bool IsShuttingDown { get; private set; } + /// /// Gets or sets the logger. /// @@ -367,7 +363,7 @@ namespace Emby.Server.Implementations protected IAuthService AuthService { get; private set; } protected readonly StartupOptions StartupOptions; - private readonly string _releaseAssetFilename; + protected readonly string ReleaseAssetFilename; internal IPowerManagement PowerManagement { get; private set; } internal IImageEncoder ImageEncoder { get; private set; } @@ -420,7 +416,7 @@ namespace Emby.Server.Implementations Logger = LogManager.GetLogger("App"); StartupOptions = options; - _releaseAssetFilename = releaseAssetFilename; + ReleaseAssetFilename = releaseAssetFilename; PowerManagement = powerManagement; ImageEncoder = imageEncoder; @@ -1755,25 +1751,35 @@ namespace Emby.Server.Implementations /// /// Restarts this instance. /// - public async Task Restart() + public void Restart() { if (!CanSelfRestart) { throw new PlatformNotSupportedException("The server is unable to self-restart. Please restart manually."); } - try + if (IsShuttingDown) { - await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error sending server restart notification", ex); + return; } - Logger.Info("Calling RestartInternal"); + IsShuttingDown = true; - RestartInternal(); + Task.Run(async () => + { + try + { + await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.ErrorException("Error sending server restart notification", ex); + } + + Logger.Info("Calling RestartInternal"); + + RestartInternal(); + }); } protected abstract void RestartInternal(); @@ -1880,6 +1886,7 @@ namespace Emby.Server.Implementations return new SystemInfo { HasPendingRestart = HasPendingRestart, + IsShuttingDown = IsShuttingDown, Version = ApplicationVersion.ToString(), WebSocketPortNumber = HttpPort, FailedPluginAssemblies = FailedAssemblies.ToArray(), @@ -2107,6 +2114,13 @@ namespace Emby.Server.Implementations /// public async Task Shutdown() { + if (IsShuttingDown) + { + return; + } + + IsShuttingDown = true; + try { await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false); @@ -2185,22 +2199,29 @@ namespace Emby.Server.Implementations /// Task{CheckForUpdateResult}. public async Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress) { - var cacheLength = TimeSpan.FromHours(1); + var cacheLength = TimeSpan.FromMinutes(5); var updateLevel = SystemUpdateLevel; - if (updateLevel != PackageVersionClass.Release) - { - cacheLength = TimeSpan.FromMinutes(5); - } - - var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser", "Emby", ApplicationVersion, updateLevel, _releaseAssetFilename, - "MBServer", "Mbserver.zip", cacheLength, cancellationToken).ConfigureAwait(false); + var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser", + "Emby", + ApplicationVersion, + updateLevel, + ReleaseAssetFilename, + "MBServer", + UpdateTargetFileName, + cacheLength, + cancellationToken).ConfigureAwait(false); HasUpdateAvailable = result.IsUpdateAvailable; return result; } + protected virtual string UpdateTargetFileName + { + get { return "Mbserver.zip"; } + } + /// /// Updates the application. /// diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 719510fc39..bcda149d63 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -129,7 +129,6 @@ - diff --git a/Emby.Server.Implementations/IO/AsyncStreamCopier.cs b/Emby.Server.Implementations/IO/AsyncStreamCopier.cs deleted file mode 100644 index 9e5ce0604c..0000000000 --- a/Emby.Server.Implementations/IO/AsyncStreamCopier.cs +++ /dev/null @@ -1,459 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Emby.Server.Implementations.IO -{ - public class AsyncStreamCopier : IDisposable - { - // size in bytes of the buffers in the buffer pool - private const int DefaultBufferSize = 81920; - private readonly int _bufferSize; - // number of buffers in the pool - private const int DefaultBufferCount = 4; - private readonly int _bufferCount; - - // indexes of the next buffer to read into/write from - private int _nextReadBuffer = -1; - private int _nextWriteBuffer = -1; - - // the buffer pool, implemented as an array, and used in a cyclic way - private readonly byte[][] _buffers; - // sizes in bytes of the available (read) data in the buffers - private readonly int[] _sizes; - // the streams... - private Stream _source; - private Stream _target; - private readonly bool _closeStreamsOnEnd; - - // number of buffers that are ready to be written - private int _buffersToWrite; - // flag indicating that there is still a read operation to be scheduled - // (source end of stream not reached) - private volatile bool _moreDataToRead; - // the result of the whole operation, returned by BeginCopy() - private AsyncResult _asyncResult; - // any exception that occurs during an async operation - // stored here for rethrow - private Exception _exception; - - public TaskCompletionSource TaskCompletionSource; - private long _bytesToRead; - private long _totalBytesWritten; - private CancellationToken _cancellationToken; - public int IndividualReadOffset = 0; - - public AsyncStreamCopier(Stream source, - Stream target, - long bytesToRead, - CancellationToken cancellationToken, - bool closeStreamsOnEnd = false, - int bufferSize = DefaultBufferSize, - int bufferCount = DefaultBufferCount) - { - if (source == null) - throw new ArgumentNullException("source"); - if (target == null) - throw new ArgumentNullException("target"); - if (!source.CanRead) - throw new ArgumentException("Cannot copy from a non-readable stream."); - if (!target.CanWrite) - throw new ArgumentException("Cannot copy to a non-writable stream."); - _source = source; - _target = target; - _moreDataToRead = true; - _closeStreamsOnEnd = closeStreamsOnEnd; - _bufferSize = bufferSize; - _bufferCount = bufferCount; - _buffers = new byte[_bufferCount][]; - _sizes = new int[_bufferCount]; - _bytesToRead = bytesToRead; - _cancellationToken = cancellationToken; - } - - ~AsyncStreamCopier() - { - // ensure any exception cannot be ignored - ThrowExceptionIfNeeded(); - } - - public static Task CopyStream(Stream source, Stream target, int bufferSize, int bufferCount, CancellationToken cancellationToken) - { - return CopyStream(source, target, 0, bufferSize, bufferCount, cancellationToken); - } - - public static Task CopyStream(Stream source, Stream target, long size, int bufferSize, int bufferCount, CancellationToken cancellationToken) - { - var copier = new AsyncStreamCopier(source, target, size, cancellationToken, false, bufferSize, bufferCount); - var taskCompletion = new TaskCompletionSource(); - - copier.TaskCompletionSource = taskCompletion; - - var result = copier.BeginCopy(StreamCopyCallback, copier); - - if (result.CompletedSynchronously) - { - StreamCopyCallback(result); - } - - cancellationToken.Register(() => taskCompletion.TrySetCanceled()); - - return taskCompletion.Task; - } - - private static void StreamCopyCallback(IAsyncResult result) - { - var copier = (AsyncStreamCopier)result.AsyncState; - var taskCompletion = copier.TaskCompletionSource; - - try - { - copier.EndCopy(result); - taskCompletion.TrySetResult(copier._totalBytesWritten); - } - catch (Exception ex) - { - taskCompletion.TrySetException(ex); - } - } - - public void Dispose() - { - if (_asyncResult != null) - _asyncResult.Dispose(); - if (_closeStreamsOnEnd) - { - if (_source != null) - { - _source.Dispose(); - _source = null; - } - if (_target != null) - { - _target.Dispose(); - _target = null; - } - } - GC.SuppressFinalize(this); - ThrowExceptionIfNeeded(); - } - - public IAsyncResult BeginCopy(AsyncCallback callback, object state) - { - // avoid concurrent start of the copy on separate threads - if (Interlocked.CompareExchange(ref _asyncResult, new AsyncResult(callback, state), null) != null) - throw new InvalidOperationException("A copy operation has already been started on this object."); - // allocate buffers - for (int i = 0; i < _bufferCount; i++) - _buffers[i] = new byte[_bufferSize]; - - // we pass false to BeginRead() to avoid completing the async result - // immediately which would result in invoking the callback - // when the method fails synchronously - BeginRead(false); - // throw exception synchronously if there is one - ThrowExceptionIfNeeded(); - return _asyncResult; - } - - public void EndCopy(IAsyncResult ar) - { - if (ar != _asyncResult) - throw new InvalidOperationException("Invalid IAsyncResult object."); - - if (!_asyncResult.IsCompleted) - _asyncResult.AsyncWaitHandle.WaitOne(); - - if (_closeStreamsOnEnd) - { - _source.Close(); - _source = null; - _target.Close(); - _target = null; - } - - //_logger.Info("AsyncStreamCopier {0} bytes requested. {1} bytes transferred", _bytesToRead, _totalBytesWritten); - ThrowExceptionIfNeeded(); - } - - /// - /// Here we'll throw a pending exception if there is one, - /// and remove it from our instance, so we know it has been consumed. - /// - private void ThrowExceptionIfNeeded() - { - if (_exception != null) - { - var exception = _exception; - _exception = null; - throw exception; - } - } - - private void BeginRead(bool completeOnError = true) - { - if (!_moreDataToRead) - { - return; - } - if (_asyncResult.IsCompleted) - return; - int bufferIndex = Interlocked.Increment(ref _nextReadBuffer) % _bufferCount; - - try - { - _source.BeginRead(_buffers[bufferIndex], 0, _bufferSize, EndRead, bufferIndex); - } - catch (Exception exception) - { - _exception = exception; - if (completeOnError) - _asyncResult.Complete(false); - } - } - - private void BeginWrite() - { - if (_asyncResult.IsCompleted) - return; - // this method can actually be called concurrently!! - // indeed, let's say we call a BeginWrite, and the thread gets interrupted - // just after making the IO request. - // At that moment, the thread is still in the method. And then the IO request - // ends (extremely fast io, or caching...), EndWrite gets called - // on another thread, and calls BeginWrite again! There we have it! - // That is the reason why an Interlocked is needed here. - int bufferIndex = Interlocked.Increment(ref _nextWriteBuffer) % _bufferCount; - - try - { - int bytesToWrite; - if (_bytesToRead > 0) - { - var bytesLeftToWrite = _bytesToRead - _totalBytesWritten; - bytesToWrite = Convert.ToInt32(Math.Min(_sizes[bufferIndex], bytesLeftToWrite)); - } - else - { - bytesToWrite = _sizes[bufferIndex]; - } - - _target.BeginWrite(_buffers[bufferIndex], IndividualReadOffset, bytesToWrite - IndividualReadOffset, EndWrite, null); - - _totalBytesWritten += bytesToWrite; - } - catch (Exception exception) - { - _exception = exception; - _asyncResult.Complete(false); - } - } - - private void EndRead(IAsyncResult ar) - { - try - { - int read = _source.EndRead(ar); - _moreDataToRead = read > 0; - var bufferIndex = (int)ar.AsyncState; - _sizes[bufferIndex] = read; - } - catch (Exception exception) - { - _exception = exception; - _asyncResult.Complete(false); - return; - } - - if (_moreDataToRead && !_cancellationToken.IsCancellationRequested) - { - int usedBuffers = Interlocked.Increment(ref _buffersToWrite); - // if we incremented from zero to one, then it means we just - // added the single buffer to write, so a writer could not - // be busy, and we have to schedule one. - if (usedBuffers == 1) - BeginWrite(); - // test if there is at least a free buffer, and schedule - // a read, as we have read some data - if (usedBuffers < _bufferCount) - BeginRead(); - } - else - { - // we did not add a buffer, because no data was read, and - // there is no buffer left to write so this is the end... - if (Thread.VolatileRead(ref _buffersToWrite) == 0) - { - _asyncResult.Complete(false); - } - } - } - - private void EndWrite(IAsyncResult ar) - { - try - { - _target.EndWrite(ar); - } - catch (Exception exception) - { - _exception = exception; - _asyncResult.Complete(false); - return; - } - - int buffersLeftToWrite = Interlocked.Decrement(ref _buffersToWrite); - // no reader could be active if all buffers were full of data waiting to be written - bool noReaderIsBusy = buffersLeftToWrite == _bufferCount - 1; - // note that it is possible that both a reader and - // a writer see the end of the copy and call Complete - // on the _asyncResult object. That race condition is handled by - // Complete that ensures it is only executed fully once. - - long bytesLeftToWrite; - if (_bytesToRead > 0) - { - bytesLeftToWrite = _bytesToRead - _totalBytesWritten; - } - else - { - bytesLeftToWrite = 1; - } - - if (!_moreDataToRead || bytesLeftToWrite <= 0 || _cancellationToken.IsCancellationRequested) - { - // at this point we know no reader can schedule a read or write - if (Thread.VolatileRead(ref _buffersToWrite) == 0) - { - // nothing left to write, so it is the end - _asyncResult.Complete(false); - return; - } - } - else - // here, we know we have something left to read, - // so schedule a read if no read is busy - if (noReaderIsBusy) - BeginRead(); - - // also schedule a write if we are sure we did not write the last buffer - // note that if buffersLeftToWrite is zero and a reader has put another - // buffer to write between the time we decremented _buffersToWrite - // and now, that reader will also schedule another write, - // as it will increment _buffersToWrite from zero to one - if (buffersLeftToWrite > 0) - BeginWrite(); - } - } - - internal class AsyncResult : IAsyncResult, IDisposable - { - // Fields set at construction which never change while - // operation is pending - private readonly AsyncCallback _asyncCallback; - private readonly object _asyncState; - - // Fields set at construction which do change after - // operation completes - private const int StatePending = 0; - private const int StateCompletedSynchronously = 1; - private const int StateCompletedAsynchronously = 2; - private int _completedState = StatePending; - - // Field that may or may not get set depending on usage - private ManualResetEvent _waitHandle; - - internal AsyncResult( - AsyncCallback asyncCallback, - object state) - { - _asyncCallback = asyncCallback; - _asyncState = state; - } - - internal bool Complete(bool completedSynchronously) - { - bool result = false; - - // The _completedState field MUST be set prior calling the callback - int prevState = Interlocked.CompareExchange(ref _completedState, - completedSynchronously ? StateCompletedSynchronously : - StateCompletedAsynchronously, StatePending); - if (prevState == StatePending) - { - // If the event exists, set it - if (_waitHandle != null) - _waitHandle.Set(); - - if (_asyncCallback != null) - _asyncCallback(this); - - result = true; - } - - return result; - } - - #region Implementation of IAsyncResult - - public Object AsyncState { get { return _asyncState; } } - - public bool CompletedSynchronously - { - get - { - return Thread.VolatileRead(ref _completedState) == - StateCompletedSynchronously; - } - } - - public WaitHandle AsyncWaitHandle - { - get - { - if (_waitHandle == null) - { - bool done = IsCompleted; - var mre = new ManualResetEvent(done); - if (Interlocked.CompareExchange(ref _waitHandle, - mre, null) != null) - { - // Another thread created this object's event; dispose - // the event we just created - mre.Close(); - } - else - { - if (!done && IsCompleted) - { - // If the operation wasn't done when we created - // the event but now it is done, set the event - _waitHandle.Set(); - } - } - } - return _waitHandle; - } - } - - public bool IsCompleted - { - get - { - return Thread.VolatileRead(ref _completedState) != - StatePending; - } - } - #endregion - - public void Dispose() - { - if (_waitHandle != null) - { - _waitHandle.Dispose(); - _waitHandle = null; - } - } - } -} diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs index d2e9c8bf02..84adf0cfc9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHttpStream.cs @@ -158,58 +158,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public Task CopyToAsync(Stream stream, CancellationToken cancellationToken) { - if (_enableFileBuffer) - { - return CopyFileTo(_tempFilePath, stream, cancellationToken); - } return _multicastStream.CopyToAsync(stream, cancellationToken); - //return CopyFileTo(_tempFilePath, stream, cancellationToken); - } - - protected async Task CopyFileTo(string path, Stream outputStream, CancellationToken cancellationToken) - { - long startPosition = -20000; - if (startPosition < 0) - { - var length = FileSystem.GetFileInfo(path).Length; - startPosition = Math.Max(length - startPosition, 0); - } - - _logger.Info("Live stream starting position is {0} bytes", startPosition.ToString(CultureInfo.InvariantCulture)); - - var allowAsync = Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows; - // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 - - using (var inputStream = GetInputStream(path, startPosition, allowAsync)) - { - if (startPosition > 0) - { - inputStream.Position = startPosition; - } - - while (!cancellationToken.IsCancellationRequested) - { - long bytesRead; - - if (allowAsync) - { - bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 2, cancellationToken).ConfigureAwait(false); - } - else - { - StreamHelper.CopyTo(inputStream, outputStream, 81920, cancellationToken); - bytesRead = 1; - } - - //var position = fs.Position; - //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); - - if (bytesRead == 0) - { - await Task.Delay(100, cancellationToken).ConfigureAwait(false); - } - } - } } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 5ad6e2e161..69fe59b4a0 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -211,15 +211,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { long bytesRead; - if (allowAsync) - { - bytesRead = await AsyncStreamCopier.CopyStream(inputStream, outputStream, 81920, 2, cancellationToken).ConfigureAwait(false); - } - else - { - StreamHelper.CopyTo(inputStream, outputStream, 81920, cancellationToken); - bytesRead = 1; - } + StreamHelper.CopyTo(inputStream, outputStream, 81920, cancellationToken); + bytesRead = 1; //var position = fs.Position; //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); @@ -285,22 +278,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun //return taskCompletion.Task; } - private void StreamCopyCallback(IAsyncResult result) - { - var copier = (AsyncStreamCopier)result.AsyncState; - var taskCompletion = copier.TaskCompletionSource; - - try - { - copier.EndCopy(result); - taskCompletion.TrySetResult(0); - } - catch (Exception ex) - { - taskCompletion.TrySetException(ex); - } - } - public class UdpClientStream : Stream { private static int RtpHeaderBytes = 12; diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs index 4590369fab..d684dce5d4 100644 --- a/Emby.Server.Implementations/Services/SwaggerService.cs +++ b/Emby.Server.Implementations/Services/SwaggerService.cs @@ -30,6 +30,7 @@ namespace Emby.Server.Implementations.Services public string description { get; set; } public string version { get; set; } public string title { get; set; } + public string termsOfService { get; set; } public SwaggerConcactInfo contact { get; set; } } @@ -90,10 +91,12 @@ namespace Emby.Server.Implementations.Services public string @default { get; set; } } - public class SwaggerService : IService + public class SwaggerService : IService, IRequiresRequest { private SwaggerSpec _spec; + public IRequest Request { get; set; } + public object Get(GetSwaggerSpec request) { return _spec ?? (_spec = GetSpec()); @@ -101,6 +104,13 @@ namespace Emby.Server.Implementations.Services private SwaggerSpec GetSpec() { + string host = null; + Uri uri; + if (Uri.TryCreate(Request.RawUrl, UriKind.Absolute, out uri)) + { + host = uri.Host; + } + var spec = new SwaggerSpec { schemes = new[] { "http" }, @@ -109,15 +119,18 @@ namespace Emby.Server.Implementations.Services info = new SwaggerInfo { title = "Emby Server API", - version = "1", + version = "1.0.0", description = "Explore the Emby Server API", contact = new SwaggerConcactInfo { email = "api@emby.media" - } + }, + termsOfService = "https://emby.media/terms" }, paths = GetPaths(), - definitions = GetDefinitions() + definitions = GetDefinitions(), + basePath = "/emby", + host = host }; return spec; @@ -144,6 +157,15 @@ namespace Emby.Server.Implementations.Services { foreach (var info in current.Value) { + if (info.Path.StartsWith("/mediabrowser", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + if (info.Path.StartsWith("/emby", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + paths[info.Path] = GetPathInfo(info); } } @@ -154,10 +176,19 @@ namespace Emby.Server.Implementations.Services private Dictionary GetPathInfo(RestPath info) { var result = new Dictionary(); - + foreach (var verb in info.Verbs) { - result[verb] = new SwaggerMethod + var responses = new Dictionary + { + }; + + responses["200"] = new SwaggerResponse + { + description = "OK" + }; + + result[verb.ToLower()] = new SwaggerMethod { summary = info.Summary, produces = new[] @@ -173,7 +204,9 @@ namespace Emby.Server.Implementations.Services operationId = info.RequestType.Name, tags = new string[] { }, - parameters = new SwaggerParam[] { } + parameters = new SwaggerParam[] { }, + + responses = responses }; } diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index 37613c1c81..d850b2ce72 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -192,11 +192,7 @@ namespace MediaBrowser.Api.System /// The request. public void Post(RestartApplication request) { - Task.Run(async () => - { - await Task.Delay(100).ConfigureAwait(false); - await _appHost.Restart().ConfigureAwait(false); - }); + _appHost.Restart(); } /// diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 2b5ae67992..dc0c9ac9bd 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -42,6 +42,8 @@ namespace MediaBrowser.Common /// true if this instance has pending kernel reload; otherwise, false. bool HasPendingRestart { get; } + bool IsShuttingDown { get; } + /// /// Gets a value indicating whether this instance can self restart. /// @@ -57,11 +59,11 @@ namespace MediaBrowser.Common /// Notifies the pending restart. /// void NotifyPendingRestart(); - + /// /// Restarts this instance. /// - Task Restart(); + void Restart(); /// /// Gets the application version. diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index fce9dea4f1..f9f3bf52e5 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -36,6 +36,8 @@ namespace MediaBrowser.Model.System /// true if this instance has pending restart; otherwise, false. public bool HasPendingRestart { get; set; } + public bool IsShuttingDown { get; set; } + /// /// Gets or sets a value indicating whether [supports library monitor]. /// diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 465c417e72..a2c10d6a86 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -120,7 +120,7 @@ namespace MediaBrowser.Providers.MediaInfo { get { - return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami" }; + return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami", ".vtt" }; } } diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs index 98ad8bb026..c53a80ae63 100644 --- a/MediaBrowser.Server.Mono/Program.cs +++ b/MediaBrowser.Server.Mono/Program.cs @@ -11,6 +11,7 @@ using System.Net.Security; using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Emby.Drawing; using Emby.Server.Core.Cryptography; using Emby.Server.Core; using Emby.Server.Implementations; @@ -18,6 +19,7 @@ using Emby.Server.Implementations.EnvironmentInfo; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Logging; using Emby.Server.Implementations.Networking; +using MediaBrowser.Controller; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; using Mono.Unix.Native; @@ -28,10 +30,10 @@ namespace MediaBrowser.Server.Mono { public class MainClass { - private static ApplicationHost _appHost; - private static ILogger _logger; private static IFileSystem FileSystem; + private static IServerApplicationPaths _appPaths; + private static ILogManager _logManager; private static readonly TaskCompletionSource ApplicationTaskCompletionSource = new TaskCompletionSource(); private static bool _restartOnShutdown; @@ -49,9 +51,12 @@ namespace MediaBrowser.Server.Mono var customProgramDataPath = options.GetOption("-programdata"); var appPaths = CreateApplicationPaths(applicationPath, customProgramDataPath); + _appPaths = appPaths; using (var logManager = new SimpleLogManager(appPaths.LogDirectoryPath, "server")) { + _logManager = logManager; + logManager.ReloadLogger(LogSeverity.Info); logManager.AddConsoleOutput(); @@ -68,7 +73,6 @@ namespace MediaBrowser.Server.Mono finally { _logger.Info("Disposing app host"); - _appHost.Dispose(); if (_restartOnShutdown) { @@ -106,40 +110,41 @@ namespace MediaBrowser.Server.Mono FileSystem = fileSystem; - var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths, environmentInfo); - - _appHost = new MonoAppHost(appPaths, + using (var appHost = new MonoAppHost(appPaths, logManager, options, fileSystem, new PowerManagement(), "emby.mono.zip", environmentInfo, - imageEncoder, + new NullImageEncoder(), new SystemEvents(logManager.GetLogger("SystemEvents")), - new NetworkManager(logManager.GetLogger("NetworkManager"))); - - if (options.ContainsOption("-v")) + new NetworkManager(logManager.GetLogger("NetworkManager")))) { - Console.WriteLine(_appHost.ApplicationVersion.ToString()); - return; + appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths, environmentInfo); + + if (options.ContainsOption("-v")) + { + Console.WriteLine(appHost.ApplicationVersion.ToString()); + return; + } + + Console.WriteLine("appHost.Init"); + + var initProgress = new Progress(); + + var task = appHost.Init(initProgress); + Task.WaitAll(task); + + Console.WriteLine("Running startup tasks"); + + task = appHost.RunStartupTasks(); + Task.WaitAll(task); + + task = ApplicationTaskCompletionSource.Task; + + Task.WaitAll(task); } - - Console.WriteLine("appHost.Init"); - - var initProgress = new Progress(); - - var task = _appHost.Init(initProgress); - Task.WaitAll(task); - - Console.WriteLine("Running startup tasks"); - - task = _appHost.RunStartupTasks(); - Task.WaitAll(task); - - task = ApplicationTaskCompletionSource.Task; - - Task.WaitAll(task); } private static MonoEnvironmentInfo GetEnvironmentInfo() @@ -231,7 +236,7 @@ namespace MediaBrowser.Server.Mono { var exception = (Exception)e.ExceptionObject; - new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager, FileSystem, new ConsoleLogger()).Log(exception); + new UnhandledExceptionWriter(_appPaths, _logger, _logManager, FileSystem, new ConsoleLogger()).Log(exception); if (!Debugger.IsAttached) { diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 31acd18200..3fe9c5aa7e 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -25,6 +25,7 @@ using Emby.Server.Implementations.EnvironmentInfo; using Emby.Server.Implementations.IO; using Emby.Server.Implementations.Logging; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Model.IO; using SystemEvents = Emby.Server.Implementations.SystemEvents; @@ -32,13 +33,13 @@ namespace MediaBrowser.ServerApplication { public class MainStartup { - private static ApplicationHost _appHost; + private static IServerApplicationPaths _appPaths; + private static ILogManager _logManager; private static ILogger _logger; public static bool IsRunningAsService = false; private static bool _canRestartService = false; - private static bool _appHostDisposed; [DllImport("kernel32.dll", SetLastError = true)] static extern bool SetDllDirectory(string lpPathName); @@ -46,6 +47,7 @@ namespace MediaBrowser.ServerApplication public static string ApplicationPath; private static IFileSystem FileSystem; + private static bool _restartOnShutdown; /// /// Defines the entry point of the application. @@ -71,9 +73,12 @@ namespace MediaBrowser.ServerApplication SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3()); var appPaths = CreateApplicationPaths(ApplicationPath, IsRunningAsService); + _appPaths = appPaths; using (var logManager = new SimpleLogManager(appPaths.LogDirectoryPath, "server")) { + _logManager = logManager; + logManager.ReloadLogger(LogSeverity.Debug); logManager.AddConsoleOutput(); @@ -129,17 +134,28 @@ namespace MediaBrowser.ServerApplication return; } - try + RunApplication(appPaths, logManager, IsRunningAsService, options); + + logger.Info("Shutdown complete"); + + if (_restartOnShutdown) { - RunApplication(appPaths, logManager, IsRunningAsService, options); - } - finally - { - OnServiceShutdown(); + logger.Info("Starting new server process"); + var restartCommandLine = GetRestartCommandLine(); + + Process.Start(restartCommandLine.Item1, restartCommandLine.Item2); } } } + public static Tuple GetRestartCommandLine() + { + var currentProcess = Process.GetCurrentProcess(); + var processModulePath = currentProcess.MainModule.FileName; + + return new Tuple(processModulePath, Environment.CommandLine); + } + /// /// Determines whether [is already running] [the specified current process]. /// @@ -314,7 +330,7 @@ namespace MediaBrowser.ServerApplication FileSystem = fileSystem; - _appHost = new WindowsAppHost(appPaths, + using (var appHost = new WindowsAppHost(appPaths, logManager, options, fileSystem, @@ -323,60 +339,59 @@ namespace MediaBrowser.ServerApplication environmentInfo, new NullImageEncoder(), new SystemEvents(logManager.GetLogger("SystemEvents")), - new Networking.NetworkManager(logManager.GetLogger("NetworkManager"))); - - var initProgress = new Progress(); - - if (!runService) + new Networking.NetworkManager(logManager.GetLogger("NetworkManager")))) { - if (!options.ContainsOption("-nosplash")) ShowSplashScreen(_appHost.ApplicationVersion, initProgress, logManager.GetLogger("Splash")); + var initProgress = new Progress(); - // 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); - } + if (!runService) + { + if (!options.ContainsOption("-nosplash")) ShowSplashScreen(appHost.ApplicationVersion, initProgress, logManager.GetLogger("Splash")); - var task = _appHost.Init(initProgress); - Task.WaitAll(task); + // 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); + } - if (!runService) - { - task = InstallVcredist2013IfNeeded(_appHost, _logger); + var task = appHost.Init(initProgress); Task.WaitAll(task); - // needed by skia - task = InstallVcredist2015IfNeeded(_appHost, _logger); - Task.WaitAll(task); - } + if (!runService) + { + task = InstallVcredist2013IfNeeded(appHost.HttpClient, _logger); + Task.WaitAll(task); - // set image encoder here - _appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths); + // needed by skia + task = InstallVcredist2015IfNeeded(appHost.HttpClient, _logger); + Task.WaitAll(task); + } - task = task.ContinueWith(new Action(a => _appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent); + // set image encoder here + appHost.ImageProcessor.ImageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => appHost.HttpClient, appPaths); - if (runService) - { - StartService(logManager); - } - else - { - Task.WaitAll(task); + task = task.ContinueWith(new Action(a => appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent); - Microsoft.Win32.SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; + if (runService) + { + StartService(logManager); + } + else + { + Task.WaitAll(task); - HideSplashScreen(); + HideSplashScreen(); - ShowTrayIcon(); + ShowTrayIcon(appHost); + } } } private static ServerNotifyIcon _serverNotifyIcon; private static TaskScheduler _mainTaskScheduler; - private static void ShowTrayIcon() + private static void ShowTrayIcon(ApplicationHost appHost) { //Application.EnableVisualStyles(); //Application.SetCompatibleTextRenderingDefault(false); - _serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.LocalizationManager); + _serverNotifyIcon = new ServerNotifyIcon(appHost.LogManager, appHost, appHost.ServerConfigurationManager, appHost.LocalizationManager); _mainTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); Application.Run(); } @@ -413,14 +428,6 @@ namespace MediaBrowser.ServerApplication } } - static void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e) - { - if (e.Reason == SessionSwitchReason.SessionLogon) - { - BrowserLauncher.OpenDashboard(_appHost); - } - } - public static void Invoke(Action action) { if (IsRunningAsService) @@ -452,14 +459,6 @@ namespace MediaBrowser.ServerApplication /// The instance containing the event data. static void service_Disposed(object sender, EventArgs e) { - OnServiceShutdown(); - } - - private static void OnServiceShutdown() - { - _logger.Info("Shutting down"); - - DisposeAppHost(); } /// @@ -562,7 +561,7 @@ namespace MediaBrowser.ServerApplication { var exception = (Exception)e.ExceptionObject; - new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager, FileSystem, new ConsoleLogger()).Log(exception); + new UnhandledExceptionWriter(_appPaths, _logger, _logManager, FileSystem, new ConsoleLogger()).Log(exception); if (!IsRunningAsService) { @@ -623,45 +622,22 @@ namespace MediaBrowser.ServerApplication } 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(ApplicationPath); - + _restartOnShutdown = true; ShutdownWindowsApplication(); } } - private static void DisposeAppHost() - { - if (!_appHostDisposed) - { - _logger.Info("Disposing app host"); - - _appHostDisposed = true; - _appHost.Dispose(); - _logger.Info("App host dispose complete"); - } - } - private static void ShutdownWindowsApplication() { if (_serverNotifyIcon != null) @@ -671,9 +647,7 @@ namespace MediaBrowser.ServerApplication } _logger.Info("Calling Application.Exit"); - //Application.Exit(); - - Environment.Exit(0); + Application.Exit(); } private static void ShutdownWindowsService() @@ -689,23 +663,7 @@ namespace MediaBrowser.ServerApplication } } - private static void RestartWindowsService() - { - _logger.Info("Restarting background service"); - - var startInfo = new ProcessStartInfo - { - FileName = "cmd.exe", - CreateNoWindow = true, - WindowStyle = ProcessWindowStyle.Hidden, - Verb = "runas", - ErrorDialog = false, - Arguments = String.Format("/c sc stop {0} & sc start {0} & sc start {0}", BackgroundService.GetExistingServiceName()) - }; - Process.Start(startInfo); - } - - private static async Task InstallVcredist2013IfNeeded(ApplicationHost appHost, ILogger logger) + private static async Task InstallVcredist2013IfNeeded(IHttpClient httpClient, ILogger logger) { // Reference // http://stackoverflow.com/questions/12206314/detect-if-visual-c-redistributable-for-visual-studio-2012-is-installed @@ -737,7 +695,7 @@ namespace MediaBrowser.ServerApplication try { - await InstallVcredist(GetVcredist2013Url()).ConfigureAwait(false); + await InstallVcredist(GetVcredist2013Url(), httpClient).ConfigureAwait(false); } catch (Exception ex) { @@ -757,7 +715,7 @@ namespace MediaBrowser.ServerApplication return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2013/vcredist_x86.exe"; } - private static async Task InstallVcredist2015IfNeeded(ApplicationHost appHost, ILogger logger) + private static async Task InstallVcredist2015IfNeeded(IHttpClient httpClient, ILogger logger) { // Reference // http://stackoverflow.com/questions/12206314/detect-if-visual-c-redistributable-for-visual-studio-2012-is-installed @@ -813,7 +771,7 @@ namespace MediaBrowser.ServerApplication try { - await InstallVcredist(GetVcredist2015Url()).ConfigureAwait(false); + await InstallVcredist(GetVcredist2015Url(), httpClient).ConfigureAwait(false); } catch (Exception ex) { @@ -833,10 +791,8 @@ namespace MediaBrowser.ServerApplication return "https://github.com/MediaBrowser/Emby.Resources/raw/master/vcredist2015/vc_redist.x86.exe"; } - private async static Task InstallVcredist(string url) + private async static Task InstallVcredist(string url, IHttpClient httpClient) { - var httpClient = _appHost.HttpClient; - var tmp = await httpClient.GetTempFile(new HttpRequestOptions { Url = url, diff --git a/SharedVersion.cs b/SharedVersion.cs index 93d7c2e196..2de4c7944b 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.30.10")] +[assembly: AssemblyVersion("3.2.30.11")]