diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs index 860c736ead..36ad27290d 100644 --- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs @@ -63,4 +63,15 @@ namespace MediaBrowser.Controller.Sync /// Task<SyncedFileInfo>. Task SendFile(string path, string[] pathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken); } + + public interface IHasDuplicateCheck + { + /// + /// Allows the duplicate job item. + /// + /// The original. + /// The duplicate. + /// true if XXXX, false otherwise. + bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate); + } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs index db6e8de3f6..2993740d63 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs @@ -50,7 +50,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby { Id = info.listingID.ToString(CultureInfo.InvariantCulture), Name = GetStringValue(info.showName), - EpisodeTitle = GetStringValue(info.episodeTitle), HomePageUrl = GetStringValue(info.webLink), Overview = info.description, IsHD = info.hd, @@ -101,6 +100,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby program.SeriesId = info.seriesID.ToString(CultureInfo.InvariantCulture); program.IsSeries = true; program.IsRepeat = info.repeat; + + program.EpisodeTitle = GetStringValue(info.episodeTitle); + + if (string.Equals(program.Name, program.EpisodeTitle, StringComparison.OrdinalIgnoreCase)) + { + program.EpisodeTitle = null; + } } if (info.starRating > 0) diff --git a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs index 7b1fa4dec2..03485012f0 100644 --- a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs @@ -9,7 +9,7 @@ using System.Linq; namespace MediaBrowser.Server.Implementations.Sync { - public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncQuality + public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncQuality, IHasDuplicateCheck { private readonly IDeviceManager _deviceManager; @@ -104,5 +104,10 @@ namespace MediaBrowser.Server.Implementations.Sync IsConverting = isConverting }; } + + public bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate) + { + return false; + } } } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 04ebcd9034..1061a373e9 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -466,6 +466,32 @@ namespace MediaBrowser.Server.Implementations.Sync return; } + // See if there's already another active job item for the same target + var existingJobItems = _syncManager.GetJobItems(new SyncJobItemQuery + { + AddMetadata = false, + ItemId = jobItem.ItemId, + TargetId = job.TargetId, + Statuses = new[] { SyncJobItemStatus.Converting, SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Synced, SyncJobItemStatus.Transferring } + }); + + var duplicateJobItems = existingJobItems.Items + .Where(i => !string.Equals(i.Id, jobItem.Id, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (duplicateJobItems.Count > 0) + { + var syncProvider = _syncManager.GetSyncProvider(jobItem, job) as IHasDuplicateCheck; + + if (!duplicateJobItems.Any(i => AllowDuplicateJobItem(syncProvider, i, jobItem))) + { + _logger.Debug("Cancelling sync job item because there is already another active job for the same target."); + jobItem.Status = SyncJobItemStatus.Cancelled; + await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); + return; + } + } + var video = item as Video; if (video != null) { @@ -488,6 +514,16 @@ namespace MediaBrowser.Server.Implementations.Sync } } + private bool AllowDuplicateJobItem(IHasDuplicateCheck provider, SyncJobItem original, SyncJobItem duplicate) + { + if (provider != null) + { + return provider.AllowDuplicateJobItem(original, duplicate); + } + + return true; + } + private async Task Sync(SyncJobItem jobItem, SyncJob job, Video item, User user, bool enableConversion, SyncOptions syncOptions, IProgress progress, CancellationToken cancellationToken) { var jobOptions = _syncManager.GetVideoOptions(jobItem, job); diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 5bd35a325f..18fcb4e794 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -1159,6 +1159,21 @@ namespace MediaBrowser.Server.Implementations.Sync return options; } + public ISyncProvider GetSyncProvider(SyncJobItem jobItem, SyncJob job) + { + foreach (var provider in _providers) + { + foreach (var target in GetSyncTargets(provider)) + { + if (string.Equals(target.Id, jobItem.TargetId, StringComparison.OrdinalIgnoreCase)) + { + return provider; + } + } + } + return null; + } + public SyncJobOptions GetVideoOptions(SyncJobItem jobItem, SyncJob job) { var options = GetSyncJobOptions(jobItem.TargetId, job.Profile, job.Quality);