2
0
Эх сурвалжийг харах

Merge pull request #2087 from MediaBrowser/dev

Dev
Luke 8 жил өмнө
parent
commit
3f6b5feffb

+ 8 - 6
MediaBrowser.Api/Library/FileOrganizationService.cs

@@ -154,9 +154,12 @@ namespace MediaBrowser.Api.Library
 
 
         public void Post(PerformOrganization request)
         public void Post(PerformOrganization request)
         {
         {
+            // Don't await this
             var task = _iFileOrganizationService.PerformOrganization(request.Id);
             var task = _iFileOrganizationService.PerformOrganization(request.Id);
 
 
-            Task.WaitAll(task);
+            // Async processing (close dialog early instead of waiting until the file has been copied)
+            // Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
+            task.Wait(2000);
         }
         }
 
 
         public void Post(OrganizeEpisode request)
         public void Post(OrganizeEpisode request)
@@ -168,6 +171,7 @@ namespace MediaBrowser.Api.Library
                 dicNewProviderIds = request.NewSeriesProviderIds;
                 dicNewProviderIds = request.NewSeriesProviderIds;
             }
             }
 
 
+            // Don't await this
             var task = _iFileOrganizationService.PerformEpisodeOrganization(new EpisodeFileOrganizationRequest
             var task = _iFileOrganizationService.PerformEpisodeOrganization(new EpisodeFileOrganizationRequest
             {
             {
                 EndingEpisodeNumber = request.EndingEpisodeNumber,
                 EndingEpisodeNumber = request.EndingEpisodeNumber,
@@ -182,11 +186,9 @@ namespace MediaBrowser.Api.Library
                 TargetFolder = request.TargetFolder
                 TargetFolder = request.TargetFolder
             });
             });
 
 
-            // For async processing (close dialog early instead of waiting until the file has been copied)
-            //var tasks = new Task[] { task };
-            //Task.WaitAll(tasks, 8000);
-
-            Task.WaitAll(task);
+            // Async processing (close dialog early instead of waiting until the file has been copied)
+            // Wait 2s for exceptions that may occur to have them forwarded to the client for immediate error display
+            task.Wait(2000);
         }
         }
 
 
         public object Get(GetSmartMatchInfos request)
         public object Get(GetSmartMatchInfos request)

+ 23 - 1
MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs

@@ -1,5 +1,7 @@
-using MediaBrowser.Model.FileOrganization;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.FileOrganization;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
+using System;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
@@ -7,6 +9,11 @@ namespace MediaBrowser.Controller.FileOrganization
 {
 {
     public interface IFileOrganizationService
     public interface IFileOrganizationService
     {
     {
+        event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
+        event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
+        event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
+        event EventHandler LogReset;
+
         /// <summary>
         /// <summary>
         /// Processes the new files.
         /// Processes the new files.
         /// </summary>
         /// </summary>
@@ -81,5 +88,20 @@ namespace MediaBrowser.Controller.FileOrganization
         /// <param name="ItemName">Item name.</param>
         /// <param name="ItemName">Item name.</param>
         /// <param name="matchString">The match string to delete.</param>
         /// <param name="matchString">The match string to delete.</param>
         void DeleteSmartMatchEntry(string ItemName, string matchString);
         void DeleteSmartMatchEntry(string ItemName, string matchString);
+
+        /// <summary>
+        /// Attempts to add a an item to the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <param name="fullClientRefresh">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
+        /// <returns>True if the item was added, False if the item is already contained in the list.</returns>
+        bool AddToInProgressList(FileOrganizationResult result, bool fullClientRefresh);
+
+        /// <summary>
+        /// Removes an item from the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
+        bool RemoveFromInprogressList(FileOrganizationResult result);
     }
     }
 }
 }

+ 9 - 3
MediaBrowser.Controller/Session/ISessionManager.cs

@@ -172,15 +172,21 @@ namespace MediaBrowser.Controller.Session
         Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken);
         Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
-        /// Sends the message to user sessions.
+        /// Sends the message to admin sessions.
         /// </summary>
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <typeparam name="T"></typeparam>
-        /// <param name="userId">The user identifier.</param>
         /// <param name="name">The name.</param>
         /// <param name="name">The name.</param>
         /// <param name="data">The data.</param>
         /// <param name="data">The data.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        Task SendMessageToUserSessions<T>(string userId, string name, T data, CancellationToken cancellationToken);
+        Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Sends the message to user sessions.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <returns>Task.</returns>
+        Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Sends the message to user device sessions.
         /// Sends the message to user device sessions.

+ 6 - 0
MediaBrowser.Model/FileOrganization/FileOrganizationResult.cs

@@ -95,6 +95,12 @@ namespace MediaBrowser.Model.FileOrganization
         /// <value>The size of the file.</value>
         /// <value>The size of the file.</value>
         public long FileSize { get; set; }
         public long FileSize { get; set; }
 
 
+        /// <summary>
+        /// Indicates if the item is currently being processed.
+        /// </summary>
+        /// <remarks>Runtime property not persisted to the store.</remarks>
+        public bool IsInProgress { get; set; }
+
         public FileOrganizationResult()
         public FileOrganizationResult()
         {
         {
             DuplicatePaths = new List<string>();
             DuplicatePaths = new List<string>();

+ 7 - 10
MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs

@@ -54,18 +54,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
 
 
         private async void SendMessage(string name, TimerEventInfo info)
         private async void SendMessage(string name, TimerEventInfo info)
         {
         {
-            var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).ToList();
+            var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id.ToString("N")).ToList();
 
 
-            foreach (var user in users)
+            try
             {
             {
-                try
-                {
-                    await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(user.Id.ToString("N"), name, info, CancellationToken.None);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error sending message", ex);
-                }
+                await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(users, name, info, CancellationToken.None);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error sending message", ex);
             }
             }
         }
         }
 
 

+ 2 - 1
MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs

@@ -11,6 +11,7 @@ using MediaBrowser.Controller.Sync;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Sync;
 using MediaBrowser.Model.Sync;
 using System;
 using System;
+using System.Collections.Generic;
 using System.Threading;
 using System.Threading;
 
 
 namespace MediaBrowser.Server.Implementations.EntryPoints
 namespace MediaBrowser.Server.Implementations.EntryPoints
@@ -164,7 +165,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
 
 
         private async void SendMessageToUserSession<T>(User user, string name, T data)
         private async void SendMessageToUserSession<T>(User user, string name, T data)
         {
         {
-            await _sessionManager.SendMessageToUserSessions(user.Id.ToString("N"), name, data, CancellationToken.None);
+            await _sessionManager.SendMessageToUserSessions(new List<string> { user.Id.ToString("N") }, name, data, CancellationToken.None);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 16 - 0
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -272,6 +272,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
 
             var originalExtractedSeriesString = result.ExtractedName;
             var originalExtractedSeriesString = result.ExtractedName;
 
 
+            bool isNew = string.IsNullOrWhiteSpace(result.Id);
+
+            if (isNew)
+            {
+                await _organizationService.SaveResult(result, cancellationToken);
+            }
+
+            if (!_organizationService.AddToInProgressList(result, isNew))
+            {
+                throw new Exception("File is currently processed otherwise. Please try again later.");
+            }
+            
             try
             try
             {
             {
             // Proceed to sort the file
             // Proceed to sort the file
@@ -363,6 +375,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 _logger.Warn(ex.Message);
                 _logger.Warn(ex.Message);
                 return;
                 return;
             }
             }
+            finally
+            {
+                _organizationService.RemoveFromInprogressList(result);
+            }
 
 
             if (rememberCorrection)
             if (rememberCorrection)
             {
             {

+ 68 - 0
MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs

@@ -0,0 +1,68 @@
+using MediaBrowser.Controller.FileOrganization;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.FileOrganization;
+using MediaBrowser.Controller.Session;
+using System.Threading;
+
+namespace MediaBrowser.Server.Implementations.FileOrganization
+{
+    /// <summary>
+    /// Class SessionInfoWebSocketListener
+    /// </summary>
+    class FileOrganizationNotifier : IServerEntryPoint
+    {
+        private readonly IFileOrganizationService _organizationService;
+        private readonly ISessionManager _sessionManager;
+
+        public FileOrganizationNotifier(ILogger logger, IFileOrganizationService organizationService, ISessionManager sessionManager)
+        {
+            _organizationService = organizationService;
+            _sessionManager = sessionManager;
+        }
+
+        public void Run()
+        {
+            _organizationService.ItemAdded += _organizationService_ItemAdded;
+            _organizationService.ItemRemoved += _organizationService_ItemRemoved;
+            _organizationService.ItemUpdated += _organizationService_ItemUpdated;
+            _organizationService.LogReset += _organizationService_LogReset;
+        }
+
+        private void _organizationService_LogReset(object sender, EventArgs e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
+        }
+
+        private void _organizationService_ItemUpdated(object sender, GenericEventArgs<FileOrganizationResult> e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", e.Argument, CancellationToken.None);
+        }
+
+        private void _organizationService_ItemRemoved(object sender, GenericEventArgs<FileOrganizationResult> e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
+        }
+
+        private void _organizationService_ItemAdded(object sender, GenericEventArgs<FileOrganizationResult> e)
+        {
+            _sessionManager.SendMessageToAdminSessions("AutoOrganizeUpdate", (FileOrganizationResult)null, CancellationToken.None);
+        }
+
+        public void Dispose()
+        {
+            _organizationService.ItemAdded -= _organizationService_ItemAdded;
+            _organizationService.ItemRemoved -= _organizationService_ItemRemoved;
+            _organizationService.ItemUpdated -= _organizationService_ItemUpdated;
+            _organizationService.LogReset -= _organizationService_LogReset;
+        }
+
+
+    }
+}

+ 94 - 6
MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs

@@ -3,16 +3,21 @@ using MediaBrowser.Common.ScheduledTasks;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.FileOrganization;
 using MediaBrowser.Controller.FileOrganization;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.FileOrganization;
 using MediaBrowser.Model.FileOrganization;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 using System;
 using System;
+using System.Collections.Concurrent;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Common.Events;
 
 
 namespace MediaBrowser.Server.Implementations.FileOrganization
 namespace MediaBrowser.Server.Implementations.FileOrganization
 {
 {
@@ -26,6 +31,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private readonly IProviderManager _providerManager;
         private readonly IProviderManager _providerManager;
+        private readonly ConcurrentDictionary<string, bool> _inProgressItemIds = new ConcurrentDictionary<string, bool>();
+
+        public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
+        public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
+        public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
+        public event EventHandler LogReset;
 
 
         public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager)
         public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager)
         {
         {
@@ -58,12 +69,26 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
 
         public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
         public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
         {
         {
-            return _repo.GetResults(query);
+            var results = _repo.GetResults(query);
+
+            foreach (var result in results.Items)
+            {
+                result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
+            }
+
+            return results;
         }
         }
 
 
         public FileOrganizationResult GetResult(string id)
         public FileOrganizationResult GetResult(string id)
         {
         {
-            return _repo.GetResult(id);
+            var result = _repo.GetResult(id);
+
+            if (result != null)
+            {
+                result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
+            }
+
+            return result;
         }
         }
 
 
         public FileOrganizationResult GetResultBySourcePath(string path)
         public FileOrganizationResult GetResultBySourcePath(string path)
@@ -78,11 +103,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             return GetResult(id);
             return GetResult(id);
         }
         }
 
 
-        public Task DeleteOriginalFile(string resultId)
+        public async Task DeleteOriginalFile(string resultId)
         {
         {
             var result = _repo.GetResult(resultId);
             var result = _repo.GetResult(resultId);
 
 
             _logger.Info("Requested to delete {0}", result.OriginalPath);
             _logger.Info("Requested to delete {0}", result.OriginalPath);
+
+            if (!AddToInProgressList(result, false))
+            {
+                throw new Exception("Path is currently processed otherwise. Please try again later.");
+            }
+
             try
             try
             {
             {
                 _fileSystem.DeleteFile(result.OriginalPath);
                 _fileSystem.DeleteFile(result.OriginalPath);
@@ -91,8 +122,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             {
             {
                 _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
                 _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
             }
             }
+            finally
+            {
+                RemoveFromInprogressList(result);
+            }
 
 
-            return _repo.Delete(resultId);
+            await _repo.Delete(resultId);
+
+            EventHelper.FireEventIfNotNull(ItemRemoved, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
         }
         }
 
 
         private AutoOrganizeOptions GetAutoOrganizeOptions()
         private AutoOrganizeOptions GetAutoOrganizeOptions()
@@ -121,9 +158,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }
             }
         }
         }
 
 
-        public Task ClearLog()
+        public async Task ClearLog()
         {
         {
-            return _repo.DeleteAll();
+            await _repo.DeleteAll();
+            EventHelper.FireEventIfNotNull(LogReset, this, EventArgs.Empty, _logger);
         }
         }
 
 
         public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
         public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
@@ -189,5 +227,55 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 _config.SaveAutoOrganizeOptions(options);
                 _config.SaveAutoOrganizeOptions(options);
             }
             }
         }
         }
+
+        /// <summary>
+        /// Attempts to add a an item to the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <param name="isNewItem">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
+        /// <returns>True if the item was added, False if the item is already contained in the list.</returns>
+        public bool AddToInProgressList(FileOrganizationResult result, bool isNewItem)
+        {
+            if (string.IsNullOrWhiteSpace(result.Id))
+            {
+                result.Id = result.OriginalPath.GetMD5().ToString("N");
+            }
+
+            if (!_inProgressItemIds.TryAdd(result.Id, false))
+            {
+                return false;
+            }
+
+            result.IsInProgress = true;
+
+            if (isNewItem)
+            {
+                EventHelper.FireEventIfNotNull(ItemAdded, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
+            }
+            else
+            {
+                EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Removes an item from the list of currently processed items.
+        /// </summary>
+        /// <param name="result">The result item.</param>
+        /// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
+        public bool RemoveFromInprogressList(FileOrganizationResult result)
+        {
+            bool itemValue;
+            var retval = _inProgressItemIds.TryRemove(result.Id, out itemValue);
+
+            result.IsInProgress = false;
+
+            EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
+
+            return retval;
+        }
+
     }
     }
 }
 }

+ 1 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -149,6 +149,7 @@
     <Compile Include="EntryPoints\UsageReporter.cs" />
     <Compile Include="EntryPoints\UsageReporter.cs" />
     <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
     <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
     <Compile Include="FileOrganization\Extensions.cs" />
     <Compile Include="FileOrganization\Extensions.cs" />
+    <Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
     <Compile Include="FileOrganization\FileOrganizationService.cs" />
     <Compile Include="FileOrganization\FileOrganizationService.cs" />
     <Compile Include="FileOrganization\NameUtils.cs" />
     <Compile Include="FileOrganization\NameUtils.cs" />
     <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
     <Compile Include="FileOrganization\TvFolderOrganizer.cs" />

+ 6 - 0
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -4286,6 +4286,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
             var index = 0;
             var index = 0;
             foreach (var image in images)
             foreach (var image in images)
             {
             {
+                if (string.IsNullOrWhiteSpace(image.Path))
+                {
+                    // Invalid
+                    continue;
+                }
+
                 _saveImagesCommand.GetParameter(0).Value = itemId;
                 _saveImagesCommand.GetParameter(0).Value = itemId;
                 _saveImagesCommand.GetParameter(1).Value = image.Type;
                 _saveImagesCommand.GetParameter(1).Value = image.Type;
                 _saveImagesCommand.GetParameter(2).Value = image.Path;
                 _saveImagesCommand.GetParameter(2).Value = image.Path;

+ 9 - 2
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -1869,10 +1869,17 @@ namespace MediaBrowser.Server.Implementations.Session
             return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
             return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
         }
         }
 
 
-        public Task SendMessageToUserSessions<T>(string userId, string name, T data,
+        public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
+        {
+            var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList();
+
+            return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
+        }
+
+        public Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data,
             CancellationToken cancellationToken)
             CancellationToken cancellationToken)
         {
         {
-            var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && i.ContainsUser(userId)).ToList();
+            var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && userIds.Any(i.ContainsUser)).ToList();
 
 
             var tasks = sessions.Select(session => Task.Run(async () =>
             var tasks = sessions.Select(session => Task.Run(async () =>
             {
             {

+ 6 - 3
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -182,6 +182,9 @@
     <Content Include="dashboard-ui\components\tvproviders\xmltv.template.html">
     <Content Include="dashboard-ui\components\tvproviders\xmltv.template.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
+    <Content Include="dashboard-ui\css\images\throbber.gif">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\userpasswordpage.js">
     <Content Include="dashboard-ui\scripts\userpasswordpage.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -191,6 +194,9 @@
     <Content Include="dashboard-ui\css\dashboard.css">
     <Content Include="dashboard-ui\css\dashboard.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
+    <Content Include="dashboard-ui\css\autoorganizetable.css">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\css\images\logo.png">
     <Content Include="dashboard-ui\css\images\logo.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -992,9 +998,6 @@
     <Content Include="dashboard-ui\scripts\sync.js">
     <Content Include="dashboard-ui\scripts\sync.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\scripts\thememediaplayer.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\scripts\tvupcoming.js">
     <Content Include="dashboard-ui\scripts\tvupcoming.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>