Browse Source

Merge pull request #2364 from MediaBrowser/dev

Dev
Luke 8 năm trước cách đây
mục cha
commit
026e58eb16

+ 6 - 0
Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs

@@ -10,11 +10,17 @@ namespace Emby.Common.Implementations.EnvironmentInfo
     public class EnvironmentInfo : IEnvironmentInfo
     {
         public MediaBrowser.Model.System.Architecture? CustomArchitecture { get; set; }
+        public MediaBrowser.Model.System.OperatingSystem? CustomOperatingSystem { get; set; }
 
         public MediaBrowser.Model.System.OperatingSystem OperatingSystem
         {
             get
             {
+                if (CustomOperatingSystem.HasValue)
+                {
+                    return CustomOperatingSystem.Value;
+                }
+
 #if NET46
                 switch (Environment.OSVersion.Platform)
                 {

+ 14 - 6
Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs

@@ -282,9 +282,12 @@ namespace Emby.Common.Implementations.ScheduledTasks
                     throw new ArgumentNullException("value");
                 }
 
-                SaveTriggers(value);
+                // This null check is not great, but is needed to handle bad user input, or user mucking with the config file incorrectly
+                var triggerList = value.Where(i => i != null).ToArray();
 
-                InternalTriggers = value.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
+                SaveTriggers(triggerList);
+
+                InternalTriggers = triggerList.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
             }
         }
 
@@ -535,7 +538,8 @@ namespace Emby.Common.Implementations.ScheduledTasks
         /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
         private Tuple<TaskTriggerInfo, ITaskTrigger>[] LoadTriggers()
         {
-            var settings = LoadTriggerSettings();
+            // This null check is not great, but is needed to handle bad user input, or user mucking with the config file incorrectly
+            var settings = LoadTriggerSettings().Where(i => i != null).ToArray();
 
             return settings.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
         }
@@ -544,8 +548,12 @@ namespace Emby.Common.Implementations.ScheduledTasks
         {
             try
             {
-                return JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath())
-                .ToArray();
+                var list = JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath());
+
+                if (list != null)
+                {
+                    return list.ToArray();
+                }
             }
             catch (FileNotFoundException)
             {
@@ -555,8 +563,8 @@ namespace Emby.Common.Implementations.ScheduledTasks
             catch (DirectoryNotFoundException)
             {
                 // File doesn't exist. No biggie. Return defaults.
-                return ScheduledTask.GetDefaultTriggers().ToArray();
             }
+            return ScheduledTask.GetDefaultTriggers().ToArray();
         }
 
         /// <summary>

+ 3 - 2
Emby.Server.Core/ApplicationHost.cs

@@ -1583,7 +1583,8 @@ namespace Emby.Server.Core
 
         public void LaunchUrl(string url)
         {
-            if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows)
+            if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows &&
+                EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX)
             {
                 throw new NotImplementedException();
             }
@@ -1591,7 +1592,7 @@ namespace Emby.Server.Core
             var process = ProcessFactory.Create(new ProcessOptions
             {
                 FileName = url,
-                EnableRaisingEvents = true,
+                //EnableRaisingEvents = true,
                 UseShellExecute = true,
                 ErrorDialog = false
             });

+ 33 - 38
Emby.Server.Implementations/HttpServer/HttpResultFactory.cs

@@ -203,20 +203,12 @@ 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 compressionStream = GetCompressionStream(ms, compressionType))
-                {
-                    ContentTypes.Instance.SerializeToStream(request, dto, compressionStream);
-                    compressionStream.Dispose();
-
-                    var compressedBytes = ms.ToArray();
+                ContentTypes.Instance.SerializeToStream(request, dto, ms);
+                ms.Position = 0;
 
-                    var httpResult = new StreamWriter(compressedBytes, request.ResponseContentType, _logger);
+                var responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
-                    //httpResult.Headers["Content-Length"] = compressedBytes.Length.ToString(UsCulture);
-                    httpResult.Headers["Content-Encoding"] = compressionType;
-
-                    return httpResult;
-                }
+                return GetCompressedResult(ms, compressionType, responseHeaders, false, request.ResponseContentType).Result;
             }
         }
 
@@ -591,45 +583,53 @@ namespace Emby.Server.Implementations.HttpServer
                 };
             }
 
-            string content;
-
             using (var stream = await factoryFn().ConfigureAwait(false))
             {
-                using (var reader = new StreamReader(stream))
+                return await GetCompressedResult(stream, requestedCompressionType, responseHeaders, isHeadRequest, contentType).ConfigureAwait(false);
+            }
+        }
+
+        private async Task<IHasHeaders> GetCompressedResult(Stream stream, 
+            string requestedCompressionType, 
+            IDictionary<string,string> responseHeaders,
+            bool isHeadRequest,
+            string contentType)
+        {
+            using (var reader = new MemoryStream())
+            {
+                await stream.CopyToAsync(reader).ConfigureAwait(false);
+
+                reader.Position = 0;
+                var content = reader.ToArray();
+
+                if (content.Length >= 1024)
                 {
-                    content = await reader.ReadToEndAsync().ConfigureAwait(false);
+                    content = Compress(content, requestedCompressionType);
+                    responseHeaders["Content-Encoding"] = requestedCompressionType;
                 }
-            }
 
-            var contents = Compress(content, requestedCompressionType);
+                responseHeaders["Content-Length"] = content.Length.ToString(UsCulture);
 
-            responseHeaders["Content-Length"] = contents.Length.ToString(UsCulture);
-            responseHeaders["Content-Encoding"] = requestedCompressionType;
+                if (isHeadRequest)
+                {
+                    return GetHttpResult(new byte[] { }, contentType, true);
+                }
 
-            if (isHeadRequest)
-            {
-                return GetHttpResult(new byte[] { }, contentType, true);
+                return GetHttpResult(content, contentType, true, responseHeaders);
             }
-
-            return GetHttpResult(contents, contentType, true, responseHeaders);
         }
 
-        private byte[] Compress(string text, string compressionType)
+        private byte[] Compress(byte[] bytes, string compressionType)
         {
             if (compressionType == "deflate")
-                return Deflate(text);
+                return Deflate(bytes);
 
             if (compressionType == "gzip")
-                return GZip(text);
+                return GZip(bytes);
 
             throw new NotSupportedException(compressionType);
         }
 
-        private byte[] Deflate(string text)
-        {
-            return Deflate(Encoding.UTF8.GetBytes(text));
-        }
-
         private byte[] Deflate(byte[] bytes)
         {
             // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream
@@ -644,11 +644,6 @@ namespace Emby.Server.Implementations.HttpServer
             }
         }
 
-        private byte[] GZip(string text)
-        {
-            return GZip(Encoding.UTF8.GetBytes(text));
-        }
-
         private byte[] GZip(byte[] buffer)
         {
             using (var ms = new MemoryStream())

+ 19 - 8
Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -74,21 +74,21 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
 
             if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
             {
-                return ResolveVideos<MusicVideo>(parent, files, directoryService, false);
+                return ResolveVideos<MusicVideo>(parent, files, directoryService, false, collectionType);
             }
 
             if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
                             string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
             {
-                return ResolveVideos<Video>(parent, files, directoryService, false);
+                return ResolveVideos<Video>(parent, files, directoryService, false, collectionType);
             }
 
-            if (string.IsNullOrEmpty(collectionType))
+            if (string.IsNullOrWhiteSpace(collectionType))
             {
                 // Owned items should just use the plain video type
                 if (parent == null)
                 {
-                    return ResolveVideos<Video>(parent, files, directoryService, false);
+                    return ResolveVideos<Video>(parent, files, directoryService, false, collectionType);
                 }
 
                 if (parent is Series || parent.GetParents().OfType<Series>().Any())
@@ -96,18 +96,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                     return null;
                 }
 
-                return ResolveVideos<Movie>(parent, files, directoryService, false);
+                return ResolveVideos<Movie>(parent, files, directoryService, false, collectionType);
             }
 
             if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
             {
-                return ResolveVideos<Movie>(parent, files, directoryService, true);
+                return ResolveVideos<Movie>(parent, files, directoryService, true, collectionType);
             }
 
             return null;
         }
 
-        private MultiItemResolverResult ResolveVideos<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions)
+        private MultiItemResolverResult ResolveVideos<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType)
             where T : Video, new()
         {
             var files = new List<FileSystemMetadata>();
@@ -117,6 +117,16 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             // Loop through each child file/folder and see if we find a video
             foreach (var child in fileSystemEntries)
             {
+                // This is a hack but currently no better way to resolve a sometimes ambiguous situation
+                if (string.IsNullOrWhiteSpace(collectionType))
+                {
+                    if (string.Equals(child.Name, "tvshow.nfo", StringComparison.OrdinalIgnoreCase) ||
+                        string.Equals(child.Name, "season.nfo", StringComparison.OrdinalIgnoreCase))
+                    {
+                        return null;
+                    }
+                }
+
                 if (child.IsDirectory)
                 {
                     leftOver.Add(child);
@@ -408,7 +418,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                                         !string.Equals(collectionType, CollectionType.Photos) &&
                                         !string.Equals(collectionType, CollectionType.MusicVideos);
 
-                var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion);
+                var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType) ??
+                    new MultiItemResolverResult();
 
                 if (result.Items.Count == 1)
                 {

+ 15 - 2
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             }
 
             var showType = details.showType ?? string.Empty;
-
+            
             var info = new ProgramInfo
             {
                 ChannelId = channel,
@@ -440,10 +440,23 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
                 IsSports = showType.IndexOf("sports", StringComparison.OrdinalIgnoreCase) != -1,
                 IsMovie = showType.IndexOf("movie", StringComparison.OrdinalIgnoreCase) != -1 || showType.IndexOf("film", StringComparison.OrdinalIgnoreCase) != -1,
-                ShowId = programInfo.programID,
                 Etag = programInfo.md5
             };
 
+            var showId = programInfo.programID ?? string.Empty;
+
+            // According to SchedulesDirect, these are generic, unidentified episodes
+            // SH005316560000
+            var hasUniqueShowId = !showId.StartsWith("SH", StringComparison.OrdinalIgnoreCase) ||
+                !showId.EndsWith("0000", StringComparison.OrdinalIgnoreCase);
+
+            if (!hasUniqueShowId)
+            {
+                showId = newID;
+            }
+
+            info.ShowId = showId;
+
             if (programInfo.videoProperties != null)
             {
                 info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);

+ 1 - 1
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -2647,7 +2647,7 @@ namespace Emby.Server.Implementations.LiveTv
         public GuideInfo GetGuideInfo()
         {
             var startDate = DateTime.UtcNow;
-            var endDate = startDate.AddDays(14);
+            var endDate = startDate.AddDays(GetGuideDays());
 
             return new GuideInfo
             {

+ 12 - 15
Emby.Server.Implementations/Sync/SyncManager.cs

@@ -1042,10 +1042,7 @@ namespace Emby.Server.Implementations.Sync
                 throw new ArgumentException("Operation is not valid for this job item");
             }
 
-            if (jobItem.Status != SyncJobItemStatus.Synced)
-            {
-                jobItem.Status = SyncJobItemStatus.Cancelled;
-            }
+            jobItem.Status = SyncJobItemStatus.Cancelled;
 
             jobItem.Progress = 0;
             jobItem.IsMarkedForRemoval = true;
@@ -1071,18 +1068,18 @@ namespace Emby.Server.Implementations.Sync
                 _logger.ErrorException("Error deleting directory {0}", ex, path);
             }
 
-            //var jobItemsResult = GetJobItems(new SyncJobItemQuery
-            //{
-            //    AddMetadata = false,
-            //    JobId = jobItem.JobId,
-            //    Limit = 0,
-            //    Statuses = new[] { SyncJobItemStatus.Converting, SyncJobItemStatus.Failed, SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Synced, SyncJobItemStatus.Transferring }
-            //});
+            var jobItemsResult = GetJobItems(new SyncJobItemQuery
+            {
+                AddMetadata = false,
+                JobId = jobItem.JobId,
+                Limit = 0,
+                Statuses = new[] { SyncJobItemStatus.Converting, SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Synced, SyncJobItemStatus.Transferring }
+            });
 
-            //if (jobItemsResult.TotalRecordCount == 0)
-            //{
-            //    await CancelJob(jobItem.JobId).ConfigureAwait(false);
-            //}
+            if (jobItemsResult.TotalRecordCount == 0)
+            {
+                await CancelJob(jobItem.JobId).ConfigureAwait(false);
+            }
         }
 
         public Task MarkJobItemForRemoval(string id)

+ 12 - 0
Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs

@@ -38,6 +38,18 @@ namespace Emby.Server.Implementations.Sync
 
                 }
             }
+
+            if (item.Status == SyncJobItemStatus.Cancelled)
+            {
+                try
+                {
+                    await _sessionManager.SendMessageToUserDeviceSessions(item.TargetId, "SyncJobItemCancelled", item, CancellationToken.None).ConfigureAwait(false);
+                }
+                catch
+                {
+
+                }
+            }
         }
 
         public void Dispose()

+ 1 - 1
MediaBrowser.Api/Playback/Progressive/VideoService.cs

@@ -153,7 +153,7 @@ namespace MediaBrowser.Api.Playback.Progressive
 
                 if (!state.RunTimeTicks.HasValue)
                 {
-                    args += " -fflags +genpts -flags +global_header";
+                    args += " -flags -global_header -fflags +genpts";
                 }
 
                 return args;

+ 6 - 1
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -95,6 +95,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
             int defaultImageExtractionTimeoutMs,
             bool enableEncoderFontFile, IEnvironmentInfo environmentInfo)
         {
+            if (jsonSerializer == null)
+            {
+                throw new ArgumentNullException("jsonSerializer");
+            }
+
             _logger = logger;
             _jsonSerializer = jsonSerializer;
             ConfigurationManager = configurationManager;
@@ -632,7 +637,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                     var result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
 
-                    if (result.streams == null && result.format == null)
+                    if (result == null || (result.streams == null && result.format == null))
                     {
                         throw new Exception("ffprobe failed - streams and format are both null.");
                     }

+ 1 - 1
MediaBrowser.Providers/TV/MissingEpisodeProvider.cs

@@ -238,7 +238,7 @@ namespace MediaBrowser.Providers.TV
                 var targetSeries = DetermineAppropriateSeries(series, tuple.Item1);
                 var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(targetSeries.ProviderIds) ?? ((targetSeries.AnimeSeriesIndex ?? 1) - 1);
 
-                var unairedThresholdDays = 1;
+                var unairedThresholdDays = 2;
                 now = now.AddDays(0 - unairedThresholdDays);
 
                 if (airDate.Value < now)

+ 4 - 1
MediaBrowser.Server.Mac/Main.cs

@@ -142,7 +142,10 @@ namespace MediaBrowser.Server.Mac
 
         private static EnvironmentInfo GetEnvironmentInfo()
         {
-            var info = new EnvironmentInfo();
+            var info = new EnvironmentInfo()
+            {
+                CustomOperatingSystem = MediaBrowser.Model.System.OperatingSystem.OSX
+            };
 
             var uname = GetUnixName();
 

+ 22 - 0
MediaBrowser.Server.Mono/MonoAppHost.cs

@@ -51,8 +51,11 @@ namespace MediaBrowser.Server.Mono
             }
             else if (environment.OperatingSystem == Model.System.OperatingSystem.Linux)
             {
+                info.FFMpegFilename = "ffmpeg";
+                info.FFProbeFilename = "ffprobe";
                 info.ArchiveType = "7z";
                 info.Version = "20160215";
+                info.DownloadUrls = GetDownloadUrls();
             }
 
             // No version available - user requirement
@@ -61,6 +64,25 @@ namespace MediaBrowser.Server.Mono
             return info;
         }
 
+        private string[] GetDownloadUrls()
+        {
+            switch (EnvironmentInfo.SystemArchitecture)
+            {
+                case Architecture.X64:
+                    return new[]
+                    {
+                                "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
+                    };
+                case Architecture.X86:
+                    return new[]
+                    {
+                                "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z"
+                    };
+            }
+
+            return new string[] { };
+        }
+
         protected override void RestartInternal()
         {
             MainClass.Restart(StartupOptions);

+ 2 - 67
MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs

@@ -1,6 +1,7 @@
 using System;
 using System.IO;
 using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
 using System.Security;
 using System.Text;
 using MediaBrowser.Model.IO;
@@ -52,7 +53,7 @@ namespace MediaBrowser.ServerApplication.Native
         /// <summary>
         /// The STG m_ READ
         /// </summary>
-        public const uint STGM_READ = 0;
+        public const int STGM_READ = 0;
     }
 
     /// <summary>
@@ -319,72 +320,6 @@ namespace MediaBrowser.ServerApplication.Native
 
     }
 
-    /// <summary>
-    /// Interface IPersist
-    /// </summary>
-    [ComImport, Guid("0000010c-0000-0000-c000-000000000046"),
-    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
-    public interface IPersist
-    {
-        /// <summary>
-        /// Gets the class ID.
-        /// </summary>
-        /// <param name="pClassID">The p class ID.</param>
-        [PreserveSig]
-        void GetClassID(out Guid pClassID);
-    }
-
-    /// <summary>
-    /// Interface IPersistFile
-    /// </summary>
-    [ComImport, Guid("0000010b-0000-0000-C000-000000000046"),
-    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
-    public interface IPersistFile : IPersist
-    {
-        /// <summary>
-        /// Gets the class ID.
-        /// </summary>
-        /// <param name="pClassID">The p class ID.</param>
-        new void GetClassID(out Guid pClassID);
-        /// <summary>
-        /// Determines whether this instance is dirty.
-        /// </summary>
-        [PreserveSig]
-        int IsDirty();
-
-        /// <summary>
-        /// Loads the specified PSZ file name.
-        /// </summary>
-        /// <param name="pszFileName">Name of the PSZ file.</param>
-        /// <param name="dwMode">The dw mode.</param>
-        [PreserveSig]
-        void Load([In, MarshalAs(UnmanagedType.LPWStr)]
-            string pszFileName, uint dwMode);
-
-        /// <summary>
-        /// Saves the specified PSZ file name.
-        /// </summary>
-        /// <param name="pszFileName">Name of the PSZ file.</param>
-        /// <param name="remember">if set to <c>true</c> [remember].</param>
-        [PreserveSig]
-        void Save([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
-            [In, MarshalAs(UnmanagedType.Bool)] bool remember);
-
-        /// <summary>
-        /// Saves the completed.
-        /// </summary>
-        /// <param name="pszFileName">Name of the PSZ file.</param>
-        [PreserveSig]
-        void SaveCompleted([In, MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
-
-        /// <summary>
-        /// Gets the cur file.
-        /// </summary>
-        /// <param name="ppszFileName">Name of the PPSZ file.</param>
-        [PreserveSig]
-        void GetCurFile([In, MarshalAs(UnmanagedType.LPWStr)] string ppszFileName);
-    }
-
     // CLSID_ShellLink from ShlGuid.h 
     /// <summary>
     /// Class ShellLink

+ 17 - 7
MediaBrowser.ServerApplication/WindowsAppHost.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Reflection;
+using System.Runtime.InteropServices.ComTypes;
 using Emby.Server.Core;
 using Emby.Server.Implementations;
 using Emby.Server.Implementations.EntryPoints;
@@ -93,21 +94,30 @@ namespace MediaBrowser.ServerApplication
 
         protected override void ConfigureAutoRunInternal(bool autorun)
         {
-            var shortcutPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.StartMenu), "Emby", "Emby Server.lnk");
-
             var startupPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Startup);
 
-            if (autorun)
+            if (autorun && !MainStartup.IsRunningAsService)
             {
                 //Copy our shortut into the startup folder for this user
-                var targetPath = Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk");
-                FileSystemManager.CreateDirectory(Path.GetDirectoryName(targetPath));
-                File.Copy(shortcutPath, targetPath, true);
+                var targetPath = Path.Combine(startupPath, "Emby Server.lnk");
+
+                IShellLinkW link = (IShellLinkW)new ShellLink();
+
+                var appPath = Process.GetCurrentProcess().MainModule.FileName;
+
+                // setup shortcut information
+                link.SetDescription(Name);
+                link.SetPath(appPath);
+                link.SetWorkingDirectory(Path.GetDirectoryName(appPath));
+
+                // save it
+                IPersistFile file = (IPersistFile)link;
+                file.Save(targetPath, false);
             }
             else
             {
                 //Remove our shortcut from the startup folder for this user
-                FileSystemManager.DeleteFile(Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk"));
+                FileSystemManager.DeleteFile(Path.Combine(startupPath, "Emby Server.lnk"));
             }
         }
 

+ 2 - 2
SocketHttpListener.Portable/Net/HttpConnection.cs

@@ -209,7 +209,7 @@ namespace SocketHttpListener.Net
             // TODO: can we get this stream before reading the input?
             if (o_stream == null)
             {
-                context.Response.DetermineIfChunked();
+                //context.Response.DetermineIfChunked();
 
                 if (context.Response.SendChunked || isExpect100Continue || context.Request.IsWebSocketRequest || true)
                 {
@@ -508,7 +508,7 @@ namespace SocketHttpListener.Net
             {
                 force_close |= !context.Request.KeepAlive;
                 if (!force_close)
-                    force_close = (context.Response.Headers["connection"] == "close");
+                    force_close = (string.Equals(context.Response.Headers["connection"], "close", StringComparison.OrdinalIgnoreCase));
                 /*
 				if (!force_close) {
 //					bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||

+ 4 - 3
SocketHttpListener.Portable/Net/HttpListenerResponse.cs

@@ -386,7 +386,7 @@ namespace SocketHttpListener.Net
 
             if (content_type != null)
             {
-                if (content_encoding != null && content_type.IndexOf("charset=", StringComparison.Ordinal) == -1)
+                if (content_encoding != null && content_type.IndexOf("charset=", StringComparison.OrdinalIgnoreCase) == -1)
                 {
                     string enc_name = content_encoding.WebName;
                     headers.SetInternal("Content-Type", content_type + "; charset=" + enc_name);
@@ -429,9 +429,10 @@ namespace SocketHttpListener.Net
              *	HttpStatusCode.InternalServerError 	500
              *	HttpStatusCode.ServiceUnavailable 	503
              */
-            bool conn_close = (status_code == 408 || status_code == 411 ||
+            bool conn_close = status_code == 400 || status_code == 408 || status_code == 411 ||
                     status_code == 413 || status_code == 414 ||
-                    status_code == 503);
+                    status_code == 500 ||
+                    status_code == 503;
 
             if (conn_close == false)
                 conn_close = !context.Request.KeepAlive;

+ 11 - 1
SocketHttpListener.Portable/Net/ResponseStream.cs

@@ -136,6 +136,11 @@ namespace SocketHttpListener.Net
             if (disposed)
                 throw new ObjectDisposedException(GetType().ToString());
 
+            if (count == 0)
+            {
+                //return;
+            }
+
             byte[] bytes = null;
             MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false);
             bool chunked = response.SendChunked;
@@ -176,6 +181,11 @@ namespace SocketHttpListener.Net
             if (disposed)
                 throw new ObjectDisposedException(GetType().ToString());
 
+            if (count == 0)
+            {
+                //return;
+            }
+
             byte[] bytes = null;
             MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false);
             bool chunked = response.SendChunked;
@@ -206,7 +216,7 @@ namespace SocketHttpListener.Net
                 await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
             }
 
-            if (response.SendChunked)
+            if (chunked)
                 stream.Write(crlf, 0, 2);
         }