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);