Browse Source

sync updates

Luke Pulverenti 10 years ago
parent
commit
1f1852f3cb

+ 1 - 0
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -130,6 +130,7 @@
     <Compile Include="SearchService.cs" />
     <Compile Include="SearchService.cs" />
     <Compile Include="Session\SessionsService.cs" />
     <Compile Include="Session\SessionsService.cs" />
     <Compile Include="SimilarItemsHelper.cs" />
     <Compile Include="SimilarItemsHelper.cs" />
+    <Compile Include="Sync\SyncJobWebSocketListener.cs" />
     <Compile Include="Sync\SyncJobsWebSocketListener.cs" />
     <Compile Include="Sync\SyncJobsWebSocketListener.cs" />
     <Compile Include="Sync\SyncService.cs" />
     <Compile Include="Sync\SyncService.cs" />
     <Compile Include="System\ActivityLogService.cs" />
     <Compile Include="System\ActivityLogService.cs" />

+ 120 - 0
MediaBrowser.Api/Sync/SyncJobWebSocketListener.cs

@@ -0,0 +1,120 @@
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Sync;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Sync
+{
+    /// <summary>
+    /// Class SessionInfoWebSocketListener
+    /// </summary>
+    class SyncJobWebSocketListener : BasePeriodicWebSocketListener<CompleteSyncJobInfo, WebSocketListenerState>
+    {
+        /// <summary>
+        /// Gets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        protected override string Name
+        {
+            get { return "SyncJob"; }
+        }
+
+        private readonly ISyncManager _syncManager;
+        private string _jobId;
+
+        public SyncJobWebSocketListener(ILogger logger, ISyncManager syncManager)
+            : base(logger)
+        {
+            _syncManager = syncManager;
+            _syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
+            _syncManager.SyncJobUpdated += _syncManager_SyncJobUpdated;
+            _syncManager.SyncJobItemCreated += _syncManager_SyncJobItemCreated;
+            _syncManager.SyncJobItemUpdated += _syncManager_SyncJobItemUpdated;
+        }
+
+        void _syncManager_SyncJobItemUpdated(object sender, GenericEventArgs<SyncJobItem> e)
+        {
+            if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
+            {
+                SendData(false);
+            }
+        }
+
+        void _syncManager_SyncJobItemCreated(object sender, GenericEventArgs<SyncJobItem> e)
+        {
+            if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
+            {
+                SendData(true);
+            }
+        }
+
+        protected override void ParseMessageParams(string[] values)
+        {
+            base.ParseMessageParams(values);
+
+            if (values.Length > 0)
+            {
+                _jobId = values[0];
+            }
+        }
+
+        void _syncManager_SyncJobUpdated(object sender, GenericEventArgs<SyncJob> e)
+        {
+            if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
+            {
+                SendData(false);
+            }
+        }
+
+        void _syncManager_SyncJobCancelled(object sender, GenericEventArgs<SyncJob> e)
+        {
+            if (string.Equals(e.Argument.Id, _jobId, StringComparison.Ordinal))
+            {
+                SendData(true);
+            }
+        }
+
+        /// <summary>
+        /// Gets the data to send.
+        /// </summary>
+        /// <param name="state">The state.</param>
+        /// <returns>Task{SystemInfo}.</returns>
+        protected override Task<CompleteSyncJobInfo> GetDataToSend(WebSocketListenerState state)
+        {
+            var job = _syncManager.GetJob(_jobId);
+            var items = _syncManager.GetJobItems(new SyncJobItemQuery
+            {
+                AddMetadata = true,
+                JobId = _jobId
+            });
+
+            var info = new CompleteSyncJobInfo
+            {
+                Job = job,
+                JobItems = items.Items.ToList()
+            };
+
+            return Task.FromResult(info);
+        }
+
+        protected override bool SendOnTimer
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        protected override void Dispose(bool dispose)
+        {
+            _syncManager.SyncJobCancelled -= _syncManager_SyncJobCancelled;
+            _syncManager.SyncJobUpdated -= _syncManager_SyncJobUpdated;
+
+            base.Dispose(dispose);
+        }
+    }
+}

+ 2 - 2
MediaBrowser.Api/Sync/SyncService.cs

@@ -206,7 +206,7 @@ namespace MediaBrowser.Api.Sync
         {
         {
             var jobItem = _syncManager.GetJobItem(request.Id);
             var jobItem = _syncManager.GetJobItem(request.Id);
 
 
-            if (jobItem.Status != SyncJobItemStatus.Transferring)
+            if (jobItem.Status < SyncJobItemStatus.ReadyToTransfer)
             {
             {
                 throw new ArgumentException("The job item is not yet ready for transfer.");
                 throw new ArgumentException("The job item is not yet ready for transfer.");
             }
             }
@@ -286,7 +286,7 @@ namespace MediaBrowser.Api.Sync
         {
         {
             var jobItem = _syncManager.GetJobItem(request.Id);
             var jobItem = _syncManager.GetJobItem(request.Id);
 
 
-            if (jobItem.Status != SyncJobItemStatus.Transferring)
+            if (jobItem.Status < SyncJobItemStatus.ReadyToTransfer)
             {
             {
                 throw new ArgumentException("The job item is not yet ready for transfer.");
                 throw new ArgumentException("The job item is not yet ready for transfer.");
             }
             }

+ 5 - 0
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -846,6 +846,11 @@ namespace MediaBrowser.Controller.Entities
             BaseItem queryParent,
             BaseItem queryParent,
             User user)
             User user)
         {
         {
+            if (items == null)
+            {
+                throw new ArgumentNullException("items");
+            }
+
             if (CollapseBoxSetItems(query, queryParent, user))
             if (CollapseBoxSetItems(query, queryParent, user))
             {
             {
                 items = BaseItem.CollectionManager.CollapseItemsWithinBoxSets(items, user);
                 items = BaseItem.CollectionManager.CollapseItemsWithinBoxSets(items, user);

+ 6 - 1
MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs

@@ -81,12 +81,17 @@ namespace MediaBrowser.Controller.MediaEncoding
             VideoCodec = info.VideoCodec;
             VideoCodec = info.VideoCodec;
             VideoBitRate = info.VideoBitrate;
             VideoBitRate = info.VideoBitrate;
             AudioStreamIndex = info.AudioStreamIndex;
             AudioStreamIndex = info.AudioStreamIndex;
-            SubtitleStreamIndex = info.SubtitleStreamIndex;
             MaxRefFrames = info.MaxRefFrames;
             MaxRefFrames = info.MaxRefFrames;
             MaxVideoBitDepth = info.MaxVideoBitDepth;
             MaxVideoBitDepth = info.MaxVideoBitDepth;
             SubtitleMethod = info.SubtitleDeliveryMethod;
             SubtitleMethod = info.SubtitleDeliveryMethod;
             Cabac = info.Cabac;
             Cabac = info.Cabac;
             Context = info.Context;
             Context = info.Context;
+
+            if (info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ||
+                info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed)
+            {
+                SubtitleStreamIndex = info.SubtitleStreamIndex;
+            }
         }
         }
     }
     }
 }
 }

+ 5 - 3
MediaBrowser.Controller/Sync/ISyncManager.cs

@@ -15,7 +15,9 @@ namespace MediaBrowser.Controller.Sync
         event EventHandler<GenericEventArgs<SyncJobCreationResult>> SyncJobCreated;
         event EventHandler<GenericEventArgs<SyncJobCreationResult>> SyncJobCreated;
         event EventHandler<GenericEventArgs<SyncJob>> SyncJobCancelled;
         event EventHandler<GenericEventArgs<SyncJob>> SyncJobCancelled;
         event EventHandler<GenericEventArgs<SyncJob>> SyncJobUpdated;
         event EventHandler<GenericEventArgs<SyncJob>> SyncJobUpdated;
-          
+        event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemUpdated;
+        event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCreated;
+
         /// <summary>
         /// <summary>
         /// Creates the job.
         /// Creates the job.
         /// </summary>
         /// </summary>
@@ -35,7 +37,7 @@ namespace MediaBrowser.Controller.Sync
         /// <param name="query">The query.</param>
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;SyncJobItem&gt;.</returns>
         /// <returns>QueryResult&lt;SyncJobItem&gt;.</returns>
         QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
         QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query);
-        
+
         /// <summary>
         /// <summary>
         /// Gets the job.
         /// Gets the job.
         /// </summary>
         /// </summary>
@@ -63,7 +65,7 @@ namespace MediaBrowser.Controller.Sync
         /// <param name="id">The identifier.</param>
         /// <param name="id">The identifier.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         Task CancelJobItem(string id);
         Task CancelJobItem(string id);
-        
+
         /// <summary>
         /// <summary>
         /// Cancels the job.
         /// Cancels the job.
         /// </summary>
         /// </summary>

+ 3 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -1043,6 +1043,9 @@
     <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
     <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
       <Link>Session\UserDataChangeInfo.cs</Link>
       <Link>Session\UserDataChangeInfo.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Sync\CompleteSyncJobInfo.cs">
+      <Link>Sync\CompleteSyncJobInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Sync\DeviceFileInfo.cs">
     <Compile Include="..\MediaBrowser.Model\Sync\DeviceFileInfo.cs">
       <Link>Sync\DeviceFileInfo.cs</Link>
       <Link>Sync\DeviceFileInfo.cs</Link>
     </Compile>
     </Compile>

+ 3 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -1002,6 +1002,9 @@
     <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
     <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
       <Link>Session\UserDataChangeInfo.cs</Link>
       <Link>Session\UserDataChangeInfo.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Sync\CompleteSyncJobInfo.cs">
+      <Link>Sync\CompleteSyncJobInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Sync\DeviceFileInfo.cs">
     <Compile Include="..\MediaBrowser.Model\Sync\DeviceFileInfo.cs">
       <Link>Sync\DeviceFileInfo.cs</Link>
       <Link>Sync\DeviceFileInfo.cs</Link>
     </Compile>
     </Compile>

+ 15 - 0
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -893,6 +893,21 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="keepExistingAuth">if set to <c>true</c> [keep existing authentication].</param>
         /// <param name="keepExistingAuth">if set to <c>true</c> [keep existing authentication].</param>
         void ChangeServerLocation(string address, bool keepExistingAuth = false);
         void ChangeServerLocation(string address, bool keepExistingAuth = false);
 
 
+        /// <summary>
+        /// Starts the receiving synchronize job updates.
+        /// </summary>
+        /// <param name="intervalMs">The interval ms.</param>
+        /// <param name="jobId">The job identifier.</param>
+        /// <returns>Task.</returns>
+        Task StartReceivingSyncJobUpdates(int intervalMs, string jobId);
+
+        /// <summary>
+        /// Stops the receiving synchronize job updates.
+        /// </summary>
+        /// <param name="intervalMs">The interval ms.</param>
+        /// <returns>Task.</returns>
+        Task StopReceivingSyncJobUpdates(int intervalMs);
+
         /// <summary>
         /// <summary>
         /// Starts the receiving synchronize jobs updates.
         /// Starts the receiving synchronize jobs updates.
         /// </summary>
         /// </summary>

+ 4 - 0
MediaBrowser.Model/ApiClient/IServerEvents.cs

@@ -144,5 +144,9 @@ namespace MediaBrowser.Model.ApiClient
         /// Occurs when [synchronize jobs updated].
         /// Occurs when [synchronize jobs updated].
         /// </summary>
         /// </summary>
         event EventHandler<GenericEventArgs<List<SyncJob>>> SyncJobsUpdated;
         event EventHandler<GenericEventArgs<List<SyncJob>>> SyncJobsUpdated;
+        /// <summary>
+        /// Occurs when [synchronize job updated].
+        /// </summary>
+        event EventHandler<GenericEventArgs<CompleteSyncJobInfo>> SyncJobUpdated;
     }
     }
 }
 }

+ 1 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -368,6 +368,7 @@
     <Compile Include="Session\TranscodingInfo.cs" />
     <Compile Include="Session\TranscodingInfo.cs" />
     <Compile Include="Session\UserDataChangeInfo.cs" />
     <Compile Include="Session\UserDataChangeInfo.cs" />
     <Compile Include="Devices\ContentUploadHistory.cs" />
     <Compile Include="Devices\ContentUploadHistory.cs" />
+    <Compile Include="Sync\CompleteSyncJobInfo.cs" />
     <Compile Include="Sync\DeviceFileInfo.cs" />
     <Compile Include="Sync\DeviceFileInfo.cs" />
     <Compile Include="Sync\ItemFIleInfo.cs" />
     <Compile Include="Sync\ItemFIleInfo.cs" />
     <Compile Include="Sync\ItemFileType.cs" />
     <Compile Include="Sync\ItemFileType.cs" />

+ 15 - 0
MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs

@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.Sync
+{
+    public class CompleteSyncJobInfo
+    {
+        public SyncJob Job { get; set; }
+        public List<SyncJobItem> JobItems { get; set; }
+
+        public CompleteSyncJobInfo()
+        {
+            JobItems = new List<SyncJobItem>();
+        }
+    }
+}

+ 6 - 5
MediaBrowser.Model/Sync/SyncJobItemStatus.cs

@@ -5,10 +5,11 @@ namespace MediaBrowser.Model.Sync
     {
     {
         Queued = 0,
         Queued = 0,
         Converting = 1,
         Converting = 1,
-        Transferring = 2,
-        Synced = 3,
-        RemovedFromDevice = 4,
-        Failed = 5,
-        Cancelled = 6
+        ReadyToTransfer = 2,
+        Transferring = 3,
+        Synced = 4,
+        RemovedFromDevice = 5,
+        Failed = 6,
+        Cancelled = 7
     }
     }
 }
 }

+ 6 - 5
MediaBrowser.Model/Sync/SyncJobStatus.cs

@@ -5,10 +5,11 @@ namespace MediaBrowser.Model.Sync
     {
     {
         Queued = 0,
         Queued = 0,
         Converting = 1,
         Converting = 1,
-        Transferring = 2,
-        Completed = 3,
-        CompletedWithError = 4,
-        Failed = 5,
-        Cancelled = 6
+        ReadyToTransfer = 2,
+        Transferring = 3,
+        Completed = 4,
+        CompletedWithError = 5,
+        Failed = 6,
+        Cancelled = 7
     }
     }
 }
 }

+ 2 - 1
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -162,7 +162,8 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                 {
                     SyncJobItemStatus.Converting,
                     SyncJobItemStatus.Converting,
                     SyncJobItemStatus.Queued,
                     SyncJobItemStatus.Queued,
-                    SyncJobItemStatus.Transferring
+                    SyncJobItemStatus.Transferring,
+                    SyncJobItemStatus.ReadyToTransfer
                 }
                 }
             });
             });
 
 

+ 5 - 0
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -517,6 +517,11 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
         public IEnumerable<BaseItem> ReplaceVideosWithPrimaryVersions(IEnumerable<BaseItem> items)
         public IEnumerable<BaseItem> ReplaceVideosWithPrimaryVersions(IEnumerable<BaseItem> items)
         {
         {
+            if (items == null)
+            {
+                throw new ArgumentNullException("items");
+            }
+
             var dict = new Dictionary<Guid, BaseItem>();
             var dict = new Dictionary<Guid, BaseItem>();
 
 
             foreach (var item in items)
             foreach (var item in items)

+ 3 - 2
MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json

@@ -53,6 +53,7 @@
     "SyncJobStatusFailed": "Failed",
     "SyncJobStatusFailed": "Failed",
     "SyncJobStatusCancelled": "Cancelled",
     "SyncJobStatusCancelled": "Cancelled",
     "SyncJobStatusCompleted": "Synced",
     "SyncJobStatusCompleted": "Synced",
+    "SyncJobStatusReadyToTransfer": "Ready to Transfer",
     "SyncJobStatusTransferring": "Transferring",
     "SyncJobStatusTransferring": "Transferring",
     "SyncJobStatusCompletedWithError": "Synced with errors",
     "SyncJobStatusCompletedWithError": "Synced with errors",
     "LabelCollection": "Collection",
     "LabelCollection": "Collection",
@@ -216,8 +217,8 @@
     "MessageConfirmRestart": "Are you sure you wish to restart Media Browser Server?",
     "MessageConfirmRestart": "Are you sure you wish to restart Media Browser Server?",
     "MessageConfirmShutdown": "Are you sure you wish to shutdown Media Browser Server?",
     "MessageConfirmShutdown": "Are you sure you wish to shutdown Media Browser Server?",
     "ButtonUpdateNow": "Update Now",
     "ButtonUpdateNow": "Update Now",
-    "ValueItemCount":  "{0} item",
-    "ValueItemCountPlural":  "{0} items",
+    "ValueItemCount": "{0} item",
+    "ValueItemCountPlural": "{0} items",
     "NewVersionOfSomethingAvailable": "A new version of {0} is available!",
     "NewVersionOfSomethingAvailable": "A new version of {0} is available!",
     "VersionXIsAvailableForDownload": "Version {0} is now available for download.",
     "VersionXIsAvailableForDownload": "Version {0} is now available for download.",
     "LabelVersionNumber": "Version {0}",
     "LabelVersionNumber": "Version {0}",

+ 32 - 23
MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs

@@ -29,7 +29,7 @@ namespace MediaBrowser.Server.Implementations.Sync
     {
     {
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly ISyncRepository _syncRepo;
         private readonly ISyncRepository _syncRepo;
-        private readonly ISyncManager _syncManager;
+        private readonly SyncManager _syncManager;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IUserManager _userManager;
         private readonly IUserManager _userManager;
         private readonly ITVSeriesManager _tvSeriesManager;
         private readonly ITVSeriesManager _tvSeriesManager;
@@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.Sync
         private readonly IConfigurationManager _config;
         private readonly IConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
 
 
-        public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder, IConfigurationManager config, IFileSystem fileSystem)
+        public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo, SyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder, IConfigurationManager config, IFileSystem fileSystem)
         {
         {
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _syncRepo = syncRepo;
             _syncRepo = syncRepo;
@@ -106,6 +106,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                 };
                 };
 
 
                 await _syncRepo.Create(jobItem).ConfigureAwait(false);
                 await _syncRepo.Create(jobItem).ConfigureAwait(false);
+                _syncManager.OnSyncJobItemCreated(jobItem);
 
 
                 jobItems.Add(jobItem);
                 jobItems.Add(jobItem);
             }
             }
@@ -184,11 +185,19 @@ namespace MediaBrowser.Server.Implementations.Sync
             {
             {
                 job.Status = SyncJobStatus.Cancelled;
                 job.Status = SyncJobStatus.Cancelled;
             }
             }
+            else if (jobItems.All(i => i.Status == SyncJobItemStatus.ReadyToTransfer))
+            {
+                job.Status = SyncJobStatus.ReadyToTransfer;
+            }
+            else if (jobItems.All(i => i.Status == SyncJobItemStatus.Transferring))
+            {
+                job.Status = SyncJobStatus.Transferring;
+            }
             else if (jobItems.Any(i => i.Status == SyncJobItemStatus.Converting))
             else if (jobItems.Any(i => i.Status == SyncJobItemStatus.Converting))
             {
             {
                 job.Status = SyncJobStatus.Converting;
                 job.Status = SyncJobStatus.Converting;
             }
             }
-            else if (pct >= 100)
+            else if (jobItems.All(i => i.Status == SyncJobItemStatus.Cancelled || i.Status == SyncJobItemStatus.Failed || i.Status == SyncJobItemStatus.Synced || i.Status == SyncJobItemStatus.RemovedFromDevice))
             {
             {
                 if (jobItems.Any(i => i.Status == SyncJobItemStatus.Failed))
                 if (jobItems.Any(i => i.Status == SyncJobItemStatus.Failed))
                 {
                 {
@@ -201,12 +210,12 @@ namespace MediaBrowser.Server.Implementations.Sync
             }
             }
             else
             else
             {
             {
-                job.Status = SyncJobStatus.Transferring;
+                job.Status = SyncJobStatus.Queued;
             }
             }
 
 
             await _syncRepo.Update(job).ConfigureAwait(false);
             await _syncRepo.Update(job).ConfigureAwait(false);
 
 
-            ((SyncManager)_syncManager).OnSyncJobUpdated(job);
+            _syncManager.OnSyncJobUpdated(job);
         }
         }
 
 
         public async Task<IEnumerable<BaseItem>> GetItemsForSync(SyncCategory? category, string parentId, IEnumerable<string> itemIds, User user, bool unwatchedOnly)
         public async Task<IEnumerable<BaseItem>> GetItemsForSync(SyncCategory? category, string parentId, IEnumerable<string> itemIds, User user, bool unwatchedOnly)
@@ -414,7 +423,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             {
             {
                 jobItem.Status = SyncJobItemStatus.Failed;
                 jobItem.Status = SyncJobItemStatus.Failed;
                 _logger.Error("Unable to locate library item for JobItem {0}, ItemId {1}", jobItem.Id, jobItem.ItemId);
                 _logger.Error("Unable to locate library item for JobItem {0}, ItemId {1}", jobItem.Id, jobItem.ItemId);
-                await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                 return;
                 return;
             }
             }
 
 
@@ -423,7 +432,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             {
             {
                 jobItem.Status = SyncJobItemStatus.Failed;
                 jobItem.Status = SyncJobItemStatus.Failed;
                 _logger.Error("Unable to locate SyncTarget for JobItem {0}, SyncTargetId {1}", jobItem.Id, jobItem.TargetId);
                 _logger.Error("Unable to locate SyncTarget for JobItem {0}, SyncTargetId {1}", jobItem.Id, jobItem.TargetId);
-                await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                 return;
                 return;
             }
             }
 
 
@@ -490,7 +499,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             if (streamInfo.PlayMethod == PlayMethod.Transcode)
             if (streamInfo.PlayMethod == PlayMethod.Transcode)
             {
             {
                 // Save the job item now since conversion could take a while
                 // Save the job item now since conversion could take a while
-                await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                 await UpdateJobStatus(job).ConfigureAwait(false);
                 await UpdateJobStatus(job).ConfigureAwait(false);
 
 
                 try
                 try
@@ -504,7 +513,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                         if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
                         if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
                         {
                         {
                             jobItem.Progress = pct / 2;
                             jobItem.Progress = pct / 2;
-                            await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                             await UpdateJobStatus(job).ConfigureAwait(false);
                             await UpdateJobStatus(job).ConfigureAwait(false);
                         }
                         }
                     });
                     });
@@ -528,7 +537,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
 
                 if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
                 if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
                 {
                 {
-                    await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                    await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }
 
 
@@ -555,14 +564,14 @@ namespace MediaBrowser.Server.Implementations.Sync
             if (externalSubs.Count > 0)
             if (externalSubs.Count > 0)
             {
             {
                 // Save the job item now since conversion could take a while
                 // Save the job item now since conversion could take a while
-                await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
 
 
                 await ConvertSubtitles(jobItem, externalSubs, streamInfo, cancellationToken).ConfigureAwait(false);
                 await ConvertSubtitles(jobItem, externalSubs, streamInfo, cancellationToken).ConfigureAwait(false);
             }
             }
 
 
             jobItem.Progress = 50;
             jobItem.Progress = 50;
-            jobItem.Status = SyncJobItemStatus.Transferring;
-            await _syncRepo.Update(jobItem).ConfigureAwait(false);
+            jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
+            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
         }
         }
 
 
         private bool RequiresExtraction(SubtitleStreamInfo stream, MediaSourceInfo mediaSource)
         private bool RequiresExtraction(SubtitleStreamInfo stream, MediaSourceInfo mediaSource)
@@ -666,7 +675,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                 }
                 }
 
 
                 jobItem.Status = SyncJobItemStatus.Converting;
                 jobItem.Status = SyncJobItemStatus.Converting;
-                await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                 await UpdateJobStatus(job).ConfigureAwait(false);
                 await UpdateJobStatus(job).ConfigureAwait(false);
 
 
                 try
                 try
@@ -680,7 +689,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                         if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
                         if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
                         {
                         {
                             jobItem.Progress = pct / 2;
                             jobItem.Progress = pct / 2;
-                            await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                             await UpdateJobStatus(job).ConfigureAwait(false);
                             await UpdateJobStatus(job).ConfigureAwait(false);
                         }
                         }
                     });
                     });
@@ -704,7 +713,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
 
                 if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
                 if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
                 {
                 {
-                    await _syncRepo.Update(jobItem).ConfigureAwait(false);
+                    await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                     return;
                     return;
                 }
                 }
 
 
@@ -729,8 +738,8 @@ namespace MediaBrowser.Server.Implementations.Sync
             }
             }
 
 
             jobItem.Progress = 50;
             jobItem.Progress = 50;
-            jobItem.Status = SyncJobItemStatus.Transferring;
-            await _syncRepo.Update(jobItem).ConfigureAwait(false);
+            jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
+            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
         }
         }
 
 
         private async Task Sync(SyncJobItem jobItem, Photo item, DeviceProfile profile, CancellationToken cancellationToken)
         private async Task Sync(SyncJobItem jobItem, Photo item, DeviceProfile profile, CancellationToken cancellationToken)
@@ -738,17 +747,17 @@ namespace MediaBrowser.Server.Implementations.Sync
             jobItem.OutputPath = item.Path;
             jobItem.OutputPath = item.Path;
 
 
             jobItem.Progress = 50;
             jobItem.Progress = 50;
-            jobItem.Status = SyncJobItemStatus.Transferring;
-            await _syncRepo.Update(jobItem).ConfigureAwait(false);
+            jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
+            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
         }
         }
 
 
         private async Task SyncGeneric(SyncJobItem jobItem, BaseItem item, CancellationToken cancellationToken)
         private async Task SyncGeneric(SyncJobItem jobItem, BaseItem item, CancellationToken cancellationToken)
         {
         {
             jobItem.OutputPath = item.Path;
             jobItem.OutputPath = item.Path;
-
+            
             jobItem.Progress = 50;
             jobItem.Progress = 50;
-            jobItem.Status = SyncJobItemStatus.Transferring;
-            await _syncRepo.Update(jobItem).ConfigureAwait(false);
+            jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
+            await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
         }
         }
 
 
         private async Task<string> DownloadFile(SyncJobItem jobItem, MediaSourceInfo mediaSource, CancellationToken cancellationToken)
         private async Task<string> DownloadFile(SyncJobItem jobItem, MediaSourceInfo mediaSource, CancellationToken cancellationToken)

+ 39 - 8
MediaBrowser.Server.Implementations/Sync/SyncManager.cs

@@ -52,6 +52,8 @@ namespace MediaBrowser.Server.Implementations.Sync
         public event EventHandler<GenericEventArgs<SyncJobCreationResult>> SyncJobCreated;
         public event EventHandler<GenericEventArgs<SyncJobCreationResult>> SyncJobCreated;
         public event EventHandler<GenericEventArgs<SyncJob>> SyncJobCancelled;
         public event EventHandler<GenericEventArgs<SyncJob>> SyncJobCancelled;
         public event EventHandler<GenericEventArgs<SyncJob>> SyncJobUpdated;
         public event EventHandler<GenericEventArgs<SyncJob>> SyncJobUpdated;
+        public event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemUpdated;
+        public event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCreated;
 
 
         public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func<IDtoService> dtoService, IApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func<IMediaEncoder> mediaEncoder, IFileSystem fileSystem, Func<ISubtitleEncoder> subtitleEncoder, IConfigurationManager config)
         public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func<IDtoService> dtoService, IApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func<IMediaEncoder> mediaEncoder, IFileSystem fileSystem, Func<ISubtitleEncoder> subtitleEncoder, IConfigurationManager config)
         {
         {
@@ -202,6 +204,32 @@ namespace MediaBrowser.Server.Implementations.Sync
             }
             }
         }
         }
 
 
+        internal async Task UpdateSyncJobItemInternal(SyncJobItem jobItem)
+        {
+            await _repo.Update(jobItem).ConfigureAwait(false);
+            
+            if (SyncJobUpdated != null)
+            {
+                EventHelper.FireEventIfNotNull(SyncJobItemUpdated, this, new GenericEventArgs<SyncJobItem>
+                {
+                    Argument = jobItem
+
+                }, _logger);
+            }
+        }
+
+        internal void OnSyncJobItemCreated(SyncJobItem job)
+        {
+            if (SyncJobUpdated != null)
+            {
+                EventHelper.FireEventIfNotNull(SyncJobItemCreated, this, new GenericEventArgs<SyncJobItem>
+                {
+                    Argument = job
+
+                }, _logger);
+            }
+        }
+
         public async Task<QueryResult<SyncJob>> GetJobs(SyncJobQuery query)
         public async Task<QueryResult<SyncJob>> GetJobs(SyncJobQuery query)
         {
         {
             var result = _repo.GetJobs(query);
             var result = _repo.GetJobs(query);
@@ -504,7 +532,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                 }
                 }
             }
             }
 
 
-            await _repo.Update(jobItem).ConfigureAwait(false);
+            await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
 
 
             var processor = GetSyncJobProcessor();
             var processor = GetSyncJobProcessor();
 
 
@@ -610,7 +638,10 @@ namespace MediaBrowser.Server.Implementations.Sync
             var jobItemResult = GetJobItems(new SyncJobItemQuery
             var jobItemResult = GetJobItems(new SyncJobItemQuery
             {
             {
                 TargetId = targetId,
                 TargetId = targetId,
-                Statuses = new List<SyncJobItemStatus> { SyncJobItemStatus.Transferring }
+                Statuses = new List<SyncJobItemStatus>
+                {
+                    SyncJobItemStatus.ReadyToTransfer
+                }
             });
             });
 
 
             return jobItemResult.Items
             return jobItemResult.Items
@@ -669,7 +700,7 @@ namespace MediaBrowser.Server.Implementations.Sync
                 {
                 {
                     // Content is no longer on the device
                     // Content is no longer on the device
                     jobItem.Status = SyncJobItemStatus.RemovedFromDevice;
                     jobItem.Status = SyncJobItemStatus.RemovedFromDevice;
-                    await _repo.Update(jobItem).ConfigureAwait(false);
+                    await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
                 }
                 }
             }
             }
 
 
@@ -761,7 +792,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             jobItem.Progress = 0;
             jobItem.Progress = 0;
             jobItem.IsMarkedForRemoval = false;
             jobItem.IsMarkedForRemoval = false;
 
 
-            await _repo.Update(jobItem).ConfigureAwait(false);
+            await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
 
 
             var processor = GetSyncJobProcessor();
             var processor = GetSyncJobProcessor();
 
 
@@ -772,7 +803,7 @@ namespace MediaBrowser.Server.Implementations.Sync
         {
         {
             var jobItem = _repo.GetJobItem(id);
             var jobItem = _repo.GetJobItem(id);
 
 
-            if (jobItem.Status != SyncJobItemStatus.Queued && jobItem.Status != SyncJobItemStatus.Transferring && jobItem.Status != SyncJobItemStatus.Converting)
+            if (jobItem.Status != SyncJobItemStatus.Queued && jobItem.Status != SyncJobItemStatus.ReadyToTransfer && jobItem.Status != SyncJobItemStatus.Converting)
             {
             {
                 throw new ArgumentException("Operation is not valid for this job item");
                 throw new ArgumentException("Operation is not valid for this job item");
             }
             }
@@ -781,7 +812,7 @@ namespace MediaBrowser.Server.Implementations.Sync
             jobItem.Progress = 0;
             jobItem.Progress = 0;
             jobItem.IsMarkedForRemoval = true;
             jobItem.IsMarkedForRemoval = true;
 
 
-            await _repo.Update(jobItem).ConfigureAwait(false);
+            await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
 
 
             var processor = GetSyncJobProcessor();
             var processor = GetSyncJobProcessor();
 
 
@@ -814,7 +845,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
 
             jobItem.IsMarkedForRemoval = true;
             jobItem.IsMarkedForRemoval = true;
 
 
-            await _repo.Update(jobItem).ConfigureAwait(false);
+            await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
 
 
             var processor = GetSyncJobProcessor();
             var processor = GetSyncJobProcessor();
 
 
@@ -832,7 +863,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
 
             jobItem.IsMarkedForRemoval = false;
             jobItem.IsMarkedForRemoval = false;
 
 
-            await _repo.Update(jobItem).ConfigureAwait(false);
+            await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
 
 
             var processor = GetSyncJobProcessor();
             var processor = GetSyncJobProcessor();
 
 

+ 1 - 1
MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs

@@ -60,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
 
         public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
         public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
         {
         {
-            return new SyncJobProcessor(_libraryManager, _syncRepo, _syncManager, _logger, _userManager, _tvSeriesManager, _mediaEncoder, _subtitleEncoder, _config, _fileSystem)
+            return new SyncJobProcessor(_libraryManager, _syncRepo, (SyncManager)_syncManager, _logger, _userManager, _tvSeriesManager, _mediaEncoder, _subtitleEncoder, _config, _fileSystem)
                 .Sync(progress, cancellationToken);
                 .Sync(progress, cancellationToken);
         }
         }
 
 

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.559</version>
+        <version>3.0.560</version>
         <title>MediaBrowser.Common.Internal</title>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.559" />
+            <dependency id="MediaBrowser.Common" version="3.0.560" />
             <dependency id="NLog" version="3.1.0.0" />
             <dependency id="NLog" version="3.1.0.0" />
             <dependency id="SimpleInjector" version="2.6.1" />
             <dependency id="SimpleInjector" version="2.6.1" />
         </dependencies>
         </dependencies>

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Common</id>
         <id>MediaBrowser.Common</id>
-        <version>3.0.559</version>
+        <version>3.0.560</version>
         <title>MediaBrowser.Common</title>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>

+ 1 - 1
Nuget/MediaBrowser.Model.Signed.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Model.Signed</id>
         <id>MediaBrowser.Model.Signed</id>
-        <version>3.0.559</version>
+        <version>3.0.560</version>
         <title>MediaBrowser.Model - Signed Edition</title>
         <title>MediaBrowser.Model - Signed Edition</title>
         <authors>Media Browser Team</authors>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
     <metadata>
         <id>MediaBrowser.Server.Core</id>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.559</version>
+        <version>3.0.560</version>
         <title>Media Browser.Server.Core</title>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.559" />
+            <dependency id="MediaBrowser.Common" version="3.0.560" />
         </dependencies>
         </dependencies>
     </metadata>
     </metadata>
     <files>
     <files>